UI: Preferences Redesign Part 2
authorSeverin <eiseljulian@gmail.com>
Fri, 4 Jan 2019 20:40:16 +0000 (21:40 +0100)
committerJulian Eisel <eiseljulian@gmail.com>
Fri, 4 Jan 2019 21:18:23 +0000 (22:18 +0100)
(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

25 files changed:
intern/cycles/blender/addon/properties.py
release/datafiles/userdef/userdef_default_theme.c
release/scripts/modules/bpy/utils/__init__.py
release/scripts/modules/rna_keymap_ui.py
release/scripts/startup/bl_ui/space_userpref.py
source/blender/blenkernel/BKE_blender_version.h
source/blender/blenloader/intern/versioning_280.c
source/blender/blenloader/intern/versioning_userdef.c
source/blender/editors/include/ED_screen.h
source/blender/editors/interface/interface_layout.c
source/blender/editors/interface/interface_ops.c
source/blender/editors/interface/interface_panel.c
source/blender/editors/interface/resources.c
source/blender/editors/screen/area.c
source/blender/editors/screen/screen_edit.c
source/blender/editors/screen/screen_intern.h
source/blender/editors/screen/screen_ops.c
source/blender/editors/space_userpref/space_userpref.c
source/blender/makesdna/DNA_screen_types.h
source/blender/makesdna/DNA_userdef_types.h
source/blender/makesrna/RNA_access.h
source/blender/makesrna/intern/rna_screen.c
source/blender/makesrna/intern/rna_space.c
source/blender/makesrna/intern/rna_userdef.c
source/blender/windowmanager/intern/wm_files.c

index 3f6c6de..9cbb2e8 100644 (file)
@@ -1485,7 +1485,6 @@ class CyclesPreferences(bpy.types.AddonPreferences):
 
     def draw_impl(self, layout, context):
         available_device_types = self.get_device_types(context)
-        layout.label(text="Cycles Compute Device:")
         if len(available_device_types) == 1:
             layout.label(text="No compatible GPUs found", icon='INFO')
             return
index 6fe61c6..7a7f804 100644 (file)
@@ -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),
index 5fbff2e..b4c8c08 100644 (file)
@@ -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")
index 3a44745..268bcb6 100644 (file)
@@ -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")
index 7dacb8d..a115213 100644 (file)
@@ -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:
index 2d044fd..bc2212f 100644 (file)
@@ -28,7 +28,7 @@
  * and keep comment above the defines.
  * Use STRINGIFY() rather than defining with quotes */
 #define BLENDER_VERSION         280
-#define BLENDER_SUBVERSION      39
+#define BLENDER_SUBVERSION      40
 /* Several breakages with 280, e.g. collections vs layers */
 #define BLENDER_MINVERSION      280
 #define BLENDER_MINSUBVERSION   0
index 3b29508..bf416fd 100644 (file)
@@ -2382,12 +2382,13 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain)
                                                ARegion *navigation_region = BKE_spacedata_find_region_type(slink, area, RGN_TYPE_NAV_BAR);
 
                                                if (!navigation_region) {
+                                                       ARegion *main_region = BKE_spacedata_find_region_type(slink, area, RGN_TYPE_WINDOW);
                                                        ListBase *regionbase = (slink == area->spacedata.first) ?
                                                                               &area->regionbase : &slink->regionbase;
 
                                                        navigation_region = MEM_callocN(sizeof(ARegion), "userpref navigation-region do_versions");
 
-                                                       BLI_addhead(regionbase, navigation_region); /* order matters, addhead not addtail! */
+                                                       BLI_insertlinkbefore(regionbase, main_region, navigation_region); /* order matters, addhead not addtail! */
                                                        navigation_region->regiontype = RGN_TYPE_NAV_BAR;
                                                        navigation_region->alignment = RGN_ALIGN_LEFT;
                                                }
@@ -2691,9 +2692,7 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain)
                }
        }
 
-       {
-               /* Versioning code until next subversion bump goes here. */
-
+       if (!MAIN_VERSION_ATLEAST(bmain, 280, 40)) {
                if (!DNA_struct_elem_find(fd->filesdna, "ToolSettings", "char", "snap_transform_mode_flag")) {
                        for (Scene *scene = bmain->scene.first; scene; scene = scene->id.next) {
                                scene->toolsettings->snap_transform_mode_flag =
@@ -2748,5 +2747,34 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain)
                                ca->gpu_dof.ratio = 0.01f;
                        }
                }
+
+               for (bScreen *screen = bmain->screen.first; screen; screen = screen->id.next) {
+                       for (ScrArea *area = screen->areabase.first; area; area = area->next) {
+                               for (SpaceLink *sl = area->spacedata.first; sl; sl = sl->next) {
+                                       if (sl->spacetype == SPACE_USERPREF) {
+                                               ARegion *execute_region = BKE_spacedata_find_region_type(sl, area, RGN_TYPE_EXECUTE);
+
+                                               if (!execute_region) {
+                                                       ListBase *regionbase = (sl == area->spacedata.first) ? &area->regionbase : &sl->regionbase;
+                                                       ARegion *ar_navbar = BKE_spacedata_find_region_type(sl, area, RGN_TYPE_NAV_BAR);
+
+                                                       execute_region = MEM_callocN(sizeof(ARegion), "execute region for properties");
+
+                                                       BLI_assert(ar_navbar);
+
+                                                       BLI_insertlinkafter(regionbase, ar_navbar, execute_region);
+
+                                                       execute_region->regiontype = RGN_TYPE_EXECUTE;
+                                                       execute_region->alignment = RGN_ALIGN_BOTTOM | RGN_SPLIT_PREV;
+                                                       execute_region->flag |= RGN_FLAG_DYNAMIC_SIZE;
+                                               }
+                                       }
+                               }
+                       }
+               }
+       }
+
+       {
+               /* Versioning code until next subversion bump goes here. */
        }
 }
index bb0ad9d..de5a1c8 100644 (file)
@@ -106,6 +106,11 @@ static void do_versions_theme(UserDef *userdef, bTheme *btheme)
                copy_v4_v4_char(btheme->tclip.metadatabg, U_theme_default.tima.metadatabg);
                copy_v4_v4_char(btheme->tclip.metadatatext, U_theme_default.tima.metadatatext);
        }
+
+       if (!USER_VERSION_ATLEAST(280, 40)) {
+               copy_v4_v4_char(btheme->tuserpref.execution_buts, btheme->tuserpref.navigation_bar);
+       }
+
 #undef USER_VERSION_ATLEAST
 }
 
index 4adf040..9ad4c06 100644 (file)
@@ -143,7 +143,7 @@ void    ED_area_tag_redraw_no_rebuild(ScrArea *sa);
 void    ED_area_tag_redraw_regiontype(ScrArea *sa, int type);
 void    ED_area_tag_refresh(ScrArea *sa);
 void    ED_area_do_refresh(struct bContext *C, ScrArea *sa);
-void    ED_area_azones_update(ScrArea *sa, const int mouse_xy[]);
+struct AZone *ED_area_azones_update(ScrArea *sa, const int mouse_xy[]);
 void    ED_area_status_text(ScrArea *sa, const char *str);
 void    ED_area_newspace(struct bContext *C, ScrArea *sa, int type, const bool skip_ar_exit);
 void    ED_area_prevspace(struct bContext *C, ScrArea *sa);
