From a77b63c56943ebd0046f738e6abbea4c85dc65b6 Mon Sep 17 00:00:00 2001 From: Severin Date: Fri, 4 Jan 2019 21:40:16 +0100 Subject: UI: Preferences Redesign Part 2 (Part 1 was 00963afc14978b) Does the following changes visible to users: * Use panels and sub-panels for more structured & logical grouping * Re-organized options more logically than before (see images in D4148) * Use flow layout (single column by default). * New layout uses horizontal margin if there's enough space. * Change size of Preferences window to suit new layout. * Move keymap related options from "Input" into own section. * Own, left-bottom aligned region for Save Preferences button. * Adjustments of names, tooltips & icons. * Move buttons from header into the main region (except editor switch). * Hide Preferences header when opened in temporary window. * Use full area width for header. * Don't use slider but regular number widget for UI scale. * Gray out animation player path option if player isn't "Custom" Internal changes: * Rearrange RNA properties to match changed UI structure. * Introduces new "EXECUTE" region type, see reasoning in D3982. * Changes to panel layout and AZone code for dynamic panel region. * Bumps subversion and does versioning for new regions. RNA changes are documented in the release notes: https://wiki.blender.org/wiki/Reference/Release_Notes/2.80/Python_API/Preferences_API Design & implementation mostly done by @billreynish and myself. I recommend checking out the screenshots posted by William: https://developer.blender.org/D4148#93787 Reviewed By: brecht Maniphest Tasks: T54115 Differential Revision: https://developer.blender.org/D4148 --- release/datafiles/userdef/userdef_default_theme.c | 3 +- release/scripts/modules/bpy/utils/__init__.py | 2 +- release/scripts/modules/rna_keymap_ui.py | 27 +- release/scripts/startup/bl_ui/space_userpref.py | 1983 +++++++++++++-------- 4 files changed, 1235 insertions(+), 780 deletions(-) (limited to 'release') diff --git a/release/datafiles/userdef/userdef_default_theme.c b/release/datafiles/userdef/userdef_default_theme.c index 6fe61c60470..7a7f804bbed 100644 --- a/release/datafiles/userdef/userdef_default_theme.c +++ b/release/datafiles/userdef/userdef_default_theme.c @@ -821,7 +821,8 @@ const bTheme U_theme_default = { .button_title = RGBA(0xffffffff), .button_text = RGBA(0xe5e5e5ff), .button_text_hi = RGBA(0xffffffff), - .navigation_bar = RGBA(0x373737ff), + .navigation_bar = RGBA(0x4b4b4bff), + .execution_buts = RGBA(0x4b4b4bff), .panelcolors = { .header = RGBA(0x42424200), .back = RGBA(0x333333b3), diff --git a/release/scripts/modules/bpy/utils/__init__.py b/release/scripts/modules/bpy/utils/__init__.py index 5fbff2eb51b..b4c8c0899ac 100644 --- a/release/scripts/modules/bpy/utils/__init__.py +++ b/release/scripts/modules/bpy/utils/__init__.py @@ -572,7 +572,7 @@ def preset_find(name, preset_path, display_name=False, ext=".py"): def keyconfig_init(): # Key configuration initialization and refresh, called from the Blender # window manager on startup and refresh. - active_config = _preferences.inputs.active_keyconfig + active_config = _preferences.keymap.active_keyconfig # Load the default key configuration. default_filepath = preset_find("blender", "keyconfig") diff --git a/release/scripts/modules/rna_keymap_ui.py b/release/scripts/modules/rna_keymap_ui.py index 3a44745e01b..268bcb6a7be 100644 --- a/release/scripts/modules/rna_keymap_ui.py +++ b/release/scripts/modules/rna_keymap_ui.py @@ -372,15 +372,24 @@ def draw_keymaps(context, layout): subcol = subsplit.column() col = subcol.column() - row = col.row(align=True) # row.prop_search(wm.keyconfigs, "active", wm, "keyconfigs", text="Key Config") text = bpy.path.display_name(kc_active.name) if not text: text = "Blender (default)" - row.menu("USERPREF_MT_keyconfigs", text=text) - row.operator("wm.keyconfig_preset_add", text="", icon='ADD') - row.operator("wm.keyconfig_preset_add", text="", icon='REMOVE').remove_active = True + + row = col.row() + + row.operator("wm.keyconfig_import", text="Import...", icon='IMPORT') + row.operator("wm.keyconfig_export", text="Export...", icon='EXPORT') + + row.separator() + + rowsub = row.row(align=True) + + rowsub.menu("USERPREF_MT_keyconfigs", text=text) + rowsub.operator("wm.keyconfig_preset_add", text="", icon='ADD') + rowsub.operator("wm.keyconfig_preset_add", text="", icon='REMOVE').remove_active = True # layout.context_pointer_set("keyconfig", wm.keyconfigs.active) # row.operator("wm.keyconfig_remove", text="", icon='X') @@ -413,14 +422,14 @@ def draw_keymaps(context, layout): box = col.box() row = box.row(align=True) - prefs = context.preferences - inputs = prefs.inputs - show_ui_keyconfig = inputs.show_ui_keyconfig + pref = context.preferences + keymappref = pref.keymap + show_ui_keyconfig = keymappref.show_ui_keyconfig row.prop( - inputs, + keymappref, "show_ui_keyconfig", text="", - icon='TRIA_DOWN' if show_ui_keyconfig else 'TRIA_RIGHT', + icon='DISCLOSURE_TRI_DOWN' if show_ui_keyconfig else 'DISCLOSURE_TRI_RIGHT', emboss=False, ) row.label(text="Preferences") diff --git a/release/scripts/startup/bl_ui/space_userpref.py b/release/scripts/startup/bl_ui/space_userpref.py index 7dacb8d9db6..a115213fc2a 100644 --- a/release/scripts/startup/bl_ui/space_userpref.py +++ b/release/scripts/startup/bl_ui/space_userpref.py @@ -32,34 +32,13 @@ class USERPREF_HT_header(Header): def draw(self, context): layout = self.layout + layout.operator_context = 'EXEC_AREA' layout.template_header() - prefs = context.preferences - - if prefs.active_section == 'INPUT': - layout.operator("wm.keyconfig_import", icon='IMPORT') - layout.operator("wm.keyconfig_export", icon='EXPORT') - elif prefs.active_section == 'ADDONS': - layout.operator("wm.addon_install", icon='FILEBROWSER') - layout.operator("wm.addon_refresh", icon='FILE_REFRESH') - layout.menu("USERPREF_MT_addons_online_resources") - elif prefs.active_section == 'LIGHTS': - layout.operator("wm.studiolight_install", text="Install MatCap").type = 'MATCAP' - layout.operator("wm.studiolight_install", text="Install LookDev HDRI").type = 'WORLD' - layout.operator("wm.studiolight_install", text="Install Studio Light").type = 'STUDIO' - elif prefs.active_section == 'THEMES': - layout.operator("wm.theme_install", icon='FILEBROWSER') - layout.operator("ui.reset_default_theme", icon='LOOP_BACK') - - layout.separator_spacer() - - layout.operator_context = 'EXEC_AREA' - layout.operator("wm.save_userpref") - class USERPREF_PT_navigation(Panel): - bl_label = "" + bl_label = "Preferences Navigation" bl_space_type = 'PREFERENCES' bl_region_type = 'NAVIGATION_BAR' bl_options = {'HIDE_HEADER'} @@ -76,309 +55,515 @@ class USERPREF_PT_navigation(Panel): col.prop(prefs, "active_section", expand=True) -class USERPREF_PT_interface(Panel): +class USERPREF_PT_save_preferences(Panel): + bl_label = "Save Preferences" bl_space_type = 'PREFERENCES' - bl_label = "Interface" - bl_region_type = 'WINDOW' + bl_region_type = 'EXECUTE' bl_options = {'HIDE_HEADER'} + def draw(self, context): + layout = self.layout + layout.operator_context = 'EXEC_AREA' + + prefs = context.preferences + + layout.scale_x = 1.3 + layout.scale_y = 1.3 + + layout.operator("wm.save_userpref") + + +class PreferencePanel(Panel): + """ + Base class for panels to center align contents with some horizontal margin. + Deriving classes need to implement a ``draw_props(context, layout)`` function. + """ + + bl_space_type = 'PREFERENCES' + bl_region_type = 'WINDOW' + + def draw(self, context): + layout = self.layout + width = context.region.width + pixel_size = context.preferences.system.pixel_size + + layout.use_property_split = True + layout.use_property_decorate = False # No animation. + + row = layout.row() + if width > (350 * pixel_size): # No horizontal margin if region is rather small. + row.label() # Needed so col below is centered. + + col = row.column() + col.ui_units_x = 50 + + # draw_props implemented by deriving classes. + self.draw_props(context, col) + + if width > (350 * pixel_size): # No horizontal margin if region is rather small. + row.label() # Needed so col above is centered. + + +class USERPREF_PT_interface_display(PreferencePanel): + bl_label = "Display" + @classmethod def poll(cls, context): prefs = context.preferences return (prefs.active_section == 'INTERFACE') - def draw(self, context): - layout = self.layout + def draw_props(self, context, layout): + prefs = context.preferences + view = prefs.view + + layout.prop(view, "ui_scale", text="Resolution Scale") + layout.prop(view, "ui_line_width", text="Line Width") + +class USERPREF_PT_interface_display_info(PreferencePanel): + bl_label = "Information" + bl_parent_id = "USERPREF_PT_interface_display" + bl_options = {'DEFAULT_CLOSED'} + + def draw_props(self, context, layout): prefs = context.preferences view = prefs.view - split = layout.split() - row = split.row() - col = row.column() + flow = layout.grid_flow(row_major=True, columns=0, even_columns=False, even_rows=False, align=False) + + flow.prop(view, "show_tooltips") + flow.prop(view, "show_object_info", text="Object Info") + flow.prop(view, "show_large_cursors") + flow.prop(view, "show_view_name", text="View Name") + flow.prop(view, "show_playback_fps", text="Playback FPS") + + +class USERPREF_PT_interface_text(PreferencePanel): + bl_label = "Text" + bl_options = {'DEFAULT_CLOSED'} - col.label(text="Display:") - col.prop(view, "ui_scale", text="Scale") - col.prop(view, "ui_line_width", text="Line Width") - col.prop(view, "show_tooltips") - col.prop(view, "show_object_info", text="Object Info") - col.prop(view, "show_large_cursors") - col.prop(view, "show_view_name", text="View Name") - col.prop(view, "show_playback_fps", text="Playback FPS") - col.prop(view, "object_origin_size") + @classmethod + def poll(cls, context): + prefs = context.preferences + return (prefs.active_section == 'INTERFACE') + + def draw_props(self, context, layout): + prefs = context.preferences + view = prefs.view + + layout.prop(view, "use_text_antialiasing", text="Anti-aliasing") + sub = layout.column() + sub.active = view.use_text_antialiasing + sub.prop(view, "text_hinting", text="Hinting") + + layout.prop(view, "font_path_ui") + layout.prop(view, "font_path_ui_mono") + + +class USERPREF_PT_interface_text_translate(PreferencePanel): + bl_label = "Translate UI" + bl_options = {'DEFAULT_CLOSED'} + bl_parent_id = "USERPREF_PT_interface_text" + + @classmethod + def poll(cls, context): + prefs = context.preferences + if bpy.app.build_options.international: + return (prefs.active_section == 'INTERFACE') + + def draw_header(self, context): + prefs = context.preferences + view = prefs.view + + self.layout.prop(view, "use_international_fonts", text="") + + def draw_props(self, context, layout): + prefs = context.preferences + view = prefs.view - col.separator() + layout.active = view.use_international_fonts + layout.prop(view, "language") - # col.prop(view, "show_gizmo_navigate") + layout.prop(view, "use_translate_tooltips", text="Translate Tooltips") + layout.prop(view, "use_translate_interface", text="Translate Interface") + layout.prop(view, "use_translate_new_dataname", text="Translate New Data") - sub = col.column(align=True) - sub.label(text="3D Viewport Axis:") - sub.row().prop(view, "mini_axis_type", text="") +class USERPREF_PT_interface_develop(PreferencePanel): + bl_label = "Develop" + bl_options = {'DEFAULT_CLOSED'} + + @classmethod + def poll(cls, context): + prefs = context.preferences + return (prefs.active_section == 'INTERFACE') + + def draw_props(self, context, layout): + prefs = context.preferences + view = prefs.view + + layout.prop(view, "show_tooltips_python") + layout.prop(view, "show_developer_ui") + + +class USERPREF_PT_interface_viewports(PreferencePanel): + bl_label = "Viewports" + bl_options = {'DEFAULT_CLOSED'} + + @classmethod + def poll(cls, context): + prefs = context.preferences + return (prefs.active_section == 'INTERFACE') + + def draw(self, context): + pass + + +class USERPREF_PT_interface_viewports_3d(PreferencePanel): + bl_label = "3D Viewports" + bl_parent_id = "USERPREF_PT_interface_viewports" + + def draw_props(self, context, layout): + prefs = context.preferences + view = prefs.view - sub = col.column(align=True) + layout.prop(view, "smooth_view") + layout.prop(view, "rotation_angle") + + layout.separator() + + layout.prop(view, "object_origin_size") + layout.prop(view, "gizmo_size", text="Gizmo Size") + layout.separator() + + layout.prop(view, "mini_axis_type", text="3D Viewport Axis") + + sub = layout.column() sub.active = view.mini_axis_type == 'MINIMAL' sub.prop(view, "mini_axis_size", text="Size") sub.prop(view, "mini_axis_brightness", text="Brightness") - col.separator() - # Toolbox doesn't exist yet - # col.label(text="Toolbox:") - #col.prop(view, "show_column_layout") - #col.label(text="Open Toolbox Delay:") - #col.prop(view, "open_left_mouse_delay", text="Hold LMB") - #col.prop(view, "open_right_mouse_delay", text="Hold RMB") - col.prop(view, "show_gizmo", text="Gizmos") - sub = col.column() - sub.active = view.show_gizmo - sub.prop(view, "gizmo_size", text="Size") +class USERPREF_PT_interface_viewports_3d_weight_paint(PreferencePanel): + bl_label = "Custom Weight Paint Range" + bl_options = {'DEFAULT_CLOSED'} + bl_parent_id = "USERPREF_PT_interface_viewports_3d" - col.separator() + def draw_header(self, context): + prefs = context.preferences + view = prefs.view - col.label(text="Development:") - col.prop(view, "show_tooltips_python") - col.prop(view, "show_developer_ui") + self.layout.prop(view, "use_weight_color_range", text="") - row = split.row() - row.separator() - col = row.column() + def draw_props(self, context, layout): + prefs = context.preferences + view = prefs.view - col.label(text="View Gizmos:") - col.prop(view, "use_mouse_depth_cursor") - col.prop(view, "use_cursor_lock_adjust") - col.prop(view, "use_mouse_depth_navigate") - col.prop(view, "use_zoom_to_mouse") - col.prop(view, "use_rotate_around_active") - col.prop(view, "use_camera_lock_parent") + layout.active = view.use_weight_color_range + layout.template_color_ramp(view, "weight_color_range", expand=True) - col.separator() - col.prop(view, "use_auto_perspective") - col.prop(view, "smooth_view") - col.prop(view, "rotation_angle") +class USERPREF_PT_interface_viewports_2d(PreferencePanel): + bl_label = "2D Viewports" + bl_parent_id = "USERPREF_PT_interface_viewports" - col.separator() - col.separator() + def draw_props(self, context, layout): + prefs = context.preferences + view = prefs.view - col.label(text="2D Viewports:") - col.prop(view, "view2d_grid_spacing_min", text="Minimum Grid Spacing") - col.prop(view, "timecode_style") - col.prop(view, "view_frame_type") + layout.prop(view, "view2d_grid_spacing_min", text="Minimum Grid Spacing") + layout.prop(view, "timecode_style") + layout.prop(view, "view_frame_type") if view.view_frame_type == 'SECONDS': - col.prop(view, "view_frame_seconds") + layout.prop(view, "view_frame_seconds") elif view.view_frame_type == 'KEYFRAMES': - col.prop(view, "view_frame_keyframes") + layout.prop(view, "view_frame_keyframes") - row = split.row() - row.separator() - col = row.column() - col.label(text="Menus:") - col.prop(view, "use_mouse_over_open") - sub = col.column() - sub.active = view.use_mouse_over_open +class USERPREF_PT_interface_menus(PreferencePanel): + bl_label = "Menus" + bl_options = {'DEFAULT_CLOSED'} - sub.prop(view, "open_toplevel_delay", text="Top Level") - sub.prop(view, "open_sublevel_delay", text="Sub Level") + @classmethod + def poll(cls, context): + prefs = context.preferences + return (prefs.active_section == 'INTERFACE') - col.separator() - col.label(text="Pie Menus:") - sub = col.column(align=True) - sub.prop(view, "pie_animation_timeout") - sub.prop(view, "pie_initial_timeout") - sub.prop(view, "pie_menu_radius") - sub.prop(view, "pie_menu_threshold") - sub.prop(view, "pie_menu_confirm") - col.separator() + def draw_props(self, context, layout): + prefs = context.preferences + view = prefs.view - col.label(text="Header:") - sub = col.split() - sub.label(text="Default Position") - sub.row().prop(view, "header_align_default", expand=True) + layout.prop(view, "color_picker_type") + layout.row().prop(view, "header_align_default", expand=True) - col.prop(view, "show_splash") + layout.prop(view, "show_splash") + layout.prop(view, "use_quit_dialog") - col.label(text="Warnings:") - col.prop(view, "use_quit_dialog") - col.separator() +class USERPREF_PT_interface_menus_mouse_over(PreferencePanel): + bl_label = "Open on Mouse Over" + bl_parent_id = "USERPREF_PT_interface_menus" + bl_options = {'DEFAULT_CLOSED'} - col.label(text="App Template:") - col.label(text="Options intended for use with app-templates only") - col.prop(view, "show_layout_ui") + def draw_header(self, context): + prefs = context.preferences + view = prefs.view + self.layout.prop(view, "use_mouse_over_open", text="") -class USERPREF_PT_edit(Panel): - bl_space_type = 'PREFERENCES' - bl_label = "Edit" - bl_region_type = 'WINDOW' - bl_options = {'HIDE_HEADER'} + def draw_props(self, context, layout): + prefs = context.preferences + view = prefs.view + + layout.active = view.use_mouse_over_open + + layout.prop(view, "open_toplevel_delay", text="Top Level") + layout.prop(view, "open_sublevel_delay", text="Sub Level") + + +class USERPREF_PT_interface_menus_pie(PreferencePanel): + bl_label = "Pie Menus" + bl_parent_id = "USERPREF_PT_interface_menus" + bl_options = {'DEFAULT_CLOSED'} + + def draw_props(self, context, layout): + prefs = context.preferences + view = prefs.view + + layout.prop(view, "pie_animation_timeout") + layout.prop(view, "pie_initial_timeout") + layout.prop(view, "pie_menu_radius") + layout.prop(view, "pie_menu_threshold") + layout.prop(view, "pie_menu_confirm") + + +class USERPREF_PT_interface_templates(PreferencePanel): + bl_label = "Templates" + bl_options = {'DEFAULT_CLOSED'} + + @classmethod + def poll(cls, context): + prefs = context.preferences + return (prefs.active_section == 'INTERFACE') + + def draw_props(self, context, layout): + prefs = context.preferences + view = prefs.view + + layout.label(text="Options intended for use with app-templates only") + layout.prop(view, "show_layout_ui") + + +class USERPREF_PT_edit_objects(PreferencePanel): + bl_label = "Objects" @classmethod def poll(cls, context): prefs = context.preferences return (prefs.active_section == 'EDITING') - def draw(self, context): - layout = self.layout + def draw_props(self, context, layout): + prefs = context.preferences + edit = prefs.edit + layout.prop(edit, "material_link", text="Link Materials to") + layout.prop(edit, "object_align", text="Align New Objects to") + layout.prop(edit, "use_enter_edit_mode", text="Enter Edit Mode for New Objects") + + +class USERPREF_PT_edit_animation(PreferencePanel): + bl_label = "Animation" + bl_options = {'DEFAULT_CLOSED'} + + @classmethod + def poll(cls, context): + prefs = context.preferences + return (prefs.active_section == 'EDITING') + + def draw_props(self, context, layout): prefs = context.preferences edit = prefs.edit - split = layout.split() - row = split.row() - col = row.column() + layout.prop(edit, "use_negative_frames") - col.label(text="Link Materials To:") - col.prop(edit, "material_link", text="") + layout.prop(edit, "use_visual_keying") + layout.prop(edit, "use_keyframe_insert_needed", text="Only Insert Needed") - col.separator() - col.separator() - col.separator() - col.label(text="New Objects:") - col.prop(edit, "use_enter_edit_mode") - col.label(text="Align To:") - col.prop(edit, "object_align", text="") +class USERPREF_PT_edit_animation_autokey(PreferencePanel): + bl_label = "Auto-Keyframing" + bl_options = {'DEFAULT_CLOSED'} + bl_parent_id = "USERPREF_PT_edit_animation" - col.separator() - col.separator() - col.separator() + def draw_header(self, context): + prefs = context.preferences + edit = prefs.edit - col.label(text="Undo:") - col.prop(edit, "use_global_undo") - col.prop(edit, "undo_steps", text="Steps") - col.prop(edit, "undo_memory_limit", text="Memory Limit") + self.layout.prop(edit, "use_auto_keying", text="") - row = split.row() - row.separator() - col = row.column() + def draw_props(self, context, layout): + prefs = context.preferences + edit = prefs.edit - col.label(text="Grease Pencil/Annotations:") - col.separator() - col.prop(edit, "grease_pencil_manhattan_distance", text="Manhattan Distance") - col.prop(edit, "grease_pencil_euclidean_distance", text="Euclidean Distance") - col.separator() - - col.label(text="Annotations:") - sub = col.row() - sub.prop(edit, "grease_pencil_default_color", text="Default Color") - col.prop(edit, "grease_pencil_eraser_radius", text="Eraser Radius") - col.separator() - col.prop(edit, "use_grease_pencil_simplify_stroke", text="Simplify Stroke") - col.separator() - - col.separator() - col.separator() - col.separator() - col.label(text="Playback:") - col.prop(edit, "use_negative_frames") - col.separator() - col.label(text="Node Editor:") - col.prop(edit, "node_margin") - col.label(text="Animation Editors:") - col.prop(edit, "fcurve_unselected_alpha", text="F-Curve Visibility") - - row = split.row() - row.separator() - col = row.column() + layout.prop(edit, "use_auto_keying_warning") + layout.prop(edit, "use_keyframe_insert_available", text="Only Insert Available") + + +class USERPREF_PT_edit_animation_fcurves(PreferencePanel): + bl_label = "F-Curves" + bl_options = {'DEFAULT_CLOSED'} + bl_parent_id = "USERPREF_PT_edit_animation" - col.label(text="Keyframing:") - col.prop(edit, "use_visual_keying") - col.prop(edit, "use_keyframe_insert_needed", text="Only Insert Needed") + def draw_props(self, context, layout): + prefs = context.preferences + edit = prefs.edit - col.separator() + layout.prop(edit, "keyframe_new_interpolation_type", text="Default Interpolation") + layout.prop(edit, "keyframe_new_handle_type", text="Default Handles") + layout.prop(edit, "use_insertkey_xyz_to_rgb", text="XYZ to RGB") - col.prop(edit, "use_auto_keying", text="Auto Keyframing:") - col.prop(edit, "use_auto_keying_warning") + layout.separator() - sub = col.column() + layout.prop(edit, "fcurve_unselected_alpha", text="F-Curve Visibility") - # ~ sub.active = edit.use_keyframe_insert_auto # incorrect, time-line can enable - sub.prop(edit, "use_keyframe_insert_available", text="Only Insert Available") - col.separator() +class USERPREF_PT_edit_transform(PreferencePanel): + bl_label = "Transform" + bl_options = {'DEFAULT_CLOSED'} - col.label(text="New F-Curve Defaults:") - col.prop(edit, "keyframe_new_interpolation_type", text="Interpolation") - col.prop(edit, "keyframe_new_handle_type", text="Handles") - col.prop(edit, "use_insertkey_xyz_to_rgb", text="XYZ to RGB") + @classmethod + def poll(cls, context): + prefs = context.preferences + return (prefs.active_section == 'EDITING') - col.separator() - col.separator() - col.separator() + def draw_props(self, context, layout): + prefs = context.preferences + edit = prefs.edit - col.label(text="Transform:") - col.prop(edit, "use_drag_immediately") - col.prop(edit, "use_numeric_input_advanced") + layout.prop(edit, "use_drag_immediately") + layout.prop(edit, "use_numeric_input_advanced") - row = split.row() - row.separator() - col = row.column() - col.prop(edit, "sculpt_paint_overlay_color", text="Sculpt Overlay Color") +class USERPREF_PT_edit_duplicate_data(PreferencePanel): + bl_label = "Duplicate Data" + bl_options = {'DEFAULT_CLOSED'} - col.separator() - col.separator() - col.separator() + @classmethod + def poll(cls, context): + prefs = context.preferences + return (prefs.active_section == 'EDITING') - col.label(text="Duplicate Data:") - col.prop(edit, "use_duplicate_mesh", text="Mesh") - col.prop(edit, "use_duplicate_surface", text="Surface") - col.prop(edit, "use_duplicate_curve", text="Curve") - col.prop(edit, "use_duplicate_text", text="Text") - col.prop(edit, "use_duplicate_metaball", text="Metaball") + def draw_props(self, context, layout): + prefs = context.preferences + edit = prefs.edit + + flow = layout.grid_flow(row_major=True, columns=0, even_columns=False, even_rows=False, align=True) + + col = flow.column() + col.prop(edit, "use_duplicate_action", text="Action") col.prop(edit, "use_duplicate_armature", text="Armature") + col.prop(edit, "use_duplicate_curve", text="Curve") + # col.prop(edit, "use_duplicate_fcurve", text="F-Curve") col.prop(edit, "use_duplicate_light", text="Light") + col = flow.column() col.prop(edit, "use_duplicate_material", text="Material") - col.prop(edit, "use_duplicate_texture", text="Texture") - #col.prop(edit, "use_duplicate_fcurve", text="F-Curve") - col.prop(edit, "use_duplicate_action", text="Action") + col.prop(edit, "use_duplicate_mesh", text="Mesh") + col.prop(edit, "use_duplicate_metaball", text="Metaball") col.prop(edit, "use_duplicate_particle", text="Particle") + col = flow.column() + col.prop(edit, "use_duplicate_surface", text="Surface") + col.prop(edit, "use_duplicate_text", text="Text") + col.prop(edit, "use_duplicate_texture", text="Texture") -class USERPREF_PT_system_general(Panel): - bl_space_type = 'PREFERENCES' - bl_label = "System General" - bl_region_type = 'WINDOW' - bl_options = {'HIDE_HEADER'} +class USERPREF_PT_edit_gpencil(PreferencePanel): + bl_label = "Grease Pencil" + bl_options = {'DEFAULT_CLOSED'} @classmethod def poll(cls, context): prefs = context.preferences - return (prefs.active_section == 'SYSTEM_GENERAL') + return (prefs.active_section == 'EDITING') + + def draw_props(self, context, layout): + prefs = context.preferences + edit = prefs.edit + + layout.prop(edit, "grease_pencil_manhattan_distance", text="Manhattan Distance") + layout.prop(edit, "grease_pencil_euclidean_distance", text="Euclidean Distance") - def draw(self, context): - import sys - layout = self.layout +class USERPREF_PT_edit_annotations(PreferencePanel): + bl_label = "Annotations" + bl_options = {'DEFAULT_CLOSED'} + + @classmethod + def poll(cls, context): prefs = context.preferences - system = prefs.system + return (prefs.active_section == 'EDITING') + + def draw_props(self, context, layout): + prefs = context.preferences + edit = prefs.edit - split = layout.split() + layout.prop(edit, "grease_pencil_default_color", text="Default Color") + layout.prop(edit, "grease_pencil_eraser_radius", text="Eraser Radius") + layout.prop(edit, "use_grease_pencil_simplify_stroke", text="Simplify Stroke") - # 1. Column - column = split.column() - colsplit = column.split(factor=0.85) - col = colsplit.column() - col.label(text="General:") +class USERPREF_PT_edit_misc(PreferencePanel): + bl_label = "Miscellaneous" + bl_options = {'DEFAULT_CLOSED'} - col.prop(system, "scrollback", text="Console Scrollback") + @classmethod + def poll(cls, context): + prefs = context.preferences + return (prefs.active_section == 'EDITING') - col.separator() + def draw_props(self, context, layout): + prefs = context.preferences + edit = prefs.edit + + layout.prop(edit, "sculpt_paint_overlay_color", text="Sculpt Overlay Color") + layout.prop(edit, "node_margin", text="Node Editor Auto-offset Margin") + + +class USERPREF_PT_system_sound(PreferencePanel): + bl_label = "Sound" + bl_options = {'DEFAULT_CLOSED'} + + @classmethod + def poll(cls, context): + prefs = context.preferences + return (prefs.active_section == 'SYSTEM_GENERAL') + + def draw_props(self, context, layout): + prefs = context.preferences + system = prefs.system - col.label(text="Sound:") - col.row().prop(system, "audio_device", expand=False) - sub = col.column() + layout.prop(system, "audio_device", expand=False) + sub = layout.column() sub.active = system.audio_device not in {'NONE', 'Null'} sub.prop(system, "audio_channels", text="Channels") sub.prop(system, "audio_mixing_buffer", text="Mixing Buffer") sub.prop(system, "audio_sample_rate", text="Sample Rate") sub.prop(system, "audio_sample_format", text="Sample Format") - col.separator() + +class USERPREF_PT_system_compute_device(PreferencePanel): + bl_label = "Cycles Compute Device" + bl_options = {'DEFAULT_CLOSED'} + + @classmethod + def poll(cls, context): + prefs = context.preferences + return (prefs.active_section == 'SYSTEM_GENERAL') + + def draw_props(self, context, layout): + prefs = context.preferences + system = prefs.system + + col = layout.column() if bpy.app.build_options.cycles: addon = prefs.addons.get("cycles") @@ -391,95 +576,94 @@ class USERPREF_PT_system_general(Panel): # col.label(text="OpenSubdiv compute:") # col.row().prop(system, "opensubdiv_compute_type", text="") - # 2. Column - column = split.column() - colsplit = column.split(factor=0.85) - - col = colsplit.column() - col.label(text="OpenGL:") - col.prop(system, "gl_clip_alpha", slider=True) - col.prop(system, "use_gpu_mipmap") - col.prop(system, "use_16bit_textures") - col.separator() - col.label(text="Selection:") - col.prop(system, "select_method", text="") - col.prop(system, "use_select_pick_depth") +class USERPREF_PT_system_opengl(PreferencePanel): + bl_label = "OpenGL" - col.separator() + @classmethod + def poll(cls, context): + prefs = context.preferences + return (prefs.active_section == 'SYSTEM_GENERAL') - col.label(text="Anisotropic Filtering:") - col.prop(system, "anisotropic_filter", text="") + def draw_props(self, context, layout): + import sys + prefs = context.preferences + system = prefs.system - col.separator() + layout.prop(system, "gpu_viewport_quality") + layout.prop(system, "gl_clip_alpha", slider=True) + layout.prop(system, "multi_sample", text="Multisampling") + layout.prop(system, "gpencil_multi_sample", text="Grease Pencil Multisampling") - col.prop(system, "multi_sample", text="") if sys.platform == "linux" and system.multi_sample != 'NONE': - col.label(text="Might fail for Mesh editing selection!") - col.separator() - col.prop(system, "use_region_overlap") + layout.label(text="Might fail for Mesh editing selection!") + layout.separator() + + layout.prop(system, "use_region_overlap") - col.separator() - col.prop(system, "gpu_viewport_quality") - col.separator() - col.label(text="Grease Pencil Options:") - col.prop(system, "gpencil_multi_sample", text="") +class USERPREF_PT_system_opengl_textures(PreferencePanel): + bl_label = "Textures" + bl_parent_id = "USERPREF_PT_system_opengl" + bl_options = {'DEFAULT_CLOSED'} - col.separator() - col.label(text="Text Draw Options:") - col.prop(system, "use_text_antialiasing", text="Anti-aliasing") - sub = col.column() - sub.active = system.use_text_antialiasing - sub.prop(system, "text_hinting", text="Hinting") + @classmethod + def poll(cls, context): + prefs = context.preferences + return (prefs.active_section == 'SYSTEM_GENERAL') - # 3. Column - column = split.column() + def draw_props(self, context, layout): + prefs = context.preferences + system = prefs.system - column.label(text="Textures:") - column.prop(system, "gl_texture_limit", text="Limit Size") - column.prop(system, "texture_time_out", text="Time Out") - column.prop(system, "texture_collection_rate", text="Collection Rate") + layout.prop(system, "gl_texture_limit", text="Limit Size") + layout.prop(system, "anisotropic_filter") + layout.prop(system, "texture_time_out", text="Time Out") + layout.prop(system, "texture_collection_rate", text="Garbage Collection Rate") + layout.prop(system, "image_draw_method", text="Image Display Method") - column.separator() + layout.prop(system, "use_16bit_textures") + layout.prop(system, "use_gpu_mipmap") - column.label(text="Images Draw Method:") - column.prop(system, "image_draw_method", text="") - column.separator() +class USERPREF_PT_system_opengl_selection(PreferencePanel): + bl_label = "Selection" + bl_parent_id = "USERPREF_PT_system_opengl" + bl_options = {'DEFAULT_CLOSED'} - column.label(text="Sequencer/Clip Editor:") - # currently disabled in the code - # column.prop(system, "prefetch_frames") - column.prop(system, "memory_cache_limit") + def draw_props(self, context, layout): + prefs = context.preferences + system = prefs.system - column.separator() + layout.prop(system, "select_method", text="Selection Method") + layout.prop(system, "use_select_pick_depth") - column.label(text="Color Picker Type:") - column.row().prop(system, "color_picker_type", text="") - column.separator() +class USERPREF_PT_system_memory(PreferencePanel): + bl_label = "Memory" + bl_options = {'DEFAULT_CLOSED'} - column.prop(system, "use_weight_color_range", text="Custom Weight Paint Range") - sub = column.column() - sub.active = system.use_weight_color_range - sub.template_color_ramp(system, "weight_color_range", expand=True) + @classmethod + def poll(cls, context): + prefs = context.preferences + return (prefs.active_section == 'SYSTEM_GENERAL') - column.separator() - column.prop(system, "font_path_ui") - column.prop(system, "font_path_ui_mono") + def draw_props(self, context, layout): + prefs = context.preferences + system = prefs.system + edit = prefs.edit - if bpy.app.build_options.international: - column.prop(system, "use_international_fonts") - sub_col = column.column() - sub_col.active = system.use_international_fonts - sub_col.prop(system, "language") - row = sub_col.row() - row.label(text="Translate:", text_ctxt=i18n_contexts.id_windowmanager) - row = sub_col.row(align=True) - row.prop(system, "use_translate_tooltips", text="Tooltips", toggle=True) - row.prop(system, "use_translate_interface", text="Interface", toggle=True) - row.prop(system, "use_translate_new_dataname", text="New Data", toggle=True) + layout.prop(edit, "undo_steps", text="Undo Steps") + layout.prop(edit, "undo_memory_limit", text="Undo Memory Limit") + layout.prop(edit, "use_global_undo") + + layout.separator() + + layout.prop(system, "memory_cache_limit", text="Sequencer Cache Limit") + + layout.separator() + + layout.prop(system, "scrollback", text="Console Scrollback Lines") class USERPREF_MT_interface_theme_presets(Menu): @@ -503,494 +687,577 @@ class USERPREF_PT_theme(Panel): bl_region_type = 'WINDOW' bl_options = {'HIDE_HEADER'} - # not essential, hard-coded UI delimiters for the theme layout - ui_delimiters = { - 'VIEW_3D': { - "text_grease_pencil", - "text_keyframe", - "speaker", - "freestyle_face_mark", - "split_normal", - "bone_solid", - "paint_curve_pivot", - }, - 'GRAPH_EDITOR': { - "handle_vertex_select", - }, - 'IMAGE_EDITOR': { - "paint_curve_pivot", - }, - 'NODE_EDITOR': { - "layout_node", - }, - 'CLIP_EDITOR': { - "handle_vertex_select", - } - } - - @staticmethod - def _theme_generic(split, themedata, theme_area): - - col = split.column() + @classmethod + def poll(cls, context): + prefs = context.preferences + return (prefs.active_section == 'THEMES') - def theme_generic_recurse(data): - col.label(text=data.rna_type.name) - row = col.row() - subsplit = row.split(factor=0.95) + def draw(self, context): + layout = self.layout - padding1 = subsplit.split(factor=0.15) - padding1.column() + theme = context.preferences.themes[0] - subsplit = row.split(factor=0.85) + row = layout.row() - padding2 = subsplit.split(factor=0.15) - padding2.column() + row.operator("wm.theme_install", text="Install...", icon='IMPORT') + row.operator("ui.reset_default_theme", text="Reset", icon='LOOP_BACK') - colsub_pair = padding1.column(), padding2.column() + subrow = row.row(align=True) + subrow.menu("USERPREF_MT_interface_theme_presets", text=USERPREF_MT_interface_theme_presets.bl_label) + subrow.operator("wm.interface_theme_preset_add", text="", icon='ADD') + subrow.operator("wm.interface_theme_preset_add", text="", icon='REMOVE').remove_active = True - props_type = {} - for i, prop in enumerate(data.rna_type.properties): - if prop.identifier == "rna_type": - continue +class USERPREF_PT_theme_user_interface(PreferencePanel): + bl_space_type = 'PREFERENCES' + bl_region_type = 'WINDOW' + bl_label = "User Interface" + bl_options = {'DEFAULT_CLOSED'} - props_type.setdefault((prop.type, prop.subtype), []).append(prop) + @classmethod + def poll(cls, context): + prefs = context.preferences + return (prefs.active_section == 'THEMES') - th_delimiters = USERPREF_PT_theme.ui_delimiters.get(theme_area) - for props_type, props_ls in sorted(props_type.items()): - if props_type[0] == 'POINTER': - for i, prop in enumerate(props_ls): - theme_generic_recurse(getattr(data, prop.identifier)) - else: - if th_delimiters is None: - # simple, no delimiters - for i, prop in enumerate(props_ls): - colsub_pair[i % 2].row().prop(data, prop.identifier) - else: - # add hard coded delimiters - i = 0 - for prop in props_ls: - colsub = colsub_pair[i] - colsub.row().prop(data, prop.identifier) - i = (i + 1) % 2 - if prop.identifier in th_delimiters: - if i: - colsub = colsub_pair[1] - colsub.row().label(text="") - colsub_pair[0].row().label(text="") - colsub_pair[1].row().label(text="") - i = 0 - - theme_generic_recurse(themedata) + def draw_header(self, context): + layout = self.layout - @staticmethod - def _theme_widget_style(layout, widget_style): + layout.label(icon='WORKSPACE') - row = layout.row() + def draw(self, context): + pass - subsplit = row.split(factor=0.95) - - padding = subsplit.split(factor=0.15) - colsub = padding.column() - colsub = padding.column() - colsub.row().prop(widget_style, "outline") - colsub.row().prop(widget_style, "item", slider=True) - colsub.row().prop(widget_style, "inner", slider=True) - colsub.row().prop(widget_style, "inner_sel", slider=True) - colsub.row().prop(widget_style, "roundness") - - subsplit = row.split(factor=0.85) - - padding = subsplit.split(factor=0.15) - colsub = padding.column() - colsub = padding.column() - colsub.row().prop(widget_style, "text") - colsub.row().prop(widget_style, "text_sel") - colsub.prop(widget_style, "show_shaded") - subsub = colsub.column(align=True) - subsub.active = widget_style.show_shaded - subsub.prop(widget_style, "shadetop") - subsub.prop(widget_style, "shadedown") - layout.separator() +# Base class for dynamically defined widget color panels. +class PreferenceThemeWidgetColorPanel(Panel): + bl_space_type = 'PREFERENCES' + bl_region_type = 'WINDOW' + bl_parent_id = "USERPREF_PT_theme_user_interface" @staticmethod - def _ui_font_style(layout, font_style): + def draw(self, context): + theme = context.preferences.themes[0] + ui = theme.user_interface + widget_style = getattr(ui, self.wcol) + layout = self.layout - split = layout.split() + layout.use_property_split = True - col = split.column() - col.label(text="Kerning Style:") - col.row().prop(font_style, "font_kerning_style", expand=True) - col.prop(font_style, "points") + flow = layout.grid_flow(row_major=True, columns=0, even_columns=False, even_rows=False, align=False) - col = split.column() - col.label(text="Shadow Offset:") - col.prop(font_style, "shadow_offset_x", text="X") - col.prop(font_style, "shadow_offset_y", text="Y") + col = flow.column() + col.prop(widget_style, "outline") + col.prop(widget_style, "item", slider=True) + col.prop(widget_style, "inner", slider=True) + col.prop(widget_style, "inner_sel", slider=True) - col = split.column() - col.prop(font_style, "shadow") - col.prop(font_style, "shadow_alpha") - col.prop(font_style, "shadow_value") + col = flow.column() + col.prop(widget_style, "text") + col.prop(widget_style, "text_sel") + col.prop(widget_style, "roundness") - layout.separator() + col = flow.column() + col.prop(widget_style, "show_shaded") + + colsub = col.column() + colsub.active = widget_style.show_shaded + colsub.prop(widget_style, "shadetop") + colsub.prop(widget_style, "shadedown") @classmethod def poll(cls, context): prefs = context.preferences return (prefs.active_section == 'THEMES') - def draw(self, context): - layout = self.layout +class USERPREF_PT_theme_interface_state(PreferencePanel): + bl_label = "State" + bl_options = {'DEFAULT_CLOSED'} + bl_parent_id = "USERPREF_PT_theme_user_interface" + + def draw_props(self, context, layout): theme = context.preferences.themes[0] + ui = theme.user_interface + ui_state = theme.user_interface.wcol_state - split_themes = layout.split(factor=0.2) + flow = layout.grid_flow(row_major=True, columns=0, even_columns=False, even_rows=False, align=False) - sub = split_themes.column() + col = flow.column(align=True) + col.prop(ui_state, "inner_anim") + col.prop(ui_state, "inner_anim_sel") - sub.label(text="Presets:") - subrow = sub.row(align=True) + col = flow.column(align=True) + col.prop(ui_state, "inner_driven") + col.prop(ui_state, "inner_driven_sel") - subrow.menu("USERPREF_MT_interface_theme_presets", text=USERPREF_MT_interface_theme_presets.bl_label) - subrow.operator("wm.interface_theme_preset_add", text="", icon='ADD') - subrow.operator("wm.interface_theme_preset_add", text="", icon='REMOVE').remove_active = True - sub.separator() + col = flow.column(align=True) + col.prop(ui_state, "inner_key") + col.prop(ui_state, "inner_key_sel") - sub.prop(theme, "theme_area", expand=True) + col = flow.column(align=True) + col.prop(ui_state, "inner_overridden") + col.prop(ui_state, "inner_overridden_sel") - split = layout.split(factor=0.4) + col = flow.column(align=True) + col.prop(ui_state, "inner_changed") + col.prop(ui_state, "inner_changed_sel") - layout.separator() - layout.separator() + col = flow.column(align=True) + col.prop(ui_state, "blend") + + +class USERPREF_PT_theme_interface_styles(PreferencePanel): + bl_label = "Styles" + bl_options = {'DEFAULT_CLOSED'} + bl_parent_id = "USERPREF_PT_theme_user_interface" + + def draw_props(self, context, layout): + theme = context.preferences.themes[0] + ui = theme.user_interface + + flow = layout.grid_flow(row_major=True, columns=0, even_columns=False, even_rows=False, align=False) + + flow.prop(ui, "menu_shadow_fac") + flow.prop(ui, "icon_alpha") + flow.prop(ui, "icon_saturation") + flow.prop(ui, "editor_outline") + flow.prop(ui, "menu_shadow_width") + flow.prop(ui, "widget_emboss") + + +class USERPREF_PT_theme_interface_gizmos(PreferencePanel): + bl_label = "Axis & Gizmo Colors" + bl_options = {'DEFAULT_CLOSED'} + bl_parent_id = "USERPREF_PT_theme_user_interface" + + def draw_props(self, context, layout): + theme = context.preferences.themes[0] + ui = theme.user_interface + + flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=True, align=False) + + col = flow.column(align=True) + col.prop(ui, "axis_x", text="Axis X") + col.prop(ui, "axis_y", text="Y") + col.prop(ui, "axis_z", text="Z") + + col = flow.column() + col.prop(ui, "gizmo_primary") + col.prop(ui, "gizmo_secondary") + + col = flow.column() + col.prop(ui, "gizmo_a") + col.prop(ui, "gizmo_b") - split = split_themes.split() - if theme.theme_area == 'USER_INTERFACE': - col = split.column() - ui = theme.user_interface +class USERPREF_PT_theme_interface_icons(PreferencePanel): + bl_label = "Icon Colors" + bl_options = {'DEFAULT_CLOSED'} + bl_parent_id = "USERPREF_PT_theme_user_interface" - col.label(text="Regular:") - self._theme_widget_style(col, ui.wcol_regular) + def draw_props(self, context, layout): + theme = context.preferences.themes[0] + ui = theme.user_interface - col.label(text="Tool:") - self._theme_widget_style(col, ui.wcol_tool) + flow = layout.grid_flow(row_major=True, columns=0, even_columns=False, even_rows=False, align=False) - col.label(text="Toolbar Item:") - self._theme_widget_style(col, ui.wcol_toolbar_item) + flow.prop(ui, "icon_collection") + flow.prop(ui, "icon_object") + flow.prop(ui, "icon_object_data") + flow.prop(ui, "icon_modifier") + flow.prop(ui, "icon_shading") - col.label(text="Radio Buttons:") - self._theme_widget_style(col, ui.wcol_radio) - col.label(text="Text:") - self._theme_widget_style(col, ui.wcol_text) +class USERPREF_PT_theme_text_style(PreferencePanel): + bl_label = "Text Style" + bl_options = {'DEFAULT_CLOSED'} - col.label(text="Option:") - self._theme_widget_style(col, ui.wcol_option) + @classmethod + def poll(cls, context): + prefs = context.preferences + return (prefs.active_section == 'THEMES') - col.label(text="Toggle:") - self._theme_widget_style(col, ui.wcol_toggle) + @staticmethod + def _ui_font_style(layout, font_style): + layout.use_property_split = True + flow = layout.grid_flow(row_major=True, columns=0, even_columns=False, even_rows=False, align=True) - col.label(text="Number Field:") - self._theme_widget_style(col, ui.wcol_num) + col = flow.column() + col.row().prop(font_style, "font_kerning_style", expand=True) + col.prop(font_style, "points") - col.label(text="Value Slider:") - self._theme_widget_style(col, ui.wcol_numslider) + col = flow.column(align=True) + col.prop(font_style, "shadow_offset_x", text="Shadow Offset X") + col.prop(font_style, "shadow_offset_y", text="Y") - col.label(text="Box:") - self._theme_widget_style(col, ui.wcol_box) + col = flow.column() + col.prop(font_style, "shadow") + col.prop(font_style, "shadow_alpha") + col.prop(font_style, "shadow_value") - col.label(text="Menu:") - self._theme_widget_style(col, ui.wcol_menu) + def draw_header(self, context): + layout = self.layout - col.label(text="Pie Menu:") - self._theme_widget_style(col, ui.wcol_pie_menu) + layout.label(icon='FONTPREVIEW') - col.label(text="Pulldown:") - self._theme_widget_style(col, ui.wcol_pulldown) + def draw_props(self, context, layout): + style = context.preferences.ui_styles[0] - col.label(text="Menu Back:") - self._theme_widget_style(col, ui.wcol_menu_back) + layout.label(text="Panel Title") + self._ui_font_style(layout, style.panel_title) - col.label(text="Tooltip:") - self._theme_widget_style(col, ui.wcol_tooltip) + layout.separator() - col.label(text="Menu Item:") - self._theme_widget_style(col, ui.wcol_menu_item) + layout.label(text="Widget") + self._ui_font_style(layout, style.widget) - col.label(text="Scroll Bar:") - self._theme_widget_style(col, ui.wcol_scroll) + layout.separator() - col.label(text="Progress Bar:") - self._theme_widget_style(col, ui.wcol_progress) + layout.label(text="Widget Label") + self._ui_font_style(layout, style.widget_label) - col.label(text="List Item:") - self._theme_widget_style(col, ui.wcol_list_item) - col.label(text="Tab:") - self._theme_widget_style(col, ui.wcol_tab) +class USERPREF_PT_theme_bone_color_sets(PreferencePanel): + bl_label = "Bone Color Sets" + bl_options = {'DEFAULT_CLOSED'} - ui_state = theme.user_interface.wcol_state - col.label(text="State:") + @classmethod + def poll(cls, context): + prefs = context.preferences + return (prefs.active_section == 'THEMES') - row = col.row() + def draw_header(self, context): + layout = self.layout - subsplit = row.split(factor=0.95) + layout.label(icon='COLOR') - padding = subsplit.split(factor=0.15) - colsub = padding.column() - colsub = padding.column() - colsub.row().prop(ui_state, "inner_anim") - colsub.row().prop(ui_state, "inner_anim_sel") - colsub.row().prop(ui_state, "inner_driven") - colsub.row().prop(ui_state, "inner_driven_sel") - colsub.row().prop(ui_state, "blend") + def draw_props(self, context, layout): + theme = context.preferences.themes[0] - subsplit = row.split(factor=0.85) + layout.use_property_split = True - padding = subsplit.split(factor=0.15) - colsub = padding.column() - colsub = padding.column() - colsub.row().prop(ui_state, "inner_key") - colsub.row().prop(ui_state, "inner_key_sel") - colsub.row().prop(ui_state, "inner_overridden") - colsub.row().prop(ui_state, "inner_overridden_sel") - colsub.row().prop(ui_state, "inner_changed") - colsub.row().prop(ui_state, "inner_changed_sel") + for i, ui in enumerate(theme.bone_color_sets, 1): + layout.label(text=iface_(f"Color Set {i:d}"), translate=False) - col.separator() - col.separator() + flow = layout.grid_flow(row_major=True, columns=0, even_columns=False, even_rows=False, align=False) - col.label(text="Styles:") + flow.prop(ui, "normal") + flow.prop(ui, "select") + flow.prop(ui, "active") + flow.prop(ui, "show_colored_constraints") - row = col.row() - subsplit = row.split(factor=0.95) +# Base class for dynamically defined theme-space panels. +class PreferenceThemeSpacePanel(Panel): + bl_space_type = 'PREFERENCES' + bl_region_type = 'WINDOW' - padding = subsplit.split(factor=0.15) - colsub = padding.column() - colsub = padding.column() - colsub.row().prop(ui, "menu_shadow_fac") - colsub.row().prop(ui, "icon_alpha") - colsub.row().prop(ui, "icon_saturation") - colsub.row().prop(ui, "editor_outline") + # not essential, hard-coded UI delimiters for the theme layout + ui_delimiters = { + 'VIEW_3D': { + "text_grease_pencil", + "text_keyframe", + "speaker", + "freestyle_face_mark", + "split_normal", + "bone_solid", + "paint_curve_pivot", + }, + 'GRAPH_EDITOR': { + "handle_vertex_select", + }, + 'IMAGE_EDITOR': { + "paint_curve_pivot", + }, + 'NODE_EDITOR': { + "layout_node", + }, + 'CLIP_EDITOR': { + "handle_vertex_select", + } + } - subsplit = row.split(factor=0.85) + # TODO theme_area should be deprecated + @staticmethod + def _theme_generic(layout, themedata, theme_area): - padding = subsplit.split(factor=0.15) - colsub = padding.column() - colsub = padding.column() - colsub.row().prop(ui, "menu_shadow_width") - colsub.row().prop(ui, "widget_emboss") + layout.use_property_split = True - col.separator() - col.separator() + flow = layout.grid_flow(row_major=True, columns=0, even_columns=False, even_rows=False, align=False) - col.label(text="Axis & Gizmo Colors:") + props_type = {} - row = col.row() + for i, prop in enumerate(themedata.rna_type.properties): + if prop.identifier == "rna_type": + continue - subsplit = row.split(factor=0.95) + props_type.setdefault((prop.type, prop.subtype), []).append(prop) - padding = subsplit.split(factor=0.15) - colsub = padding.column() - colsub = padding.column() - colsub.row().prop(ui, "axis_x") - colsub.row().prop(ui, "axis_y") - colsub.row().prop(ui, "axis_z") + th_delimiters = PreferenceThemeSpacePanel.ui_delimiters.get(theme_area) + for props_type, props_ls in sorted(props_type.items()): + if props_type[0] == 'POINTER': + continue - subsplit = row.split(factor=0.85) + if th_delimiters is None: + # simple, no delimiters + for i, prop in enumerate(props_ls): + flow.prop(themedata, prop.identifier) + else: - padding = subsplit.split(factor=0.15) - colsub = padding.column() - colsub = padding.column() - colsub.row().prop(ui, "gizmo_primary") - colsub.row().prop(ui, "gizmo_secondary") - colsub.row().prop(ui, "gizmo_a") - colsub.row().prop(ui, "gizmo_b") + for prop in props_ls: + flow.prop(themedata, prop.identifier) - col.separator() - col.separator() + @staticmethod + def draw_header(self, context): + if hasattr(self, "icon") and self.icon != 'NONE': + layout = self.layout + layout.label(icon=self.icon) - col.label(text="Icon Colors:") + @staticmethod + def draw(self, context): + layout = self.layout + theme = context.preferences.themes[0] - row = col.row() + datapath_list = self.datapath.split(".") + data = theme + for datapath_item in datapath_list: + data = getattr(data, datapath_item) + PreferenceThemeSpacePanel._theme_generic(layout, data, self.theme_area) - subsplit = row.split(factor=0.95) + @classmethod + def poll(cls, context): + prefs = context.preferences + return (prefs.active_section == 'THEMES') - padding = subsplit.split(factor=0.15) - colsub = padding.column() - colsub = padding.column() - colsub.row().prop(ui, "icon_collection") - colsub.row().prop(ui, "icon_object") - colsub.row().prop(ui, "icon_object_data") - subsplit = row.split(factor=0.85) +class ThemeGenericClassGenerator(): + generated_classes = [] - padding = subsplit.split(factor=0.15) - colsub = padding.column() - colsub = padding.column() - colsub.row().prop(ui, "icon_modifier") - colsub.row().prop(ui, "icon_shading") + @staticmethod + def generate_panel_classes_for_wcols(): + wcols = [ + ("Regular", "wcol_regular"), + ("Tool", "wcol_tool"), + ("Toolbar Item", "wcol_toolbar_item"), + ("Radio Buttons", "wcol_radio"), + ("Text", "wcol_text"), + ("Option", "wcol_option"), + ("Toggle", "wcol_toggle"), + ("Number Field", "wcol_num"), + ("Value Slider", "wcol_numslider"), + ("Box", "wcol_box"), + ("Menu", "wcol_menu"), + ("Pie Menu", "wcol_pie_menu"), + ("Pulldown", "wcol_pulldown"), + ("Menu Back", "wcol_menu_back"), + ("Tooltip", "wcol_tooltip"), + ("Menu Item", "wcol_menu_item"), + ("Scroll Bar", "wcol_scroll"), + ("Progress Bar", "wcol_progress"), + ("List Item", "wcol_list_item"), + ("Tab", "wcol_tab"), + ] - col.separator() - col.separator() - elif theme.theme_area == 'BONE_COLOR_SETS': - col = split.column() + for (name, wcol) in wcols: + panel_id = "USERPREF_PT_theme_interface_" + wcol + paneltype = type(panel_id, (PreferenceThemeWidgetColorPanel,), { + "bl_label": name, + "bl_options": {'DEFAULT_CLOSED'}, + "draw": PreferenceThemeWidgetColorPanel.draw, + "wcol": wcol, + }) - for i, ui in enumerate(theme.bone_color_sets, 1): - col.label(text=iface_(f"Color Set {i:d}"), translate=False) + ThemeGenericClassGenerator.generated_classes.append(paneltype) - row = col.row() + @staticmethod + def generate_theme_area_child_panel_classes(parent_id, rna_type, theme_area, datapath): + def generate_child_panel_classes_recurse(parent_id, rna_type, theme_area, datapath): + props_type = {} - subsplit = row.split(factor=0.95) + for i, prop in enumerate(rna_type.properties): + if prop.identifier == "rna_type": + continue - padding = subsplit.split(factor=0.15) - colsub = padding.column() - colsub = padding.column() - colsub.row().prop(ui, "normal") - colsub.row().prop(ui, "select") - colsub.row().prop(ui, "active") + props_type.setdefault((prop.type, prop.subtype), []).append(prop) - subsplit = row.split(factor=0.85) + for props_type, props_ls in sorted(props_type.items()): + if props_type[0] == 'POINTER': + for i, prop in enumerate(props_ls): + new_datapath = datapath + "." + prop.identifier if datapath else prop.identifier + panel_id = parent_id + "_" + prop.identifier + paneltype = type(panel_id, (PreferenceThemeSpacePanel,), { + "bl_label": rna_type.properties[prop.identifier].name, + "bl_parent_id": parent_id, + "bl_options": {'DEFAULT_CLOSED'}, + "draw": PreferenceThemeSpacePanel.draw, + "theme_area": theme_area.identifier, + "datapath": new_datapath, + }) + + ThemeGenericClassGenerator.generated_classes.append(paneltype) + generate_child_panel_classes_recurse(panel_id, prop.fixed_type, theme_area, new_datapath) + + generate_child_panel_classes_recurse(parent_id, rna_type, theme_area, datapath) - padding = subsplit.split(factor=0.15) - colsub = padding.column() - colsub = padding.column() - colsub.row().prop(ui, "show_colored_constraints") - elif theme.theme_area == 'STYLE': - col = split.column() + @staticmethod + def generate_panel_classes_from_theme_areas(): + from bpy.types import Theme - style = context.preferences.ui_styles[0] + for theme_area in Theme.bl_rna.properties['theme_area'].enum_items_static: + if theme_area.identifier in {'USER_INTERFACE', 'STYLE', 'BONE_COLOR_SETS'}: + continue - col.label(text="Panel Title:") - self._ui_font_style(col, style.panel_title) + panel_id = "USERPREF_PT_theme_" + theme_area.identifier.lower() + # Generate panel-class from theme_area + paneltype = type(panel_id, (PreferenceThemeSpacePanel,), { + "bl_label": theme_area.name, + "bl_options": {'DEFAULT_CLOSED'}, + "draw_header": PreferenceThemeSpacePanel.draw_header, + "draw": PreferenceThemeSpacePanel.draw, + "theme_area": theme_area.identifier, + "icon": theme_area.icon, + "datapath": theme_area.identifier.lower(), + }) + + ThemeGenericClassGenerator.generated_classes.append(paneltype) + ThemeGenericClassGenerator.generate_theme_area_child_panel_classes( + panel_id, Theme.bl_rna.properties[theme_area.identifier.lower()].fixed_type, + theme_area, theme_area.identifier.lower()) + + +class USERPREF_PT_file_paths(Panel): + bl_space_type = 'PREFERENCES' + bl_region_type = 'WINDOW' + bl_label = "File Paths" - col.separator() + @classmethod + def poll(cls, context): + prefs = context.preferences + return (prefs.active_section == 'SYSTEM_FILES') - col.label(text="Widget:") - self._ui_font_style(col, style.widget) + def draw(self, context): + layout = self.layout + prefs = context.preferences + paths = prefs.filepaths + system = prefs.system - col.separator() + layout.use_property_split = True + layout.use_property_decorate = False # No animation. + + layout.prop(paths, "render_output_directory", text="Render Output") + layout.prop(paths, "render_cache_directory", text="Render Cache") + layout.prop(paths, "font_directory", text="Fonts") + layout.prop(paths, "texture_directory", text="Textures") + layout.prop(paths, "script_directory", text="Scripts") + layout.prop(paths, "sound_directory", text="Sounds") + layout.prop(paths, "temporary_directory", text="Temp") + layout.prop(paths, "i18n_branches_directory", text="I18n Branches") + layout.prop(paths, "image_editor", text="Image Editor") + layout.prop(paths, "animation_player_preset", text="Playback Preset") - col.label(text="Widget Label:") - self._ui_font_style(col, style.widget_label) - else: - self._theme_generic(split, getattr(theme, theme.theme_area.lower()), theme.theme_area) + row = layout.row() + row.enabled = paths.animation_player_preset == 'CUSTOM' + row.prop(paths, "animation_player", text="Animation Player") -class USERPREF_PT_file(Panel): +class USERPREF_PT_file_autorun(Panel): bl_space_type = 'PREFERENCES' - bl_label = "Files" bl_region_type = 'WINDOW' - bl_options = {'HIDE_HEADER'} + bl_label = "Auto Run Python Scripts" + bl_options = {'DEFAULT_CLOSED'} @classmethod def poll(cls, context): prefs = context.preferences return (prefs.active_section == 'SYSTEM_FILES') + def draw_header(self, context): + prefs = context.preferences + paths = prefs.filepaths + + self.layout.prop(paths, "use_scripts_auto_execute", text="") + def draw(self, context): layout = self.layout + prefs = context.preferences + paths = prefs.filepaths + layout.use_property_split = True + layout.use_property_decorate = False # No animation. + + layout.active = paths.use_scripts_auto_execute + + box = layout.box() + row = box.row() + row.label(text="Excluded Paths:") + row.operator("wm.userpref_autoexec_path_add", text="", icon='ADD', emboss=False) + for i, path_cmp in enumerate(prefs.autoexec_paths): + row = box.row() + row.prop(path_cmp, "path", text="") + row.prop(path_cmp, "use_glob", text="", icon='FILTER') + row.operator("wm.userpref_autoexec_path_remove", text="", icon='X', emboss=False).index = i + + +class USERPREF_PT_file_saveload(PreferencePanel): + bl_label = "Save & Load" + bl_options = {'DEFAULT_CLOSED'} + + @classmethod + def poll(cls, context): + prefs = context.preferences + return (prefs.active_section == 'SYSTEM_FILES') + + def draw_props(self, context, layout): prefs = context.preferences paths = prefs.filepaths system = prefs.system - split = layout.split(factor=0.7) - - col = split.column() - col.label(text="File Paths:") - - colsplit = col.split(factor=0.95) - col1 = colsplit.split(factor=0.3) - - sub = col1.column() - sub.label(text="Fonts:") - sub.label(text="Textures:") - sub.label(text="Render Output:") - sub.label(text="Scripts:") - sub.label(text="Sounds:") - sub.label(text="Temp:") - sub.label(text="Render Cache:") - sub.label(text="I18n Branches:") - sub.label(text="Image Editor:") - sub.label(text="Animation Player:") - - sub = col1.column() - sub.prop(paths, "font_directory", text="") - sub.prop(paths, "texture_directory", text="") - sub.prop(paths, "render_output_directory", text="") - sub.prop(paths, "script_directory", text="") - sub.prop(paths, "sound_directory", text="") - sub.prop(paths, "temporary_directory", text="") - sub.prop(paths, "render_cache_directory", text="") - sub.prop(paths, "i18n_branches_directory", text="") - sub.prop(paths, "image_editor", text="") - subsplit = sub.split(factor=0.3) - subsplit.prop(paths, "animation_player_preset", text="") - subsplit.prop(paths, "animation_player", text="") - - col.separator() - col.separator() - - colsplit = col.split(factor=0.95) - sub = colsplit.column() - - row = sub.split(factor=0.3) - row.label(text="Auto Execution:") - row.prop(system, "use_scripts_auto_execute") - - if system.use_scripts_auto_execute: - box = sub.box() - row = box.row() - row.label(text="Excluded Paths:") - row.operator("wm.userpref_autoexec_path_add", text="", icon='ADD', emboss=False) - for i, path_cmp in enumerate(prefs.autoexec_paths): - row = box.row() - row.prop(path_cmp, "path", text="") - row.prop(path_cmp, "use_glob", text="", icon='FILTER') - row.operator("wm.userpref_autoexec_path_remove", text="", icon='X', emboss=False).index = i - - col = split.column() - col.label(text="Save & Load:") - col.prop(paths, "use_relative_paths") - col.prop(paths, "use_file_compression") - col.prop(paths, "use_load_ui") - col.prop(paths, "use_filter_files") - col.prop(paths, "show_hidden_files_datablocks") - col.prop(paths, "hide_recent_locations") - col.prop(paths, "hide_system_bookmarks") - col.prop(paths, "show_thumbnails") - - col.separator() - - col.prop(paths, "save_version") - col.prop(paths, "recent_files") - col.prop(paths, "use_save_preview_images") - - col.separator() - - col.label(text="Auto Save:") - col.prop(paths, "use_keep_session") - col.prop(paths, "use_auto_save_temporary_files") - sub = col.column() + flow = layout.grid_flow(row_major=True, columns=0, even_columns=False, even_rows=False, align=False) + + flow.prop(paths, "use_relative_paths") + flow.prop(paths, "use_file_compression") + flow.prop(paths, "use_load_ui") + flow.prop(paths, "use_filter_files") + flow.prop(paths, "show_hidden_files_datablocks") + flow.prop(paths, "hide_recent_locations") + flow.prop(paths, "hide_system_bookmarks") + flow.prop(paths, "show_thumbnails") + flow.prop(paths, "use_save_preview_images") + + layout.separator() + + layout.prop(paths, "save_version") + layout.prop(paths, "recent_files") + + +class USERPREF_PT_file_saveload_autosave(PreferencePanel): + bl_label = "Auto Save" + bl_parent_id = "USERPREF_PT_file_saveload" + bl_options = {'DEFAULT_CLOSED'} + + def draw_props(self, context, layout): + prefs = context.preferences + paths = prefs.filepaths + system = prefs.system + + layout.prop(paths, "use_keep_session") + layout.prop(paths, "use_auto_save_temporary_files") + sub = layout.column() sub.active = paths.use_auto_save_temporary_files sub.prop(paths, "auto_save_time", text="Timer (mins)") - col.separator() - col.label(text="Text Editor:") - col.prop(system, "use_tabs_as_spaces") +class USERPREF_PT_file_saveload_texteditor(PreferencePanel): + bl_label = "Text Editor" + bl_parent_id = "USERPREF_PT_file_saveload" + bl_options = {'DEFAULT_CLOSED'} - colsplit = col.split(factor=0.95) - col1 = colsplit.split(factor=0.3) + def draw_props(self, context, layout): + prefs = context.preferences + paths = prefs.filepaths - sub = col1.column() - sub.label(text="Author:") - sub = col1.column() - sub.prop(system, "author", text="") + layout.prop(paths, "use_tabs_as_spaces") + layout.prop(paths, "author", text="Author") class USERPREF_MT_ndof_settings(Menu): @@ -1040,146 +1307,231 @@ class USERPREF_MT_ndof_settings(Menu): layout.prop(input_prefs, "ndof_lock_horizon", icon='NDOF_DOM') -class USERPREF_MT_keyconfigs(Menu): - bl_label = "KeyPresets" - preset_subdir = "keyconfig" - preset_operator = "wm.keyconfig_activate" +class USERPREF_PT_input_devices(PreferencePanel): + bl_label = "Devices" + + @classmethod + def poll(cls, context): + prefs = context.preferences + return (prefs.active_section == 'INPUT') def draw(self, context): - Menu.draw_preset(self, context) + pass -class USERPREF_PT_input(Panel): - bl_space_type = 'PREFERENCES' - bl_label = "Input" - bl_region_type = 'WINDOW' - bl_options = {'HIDE_HEADER'} +class USERPREF_PT_input_devices_keyboard(PreferencePanel): + bl_label = "Keyboard" + bl_parent_id = "USERPREF_PT_input_devices" + + def draw_props(self, context, layout): + prefs = context.preferences + inputs = prefs.inputs + + layout.prop(inputs, "use_emulate_numpad") + + +class USERPREF_PT_input_devices_mouse(PreferencePanel): + bl_label = "Mouse" + bl_parent_id = "USERPREF_PT_input_devices" + + def draw_props(self, context, layout): + prefs = context.preferences + inputs = prefs.inputs + + layout.prop(inputs, "drag_threshold") + layout.prop(inputs, "tweak_threshold") + layout.prop(inputs, "mouse_double_click_time", text="Double Click Speed") + layout.prop(inputs, "use_mouse_emulate_3_button") + layout.prop(inputs, "use_mouse_continuous") + + +class USERPREF_PT_input_view(PreferencePanel): + bl_label = "View Manipulation" + bl_options = {'DEFAULT_CLOSED'} @classmethod def poll(cls, context): prefs = context.preferences return (prefs.active_section == 'INPUT') - @staticmethod - def draw_input_prefs(inputs, layout): - import sys + def draw(self, context): + pass - # General settings - sub = layout.column() - sub.label(text="Mouse:") - sub.prop(inputs, "use_mouse_emulate_3_button") - sub.prop(inputs, "use_mouse_continuous") - sub.prop(inputs, "drag_threshold") - sub.prop(inputs, "tweak_threshold") +class USERPREF_PT_input_view_orbit(PreferencePanel): + bl_label = "Orbit & Pan" + bl_parent_id = "USERPREF_PT_input_view" + bl_options = {'DEFAULT_CLOSED'} - sub = layout.column() - sub.label(text="Double Click:") - sub.prop(inputs, "mouse_double_click_time", text="Speed") + def draw_props(self, context, layout): + import sys + prefs = context.preferences + inputs = prefs.inputs - sub.separator() + layout.row().prop(inputs, "view_rotate_method", expand=True) + layout.prop(inputs, "use_rotate_around_active") + layout.prop(inputs, "use_auto_perspective") + layout.prop(inputs, "use_mouse_depth_navigate") - sub.prop(inputs, "use_emulate_numpad") + if sys.platform == "darwin": + layout.prop(inputs, "use_trackpad_natural", text="Natural Trackpad Direction") - sub.separator() - sub.label(text="Orbit Style:") - sub.row().prop(inputs, "view_rotate_method", expand=True) +class USERPREF_PT_input_view_zoom(PreferencePanel): + bl_label = "Zoom" + bl_parent_id = "USERPREF_PT_input_view" + bl_options = {'DEFAULT_CLOSED'} - sub.separator() + def draw_props(self, context, layout): + prefs = context.preferences + inputs = prefs.inputs - sub.label(text="Zoom Style:") - sub.row().prop(inputs, "view_zoom_method", text="") + layout.row().prop(inputs, "view_zoom_method", text="Zoom Method", expand=True) if inputs.view_zoom_method in {'DOLLY', 'CONTINUE'}: - sub.row().prop(inputs, "view_zoom_axis", expand=True) - sub.prop(inputs, "invert_mouse_zoom", text="Invert Mouse Zoom Direction") + layout.row().prop(inputs, "view_zoom_axis", expand=True) + layout.prop(inputs, "invert_mouse_zoom", text="Invert Mouse Zoom Direction") - # layout.separator() + layout.prop(inputs, "invert_zoom_wheel", text="Invert Wheel Zoom Direction") + # sub.prop(view, "wheel_scroll_lines", text="Scroll Lines") + layout.prop(inputs, "use_zoom_to_mouse") - sub = layout.column() - sub.prop(inputs, "invert_zoom_wheel", text="Invert Wheel Zoom Direction") - #sub.prop(view, "wheel_scroll_lines", text="Scroll Lines") - if sys.platform == "darwin": - sub = layout.column() - sub.prop(inputs, "use_trackpad_natural", text="Natural Trackpad Direction") +class USERPREF_PT_input_view_cursor(PreferencePanel): + bl_label = "Cursor" + bl_parent_id = "USERPREF_PT_input_view" + bl_options = {'DEFAULT_CLOSED'} - layout.separator() - sub = layout.column() - sub.label(text="View Navigation:") - sub.row().prop(inputs, "navigation_mode", expand=True) + def draw_props(self, context, layout): + prefs = context.preferences + inputs = prefs.inputs + + layout.prop(inputs, "use_mouse_depth_cursor") + layout.prop(inputs, "use_cursor_lock_adjust") - sub.label(text="Walk Navigation:") + +class USERPREF_PT_input_view_fly_walk(PreferencePanel): + bl_label = "Fly & Walk" + bl_parent_id = "USERPREF_PT_input_view" + bl_options = {'DEFAULT_CLOSED'} + + def draw_props(self, context, layout): + prefs = context.preferences + inputs = prefs.inputs + + layout.row().prop(inputs, "navigation_mode", expand=True) + layout.prop(inputs, "use_camera_lock_parent") + + layout.label(text="Walk Navigation:") walk = inputs.walk_navigation - sub.prop(walk, "use_mouse_reverse") - sub.prop(walk, "mouse_speed") - sub.prop(walk, "teleport_time") + layout.prop(walk, "use_mouse_reverse") + layout.prop(walk, "mouse_speed") + layout.prop(walk, "teleport_time") sub = layout.column(align=True) sub.prop(walk, "walk_speed") sub.prop(walk, "walk_speed_factor") - sub.separator() - sub.prop(walk, "use_gravity") - sub = layout.column(align=True) - sub.active = walk.use_gravity - sub.prop(walk, "view_height") - sub.prop(walk, "jump_height") - sub.separator() - sub = layout.column() - sub.label(text="Tablet Pressure:") - sub.prop(inputs, "pressure_threshold_max") - sub.prop(inputs, "pressure_softness") +class USERPREF_PT_input_view_fly_walk_gravity(PreferencePanel): + bl_label = "Gravity" + bl_parent_id = "USERPREF_PT_input_view_fly_walk" - if inputs.use_ndof: - layout.separator() - layout.label(text="NDOF Device:") - sub = layout.column(align=True) - sub.prop(inputs, "ndof_sensitivity", text="Pan Sensitivity") - sub.prop(inputs, "ndof_orbit_sensitivity", text="Orbit Sensitivity") - sub.prop(inputs, "ndof_deadzone", text="Deadzone") - - sub.separator() - layout.label(text="Navigation Style:") - sub = layout.column(align=True) - sub.row().prop(inputs, "ndof_view_navigate_method", expand=True) - - sub.separator() - layout.label(text="Rotation Style:") - sub = layout.column(align=True) - sub.row().prop(inputs, "ndof_view_rotate_method", expand=True) + def draw_header(self, context): + prefs = context.preferences + inputs = prefs.inputs + walk = inputs.walk_navigation - def draw(self, context): - from rna_keymap_ui import draw_keymaps + self.layout.prop(walk, "use_gravity", text="") - layout = self.layout + def draw_props(self, context, layout): + prefs = context.preferences + inputs = prefs.inputs + walk = inputs.walk_navigation + + layout.active = walk.use_gravity + layout.prop(walk, "view_height") + layout.prop(walk, "jump_height") + + +class USERPREF_PT_input_devices_tablet(PreferencePanel): + bl_label = "Tablet" + bl_parent_id = "USERPREF_PT_input_devices" + bl_options = {'DEFAULT_CLOSED'} + + def draw_props(self, context, layout): + prefs = context.preferences + inputs = prefs.inputs + + layout.prop(inputs, "pressure_threshold_max") + layout.prop(inputs, "pressure_softness") - #import time - #start = time.time() +class USERPREF_PT_input_devices_ndof(PreferencePanel): + bl_label = "NDOF" + bl_parent_id = "USERPREF_PT_input_devices" + bl_options = {'DEFAULT_CLOSED'} + @classmethod + def poll(cls, context): prefs = context.preferences + inputs = prefs.inputs + if inputs.use_ndof: + return (prefs.active_section == 'INPUT') + def draw_props(self, context, layout): + prefs = context.preferences inputs = prefs.inputs - split = layout.split(factor=0.25) + layout.prop(inputs, "ndof_sensitivity", text="Pan Sensitivity") + layout.prop(inputs, "ndof_orbit_sensitivity", text="Orbit Sensitivity") + layout.prop(inputs, "ndof_deadzone", text="Deadzone") - row = split.row() - col = row.column() + layout.separator() + + layout.row().prop(inputs, "ndof_view_navigate_method", expand=True) + layout.row().prop(inputs, "ndof_view_rotate_method", expand=True) + + +class USERPREF_MT_keyconfigs(Menu): + bl_label = "KeyPresets" + preset_subdir = "keyconfig" + preset_operator = "wm.keyconfig_activate" + + def draw(self, context): + Menu.draw_preset(self, context) + + +class USERPREF_PT_keymap(Panel): + bl_space_type = 'PREFERENCES' + bl_label = "Keymap" + bl_region_type = 'WINDOW' + bl_options = {'HIDE_HEADER'} + + @classmethod + def poll(cls, context): + prefs = context.preferences + return (prefs.active_section == 'KEYMAP') + + def draw(self, context): + from rna_keymap_ui import draw_keymaps + + layout = self.layout + + # import time - # Input settings - self.draw_input_prefs(inputs, col) + # start = time.time() + + prefs = context.preferences + keymappref = prefs.keymap - row.separator() + col = layout.column() # Keymap Settings - col = split.column() draw_keymaps(context, col) - #print("runtime", time.time() - start) + # print("runtime", time.time() - start) class USERPREF_MT_addons_online_resources(Menu): @@ -1281,17 +1633,19 @@ class USERPREF_PT_addons(Panel): for mod in addon_utils.modules(refresh=False) ] - split = layout.split(factor=0.2) - col = split.column() - col.prop(context.window_manager, "addon_search", text="", icon='VIEWZOOM') + row = layout.row() + row.operator("wm.addon_install", icon='IMPORT', text="Install...") + row.operator("wm.addon_refresh", icon='FILE_REFRESH', text="Refresh") + row.menu("USERPREF_MT_addons_online_resources", text="Online Resources") - col.label(text="Supported Level") - col.prop(context.window_manager, "addon_support", expand=True) + layout.separator() - col.label(text="Categories") - col.prop(context.window_manager, "addon_filter", expand=True) + row = layout.row() + row.prop(context.window_manager, "addon_support", expand=True) + row.prop(context.window_manager, "addon_filter", text="") + row.prop(context.window_manager, "addon_search", text="", icon='VIEWZOOM') - col = split.column() + col = layout.column() # set in addon_utils.modules_refresh() if addon_utils.error_duplicates: @@ -1378,7 +1732,7 @@ class USERPREF_PT_addons(Panel): # WARNING: 2.8x exception, may be removed # use disabled state for old add-ons, chances are they are broken. if is_addon_27x: - sub.label(text="Upgrade to 2.8x required") + sub.label(text="upgrade to 2.8x required") sub.label(icon='ERROR') # Remove code above after 2.8x migration is complete. elif info["warning"]: @@ -1441,7 +1795,7 @@ class USERPREF_PT_addons(Panel): for _ in range(4 - tot_row): split.separator() - # Show addon preferences + # Show addon user preferences if is_enabled: addon_preferences = prefs.addons[module_name].preferences if addon_preferences is not None: @@ -1486,6 +1840,29 @@ class USERPREF_PT_addons(Panel): row.label(text=module_name, translate=False) +class USERPREF_PT_studiolight_add(PreferencePanel): + bl_space_type = 'PREFERENCES' + bl_label = "Add Lights" + bl_region_type = 'WINDOW' + bl_options = {'HIDE_HEADER'} + + @classmethod + def poll(cls, context): + prefs = context.preferences + return (prefs.active_section == 'LIGHTS') + + def draw(self, context): + layout = self.layout + prefs = context.preferences + + row = layout.row() + row.operator("wm.studiolight_install", icon='IMPORT', text="Add MatCap...").type = 'MATCAP' + row.operator("wm.studiolight_install", icon='IMPORT', text="Add LookDev HDRI...").type = 'WORLD' + op = row.operator("wm.studiolight_install", icon='IMPORT', text="Add Studio Light...") + op.type = 'STUDIO' + op.filter_glob = ".sl" + + class StudioLightPanelMixin(): bl_space_type = 'PREFERENCES' bl_region_type = 'WINDOW' @@ -1507,7 +1884,7 @@ class StudioLightPanelMixin(): def draw_light_list(self, layout, lights): if lights: - flow = layout.column_flow(columns=4) + flow = layout.grid_flow(row_major=True, columns=0, even_columns=False, even_rows=False, align=False) for studio_light in lights: self.draw_studio_light(flow, studio_light) else: @@ -1549,6 +1926,7 @@ class USERPREF_PT_studiolight_light_editor(Panel): bl_parent_id = "USERPREF_PT_studiolight_lights" bl_space_type = 'PREFERENCES' bl_region_type = 'WINDOW' + bl_options = {'DEFAULT_CLOSED'} def opengl_light_buttons(self, layout, light): @@ -1597,26 +1975,93 @@ class USERPREF_PT_studiolight_light_editor(Panel): layout.prop(system, "light_ambient") -classes = ( +ThemeGenericClassGenerator.generate_panel_classes_for_wcols() + +# Order of registration defines order in UI, so dynamically generated classes are 'injected' in the intended order. +classes = (USERPREF_PT_theme_user_interface,) + tuple(ThemeGenericClassGenerator.generated_classes) + +classes += ( USERPREF_HT_header, USERPREF_PT_navigation, - USERPREF_PT_interface, - USERPREF_PT_edit, - USERPREF_PT_system_general, + USERPREF_PT_save_preferences, + + USERPREF_PT_interface_display, + USERPREF_PT_interface_display_info, + USERPREF_PT_interface_text, + USERPREF_PT_interface_text_translate, + USERPREF_PT_interface_viewports, + USERPREF_PT_interface_viewports_3d, + USERPREF_PT_interface_viewports_3d_weight_paint, + USERPREF_PT_interface_viewports_2d, + USERPREF_PT_interface_menus, + USERPREF_PT_interface_menus_mouse_over, + USERPREF_PT_interface_menus_pie, + USERPREF_PT_interface_develop, + USERPREF_PT_interface_templates, + + USERPREF_PT_edit_objects, + USERPREF_PT_edit_animation, + USERPREF_PT_edit_animation_autokey, + USERPREF_PT_edit_animation_fcurves, + USERPREF_PT_edit_transform, + USERPREF_PT_edit_duplicate_data, + USERPREF_PT_edit_gpencil, + USERPREF_PT_edit_annotations, + USERPREF_PT_edit_misc, + + USERPREF_PT_system_opengl, + USERPREF_PT_system_opengl_textures, + USERPREF_PT_system_opengl_selection, + USERPREF_PT_system_sound, + USERPREF_PT_system_compute_device, + USERPREF_PT_system_memory, + USERPREF_MT_interface_theme_presets, USERPREF_PT_theme, - USERPREF_PT_file, + USERPREF_PT_theme_interface_state, + USERPREF_PT_theme_interface_styles, + USERPREF_PT_theme_interface_gizmos, + USERPREF_PT_theme_interface_icons, + USERPREF_PT_theme_text_style, + USERPREF_PT_theme_bone_color_sets, + + USERPREF_PT_file_paths, + USERPREF_PT_file_autorun, + USERPREF_PT_file_saveload, + USERPREF_PT_file_saveload_autosave, + USERPREF_PT_file_saveload_texteditor, + USERPREF_MT_ndof_settings, USERPREF_MT_keyconfigs, - USERPREF_PT_input, + + USERPREF_PT_input_devices, + USERPREF_PT_input_devices_keyboard, + USERPREF_PT_input_devices_mouse, + USERPREF_PT_input_devices_tablet, + USERPREF_PT_input_devices_ndof, + USERPREF_PT_input_view, + USERPREF_PT_input_view_orbit, + USERPREF_PT_input_view_zoom, + USERPREF_PT_input_view_cursor, + USERPREF_PT_input_view_fly_walk, + USERPREF_PT_input_view_fly_walk_gravity, + + USERPREF_PT_keymap, USERPREF_MT_addons_online_resources, USERPREF_PT_addons, + + USERPREF_PT_studiolight_add, USERPREF_PT_studiolight_lights, USERPREF_PT_studiolight_light_editor, USERPREF_PT_studiolight_matcaps, USERPREF_PT_studiolight_world, ) +# Add dynamically generated editor theme panels last, so they show up last in the theme section. +ThemeGenericClassGenerator.generated_classes.clear() +ThemeGenericClassGenerator.generate_panel_classes_from_theme_areas() +classes += tuple(ThemeGenericClassGenerator.generated_classes) + if __name__ == "__main__": # only for live edit. from bpy.utils import register_class for cls in classes: -- cgit v1.2.3