index 45c0498..09033a5 100644 (file)
@@ -658,7 +658,6 @@ static void ui_item_enum_expand_exec(
        uiLayout *layout_radial = NULL;
        const EnumPropertyItem *item, *item_array;
        const char *name;
-       char group_name[UI_MAX_NAME_STR];
        int itemw, icon, value;
        bool free;
        bool radial = (layout->root->type == UI_LAYOUT_PIEMENU);
@@ -703,8 +702,7 @@ static void ui_item_enum_expand_exec(
                                        if (!is_first) {
                                                uiItemS(block->curlayout);
                                        }
-                                       BLI_snprintf(group_name, sizeof(group_name), "%s:", item->name);
-                                       uiItemL(block->curlayout, group_name, item->icon);
+                                       uiItemL(block->curlayout, item->name, item->icon);
                                }
                                else if (radial && layout_radial) {
                                        uiItemS(layout_radial);
index 1cb9f15..484debf 100644 (file)
@@ -1345,7 +1345,7 @@ static int edittranslation_exec(bContext *C, wmOperator *op)
                if (!BLI_is_dir(root)) {
                        BKE_report(
                                op->reports, RPT_ERROR,
-                               "Please set your User Preferences' 'Translation Branches "
+                               "Please set your Preferences' 'Translation Branches "
                                "Directory' path to a valid directory");
                        return OPERATOR_CANCELLED;
                }
index 610c0d3..469720c 100644 (file)
@@ -147,7 +147,7 @@ static int panel_aligned(ScrArea *sa, ARegion *ar)
                return BUT_VERTICAL;
        else if (sa->spacetype == SPACE_IMAGE && ar->regiontype == RGN_TYPE_PREVIEW)
                return BUT_VERTICAL;
-       else if (ELEM(ar->regiontype, RGN_TYPE_UI, RGN_TYPE_TOOLS, RGN_TYPE_TOOL_PROPS, RGN_TYPE_HUD, RGN_TYPE_NAV_BAR))
+       else if (ELEM(ar->regiontype, RGN_TYPE_UI, RGN_TYPE_TOOLS, RGN_TYPE_TOOL_PROPS, RGN_TYPE_HUD, RGN_TYPE_NAV_BAR, RGN_TYPE_EXECUTE))
                return BUT_VERTICAL;
 
        return 0;
index fc761ce..d7451b0 100644 (file)
@@ -183,6 +183,8 @@ const uchar *UI_ThemeGetColorPtr(bTheme *btheme, int spacetype, int colorid)
                                                cp = ts->header;
                                        else if (theme_regionid == RGN_TYPE_NAV_BAR)
                                                cp = ts->navigation_bar;
+                                       else if (theme_regionid == RGN_TYPE_EXECUTE)
+                                               cp = ts->execution_buts;
                                        else
                                                cp = ts->button;
 
index 47c4446..34a20c8 100644 (file)
@@ -186,52 +186,6 @@ void ED_area_do_refresh(bContext *C, ScrArea *sa)
        sa->do_refresh = false;
 }
 
-/**
- * Action zones are only updated if the mouse is inside of them, but in some cases (currently only fullscreen icon)
- * it might be needed to update their properties and redraw if the mouse isn't inside.
- */
-void ED_area_azones_update(ScrArea *sa, const int mouse_xy[2])
-{
-       AZone *az;
-       bool changed = false;
-
-       for (az = sa->actionzones.first; az; az = az->next) {
-               if (az->type == AZONE_FULLSCREEN) {
-                       /* only if mouse is not hovering the azone */
-                       if (BLI_rcti_isect_pt_v(&az->rect, mouse_xy) == false) {
-                               az->alpha = 0.0f;
-                               changed = true;
-
-                               /* can break since currently only this is handled here */
-                               break;
-                       }
-               }
-               else if (az->type == AZONE_REGION_SCROLL) {
-                       /* only if mouse is not hovering the azone */
-                       if (BLI_rcti_isect_pt_v(&az->rect, mouse_xy) == false) {
-                               View2D *v2d = &az->ar->v2d;
-
-                               if (az->direction == AZ_SCROLL_VERT) {
-                                       az->alpha = v2d->alpha_vert = 0;
-                                       changed = true;
-                               }
-                               else if (az->direction == AZ_SCROLL_HOR) {
-                                       az->alpha = v2d->alpha_hor = 0;
-                                       changed = true;
-                               }
-                               else {
-                                       BLI_assert(0);
-                               }
-                       }
-               }
-       }
-
-       if (changed) {
-               sa->flag &= ~AREA_FLAG_ACTIONZONES_UPDATE;
-               ED_area_tag_redraw_no_rebuild(sa);
-       }
-}
-
 /**
  * \brief Corner widget use for quitting fullscreen.
  */
@@ -412,18 +366,11 @@ static void region_draw_azones(ScrArea *sa, ARegion *ar)
                        }
                        else if (az->type == AZONE_FULLSCREEN) {
                                area_draw_azone_fullscreen(az->x1, az->y1, az->x2, az->y2, az->alpha);
-
-                               if (az->alpha != 0.0f) {
-                                       area_azone_tag_update(sa);
-                               }
-                       }
-                       else if (az->type == AZONE_REGION_SCROLL) {
-                               if (az->alpha != 0.0f) {
-                                       area_azone_tag_update(sa);
-                               }
-                               /* Don't draw this azone. */
                        }
                }
+               if (!IS_EQF(az->alpha, 0.0f) && ELEM(az->type, AZONE_FULLSCREEN, AZONE_REGION_SCROLL)) {
+                       area_azone_tag_update(sa);
+               }
        }
 
        GPU_matrix_pop();
@@ -1605,6 +1552,7 @@ void ED_area_update_region_sizes(wmWindowManager *wm, wmWindow *win, ScrArea *ar
        if (!(area->flag & AREA_FLAG_REGION_SIZE_UPDATE)) {
                return;
        }
+       const bScreen *screen = WM_window_get_active_screen(win);
 
        WM_window_rect_calc(win, &window_rect);
        area_calc_totrct(area, &window_rect);
@@ -1614,6 +1562,9 @@ void ED_area_update_region_sizes(wmWindowManager *wm, wmWindow *win, ScrArea *ar
        overlap_rect = rect;
        region_rect_recursive(area, area->regionbase.first, &rect, &overlap_rect, 0);
 
+       /* Dynamically sized regions may have changed region sizes, so we have to force azone update. */
+       area_azone_initialize(win, screen, area);
+
        for (ARegion *ar = area->regionbase.first; ar; ar = ar->next) {
                region_subwindow(ar);
 
@@ -1621,7 +1572,11 @@ void ED_area_update_region_sizes(wmWindowManager *wm, wmWindow *win, ScrArea *ar
                if (ar->type->init) {
                        ar->type->init(wm, ar);
                }
+
+               /* Some AZones use View2D data which is only updated in region init, so call that first! */
+               region_azones_add(screen, area, ar, ar->alignment & ~RGN_SPLIT_PREV);
        }
+       ED_area_azones_update(area, &win->eventstate->x);
 
        area->flag &= ~AREA_FLAG_REGION_SIZE_UPDATE;
 }
@@ -2386,6 +2341,9 @@ void ED_region_panels_draw(const bContext *C, ARegion *ar)
        /* set the view */
        UI_view2d_view_ortho(v2d);
 
+       /* View2D matrix might have changed due to dynamic sized regions. */
+       UI_blocklist_update_window_matrix(C, &ar->uiblocks);
+
        /* draw panels */
        UI_panels_draw(C, ar);
 
index 62d60c3..7e6e31d 100644 (file)
@@ -639,7 +639,7 @@ void ED_screen_set_active_region(bContext *C, wmWindow *win, const int xy[2])
                ED_screen_areas_iter(win, scr, area_iter) {
                        if (xy[0] > area_iter->totrct.xmin && xy[0] < area_iter->totrct.xmax) {
                                if (xy[1] > area_iter->totrct.ymin && xy[1] < area_iter->totrct.ymax) {
-                                       if (ED_area_actionzone_refresh_xy(area_iter, xy) == NULL) {
+                                       if (ED_area_azones_update(area_iter, xy) == NULL) {
                                                sa = area_iter;
                                                break;
                                        }
@@ -1251,7 +1251,7 @@ ScrArea *ED_screen_state_toggle(bContext *C, wmWindow *win, ScrArea *sa, const s
                        for (ar = newa->regionbase.first; ar; ar = ar->next) {
                                ar->flagfullscreen = ar->flag;
 
-                               if (ELEM(ar->regiontype, RGN_TYPE_UI, RGN_TYPE_HEADER, RGN_TYPE_TOOLS, RGN_TYPE_NAV_BAR)) {
+                               if (ELEM(ar->regiontype, RGN_TYPE_UI, RGN_TYPE_HEADER, RGN_TYPE_TOOLS, RGN_TYPE_NAV_BAR, RGN_TYPE_EXECUTE)) {
                                        ar->flag |= RGN_FLAG_HIDDEN;
                                }
                        }
index 7722f2c..5f36f4a 100644 (file)
@@ -57,7 +57,6 @@ int         screen_area_join(struct bContext *C, bScreen *scr, ScrArea *sa1, Scr
 int         area_getorientation(ScrArea *sa, ScrArea *sb);
 
 struct AZone *ED_area_actionzone_find_xy(ScrArea *sa, const int xy[2]);
-struct AZone *ED_area_actionzone_refresh_xy(ScrArea *sa, const int xy[2]);
 
 /* screen_geometry.c */
 int         screen_geom_area_height(const ScrArea *area);
index d41bf8d..62b4db9 100644 (file)
@@ -798,6 +798,32 @@ static AZone *area_actionzone_refresh_xy(ScrArea *sa, const int xy[2], const boo
                                }
                        }
                }
+               else if (!test_only && !IS_EQF(az->alpha, 0.0f)) {
+                       bool changed = false;
+
+                       if (az->type == AZONE_FULLSCREEN) {
+                               az->alpha = 0.0f;
+                               changed = true;
+                       }
+                       else if (az->type == AZONE_REGION_SCROLL) {
+                               if (az->direction == AZ_SCROLL_VERT) {
+                                       az->alpha = az->ar->v2d.alpha_vert = 0;
+                                       changed = true;
+                               }
+                               else if (az->direction == AZ_SCROLL_HOR) {
+                                       az->alpha = az->ar->v2d.alpha_hor = 0;
+                                       changed = true;
+                               }
+                               else {
+                                       BLI_assert(0);
+                               }
+                       }
+
+                       if (changed) {
+                               sa->flag &= ~AREA_FLAG_ACTIONZONES_UPDATE;
+                               ED_area_tag_redraw_no_rebuild(sa);
+                       }
+               }
        }
 
        return az;
@@ -808,7 +834,7 @@ AZone *ED_area_actionzone_find_xy(ScrArea *sa, const int xy[2])
        return area_actionzone_refresh_xy(sa, xy, true);
 }
 
-AZone *ED_area_actionzone_refresh_xy(ScrArea *sa, const int xy[2])
+AZone *ED_area_azones_update(ScrArea *sa, const int xy[2])
 {
        return area_actionzone_refresh_xy(sa, xy, false);
 }
@@ -2382,8 +2408,12 @@ static int region_scale_modal(bContext *C, wmOperator *op, const wmEvent *event)
                                        if (!(rmd->ar->flag & RGN_FLAG_HIDDEN))
                                                region_scale_toggle_hidden(C, rmd);
                                }
-                               else if (rmd->ar->flag & RGN_FLAG_HIDDEN)
+                               else if (rmd->ar->flag & RGN_FLAG_HIDDEN) {
                                        region_scale_toggle_hidden(C, rmd);
+                               }
+                               else if (rmd->ar->flag & RGN_FLAG_DYNAMIC_SIZE) {
+                                       rmd->ar->sizex = rmd->origval;
+                               }
                        }
                        else {
                                int maxsize = region_scale_get_maxsize(rmd);
@@ -2411,10 +2441,15 @@ static int region_scale_modal(bContext *C, wmOperator *op, const wmEvent *event)
                                        if (!(rmd->ar->flag & RGN_FLAG_HIDDEN))
                                                region_scale_toggle_hidden(C, rmd);
                                }
-                               else if (maxsize > 0 && (rmd->ar->sizey > maxsize))
+                               else if (maxsize > 0 && (rmd->ar->sizey > maxsize)) {
                                        rmd->ar->sizey = maxsize;
-                               else if (rmd->ar->flag & RGN_FLAG_HIDDEN)
+                               }
+                               else if (rmd->ar->flag & RGN_FLAG_HIDDEN) {
                                        region_scale_toggle_hidden(C, rmd);
+                               }
+                               else if (rmd->ar->flag & RGN_FLAG_DYNAMIC_SIZE) {
+                                       rmd->ar->sizey = rmd->origval;
+                               }
                        }
                        ED_area_tag_redraw(rmd->sa);
                        WM_event_add_notifier(C, NC_SCREEN | NA_EDITED, NULL);
@@ -4349,11 +4384,15 @@ static void SCREEN_OT_back_to_previous(struct wmOperatorType *ot)
 
 static int userpref_show_invoke(bContext *C, wmOperator *op, const wmEvent *event)
 {
-       int sizex = (800 + UI_NAVIGATION_REGION_WIDTH) * UI_DPI_FAC;
-       int sizey = 500 * UI_DPI_FAC;
+       int sizex = (500 + UI_NAVIGATION_REGION_WIDTH) * UI_DPI_FAC;
+       int sizey = 520 * UI_DPI_FAC;
 
        /* changes context! */
        if (WM_window_open_temp(C, event->x, event->y, sizex, sizey, WM_WINDOW_USERPREFS) != NULL) {
+               /* The header only contains the editor switcher and looks empty. So hiding in the temp window makes sense. */
+               ScrArea *area = CTX_wm_area(C);
+               ARegion *region = BKE_area_find_region_type(area, RGN_TYPE_HEADER);
+               region->flag |= RGN_FLAG_HIDDEN;
                return OPERATOR_FINISHED;
        }
        else {
index a0a263c..497cca2 100644 (file)
@@ -61,6 +61,14 @@ static SpaceLink *userpref_new(const ScrArea *UNUSED(area), const Scene *UNUSED(
        spref = MEM_callocN(sizeof(SpaceUserPref), "inituserpref");
        spref->spacetype = SPACE_USERPREF;
 
+       /* header */
+       ar = MEM_callocN(sizeof(ARegion), "header for userpref");
+
+       BLI_addtail(&spref->regionbase, ar);
+       ar->regiontype = RGN_TYPE_HEADER;
+       /* Ignore user preference "USER_HEADER_BOTTOM" here (always show bottom for new types). */
+       ar->alignment = RGN_ALIGN_BOTTOM;
+
        /* navigation region */
        ar = MEM_callocN(sizeof(ARegion), "navigation region for userpref");
 
@@ -68,13 +76,13 @@ static SpaceLink *userpref_new(const ScrArea *UNUSED(area), const Scene *UNUSED(
        ar->regiontype = RGN_TYPE_NAV_BAR;
        ar->alignment = RGN_ALIGN_LEFT;
 
-       /* header */
-       ar = MEM_callocN(sizeof(ARegion), "header for userpref");
+       /* execution region */
+       ar = MEM_callocN(sizeof(ARegion), "execution region for userpref");
 
        BLI_addtail(&spref->regionbase, ar);
-       ar->regiontype = RGN_TYPE_HEADER;
-       /* Ignore user preference "USER_HEADER_BOTTOM" here (always show bottom for new types). */
-       ar->alignment = RGN_ALIGN_BOTTOM;
+       ar->regiontype = RGN_TYPE_EXECUTE;
+       ar->alignment = RGN_ALIGN_BOTTOM | RGN_SPLIT_PREV;
+       ar->flag |= RGN_FLAG_DYNAMIC_SIZE;
 
        /* main region */
        ar = MEM_callocN(sizeof(ARegion), "main region for userpref");
@@ -159,6 +167,13 @@ static void userpref_navigation_region_draw(const bContext *C, ARegion *ar)
        ED_region_panels(C, ar);
 }
 
+/* add handlers, stuff you only do once or on area/region changes */
+static void userpref_execute_region_init(wmWindowManager *wm, ARegion *ar)
+{
+       ED_region_panels_init(wm, ar);
+       ar->v2d.keepzoom |= V2D_LOCKZOOM_X | V2D_LOCKZOOM_Y;
+}
+
 static void userpref_main_region_listener(
         wmWindow *UNUSED(win), ScrArea *UNUSED(sa), ARegion *UNUSED(ar),
         wmNotifier *UNUSED(wmn), const Scene *UNUSED(scene))
@@ -186,6 +201,13 @@ static void userpref_navigation_region_listener(
        /* context changes */
 }
 
+static void userpref_execute_region_listener(
+        wmWindow *UNUSED(win), ScrArea *UNUSED(sa), ARegion *UNUSED(ar),
+        wmNotifier *UNUSED(wmn), const Scene *UNUSED(scene))
+{
+       /* context changes */
+}
+
 /* only called once, from space/spacetypes.c */
 void ED_spacetype_userpref(void)
 {
@@ -234,6 +256,17 @@ void ED_spacetype_userpref(void)
 
        BLI_addhead(&st->regiontypes, art);
 
+       /* regions: execution window */
+       art = MEM_callocN(sizeof(ARegionType), "spacetype userpref region");
+       art->regionid = RGN_TYPE_EXECUTE;
+       art->init = userpref_execute_region_init;
+       art->layout = ED_region_panels_layout;
+       art->draw = ED_region_panels_draw;
+       art->listener = userpref_execute_region_listener;
+       art->keymapflag = ED_KEYMAP_UI;
+
+       BLI_addhead(&st->regiontypes, art);
+
 
        BKE_spacetype_register(st);
 }
index a62e7bc..cdf67f1 100644 (file)
@@ -476,6 +476,8 @@ enum {
        RGN_TYPE_HUD = 8,
        /* Region to navigate the main region from (RGN_TYPE_WINDOW). */
        RGN_TYPE_NAV_BAR = 9,
+       /* A place for buttons to trigger execution of somthing that was set up in other regions. */
+       RGN_TYPE_EXECUTE = 10,
 };
 /* use for function args */
 #define RGN_TYPE_ANY -1
index 3f3fa68..41b8308 100644 (file)
@@ -236,7 +236,7 @@ typedef struct ThemeSpace {
 
        /* navigation bar regions */
        char navigation_bar[4];                 /* region background */
-       int pad2;
+       char execution_buts[4];                 /* region background */
 
        /* float panel */
 /*     char panel[4];                  unused */
@@ -687,14 +687,15 @@ typedef enum eUserPref_Section {
        USER_SECTION_INPUT             = 5,
        USER_SECTION_ADDONS            = 6,
        USER_SECTION_LIGHT             = 7,
+       USER_SECTION_KEYMAP            = 8,
 #ifdef WITH_USERDEF_WORKSPACES
-       USER_SECTION_WORKSPACE_CONFIG  = 8,
-       USER_SECTION_WORKSPACE_ADDONS  = 9,
-       USER_SECTION_WORKSPACE_KEYMAPS = 10,
+       USER_SECTION_WORKSPACE_CONFIG  = 9,
+       USER_SECTION_WORKSPACE_ADDONS  = 10,
+       USER_SECTION_WORKSPACE_KEYMAPS = 11,
 #endif
 #ifdef WITH_USERDEF_SYSTEM_SPLIT
-       USER_SECTION_SYSTEM_DISPLAY    = 11,
-       USER_SECTION_SYSTEM_DEVICES    = 12,
+       USER_SECTION_SYSTEM_DISPLAY    = 12,
+       USER_SECTION_SYSTEM_DEVICES    = 13,
 #endif
 } eUserPref_Section;
 
index 7ac03d5..5d473ed 100644 (file)
@@ -712,6 +712,7 @@ extern StructRNA RNA_Preferences;
 extern StructRNA RNA_PreferencesEdit;
 extern StructRNA RNA_PreferencesFilePaths;
 extern StructRNA RNA_PreferencesInput;
+extern StructRNA RNA_PreferencesKeymap;
 extern StructRNA RNA_PreferencesSystem;
 extern StructRNA RNA_PreferencesView;
 extern StructRNA RNA_PreferencesWalkNavigation;
index ab2a1b3..709abdd 100644 (file)
@@ -47,6 +47,7 @@ const EnumPropertyItem rna_enum_region_type_items[] = {
        {RGN_TYPE_TOOL_PROPS, "TOOL_PROPS", 0, "Tool Properties", ""},
        {RGN_TYPE_PREVIEW, "PREVIEW", 0, "Preview", ""},
        {RGN_TYPE_NAV_BAR, "NAVIGATION_BAR", 0, "Navigation Bar", ""},
+       {RGN_TYPE_EXECUTE, "EXECUTE", 0, "Execute Buttons", ""},
        {0, NULL, 0, NULL, NULL}
 };
 
index 8bdf947..d18ffd4 100644 (file)
@@ -4735,7 +4735,7 @@ static void rna_def_space_userpref(BlenderRNA *brna)
 
        srna = RNA_def_struct(brna, "SpacePreferences", "Space");
        RNA_def_struct_sdna(srna, "SpaceUserPref");
-       RNA_def_struct_ui_text(srna, "Space User Preferences", "User preferences space data");
+       RNA_def_struct_ui_text(srna, "Space Preferences", "Blender preferences space data");
 
        prop = RNA_def_property(srna, "filter_type", PROP_ENUM, PROP_NONE);
        RNA_def_property_enum_sdna(prop, NULL, "filter_type");
index f582c7c..7c7a484 100644 (file)
@@ -298,6 +298,11 @@ static PointerRNA rna_UserDef_input_get(PointerRNA *ptr)
        return rna_pointer_inherit_refine(ptr, &RNA_PreferencesInput, ptr->data);
 }
 
+static PointerRNA rna_UserDef_keymap_get(PointerRNA *ptr)
+{
+       return rna_pointer_inherit_refine(ptr, &RNA_PreferencesKeymap, ptr->data);
+}
+
 static PointerRNA rna_UserDef_filepaths_get(PointerRNA *ptr)
 {
        return rna_pointer_inherit_refine(ptr, &RNA_PreferencesFilePaths, ptr->data);
@@ -1369,6 +1374,11 @@ static void rna_def_userdef_theme_space_common(StructRNA *srna)
        RNA_def_property_ui_text(prop, "Navigation Bar Background", "");
        RNA_def_property_update(prop, 0, "rna_userdef_update");
 
+       prop = RNA_def_property(srna, "execution_buts", PROP_FLOAT, PROP_COLOR_GAMMA);
+       RNA_def_property_array(prop, 4);
+       RNA_def_property_ui_text(prop, "Execution Region Background", "");
+       RNA_def_property_update(prop, 0, "rna_userdef_update");
+
        /* tabs */
        prop = RNA_def_property(srna, "tab_active", PROP_FLOAT, PROP_COLOR_GAMMA);
        RNA_def_property_array(prop, 3);
@@ -2084,7 +2094,7 @@ static void rna_def_userdef_theme_space_userpref(BlenderRNA *brna)
        srna = RNA_def_struct(brna, "ThemePreferences", NULL);
        RNA_def_struct_sdna(srna, "ThemeSpace");
        RNA_def_struct_clear_flag(srna, STRUCT_UNDO);
-       RNA_def_struct_ui_text(srna, "Theme User Preferences", "Theme settings for the User Preferences");
+       RNA_def_struct_ui_text(srna, "Theme Preferences", "Theme settings for the Blender Preferences");
 
        rna_def_userdef_theme_spaces_main(srna);
 }
@@ -3656,6 +3666,17 @@ static void rna_def_userdef_view(BlenderRNA *brna)
                {0, NULL, 0, NULL, NULL}
        };
 
+       static const EnumPropertyItem color_picker_types[] = {
+               {USER_CP_CIRCLE_HSV, "CIRCLE_HSV", 0, "Circle (HSV)", "A circular Hue/Saturation color wheel, with "
+                                                     "Value slider"},
+               {USER_CP_CIRCLE_HSL, "CIRCLE_HSL", 0, "Circle (HSL)", "A circular Hue/Saturation color wheel, with "
+                                                                     "Lightness slider"},
+               {USER_CP_SQUARE_SV, "SQUARE_SV", 0, "Square (SV + H)", "A square showing Saturation/Value, with Hue slider"},
+               {USER_CP_SQUARE_HS, "SQUARE_HS", 0, "Square (HS + V)", "A square showing Hue/Saturation, with Value slider"},
+               {USER_CP_SQUARE_HV, "SQUARE_HV", 0, "Square (HV + S)", "A square showing Hue/Value, with Saturation slider"},
+               {0, NULL, 0, NULL, NULL}
+       };
+
        static const EnumPropertyItem zoom_frame_modes[] = {
                {ZOOM_FRAME_MODE_KEEP_RANGE, "KEEP_RANGE", 0, "Keep Range", ""},
                {ZOOM_FRAME_MODE_SECONDS, "SECONDS", 0, "Seconds", ""},
@@ -3680,7 +3701,7 @@ static void rna_def_userdef_view(BlenderRNA *brna)
        RNA_def_struct_ui_text(srna, "View & Controls", "Preferences related to viewing data");
 
        /* View  */
-       prop = RNA_def_property(srna, "ui_scale", PROP_FLOAT, PROP_FACTOR);
+       prop = RNA_def_property(srna, "ui_scale", PROP_FLOAT, PROP_NONE);
        RNA_def_property_ui_text(prop, "UI Scale", "Changes the size of the fonts and buttons in the interface");
        RNA_def_property_range(prop, 0.25f, 4.0f);
        RNA_def_property_ui_range(prop, 0.5f, 2.0f, 1, 2);
@@ -3734,6 +3755,23 @@ static void rna_def_userdef_view(BlenderRNA *brna)
                                 "Show the frames per second screen refresh rate, while animation is played back");
        RNA_def_property_update(prop, 0, "rna_userdef_update");
 
+       /* Weight Paint */
+
+       prop = RNA_def_property(srna, "use_weight_color_range", PROP_BOOLEAN, PROP_NONE);
+       RNA_def_property_boolean_sdna(prop, NULL, "flag", USER_CUSTOM_RANGE);
+       RNA_def_property_ui_text(prop, "Use Weight Color Range",
+                                "Enable color range used for weight visualization in weight painting mode");
+       RNA_def_property_update(prop, 0, "rna_UserDef_weight_color_update");
+
+       prop = RNA_def_property(srna, "weight_color_range", PROP_POINTER, PROP_NONE);
+       RNA_def_property_flag(prop, PROP_NEVER_NULL);
+       RNA_def_property_pointer_sdna(prop, NULL, "coba_weight");
+       RNA_def_property_struct_type(prop, "ColorRamp");
+       RNA_def_property_ui_text(prop, "Weight Color Range",
+                                "Color range used for weight visualization in weight painting mode");
+       RNA_def_property_update(prop, 0, "rna_UserDef_weight_color_update");
+
+
        /* app flags (use for app-templates) */
        prop = RNA_def_property(srna, "show_layout_ui", PROP_BOOLEAN, PROP_NONE);
        RNA_def_property_boolean_negative_sdna(prop, NULL, "app_flag", USER_APP_LOCK_UI_LAYOUT);
@@ -3758,6 +3796,12 @@ static void rna_def_userdef_view(BlenderRNA *brna)
        RNA_def_property_ui_text(prop, "Sub Level Menu Open Delay",
                                 "Time delay in 1/10 seconds before automatically opening sub level menus");
 
+       prop = RNA_def_property(srna, "color_picker_type", PROP_ENUM, PROP_NONE);
+       RNA_def_property_enum_items(prop, color_picker_types);
+       RNA_def_property_enum_sdna(prop, NULL, "color_picker_type");
+       RNA_def_property_ui_text(prop, "Color Picker Type", "Different styles of displaying the color picker widget");
+       RNA_def_property_update(prop, 0, "rna_userdef_update");
+
        /* pie menus */
        prop = RNA_def_property(srna, "pie_initial_timeout", PROP_INT, PROP_NONE);
        RNA_def_property_range(prop, 0, 1000);
@@ -3822,44 +3866,13 @@ static void rna_def_userdef_view(BlenderRNA *brna)
        RNA_def_property_ui_text(prop, "Header Position", "Default header position for new space-types");
        RNA_def_property_update(prop, 0, "rna_userdef_update");
 
-       prop = RNA_def_property(srna, "use_mouse_depth_navigate", PROP_BOOLEAN, PROP_NONE);
-       RNA_def_property_boolean_sdna(prop, NULL, "uiflag", USER_DEPTH_NAVIGATE);
-       RNA_def_property_ui_text(prop, "Auto Depth",
-                                "Use the depth under the mouse to improve view pan/rotate/zoom functionality");
-
-       prop = RNA_def_property(srna, "use_mouse_depth_cursor", PROP_BOOLEAN, PROP_NONE);
-       RNA_def_property_boolean_sdna(prop, NULL, "uiflag", USER_DEPTH_CURSOR);
-       RNA_def_property_ui_text(prop, "Cursor Surface Project",
-                                "Use the surface depth for cursor placement");
-
-       prop = RNA_def_property(srna, "use_cursor_lock_adjust", PROP_BOOLEAN, PROP_NONE);
-       RNA_def_property_boolean_sdna(prop, NULL, "uiflag", USER_LOCK_CURSOR_ADJUST);
-       RNA_def_property_ui_text(prop, "Cursor Lock Adjust",
-                                "Place the cursor without 'jumping' to the new location (when lock-to-cursor is used)");
-
-       prop = RNA_def_property(srna, "use_camera_lock_parent", PROP_BOOLEAN, PROP_NONE);
-       RNA_def_property_boolean_negative_sdna(prop, NULL, "uiflag", USER_CAM_LOCK_NO_PARENT);
-       RNA_def_property_ui_text(prop, "Camera Parent Lock",
-                                "When the camera is locked to the view and in fly mode, "
-                                "transform the parent rather than the camera");
-
-       /* view zoom */
-       prop = RNA_def_property(srna, "use_zoom_to_mouse", PROP_BOOLEAN, PROP_NONE);
-       RNA_def_property_boolean_sdna(prop, NULL, "uiflag", USER_ZOOM_TO_MOUSEPOS);
-       RNA_def_property_ui_text(prop, "Zoom To Mouse Position",
-                                "Zoom in towards the mouse pointer's position in the 3D view, "
-                                "rather than the 2D window center");
-
-       /* view rotation */
-       prop = RNA_def_property(srna, "use_auto_perspective", PROP_BOOLEAN, PROP_NONE);
-       RNA_def_property_boolean_sdna(prop, NULL, "uiflag", USER_AUTOPERSP);
-       RNA_def_property_ui_text(prop, "Auto Perspective",
-                                "Automatically switch between orthographic and perspective when changing "
-                                "from top/front/side views");
-
-       prop = RNA_def_property(srna, "use_rotate_around_active", PROP_BOOLEAN, PROP_NONE);
-       RNA_def_property_boolean_sdna(prop, NULL, "uiflag", USER_ORBIT_SELECTION);
-       RNA_def_property_ui_text(prop, "Rotate Around Selection", "Use selection as the pivot point");
+       static const EnumPropertyItem text_hinting_items[] = {
+               {0, "AUTO", 0, "Auto", ""},
+               {USER_TEXT_HINTING_NONE, "NONE", 0, "None", ""},
+               {USER_TEXT_HINTING_SLIGHT, "SLIGHT", 0, "Slight", ""},
+               {USER_TEXT_HINTING_FULL, "FULL", 0, "Full", ""},
+               {0, NULL, 0, NULL, NULL}
+       };
 
        /* mini axis */
        static const EnumPropertyItem mini_axis_type_items[] = {
@@ -3948,6 +3961,64 @@ static void rna_def_userdef_view(BlenderRNA *brna)
        RNA_def_property_ui_text(prop, "Zoom Seconds",
                                 "Seconds around cursor that we zoom around");
 
+
+       /* Text. */
+
+       prop = RNA_def_property(srna, "use_text_antialiasing", PROP_BOOLEAN, PROP_NONE);
+       RNA_def_property_boolean_negative_sdna(prop, NULL, "text_render", USER_TEXT_DISABLE_AA);
+       RNA_def_property_ui_text(prop, "Text Anti-aliasing", "Draw user interface text anti-aliased");
+       RNA_def_property_update(prop, 0, "rna_userdef_text_update");
+
+       prop = RNA_def_property(srna, "text_hinting", PROP_ENUM, PROP_NONE);
+       RNA_def_property_enum_bitflag_sdna(prop, NULL, "text_render");
+       RNA_def_property_enum_items(prop, text_hinting_items);
+       RNA_def_property_ui_text(prop, "Text Hinting", "Method for making user interface text render sharp");
+       RNA_def_property_update(prop, 0, "rna_userdef_text_update");
+
+       prop = RNA_def_property(srna, "font_path_ui", PROP_STRING, PROP_FILEPATH);
+       RNA_def_property_string_sdna(prop, NULL, "font_path_ui");
+       RNA_def_property_ui_text(prop, "Interface Font", "Path to interface font");
+       RNA_def_property_update(prop, NC_WINDOW, "rna_userdef_language_update");
+
+       prop = RNA_def_property(srna, "font_path_ui_mono", PROP_STRING, PROP_FILEPATH);
+       RNA_def_property_string_sdna(prop, NULL, "font_path_ui_mono");
+       RNA_def_property_ui_text(prop, "Mono-space Font", "Path to interface mono-space Font");
+       RNA_def_property_update(prop, NC_WINDOW, "rna_userdef_language_update");
+
+
+       /* Language. */
+
+       prop = RNA_def_property(srna, "use_international_fonts", PROP_BOOLEAN, PROP_NONE);
+       RNA_def_property_boolean_sdna(prop, NULL, "transopts", USER_DOTRANSLATE);
+       RNA_def_property_ui_text(prop, "Translate UI", "Enable UI translation and use international fonts");
+       RNA_def_property_update(prop, NC_WINDOW, "rna_userdef_language_update");
+
+       prop = RNA_def_property(srna, "language", PROP_ENUM, PROP_NONE);
+       RNA_def_property_enum_items(prop, rna_enum_language_default_items);
+#ifdef WITH_INTERNATIONAL
+       RNA_def_property_enum_funcs(prop, NULL, NULL, "rna_lang_enum_properties_itemf");
+#endif
+       RNA_def_property_ui_text(prop, "Language", "Language used for translation");
+       RNA_def_property_update(prop, NC_WINDOW, "rna_userdef_language_update");
+
+       prop = RNA_def_property(srna, "use_translate_tooltips", PROP_BOOLEAN, PROP_NONE);
+       RNA_def_property_boolean_sdna(prop, NULL, "transopts", USER_TR_TOOLTIPS);
+       RNA_def_property_ui_text(prop, "Translate Tooltips",
+                                "Translate the descriptions when hovering UI elements (recommended)");
+       RNA_def_property_update(prop, 0, "rna_userdef_update");
+
+       prop = RNA_def_property(srna, "use_translate_interface", PROP_BOOLEAN, PROP_NONE);
+       RNA_def_property_boolean_sdna(prop, NULL, "transopts", USER_TR_IFACE);
+       RNA_def_property_ui_text(prop, "Translate Interface",
+                                "Translate all labels in menus, buttons and panels "
+                                "(note that this might make it hard to follow tutorials or the manual)");
+       RNA_def_property_update(prop, 0, "rna_userdef_update");
+
+       prop = RNA_def_property(srna, "use_translate_new_dataname", PROP_BOOLEAN, PROP_NONE);
+       RNA_def_property_boolean_sdna(prop, NULL, "transopts", USER_TR_NEWDATANAME);
+       RNA_def_property_ui_text(prop, "Translate New Names",
+                                "Translate the names of new data-blocks (objects, materials...)");
+       RNA_def_property_update(prop, 0, "rna_userdef_update");
 }
 
 static void rna_def_userdef_edit(BlenderRNA *brna)
@@ -4011,8 +4082,8 @@ static void rna_def_userdef_edit(BlenderRNA *brna)
                "When entering numbers while transforming, "
                "default to advanced mode for full math expression evaluation");
 
-
        /* Undo */
+
        prop = RNA_def_property(srna, "undo_steps", PROP_INT, PROP_NONE);
        RNA_def_property_int_sdna(prop, NULL, "undosteps");
        RNA_def_property_range(prop, 0, 256);
@@ -4030,13 +4101,13 @@ static void rna_def_userdef_edit(BlenderRNA *brna)
                                 "Global undo works by keeping a full copy of the file itself in memory, "
                                 "so takes extra memory");
 
+
        /* auto keyframing */
        prop = RNA_def_property(srna, "use_auto_keying", PROP_BOOLEAN, PROP_NONE);
        RNA_def_property_boolean_sdna(prop, NULL, "autokey_mode", AUTOKEY_ON);
        RNA_def_property_ui_text(prop, "Auto Keying Enable",
                                 "Automatic keyframe insertion for Objects and Bones "
                                 "(default setting used for new Scenes)");
-       RNA_def_property_ui_icon(prop, ICON_REC, 0);
 
        prop = RNA_def_property(srna, "auto_keying_mode", PROP_ENUM, PROP_NONE);
        RNA_def_property_enum_items(prop, auto_key_modes);
@@ -4258,15 +4329,6 @@ static void rna_def_userdef_system(BlenderRNA *brna)
                {0, NULL, 0, NULL, NULL}
        };
 
-       static const EnumPropertyItem color_picker_types[] = {
-               {USER_CP_CIRCLE_HSV, "CIRCLE_HSV", 0, "Circle (HSV)", "A circular Hue/Saturation color wheel, with Value slider"},
-               {USER_CP_CIRCLE_HSL, "CIRCLE_HSL", 0, "Circle (HSL)", "A circular Hue/Saturation color wheel, with Lightness slider"},
-               {USER_CP_SQUARE_SV, "SQUARE_SV", 0, "Square (SV + H)", "A square showing Saturation/Value, with Hue slider"},
-               {USER_CP_SQUARE_HS, "SQUARE_HS", 0, "Square (HS + V)", "A square showing Hue/Saturation, with Value slider"},
-               {USER_CP_SQUARE_HV, "SQUARE_HV", 0, "Square (HV + S)", "A square showing Hue/Value, with Saturation slider"},
-               {0, NULL, 0, NULL, NULL}
-       };
-
        static const EnumPropertyItem multi_sample_levels[] = {
                {USER_MULTISAMPLE_NONE, "NONE", 0, "No MultiSample", "Do not use OpenGL MultiSample"},
                {USER_MULTISAMPLE_2, "2", 0, "MultiSample: 2", "Use 2x OpenGL MultiSample (requires restart)"},
@@ -4290,14 +4352,6 @@ static void rna_def_userdef_system(BlenderRNA *brna)
            {0, NULL, 0, NULL, NULL}
        };
 
-       static const EnumPropertyItem text_hinting_items[] = {
-           {0, "AUTO", 0, "Auto", ""},
-           {USER_TEXT_HINTING_NONE, "NONE", 0, "None", ""},
-           {USER_TEXT_HINTING_SLIGHT, "SLIGHT", 0, "Slight", ""},
-           {USER_TEXT_HINTING_FULL, "FULL", 0, "Full", ""},
-           {0, NULL, 0, NULL, NULL}
-       };
-
        srna = RNA_def_struct(brna, "PreferencesSystem", NULL);
        RNA_def_struct_sdna(srna, "UserDef");
        RNA_def_struct_nested(brna, srna, "Preferences");
@@ -4328,63 +4382,57 @@ static void rna_def_userdef_system(BlenderRNA *brna)
        RNA_def_property_clear_flag(prop, PROP_EDITABLE);
        RNA_def_property_float_sdna(prop, NULL, "pixelsize");
 
-       prop = RNA_def_property(srna, "font_path_ui", PROP_STRING, PROP_FILEPATH);
-       RNA_def_property_string_sdna(prop, NULL, "font_path_ui");
-       RNA_def_property_ui_text(prop, "Interface Font", "Path to interface font");
-       RNA_def_property_update(prop, NC_WINDOW, "rna_userdef_language_update");
 
-       prop = RNA_def_property(srna, "font_path_ui_mono", PROP_STRING, PROP_FILEPATH);
-       RNA_def_property_string_sdna(prop, NULL, "font_path_ui_mono");
-       RNA_def_property_ui_text(prop, "Mono-space Font", "Path to interface mono-space Font");
-       RNA_def_property_update(prop, NC_WINDOW, "rna_userdef_language_update");
+       /* Memory */
+
+       prop = RNA_def_property(srna, "prefetch_frames", PROP_INT, PROP_NONE);
+       RNA_def_property_int_sdna(prop, NULL, "prefetchframes");
+       RNA_def_property_range(prop, 0, INT_MAX);
+       RNA_def_property_ui_range(prop, 0, 500, 1, -1);
+       RNA_def_property_ui_text(prop, "Prefetch Frames", "Number of frames to render ahead during playback (sequencer only)");
+
+       prop = RNA_def_property(srna, "memory_cache_limit", PROP_INT, PROP_NONE);
+       RNA_def_property_int_sdna(prop, NULL, "memcachelimit");
+       RNA_def_property_range(prop, 0, max_memory_in_megabytes_int());
+       RNA_def_property_ui_text(prop, "Memory Cache Limit", "Memory cache limit (in megabytes)");
+       RNA_def_property_update(prop, 0, "rna_Userdef_memcache_update");
 
        prop = RNA_def_property(srna, "scrollback", PROP_INT, PROP_UNSIGNED);
        RNA_def_property_int_sdna(prop, NULL, "scrollback");
        RNA_def_property_range(prop, 32, 32768);
        RNA_def_property_ui_text(prop, "Scrollback", "Maximum number of lines to store for the console buffer");
 
-       prop = RNA_def_property(srna, "author", PROP_STRING, PROP_NONE);
-       RNA_def_property_string_sdna(prop, NULL, "author");
-       RNA_def_property_string_maxlength(prop, 80);
-       RNA_def_property_ui_text(prop, "Author",
-                                "Name that will be used in exported files when format supports such feature");
-
-       /* Language. */
+       /* OpenGL */
 
-       prop = RNA_def_property(srna, "use_international_fonts", PROP_BOOLEAN, PROP_NONE);
-       RNA_def_property_boolean_sdna(prop, NULL, "transopts", USER_DOTRANSLATE);
-       RNA_def_property_ui_text(prop, "Translate UI", "Enable UI translation and use international fonts");
-       RNA_def_property_update(prop, NC_WINDOW, "rna_userdef_language_update");
-
-       prop = RNA_def_property(srna, "language", PROP_ENUM, PROP_NONE);
-       RNA_def_property_enum_items(prop, rna_enum_language_default_items);
-#ifdef WITH_INTERNATIONAL
-       RNA_def_property_enum_funcs(prop, NULL, NULL, "rna_lang_enum_properties_itemf");
-#endif
-       RNA_def_property_ui_text(prop, "Language", "Language used for translation");
-       RNA_def_property_update(prop, NC_WINDOW, "rna_userdef_language_update");
+       /* Full scene anti-aliasing */
+       prop = RNA_def_property(srna, "multi_sample", PROP_ENUM, PROP_NONE);
+       RNA_def_property_enum_bitflag_sdna(prop, NULL, "ogl_multisamples");
+       RNA_def_property_enum_items(prop, multi_sample_levels);
+       RNA_def_property_ui_text(prop, "MultiSample",
+                                "Enable OpenGL multi-sampling, only for systems that support it, requires restart");
+       RNA_def_property_update(prop, 0, "rna_userdef_dpi_update");
 
-       prop = RNA_def_property(srna, "use_translate_tooltips", PROP_BOOLEAN, PROP_NONE);
-       RNA_def_property_boolean_sdna(prop, NULL, "transopts", USER_TR_TOOLTIPS);
-       RNA_def_property_ui_text(prop, "Translate Tooltips",
-                                "Translate the descriptions when hovering UI elements (recommended)");
-       RNA_def_property_update(prop, 0, "rna_userdef_update");
+       /* grease pencil anti-aliasing */
+       prop = RNA_def_property(srna, "gpencil_multi_sample", PROP_ENUM, PROP_NONE);
+       RNA_def_property_enum_bitflag_sdna(prop, NULL, "gpencil_multisamples");
+       RNA_def_property_enum_items(prop, multi_sample_levels);
+       RNA_def_property_ui_text(prop, "Gpencil MultiSample",
+                                "Enable Grease Pencil OpenGL multi-sampling, only for systems that support it");
+       RNA_def_property_update(prop, 0, "rna_userdef_dpi_update");
 
-       prop = RNA_def_property(srna, "use_translate_interface", PROP_BOOLEAN, PROP_NONE);
-       RNA_def_property_boolean_sdna(prop, NULL, "transopts", USER_TR_IFACE);
-       RNA_def_property_ui_text(prop, "Translate Interface",
-                                "Translate all labels in menus, buttons and panels "
-                                "(note that this might make it hard to follow tutorials or the manual)");
-       RNA_def_property_update(prop, 0, "rna_userdef_update");
+       prop = RNA_def_property(srna, "use_region_overlap", PROP_BOOLEAN, PROP_NONE);
+       RNA_def_property_boolean_sdna(prop, NULL, "uiflag2", USER_REGION_OVERLAP);
+       RNA_def_property_ui_text(prop, "Region Overlap",
+                                "Draw tool/property regions over the main region, when using Triple Buffer");
+       RNA_def_property_update(prop, 0, "rna_userdef_dpi_update");
 
-       prop = RNA_def_property(srna, "use_translate_new_dataname", PROP_BOOLEAN, PROP_NONE);
-       RNA_def_property_boolean_sdna(prop, NULL, "transopts", USER_TR_NEWDATANAME);
-       RNA_def_property_ui_text(prop, "Translate New Names",
-                                "Translate the names of new data-blocks (objects, materials...)");
+       prop = RNA_def_property(srna, "gpu_viewport_quality", PROP_FLOAT, PROP_FACTOR);
+       RNA_def_property_float_sdna(prop, NULL, "gpu_viewport_quality");
+       RNA_def_property_float_default(prop, 0.6f);
+       RNA_def_property_range(prop, 0.0f, 1.0f);
+       RNA_def_property_ui_text(prop, "Viewport Quality", "Quality setting for Solid mode rendering in the 3d viewport");
        RNA_def_property_update(prop, 0, "rna_userdef_update");
 
-       /* System & OpenGL */
-
        prop = RNA_def_property(srna, "solid_lights", PROP_COLLECTION, PROP_NONE);
        RNA_def_property_collection_sdna(prop, NULL, "light_param", "");
        RNA_def_property_struct_type(prop, "UserSolidLight");
@@ -4402,56 +4450,14 @@ static void rna_def_userdef_system(BlenderRNA *brna)
                                       "View the result of the studio light editor in the viewport");
        RNA_def_property_update(prop, 0, "rna_UserDef_viewport_lights_update");
 
-       prop = RNA_def_property(srna, "use_weight_color_range", PROP_BOOLEAN, PROP_NONE);
-       RNA_def_property_boolean_sdna(prop, NULL, "flag", USER_CUSTOM_RANGE);
-       RNA_def_property_ui_text(prop, "Use Weight Color Range",
-                                "Enable color range used for weight visualization in weight painting mode");
-       RNA_def_property_update(prop, 0, "rna_UserDef_weight_color_update");
-
-       prop = RNA_def_property(srna, "weight_color_range", PROP_POINTER, PROP_NONE);
-       RNA_def_property_flag(prop, PROP_NEVER_NULL);
-       RNA_def_property_pointer_sdna(prop, NULL, "coba_weight");
-       RNA_def_property_struct_type(prop, "ColorRamp");
-       RNA_def_property_ui_text(prop, "Weight Color Range",
-                                "Color range used for weight visualization in weight painting mode");
-       RNA_def_property_update(prop, 0, "rna_UserDef_weight_color_update");
-
-       prop = RNA_def_property(srna, "color_picker_type", PROP_ENUM, PROP_NONE);
-       RNA_def_property_enum_items(prop, color_picker_types);
-       RNA_def_property_enum_sdna(prop, NULL, "color_picker_type");
-       RNA_def_property_ui_text(prop, "Color Picker Type", "Different styles of displaying the color picker widget");
-       RNA_def_property_update(prop, 0, "rna_userdef_update");
-
-       prop = RNA_def_property(srna, "use_scripts_auto_execute", PROP_BOOLEAN, PROP_NONE);
-       RNA_def_property_boolean_negative_sdna(prop, NULL, "flag", USER_SCRIPT_AUTOEXEC_DISABLE);
-       RNA_def_property_ui_text(prop, "Auto Run Python Scripts",
-                                "Allow any .blend file to run scripts automatically "
-                                "(unsafe with blend files from an untrusted source)");
-       RNA_def_property_update(prop, 0, "rna_userdef_script_autoexec_update");
-
-       prop = RNA_def_property(srna, "use_tabs_as_spaces", PROP_BOOLEAN, PROP_NONE);
-       RNA_def_property_boolean_negative_sdna(prop, NULL, "flag", USER_TXT_TABSTOSPACES_DISABLE);
-       RNA_def_property_ui_text(prop, "Tabs as Spaces",
-                                "Automatically convert all new tabs into spaces for new and loaded text files");
-
-       prop = RNA_def_property(srna, "prefetch_frames", PROP_INT, PROP_NONE);
-       RNA_def_property_int_sdna(prop, NULL, "prefetchframes");
-       RNA_def_property_range(prop, 0, INT_MAX);
-       RNA_def_property_ui_range(prop, 0, 500, 1, -1);
-       RNA_def_property_ui_text(prop, "Prefetch Frames", "Number of frames to render ahead during playback (sequencer only)");
-
-       prop = RNA_def_property(srna, "memory_cache_limit", PROP_INT, PROP_NONE);
-       RNA_def_property_int_sdna(prop, NULL, "memcachelimit");
-       RNA_def_property_range(prop, 0, max_memory_in_megabytes_int());
-       RNA_def_property_ui_text(prop, "Memory Cache Limit", "Memory cache limit (in megabytes)");
-       RNA_def_property_update(prop, 0, "rna_Userdef_memcache_update");
-
        prop = RNA_def_property(srna, "gl_clip_alpha", PROP_FLOAT, PROP_NONE);
        RNA_def_property_float_sdna(prop, NULL, "glalphaclip");
        RNA_def_property_range(prop, 0.0f, 1.0f);
        RNA_def_property_ui_text(prop, "Clip Alpha", "Clip alpha below this threshold in the 3D textured view");
        RNA_def_property_update(prop, 0, "rna_userdef_update");
 
+       /* Textures */
+
        prop = RNA_def_property(srna, "use_16bit_textures", PROP_BOOLEAN, PROP_NONE);
        RNA_def_property_boolean_sdna(prop, NULL, "use_16bit_textures", 1);
        RNA_def_property_ui_text(prop, "16 Bit Float Textures", "Use 16 bit per component texture for float images");
@@ -4465,7 +4471,7 @@ static void rna_def_userdef_system(BlenderRNA *brna)
        prop = RNA_def_property(srna, "image_draw_method", PROP_ENUM, PROP_NONE);
        RNA_def_property_enum_items(prop, image_draw_methods);
        RNA_def_property_enum_sdna(prop, NULL, "image_draw_method");
-       RNA_def_property_ui_text(prop, "Image Draw Method", "Method used for displaying images on the screen");
+       RNA_def_property_ui_text(prop, "Image Display Method", "Method used for displaying images on the screen");
        RNA_def_property_update(prop, 0, "rna_userdef_update");
 
        prop = RNA_def_property(srna, "anisotropic_filter", PROP_ENUM, PROP_NONE);
@@ -4496,6 +4502,20 @@ static void rna_def_userdef_system(BlenderRNA *brna)
        RNA_def_property_ui_text(prop, "Texture Collection Rate",
                                 "Number of seconds between each run of the GL texture garbage collector");
 
+       /* Select */
+
+       prop = RNA_def_property(srna, "select_method", PROP_ENUM, PROP_NONE);
+       RNA_def_property_enum_sdna(prop, NULL, "gpu_select_method");
+       RNA_def_property_enum_items(prop, gpu_select_method_items);
+       RNA_def_property_ui_text(prop, "Selection Method",
+                                "Use OpenGL occlusion queries or selection render mode to accelerate selection");
+
+       prop = RNA_def_property(srna, "use_select_pick_depth", PROP_BOOLEAN, PROP_NONE);
+       RNA_def_property_boolean_sdna(prop, NULL, "gpu_select_pick_deph", 1);
+       RNA_def_property_ui_text(prop, "OpenGL Depth Picking", "Use the depth buffer for picking 3D View selection");
+
+       /* Audio */
+
        prop = RNA_def_property(srna, "audio_mixing_buffer", PROP_ENUM, PROP_NONE);
        RNA_def_property_enum_sdna(prop, NULL, "mixbufsize");
        RNA_def_property_enum_items(prop, audio_mixing_samples_items);
@@ -4527,56 +4547,6 @@ static void rna_def_userdef_system(BlenderRNA *brna)
        RNA_def_property_ui_text(prop, "Audio Channels", "Audio channel count");
        RNA_def_property_update(prop, 0, "rna_UserDef_audio_update");
 
-       prop = RNA_def_property(srna, "use_text_antialiasing", PROP_BOOLEAN, PROP_NONE);
-       RNA_def_property_boolean_negative_sdna(prop, NULL, "text_render", USER_TEXT_DISABLE_AA);
-       RNA_def_property_ui_text(prop, "Text Anti-aliasing", "Draw user interface text anti-aliased");
-       RNA_def_property_update(prop, 0, "rna_userdef_text_update");
-
-       prop = RNA_def_property(srna, "text_hinting", PROP_ENUM, PROP_NONE);
-       RNA_def_property_enum_bitflag_sdna(prop, NULL, "text_render");
-       RNA_def_property_enum_items(prop, text_hinting_items);
-       RNA_def_property_ui_text(prop, "Text Hinting", "Method for making user interface text render sharp");
-       RNA_def_property_update(prop, 0, "rna_userdef_text_update");
-
-       prop = RNA_def_property(srna, "select_method", PROP_ENUM, PROP_NONE);
-       RNA_def_property_enum_sdna(prop, NULL, "gpu_select_method");
-       RNA_def_property_enum_items(prop, gpu_select_method_items);
-       RNA_def_property_ui_text(prop, "Selection Method",
-                                "Use OpenGL occlusion queries or selection render mode to accelerate selection");
-
-       prop = RNA_def_property(srna, "use_select_pick_depth", PROP_BOOLEAN, PROP_NONE);
-       RNA_def_property_boolean_sdna(prop, NULL, "gpu_select_pick_deph", 1);
-       RNA_def_property_ui_text(prop, "OpenGL Depth Picking", "Use the depth buffer for picking 3D View selection");
-
-       /* Full scene anti-aliasing */
-       prop = RNA_def_property(srna, "multi_sample", PROP_ENUM, PROP_NONE);
-       RNA_def_property_enum_bitflag_sdna(prop, NULL, "ogl_multisamples");
-       RNA_def_property_enum_items(prop, multi_sample_levels);
-       RNA_def_property_ui_text(prop, "MultiSample",
-                                "Enable OpenGL multi-sampling, only for systems that support it, requires restart");
-       RNA_def_property_update(prop, 0, "rna_userdef_dpi_update");
-
-       /* grease pencil anti-aliasing */
-       prop = RNA_def_property(srna, "gpencil_multi_sample", PROP_ENUM, PROP_NONE);
-       RNA_def_property_enum_bitflag_sdna(prop, NULL, "gpencil_multisamples");
-       RNA_def_property_enum_items(prop, multi_sample_levels);
-       RNA_def_property_ui_text(prop, "Gpencil MultiSample",
-               "Enable Grease Pencil OpenGL multi-sampling, only for systems that support it");
-       RNA_def_property_update(prop, 0, "rna_userdef_dpi_update");
-
-       prop = RNA_def_property(srna, "use_region_overlap", PROP_BOOLEAN, PROP_NONE);
-       RNA_def_property_boolean_sdna(prop, NULL, "uiflag2", USER_REGION_OVERLAP);
-       RNA_def_property_ui_text(prop, "Region Overlap",
-                                "Draw tool/property regions over the main region, when using Triple Buffer");
-       RNA_def_property_update(prop, 0, "rna_userdef_dpi_update");
-
-       prop = RNA_def_property(srna, "gpu_viewport_quality", PROP_FLOAT, PROP_FACTOR);
-       RNA_def_property_float_sdna(prop, NULL, "gpu_viewport_quality");
-       RNA_def_property_float_default(prop, 0.6f);
-       RNA_def_property_range(prop, 0.0f, 1.0f);
-       RNA_def_property_ui_text(prop, "Viewport Quality", "Quality setting for Solid mode rendering in the 3d viewport");
-       RNA_def_property_update(prop, 0, "rna_userdef_update");
-
 
 #ifdef WITH_OPENSUBDIV
        prop = RNA_def_property(srna, "opensubdiv_compute_type", PROP_ENUM, PROP_NONE);
@@ -4603,8 +4573,8 @@ static void rna_def_userdef_input(BlenderRNA *brna)
        StructRNA *srna;
 
        static const EnumPropertyItem view_rotation_items[] = {
-               {0, "TURNTABLE", 0, "Turntable", "Use turntable style rotation in the viewport"},
-               {USER_TRACKBALL, "TRACKBALL", 0, "Trackball", "Use trackball style rotation in the viewport"},
+               {0, "TURNTABLE", 0, "Turntable", "Turntable keeps the Z-axis upright while orbiting"},
+               {USER_TRACKBALL, "TRACKBALL", 0, "Trackball", "Trackball allows you to tumble your view at any angle"},
                {0, NULL, 0, NULL, NULL}
        };
 
@@ -4642,10 +4612,6 @@ static void rna_def_userdef_input(BlenderRNA *brna)
        RNA_def_struct_clear_flag(srna, STRUCT_UNDO);
        RNA_def_struct_ui_text(srna, "Input", "Settings for input devices");
 
-       prop = RNA_def_property(srna, "show_ui_keyconfig", PROP_BOOLEAN, PROP_NONE);
-       RNA_def_property_boolean_negative_sdna(prop, NULL, "userpref_flag", USER_SECTION_INPUT_HIDE_UI_KEYCONFIG);
-       RNA_def_property_ui_text(prop, "Show UI Key-Config", "");
-
        prop = RNA_def_property(srna, "view_zoom_method", PROP_ENUM, PROP_NONE);
        RNA_def_property_enum_sdna(prop, NULL, "viewzoom");
        RNA_def_property_enum_items(prop, view_zoom_styles);
@@ -4660,10 +4626,49 @@ static void rna_def_userdef_input(BlenderRNA *brna)
        RNA_def_property_boolean_sdna(prop, NULL, "uiflag", USER_ZOOM_INVERT);
        RNA_def_property_ui_text(prop, "Invert Zoom Direction", "Invert the axis of mouse movement for zooming");
 
+       prop = RNA_def_property(srna, "use_cursor_lock_adjust", PROP_BOOLEAN, PROP_NONE);
+       RNA_def_property_boolean_sdna(prop, NULL, "uiflag", USER_LOCK_CURSOR_ADJUST);
+       RNA_def_property_ui_text(prop, "Cursor Lock Adjust",
+                                "Place the cursor without 'jumping' to the new location (when lock-to-cursor is used)");
+
+       prop = RNA_def_property(srna, "use_mouse_depth_navigate", PROP_BOOLEAN, PROP_NONE);
+       RNA_def_property_boolean_sdna(prop, NULL, "uiflag", USER_DEPTH_NAVIGATE);
+       RNA_def_property_ui_text(prop, "Auto Depth",
+                                "Use the depth under the mouse to improve view pan/rotate/zoom functionality");
+
+       prop = RNA_def_property(srna, "use_mouse_depth_cursor", PROP_BOOLEAN, PROP_NONE);
+       RNA_def_property_boolean_sdna(prop, NULL, "uiflag", USER_DEPTH_CURSOR);
+       RNA_def_property_ui_text(prop, "Cursor Surface Project",
+                                "Use the surface depth for cursor placement");
+
+       prop = RNA_def_property(srna, "use_camera_lock_parent", PROP_BOOLEAN, PROP_NONE);
+       RNA_def_property_boolean_negative_sdna(prop, NULL, "uiflag", USER_CAM_LOCK_NO_PARENT);
+       RNA_def_property_ui_text(prop, "Camera Parent Lock",
+                                "When the camera is locked to the view and in fly mode, "
+                                "transform the parent rather than the camera");
+
+       /* view zoom */
+       prop = RNA_def_property(srna, "use_zoom_to_mouse", PROP_BOOLEAN, PROP_NONE);
+       RNA_def_property_boolean_sdna(prop, NULL, "uiflag", USER_ZOOM_TO_MOUSEPOS);
+       RNA_def_property_ui_text(prop, "Zoom To Mouse Position",
+                                "Zoom in towards the mouse pointer's position in the 3D view, "
+                                "rather than the 2D window center");
+
+       /* view rotation */
+       prop = RNA_def_property(srna, "use_auto_perspective", PROP_BOOLEAN, PROP_NONE);
+       RNA_def_property_boolean_sdna(prop, NULL, "uiflag", USER_AUTOPERSP);
+       RNA_def_property_ui_text(prop, "Auto Perspective",
+                                "Automatically switch between orthographic and perspective when changing "
+                                "from top/front/side views");
+
+       prop = RNA_def_property(srna, "use_rotate_around_active", PROP_BOOLEAN, PROP_NONE);
+       RNA_def_property_boolean_sdna(prop, NULL, "uiflag", USER_ORBIT_SELECTION);
+       RNA_def_property_ui_text(prop, "Orbit Around Selection", "Use selection as the pivot point");
+
        prop = RNA_def_property(srna, "view_rotate_method", PROP_ENUM, PROP_NONE);
        RNA_def_property_enum_bitflag_sdna(prop, NULL, "flag");
        RNA_def_property_enum_items(prop, view_rotation_items);
-       RNA_def_property_ui_text(prop, "View Rotation", "Rotation style in the viewport");
+       RNA_def_property_ui_text(prop, "Orbit Method", "Orbit method in the viewport");
 
        prop = RNA_def_property(srna, "use_mouse_continuous", PROP_BOOLEAN, PROP_NONE);
        RNA_def_property_boolean_sdna(prop, NULL, "uiflag", USER_CONTINUOUS_MOUSE);
@@ -4807,7 +4812,7 @@ static void rna_def_userdef_input(BlenderRNA *brna)
        prop = RNA_def_property(srna, "use_mouse_emulate_3_button", PROP_BOOLEAN, PROP_NONE);
        RNA_def_property_boolean_sdna(prop, NULL, "flag", USER_TWOBUTTONMOUSE);
        RNA_def_property_ui_text(prop, "Emulate 3 Button Mouse",
-                                "Emulate Middle Mouse with Alt+Left Mouse (doesn't work with Left Mouse Select option)");
+                                "Emulate Middle Mouse with Alt+Left Mouse");
        RNA_def_property_flag(prop, PROP_CONTEXT_UPDATE);
        RNA_def_property_update(prop, 0, "rna_userdef_keyconfig_reload_update");
 
@@ -4828,6 +4833,21 @@ static void rna_def_userdef_input(BlenderRNA *brna)
        RNA_def_property_boolean_sdna(prop, NULL, "uiflag2", USER_TRACKPAD_NATURAL);
        RNA_def_property_ui_text(prop, "Trackpad Natural",
                                 "If your system uses 'natural' scrolling, this option keeps consistent trackpad usage throughout the UI");
+}
+
+static void rna_def_userdef_keymap(BlenderRNA *brna)
+{
+       PropertyRNA *prop;
+
+       StructRNA *srna = RNA_def_struct(brna, "PreferencesKeymap", NULL);
+       RNA_def_struct_sdna(srna, "UserDef");
+       RNA_def_struct_nested(brna, srna, "Preferences");
+       RNA_def_struct_clear_flag(srna, STRUCT_UNDO);
+       RNA_def_struct_ui_text(srna, "Keymap", "Shortcut setup for keyboards and other input devices");
+
+       prop = RNA_def_property(srna, "show_ui_keyconfig", PROP_BOOLEAN, PROP_NONE);
+       RNA_def_property_boolean_negative_sdna(prop, NULL, "userpref_flag", USER_SECTION_INPUT_HIDE_UI_KEYCONFIG);
+       RNA_def_property_ui_text(prop, "Show UI Key-Config", "");
 
        prop = RNA_def_property(srna, "active_keyconfig", PROP_STRING, PROP_DIRPATH);
        RNA_def_property_string_sdna(prop, NULL, "keyconfigstr");
@@ -4889,6 +4909,27 @@ static void rna_def_userdef_filepaths(BlenderRNA *brna)
        RNA_def_property_ui_text(prop, "Load UI", "Load user interface setup when loading .blend files");
        RNA_def_property_update(prop, 0, "rna_userdef_load_ui_update");
 
+
+       prop = RNA_def_property(srna, "use_scripts_auto_execute", PROP_BOOLEAN, PROP_NONE);
+       RNA_def_property_boolean_negative_sdna(prop, NULL, "flag", USER_SCRIPT_AUTOEXEC_DISABLE);
+       RNA_def_property_ui_text(prop, "Auto Run Python Scripts",
+                                "Allow any .blend file to run scripts automatically "
+                                "(unsafe with blend files from an untrusted source)");
+       RNA_def_property_update(prop, 0, "rna_userdef_script_autoexec_update");
+
+       prop = RNA_def_property(srna, "author", PROP_STRING, PROP_NONE);
+       RNA_def_property_string_sdna(prop, NULL, "author");
+       RNA_def_property_string_maxlength(prop, 80);
+       RNA_def_property_ui_text(prop, "Author",
+                                "Name that will be used in exported files when format supports such feature");
+
+       prop = RNA_def_property(srna, "use_tabs_as_spaces", PROP_BOOLEAN, PROP_NONE);
+       RNA_def_property_boolean_negative_sdna(prop, NULL, "flag", USER_TXT_TABSTOSPACES_DISABLE);
+       RNA_def_property_ui_text(prop, "Tabs as Spaces",
+                                "Automatically convert all new tabs into spaces for new and loaded text files");
+
+       /* Directories  */
+
        prop = RNA_def_property(srna, "font_directory", PROP_STRING, PROP_DIRPATH);
        RNA_def_property_string_sdna(prop, NULL, "fontdir");
        RNA_def_property_ui_text(prop, "Fonts Directory", "The default directory to search for loading fonts");
@@ -5035,13 +5076,14 @@ void RNA_def_userdef(BlenderRNA *brna)
        PropertyRNA *prop;
 
        static const EnumPropertyItem preference_section_items[] = {
-               {0, "", ICON_USER, "User Preferences", ""},
+               {0, "", ICON_USER, "User", ""},
                {USER_SECTION_INTERFACE, "INTERFACE", 0, "Interface", ""},
+               {USER_SECTION_THEME, "THEMES", 0, "Themes", ""},
+               {USER_SECTION_LIGHT, "LIGHTS", 0, "Lights", ""},
                {USER_SECTION_EDIT, "EDITING", 0, "Editing", ""},
                {USER_SECTION_INPUT, "INPUT", 0, "Input", ""},
+               {USER_SECTION_KEYMAP, "KEYMAP", 0, "Keymap", ""},
                {USER_SECTION_ADDONS, "ADDONS", 0, "Add-ons", ""},
-               {USER_SECTION_THEME, "THEMES", 0, "Themes", ""},
-               {USER_SECTION_LIGHT, "LIGHTS", 0, "Lights", ""},
 #ifdef WITH_USERDEF_WORKSPACES
                {0, "", ICON_WORKSPACE, "Workspaces", ""},
                {USER_SECTION_WORKSPACE_CONFIG, "WORKSPACE_CONFIG", 0, "Configuration File", ""},
@@ -5071,7 +5113,7 @@ void RNA_def_userdef(BlenderRNA *brna)
        RNA_def_property_enum_sdna(prop, NULL, "userpref");
        RNA_def_property_enum_items(prop, preference_section_items);
        RNA_def_property_ui_text(prop, "Active Section",
-                                "Active section of the user preferences shown in the user interface");
+                                "Active section of the preferences shown in the user interface");
        RNA_def_property_update(prop, 0, "rna_userdef_update");
 
        /* don't expose this directly via the UI, modify via an operator */
@@ -5120,6 +5162,12 @@ void RNA_def_userdef(BlenderRNA *brna)
        RNA_def_property_pointer_funcs(prop, "rna_UserDef_input_get", NULL, NULL, NULL);
        RNA_def_property_ui_text(prop, "Inputs", "Settings for input devices");
 
+       prop = RNA_def_property(srna, "keymap", PROP_POINTER, PROP_NONE);
+       RNA_def_property_flag(prop, PROP_NEVER_NULL);
+       RNA_def_property_struct_type(prop, "PreferencesKeymap");
+       RNA_def_property_pointer_funcs(prop, "rna_UserDef_keymap_get", NULL, NULL, NULL);
+       RNA_def_property_ui_text(prop, "Keymap", "Shortcut setup for keyboards and other input devices");
+
        prop = RNA_def_property(srna, "filepaths", PROP_POINTER, PROP_NONE);
        RNA_def_property_flag(prop, PROP_NEVER_NULL);
        RNA_def_property_struct_type(prop, "PreferencesFilePaths");
@@ -5151,6 +5199,7 @@ void RNA_def_userdef(BlenderRNA *brna)
        rna_def_userdef_view(brna);
        rna_def_userdef_edit(brna);
        rna_def_userdef_input(brna);
+       rna_def_userdef_keymap(brna);
        rna_def_userdef_filepaths(brna);
        rna_def_userdef_system(brna);
        rna_def_userdef_addon(brna);
index 715ab51..cc3c4ed 100644 (file)
@@ -1684,7 +1684,7 @@ void WM_OT_save_userpref(wmOperatorType *ot)
 {
        ot->name = "Save Preferences";
        ot->idname = "WM_OT_save_userpref";
-       ot->description = "Save user preferences separately, overrides startup file preferences";
+       ot->description = "Save preferences separately, overrides startup file preferences";
 
        ot->invoke = WM_operator_confirm;
        ot->exec = wm_userpref_write_exec;
@@ -1825,7 +1825,7 @@ void WM_OT_read_factory_settings(wmOperatorType *ot)
 
        ot->name = "Load Factory Settings";
        ot->idname = "WM_OT_read_factory_settings";
-       ot->description = "Load default file and user preferences";
+       ot->description = "Load default file and preferences";
 
        ot->invoke = WM_operator_confirm;
        ot->exec = wm_homefile_read_exec;