Merge branch 'master' into blender2.8
authorCampbell Barton <ideasman42@gmail.com>
Mon, 17 Dec 2018 06:26:47 +0000 (17:26 +1100)
committerCampbell Barton <ideasman42@gmail.com>
Mon, 17 Dec 2018 06:32:42 +0000 (17:32 +1100)
13 files changed:
1  2 
release/scripts/startup/bl_ui/properties_grease_pencil_common.py
release/scripts/startup/bl_ui/properties_paint_common.py
release/scripts/startup/bl_ui/space_clip.py
release/scripts/startup/bl_ui/space_dopesheet.py
release/scripts/startup/bl_ui/space_graph.py
release/scripts/startup/bl_ui/space_image.py
release/scripts/startup/bl_ui/space_node.py
release/scripts/startup/bl_ui/space_sequencer.py
release/scripts/startup/bl_ui/space_time.py
release/scripts/startup/bl_ui/space_toolsystem_common.py
release/scripts/startup/bl_ui/space_topbar.py
release/scripts/startup/bl_ui/space_view3d.py
release/scripts/startup/bl_ui/space_view3d_toolbar.py

@@@ -25,15 -25,15 +25,15 @@@ from bpy.app.translations import pgette
  
  def gpencil_stroke_placement_settings(context, layout):
      if context.space_data.type == 'VIEW_3D':
 -        propname = "gpencil_stroke_placement_view3d"
 +        propname = "annotation_stroke_placement_view3d"
      elif context.space_data.type == 'SEQUENCE_EDITOR':
 -        propname = "gpencil_stroke_placement_sequencer_preview"
 +        propname = "annotation_stroke_placement_sequencer_preview"
      elif context.space_data.type == 'IMAGE_EDITOR':
 -        propname = "gpencil_stroke_placement_image_editor"
 +        propname = "annotation_stroke_placement_image_editor"
      else:
 -        propname = "gpencil_stroke_placement_view2d"
 +        propname = "annotation_stroke_placement_view2d"
  
-     ts = context.tool_settings
+     tool_settings = context.tool_settings
  
      col = layout.column(align=True)
  
@@@ -249,18 -140,11 +249,18 @@@ class CLIP_HT_header(Header)
              layout.prop(sc, "pivot_point", text="", icon_only=True)
  
              row = layout.row(align=True)
-             row.prop(toolsettings, "use_proportional_edit_mask", text="", icon_only=True)
 -            row.prop(tool_settings, "use_proportional_edit_mask",
 -                     text="", icon_only=True)
 -            if tool_settings.use_proportional_edit_mask:
 -                row.prop(tool_settings, "proportional_edit_falloff",
 -                         text="", icon_only=True)
++            row.prop(tool_settings, "use_proportional_edit_mask", text="", icon_only=True)
 +            sub = row.row(align=True)
-             sub.active = toolsettings.use_proportional_edit_mask
-             sub.prop(toolsettings, "proportional_edit_falloff", text="", icon_only=True)
++            sub.active = tool_settings.use_proportional_edit_mask
++            sub.prop(tool_settings, "proportional_edit_falloff", text="", icon_only=True)
 +
 +            row = layout.row()
 +            row.template_ID(sc, "mask", new="mask.new")
 +            row.popover(panel='CLIP_PT_mask_display')
 +            row = layout.row(align=True)
 +            icon = 'LOCKED' if sc.lock_selection else 'UNLOCKED'
 +            row.prop(sc, "lock_selection", icon=icon, text="")
 +            row.popover(panel='CLIP_PT_display')
  
      def draw(self, context):
          layout = self.layout
@@@ -211,36 -119,11 +211,36 @@@ class DOPESHEET_HT_header(Header)
          row = layout.row(align=True)
          row.template_header()
  
 -        DOPESHEET_MT_editor_menus.draw_collapsible(context, layout)
 +        if st.mode == 'TIMELINE':
 +            from .space_time import (
 +                TIME_MT_editor_menus,
 +                TIME_HT_editor_buttons,
 +            )
 +            TIME_MT_editor_menus.draw_collapsible(context, layout)
 +            TIME_HT_editor_buttons.draw_header(context, layout)
 +        else:
 +            layout.prop(st, "ui_mode", text="")
 +
 +            DOPESHEET_MT_editor_menus.draw_collapsible(context, layout)
 +            DOPESHEET_HT_editor_buttons.draw_header(context, layout)
  
 -        layout.prop(st, "mode", text="")
 +
 +# Header for "normal" dopesheet editor modes (e.g. Dope Sheet, Action, Shape Keys, etc.)
 +class DOPESHEET_HT_editor_buttons(Header):
 +    bl_idname = "DOPESHEET_HT_editor_buttons"
 +    bl_space_type = 'DOPESHEET_EDITOR'
 +    bl_label = ""
 +
 +    def draw(self, context):
 +        pass
 +
 +    @staticmethod
 +    def draw_header(context, layout):
 +        st = context.space_data
-         toolsettings = context.tool_settings
++        tool_settings = context.tool_settings
  
          if st.mode in {'ACTION', 'SHAPEKEY'}:
 +            # TODO: These buttons need some tidying up - Probably by using a popover, and bypassing the template_id() here
              row = layout.row(align=True)
              row.operator("action.layer_prev", text="", icon='TRIA_DOWN')
              row.operator("action.layer_next", text="", icon='TRIA_UP')
              layout.prop(st, "auto_snap", text="")
  
          row = layout.row(align=True)
-         row.prop(toolsettings, "use_proportional_action", text="", icon_only=True)
 -        row.operator("action.copy", text="", icon='COPYDOWN')
 -        row.operator("action.paste", text="", icon='PASTEDOWN')
 -        if st.mode not in ('GPENCIL', 'MASK'):
 -            row.operator("action.paste", text="", icon='PASTEFLIPDOWN').flipped = True
++        row.prop(tool_settings, "use_proportional_action", text="", icon_only=True)
 +        sub = row.row(align=True)
-         sub.active = toolsettings.use_proportional_action
-         sub.prop(toolsettings, "proportional_edit_falloff", text="", icon_only=True)
++        sub.active = tool_settings.use_proportional_action
++        sub.prop(tool_settings, "proportional_edit_falloff", text="", icon_only=True)
  
  
  class DOPESHEET_MT_editor_menus(Menu):
@@@ -30,8 -26,10 +30,8 @@@ class GRAPH_HT_header(Header)
      bl_space_type = 'GRAPH_EDITOR'
  
      def draw(self, context):
 -        from .space_dopesheet import dopesheet_filter
 -
          layout = self.layout
-         toolsettings = context.tool_settings
+         tool_settings = context.tool_settings
  
          st = context.space_data
  
          else:
              row.operator("graph.ghost_curves_create", text="", icon='GHOST_ENABLED')
  
-         row.prop(toolsettings, "use_proportional_fcurve", text="", icon_only=True)
 +        layout.popover(
 +            panel="GRAPH_PT_filters",
 +            text="",
 +            icon='FILTER',
 +        )
 +
 +        layout.prop(st, "auto_snap", text="")
 +
 +        row = layout.row(align=True)
-         sub.active = toolsettings.use_proportional_fcurve
-         sub.prop(toolsettings, "proportional_edit_falloff", text="", icon_only=True)
++        row.prop(tool_settings, "use_proportional_fcurve", text="", icon_only=True)
 +        sub = row.row(align=True)
++        sub.active = tool_settings.use_proportional_fcurve
++        sub.prop(tool_settings, "proportional_edit_falloff", text="", icon_only=True)
 +
 +        layout.prop(st, "pivot_point", icon_only=True)
 +
 +
 +class GRAPH_PT_filters(DopesheetFilterPopoverBase, Panel):
 +    bl_space_type = 'GRAPH_EDITOR'
 +    bl_region_type = 'HEADER'
 +    bl_label = "Filters"
 +
 +    def draw(self, context):
 +        layout = self.layout
 +
 +        DopesheetFilterPopoverBase.draw_generic_filters(context, layout)
 +        layout.separator()
 +        DopesheetFilterPopoverBase.draw_search_filters(context, layout)
 +        layout.separator()
 +        DopesheetFilterPopoverBase.draw_standard_filters(context, layout)
 +
  
  class GRAPH_MT_editor_menus(Menu):
      bl_idname = "GRAPH_MT_editor_menus"
@@@ -49,8 -49,20 +49,8 @@@ class BrushButtonsPanel(UnifiedPaintPan
  
      @classmethod
      def poll(cls, context):
-         toolsettings = context.tool_settings.image_paint
-         return toolsettings.brush
 -        sima = context.space_data
+         tool_settings = context.tool_settings.image_paint
 -        return sima.show_paint and tool_settings.brush
 -
 -
 -class UVToolsPanel:
 -    bl_space_type = 'IMAGE_EDITOR'
 -    bl_region_type = 'TOOLS'
 -    bl_category = "Tools"
 -
 -    @classmethod
 -    def poll(cls, context):
 -        sima = context.space_data
 -        return sima.show_uvedit and not context.tool_settings.use_uv_sculpt
++        return tool_settings.brush
  
  
  class IMAGE_MT_view(Menu):
  
          layout.prop(sima, "use_realtime_update")
          if show_uvedit:
-             layout.prop(toolsettings, "show_uv_local_view")
+             layout.prop(tool_settings, "show_uv_local_view")
  
 -        layout.prop(uv, "show_other_objects")
          layout.prop(uv, "show_metadata")
 +
          if paint.brush and (context.image_paint_object or sima.mode == 'PAINT'):
              layout.prop(uv, "show_texpaint")
-             layout.prop(toolsettings, "show_uv_local_view", text="Show Same Material")
+             layout.prop(tool_settings, "show_uv_local_view", text="Show Same Material")
  
          layout.separator()
  
@@@ -403,11 -393,11 +403,11 @@@ class IMAGE_MT_uvs_select_mode(Menu)
          layout = self.layout
  
          layout.operator_context = 'INVOKE_REGION_WIN'
-         toolsettings = context.tool_settings
+         tool_settings = context.tool_settings
  
 -        # do smart things depending on whether uv_select_sync is on
 +        # Do smart things depending on whether uv_select_sync is on.
  
-         if toolsettings.use_uv_select_sync:
+         if tool_settings.use_uv_select_sync:
              props = layout.operator("wm.context_set_value", text="Vertex", icon='VERTEXSEL')
              props.value = "(True, False, False)"
              props.data_path = "tool_settings.mesh_select_mode"
@@@ -514,7 -437,8 +514,7 @@@ class IMAGE_HT_header(Header)
          sima = context.space_data
          ima = sima.image
          iuser = sima.image_user
-         toolsettings = context.tool_settings
+         tool_settings = context.tool_settings
 -        mode = sima.mode
  
          show_render = sima.show_render
          show_uvedit = sima.show_uvedit
          if show_uvedit:
              uvedit = sima.uv_editor
  
-             layout.prop(toolsettings, "use_uv_select_sync", text="")
+             layout.prop(tool_settings, "use_uv_select_sync", text="")
  
-             if toolsettings.use_uv_select_sync:
+             if tool_settings.use_uv_select_sync:
                  layout.template_edit_mode_selection()
              else:
-                 layout.prop(toolsettings, "uv_select_mode", text="", expand=True)
+                 layout.prop(tool_settings, "uv_select_mode", text="", expand=True)
                  layout.prop(uvedit, "sticky_select_mode", icon_only=True)
  
 -            row = layout.row(align=True)
 -            row.prop(tool_settings, "proportional_edit", icon_only=True)
 -            if tool_settings.proportional_edit != 'DISABLED':
 -                row.prop(tool_settings, "proportional_edit_falloff", icon_only=True)
 +        MASK_MT_editor_menus.draw_collapsible(context, layout)
  
 +        layout.separator_spacer()
 +
 +        layout.template_ID(sima, "image", new="image.new", open="image.open")
 +
 +        if show_maskedit:
 +            row = layout.row()
 +            row.template_ID(sima, "mask", new="mask.new")
 +
 +        if not show_render:
 +            layout.prop(sima, "use_image_pin", text="")
 +
 +        layout.separator_spacer()
 +
 +        if show_uvedit:
 +            uvedit = sima.uv_editor
 +
 +            mesh = context.edit_object.data
 +            layout.prop_search(mesh.uv_layers, "active", mesh, "uv_layers", text="")
 +
 +            # Snap.
              row = layout.row(align=True)
-             row.prop(toolsettings, "use_snap", text="")
-             row.prop(toolsettings, "snap_uv_element", icon_only=True)
-             if toolsettings.snap_uv_element != 'INCREMENT':
-                 row.prop(toolsettings, "snap_target", text="")
+             row.prop(tool_settings, "use_snap", text="")
+             row.prop(tool_settings, "snap_uv_element", icon_only=True)
+             if tool_settings.snap_uv_element != 'INCREMENT':
+                 row.prop(tool_settings, "snap_target", text="")
  
 -            mesh = context.edit_object.data
 -            layout.prop_search(mesh.uv_textures, "active", mesh, "uv_textures", text="")
 +            row = layout.row(align=True)
-             row.prop(toolsettings, "proportional_edit", icon_only=True)
-             # if toolsettings.proportional_edit != 'DISABLED':
++            row.prop(tool_settings, "proportional_edit", icon_only=True)
++            # if tool_settings.proportional_edit != 'DISABLED':
 +            sub = row.row(align=True)
-             sub.active = toolsettings.proportional_edit != 'DISABLED'
-             sub.prop(toolsettings, "proportional_edit_falloff", icon_only=True)
++            sub.active = tool_settings.proportional_edit != 'DISABLED'
++            sub.prop(tool_settings, "proportional_edit_falloff", icon_only=True)
 +
 +        if show_uvedit or show_maskedit:
 +            layout.prop(sima, "pivot_point", icon_only=True)
 +
 +        row = layout.row()
 +        row.popover(
 +            panel="IMAGE_PT_view_display",
 +            text="Display"
 +        )
  
          if ima:
              if ima.is_stereo_3d:
@@@ -1079,10 -1064,10 +1079,10 @@@ class IMAGE_PT_tools_brush_appearance(B
      def draw(self, context):
          layout = self.layout
  
-         toolsettings = context.tool_settings.image_paint
-         brush = toolsettings.brush
+         tool_settings = context.tool_settings.image_paint
+         brush = tool_settings.brush
  
 -        if brush is None:  # unlikely but can happen
 +        if brush is None:  # unlikely but can happen.
              layout.label(text="Brush Unset")
              return
  
@@@ -1140,35 -1145,35 +1140,35 @@@ class IMAGE_PT_uv_sculpt(Panel)
  
      @classmethod
      def poll(cls, context):
 -        sima = context.space_data
 -        tool_settings = context.tool_settings.image_paint
 -        return sima.show_uvedit and context.tool_settings.use_uv_sculpt and not (sima.show_paint and tool_settings.brush)
 +        return (context.uv_sculpt_object is not None)
  
      def draw(self, context):
 +        from .properties_paint_common import UnifiedPaintPanel
          layout = self.layout
  
-         toolsettings = context.tool_settings
-         uvsculpt = toolsettings.uv_sculpt
+         tool_settings = context.tool_settings
+         uvsculpt = tool_settings.uv_sculpt
          brush = uvsculpt.brush
  
 -        if brush:
 -            col = layout.column()
 +        if not self.is_popover:
 +            if brush:
 +                col = layout.column()
  
 -            row = col.row(align=True)
 -            self.prop_unified_size(row, context, brush, "size", slider=True, text="Radius")
 -            self.prop_unified_size(row, context, brush, "use_pressure_size")
 +                row = col.row(align=True)
 +                UnifiedPaintPanel.prop_unified_size(row, context, brush, "size", slider=True, text="Radius")
 +                UnifiedPaintPanel.prop_unified_size(row, context, brush, "use_pressure_size")
  
 -            row = col.row(align=True)
 -            self.prop_unified_strength(row, context, brush, "strength", slider=True, text="Strength")
 -            self.prop_unified_strength(row, context, brush, "use_pressure_strength")
 +                row = col.row(align=True)
 +                UnifiedPaintPanel.prop_unified_strength(row, context, brush, "strength", slider=True, text="Strength")
 +                UnifiedPaintPanel.prop_unified_strength(row, context, brush, "use_pressure_strength")
  
          col = layout.column()
-         col.prop(toolsettings, "uv_sculpt_lock_borders")
-         col.prop(toolsettings, "uv_sculpt_all_islands")
+         col.prop(tool_settings, "uv_sculpt_lock_borders")
+         col.prop(tool_settings, "uv_sculpt_all_islands")
  
-         col.prop(toolsettings, "uv_sculpt_tool")
-         if toolsettings.uv_sculpt_tool == 'RELAX':
-             col.prop(toolsettings, "uv_relax_method")
+         col.prop(tool_settings, "uv_sculpt_tool")
+         if tool_settings.uv_sculpt_tool == 'RELAX':
+             col.prop(tool_settings, "uv_relax_method")
  
          col.prop(uvsculpt, "show_brush")
  
@@@ -155,11 -123,17 +155,11 @@@ class NODE_HT_header(Header)
  
          # Snap
          row = layout.row(align=True)
-         row.prop(toolsettings, "use_snap", text="")
-         row.prop(toolsettings, "snap_node_element", icon_only=True)
-         if toolsettings.snap_node_element != 'GRID':
-             row.prop(toolsettings, "snap_target", text="")
+         row.prop(tool_settings, "use_snap", text="")
+         row.prop(tool_settings, "snap_node_element", icon_only=True)
+         if tool_settings.snap_node_element != 'GRID':
+             row.prop(tool_settings, "snap_target", text="")
  
 -        row = layout.row(align=True)
 -        row.operator("node.clipboard_copy", text="", icon='COPYDOWN')
 -        row.operator("node.clipboard_paste", text="", icon='PASTEDOWN')
 -
 -        layout.template_running_jobs()
 -
  
  class NODE_MT_editor_menus(Menu):
      bl_idname = "NODE_MT_editor_menus"
@@@ -126,10 -124,18 +126,10 @@@ class SEQUENCER_HT_header(Header)
              # Proportional editing
              if gpd and gpd.use_stroke_edit_mode:
                  row = layout.row(align=True)
-                 row.prop(toolsettings, "proportional_edit", icon_only=True)
-                 if toolsettings.proportional_edit != 'DISABLED':
-                     row.prop(toolsettings, "proportional_edit_falloff", icon_only=True)
+                 row.prop(tool_settings, "proportional_edit", icon_only=True)
+                 if tool_settings.proportional_edit != 'DISABLED':
+                     row.prop(tool_settings, "proportional_edit_falloff", icon_only=True)
  
 -        row = layout.row(align=True)
 -        row.operator("render.opengl", text="", icon='RENDER_STILL').sequencer = True
 -        props = row.operator("render.opengl", text="", icon='RENDER_ANIMATION')
 -        props.animation = True
 -        props.sequencer = True
 -
 -        layout.template_running_jobs()
 -
  
  class SEQUENCER_MT_editor_menus(Menu):
      bl_idname = "SEQUENCER_MT_editor_menus"
  
  # <pep8 compliant>
  import bpy
 -from bpy.types import Header, Menu
 +from bpy.types import Header, Menu, Panel
  
  
 -class TIME_HT_header(Header):
 -    bl_space_type = 'TIMELINE'
 +# Header buttons for timeline header (play, etc.)
 +class TIME_HT_editor_buttons(Header):
 +    bl_idname = "TIME_HT_editor_buttons"
 +    bl_space_type = 'DOPESHEET_EDITOR'
 +    bl_label = ""
  
      def draw(self, context):
 -        layout = self.layout
 +        pass
  
 +    @staticmethod
 +    def draw_header(context, layout):
          scene = context.scene
-         toolsettings = context.tool_settings
+         tool_settings = context.tool_settings
          screen = context.screen
 -        userprefs = context.user_preferences
  
 -        row = layout.row(align=True)
 -        row.template_header()
 -
 -        TIME_MT_editor_menus.draw_collapsible(context, layout)
 -
 -        row = layout.row(align=True)
 -        row.prop(scene, "use_preview_range", text="", toggle=True)
 -        row.prop(scene, "lock_frame_selection_to_range", text="", toggle=True)
 -
 -        row = layout.row(align=True)
 -        if not scene.use_preview_range:
 -            row.prop(scene, "frame_start", text="Start")
 -            row.prop(scene, "frame_end", text="End")
 -        else:
 -            row.prop(scene, "frame_preview_start", text="Start")
 -            row.prop(scene, "frame_preview_end", text="End")
 -
 -        if scene.show_subframe:
 -            layout.prop(scene, "frame_float", text="")
 -        else:
 -            layout.prop(scene, "frame_current", text="")
 +        layout.separator_spacer()
  
-         layout.prop(toolsettings, "use_keyframe_insert_auto", text="", toggle=True)
 -        layout.separator()
++        layout.prop(tool_settings, "use_keyframe_insert_auto", text="", toggle=True)
  
          row = layout.row(align=True)
          row.operator("screen.frame_jump", text="", icon='REW').end = False
@@@ -176,56 -182,25 +176,56 @@@ class TIME_MT_cache(Menu)
          col.prop(st, "cache_rigidbody")
  
  
 -class TIME_MT_frame(Menu):
 -    bl_label = "Frame"
 +def marker_menu_generic(layout):
 +    from bpy import context
  
 -    def draw(self, context):
 -        layout = self.layout
 +    # layout.operator_context = 'EXEC_REGION_WIN'
  
 -        layout.operator("anim.previewrange_clear")
 -        layout.operator("anim.previewrange_set")
 -        layout.separator()
 -        layout.operator("time.end_frame_set")
 -        layout.operator("time.start_frame_set")
 +    layout.column()
 +    layout.operator("marker.add", text="Add Marker")
 +    layout.operator("marker.duplicate", text="Duplicate Marker")
  
 -        layout.separator()
 +    if len(bpy.data.scenes) > 10:
 +        layout.operator_context = 'INVOKE_DEFAULT'
 +        layout.operator("marker.make_links_scene", text="Duplicate Marker to Scene...", icon='OUTLINER_OB_EMPTY')
 +    else:
 +        layout.operator_menu_enum("marker.make_links_scene", "scene", text="Duplicate Marker to Scene")
 +
 +    layout.operator("marker.delete", text="Delete Marker")
 +
 +    layout.separator()
 +
 +    layout.operator("marker.rename", text="Rename Marker")
 +    layout.operator("marker.move", text="Move Marker")
 +
 +    layout.separator()
 +
 +    layout.operator("marker.camera_bind")
 +
 +    layout.separator()
  
 -        layout.menu("TIME_MT_autokey")
 +    layout.operator("screen.marker_jump", text="Jump to Next Marker").next = True
 +    layout.operator("screen.marker_jump", text="Jump to Previous Marker").next = False
 +
 +    layout.separator()
-     ts = context.tool_settings
-     layout.prop(ts, "lock_markers")
++    tool_settings = context.tool_settings
++    layout.prop(tool_settings, "lock_markers")
 +
 +###################################
 +
 +
 +class TimelinePanelButtons:
 +    bl_space_type = 'DOPESHEET_EDITOR'
 +    bl_region_type = 'UI'
 +
 +    @staticmethod
 +    def has_timeline(context):
 +        return context.space_data.mode == 'TIMELINE'
  
  
 -class TIME_MT_playback(Menu):
 +class TIME_PT_playback(TimelinePanelButtons, Panel):
      bl_label = "Playback"
 +    bl_region_type = 'HEADER'
  
      def draw(self, context):
          layout = self.layout
@@@ -274,37 -232,46 +274,37 @@@ class TIME_PT_keyframing_settings(Timel
  
      def draw(self, context):
          layout = self.layout
 -        tool_settings = context.tool_settings
 -
 -        layout.prop_enum(tool_settings, "auto_keying_mode", 'ADD_REPLACE_KEYS')
 -        layout.prop_enum(tool_settings, "auto_keying_mode", 'REPLACE_KEYS')
 -
  
 -def marker_menu_generic(layout):
 -    from bpy import context
 -
 -    # layout.operator_context = 'EXEC_REGION_WIN'
 -
 -    layout.column()
 -    layout.operator("marker.add", "Add Marker")
 -    layout.operator("marker.duplicate", text="Duplicate Marker")
 -
 -    if len(bpy.data.scenes) > 10:
 -        layout.operator_context = 'INVOKE_DEFAULT'
 -        layout.operator("marker.make_links_scene", text="Duplicate Marker to Scene...", icon='OUTLINER_OB_EMPTY')
 -    else:
 -        layout.operator_menu_enum("marker.make_links_scene", "scene", text="Duplicate Marker to Scene")
 -
 -    layout.operator("marker.delete", text="Delete Marker")
 +        scene = context.scene
-         toolsettings = context.tool_settings
++        tool_settings = context.tool_settings
 +        userprefs = context.user_preferences
  
 -    layout.separator()
 +        col = layout.column(align=True)
 +        col.label(text="Active Keying Set:")
 +        row = col.row(align=True)
 +        row.prop_search(scene.keying_sets_all, "active", scene, "keying_sets_all", text="")
 +        row.operator("anim.keyframe_insert", text="", icon='KEY_HLT')
 +        row.operator("anim.keyframe_delete", text="", icon='KEY_DEHLT')
  
 -    layout.operator("marker.rename", text="Rename Marker")
 -    layout.operator("marker.move", text="Grab/Move Marker")
 +        col = layout.column(align=True)
 +        col.label(text="New Keyframe Type:")
-         col.prop(toolsettings, "keyframe_type", text="")
++        col.prop(tool_settings, "keyframe_type", text="")
  
 -    layout.separator()
 +        col = layout.column(align=True)
 +        col.label(text="Auto Keyframing:")
 +        row = col.row()
-         row.prop(toolsettings, "auto_keying_mode", text="")
-         row.prop(toolsettings, "use_keyframe_insert_keyingset", text="")
++        row.prop(tool_settings, "auto_keying_mode", text="")
++        row.prop(tool_settings, "use_keyframe_insert_keyingset", text="")
 +        if not userprefs.edit.use_keyframe_insert_available:
-             col.prop(toolsettings, "use_record_with_nla", text="Layered Recording")
++            col.prop(tool_settings, "use_record_with_nla", text="Layered Recording")
  
-         layout.prop(toolsettings, "use_keyframe_cycle_aware")
 -    layout.operator("screen.marker_jump", text="Jump to Next Marker").next = True
 -    layout.operator("screen.marker_jump", text="Jump to Previous Marker").next = False
++        layout.prop(tool_settings, "use_keyframe_cycle_aware")
  
 -    layout.separator()
 -    tool_settings = context.tool_settings
 -    layout.prop(tool_settings, "lock_markers")
  
 +###################################
  
  classes = (
 -    TIME_HT_header,
 +    TIME_HT_editor_buttons,
      TIME_MT_editor_menus,
      TIME_MT_marker,
      TIME_MT_view,
index a52b9d0,0000000..d8a8c2a
mode 100644,000000..100644
--- /dev/null
@@@ -1,740 -1,0 +1,740 @@@
-         # Optional draw settings (operator options, toolsettings).
 +# ##### BEGIN GPL LICENSE BLOCK #####
 +#
 +#  This program is free software; you can redistribute it and/or
 +#  modify it under the terms of the GNU General Public License
 +#  as published by the Free Software Foundation; either version 2
 +#  of the License, or (at your option) any later version.
 +#
 +#  This program is distributed in the hope that it will be useful,
 +#  but WITHOUT ANY WARRANTY; without even the implied warranty of
 +#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 +#  GNU General Public License for more details.
 +#
 +#  You should have received a copy of the GNU General Public License
 +#  along with this program; if not, write to the Free Software Foundation,
 +#  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 +#
 +# ##### END GPL LICENSE BLOCK #####
 +
 +# <pep8 compliant>
 +import bpy
 +from bpy.types import (
 +    Menu,
 +)
 +
 +__all__ = (
 +    "ToolDef",
 +    "ToolSelectPanelHelper",
 +    "activate_by_name",
 +    "activate_by_name_or_cycle",
 +    "description_from_name",
 +    "keymap_from_name",
 +)
 +
 +# Support reloading icons.
 +if "_icon_cache" in locals():
 +    release = bpy.app.icons.release
 +    for icon_value in _icon_cache.values():
 +        if icon_value != 0:
 +            release(icon_value)
 +    del release
 +
 +
 +# (filename -> icon_value) map
 +_icon_cache = {}
 +
 +
 +def _keymap_fn_from_seq(keymap_data):
 +
 +    def keymap_fn(km):
 +        if keymap_fn.keymap_data:
 +            from bl_keymap_utils.io import keymap_init_from_data
 +            keymap_init_from_data(km, keymap_fn.keymap_data)
 +    keymap_fn.keymap_data = keymap_data
 +    return keymap_fn
 +
 +
 +def _item_is_fn(item):
 +    return (not (type(item) is ToolDef) and callable(item))
 +
 +
 +from collections import namedtuple
 +ToolDef = namedtuple(
 +    "ToolDef",
 +    (
 +        # The name to display in the interface.
 +        "text",
 +        # Description (for tooltip), when not set, use the description of 'operator',
 +        # may be a string or a 'function(context, item, keymap) -> string'.
 +        "description",
 +        # The name of the icon to use (found in ``release/datafiles/icons``) or None for no icon.
 +        "icon",
 +        # An optional cursor to use when this tool is active.
 +        "cursor",
 +        # An optional gizmo group to activate when the tool is set or None for no gizmo.
 +        "widget",
 +        # Optional keymap for tool, either:
 +        # - A function that populates a keymaps passed in as an argument.
 +        # - A tuple filled with triple's of:
 +        #   ``(operator_id, operator_properties, keymap_item_args)``.
 +        #
 +        # Warning: currently 'from_dict' this is a list of one item,
 +        # so internally we can swap the keymap function for the keymap it's self.
 +        # This isn't very nice and may change, tool definitions shouldn't care about this.
 +        "keymap",
 +        # Optional data-block assosiated with this tool.
 +        # (Typically brush name, usage depends on mode, we could use for non-brush ID's in other modes).
 +        "data_block",
 +        # Optional primary operator (for introspection only).
 +        "operator",
++        # Optional draw settings (operator options, tool_settings).
 +        "draw_settings",
 +        # Optional draw cursor.
 +        "draw_cursor",
 +    )
 +)
 +del namedtuple
 +
 +
 +def from_dict(kw_args):
 +    """
 +    Use so each tool can avoid defining all members of the named tuple.
 +    Also convert the keymap from a tuple into a function
 +    (since keymap is a callback).
 +    """
 +    kw = {
 +        "description": None,
 +        "icon": None,
 +        "cursor": None,
 +        "widget": None,
 +        "keymap": None,
 +        "data_block": None,
 +        "operator": None,
 +        "draw_settings": None,
 +        "draw_cursor": None,
 +    }
 +    kw.update(kw_args)
 +
 +    keymap = kw["keymap"]
 +    if kw["keymap"] is None:
 +        pass
 +    elif type(keymap) is tuple:
 +        keymap = [_keymap_fn_from_seq(keymap)]
 +    else:
 +        keymap = [keymap]
 +    kw["keymap"] = keymap
 +    return ToolDef(**kw)
 +
 +
 +def from_fn(fn):
 +    """
 +    Use as decorator so we can define functions.
 +    """
 +    return ToolDef.from_dict(fn())
 +
 +
 +def with_args(**kw):
 +    def from_fn(fn):
 +        return ToolDef.from_dict(fn(**kw))
 +    return from_fn
 +
 +
 +from_fn.with_args = with_args
 +ToolDef.from_dict = from_dict
 +ToolDef.from_fn = from_fn
 +del from_dict, from_fn, with_args
 +
 +
 +class ToolSelectPanelHelper:
 +    """
 +    Generic Class, can be used for any toolbar.
 +
 +    - keymap_prefix:
 +      The text prefix for each key-map for this spaces tools.
 +    - tools_all():
 +      Returns (context_mode, tools) tuple pair for all tools defined.
 +    - tools_from_context(context, mode=None):
 +      Returns tools available in this context.
 +
 +    Each tool is a 'ToolDef' or None for a separator in the toolbar, use ``None``.
 +    """
 +
 +    @staticmethod
 +    def _tool_class_from_space_type(space_type):
 +        return next(
 +            (cls for cls in ToolSelectPanelHelper.__subclasses__()
 +             if cls.bl_space_type == space_type),
 +            None
 +        )
 +
 +    @staticmethod
 +    def _icon_value_from_icon_handle(icon_name):
 +        import os
 +        if icon_name is not None:
 +            assert(type(icon_name) is str)
 +            icon_value = _icon_cache.get(icon_name)
 +            if icon_value is None:
 +                dirname = bpy.utils.resource_path('LOCAL')
 +                if not os.path.exists(dirname):
 +                    # TODO(campbell): use a better way of finding datafiles.
 +                    dirname = bpy.utils.resource_path('SYSTEM')
 +                filename = os.path.join(dirname, "datafiles", "icons", icon_name + ".dat")
 +                try:
 +                    icon_value = bpy.app.icons.new_triangles_from_file(filename)
 +                except Exception as ex:
 +                    if not os.path.exists(filename):
 +                        print("Missing icons:", filename, ex)
 +                    else:
 +                        print("Corrupt icon:", filename, ex)
 +                    # Use none as a fallback (avoids layout issues).
 +                    if icon_name != "none":
 +                        icon_value = ToolSelectPanelHelper._icon_value_from_icon_handle("none")
 +                    else:
 +                        icon_value = 0
 +                _icon_cache[icon_name] = icon_value
 +            return icon_value
 +        else:
 +            return 0
 +
 +    @staticmethod
 +    def _tools_flatten(tools):
 +        """
 +        Flattens, skips None and calls generators.
 +        """
 +        for item in tools:
 +            if item is None:
 +                yield None
 +            elif type(item) is tuple:
 +                for sub_item in item:
 +                    if sub_item is None:
 +                        yield None
 +                    elif _item_is_fn(sub_item):
 +                        yield from sub_item(context)
 +                    else:
 +                        yield sub_item
 +            else:
 +                if _item_is_fn(item):
 +                    yield from item(context)
 +                else:
 +                    yield item
 +
 +    @staticmethod
 +    def _tools_flatten_with_tool_index(tools):
 +        for item in tools:
 +            if item is None:
 +                yield None, -1
 +            elif type(item) is tuple:
 +                i = 0
 +                for sub_item in item:
 +                    if sub_item is None:
 +                        yield None
 +                    elif _item_is_fn(sub_item):
 +                        for item_dyn in sub_item(context):
 +                            yield item_dyn, i
 +                            i += 1
 +                    else:
 +                        yield sub_item, i
 +                        i += 1
 +            else:
 +                if _item_is_fn(item):
 +                    for item_dyn in item(context):
 +                        yield item_dyn, -1
 +                else:
 +                    yield item, -1
 +
 +    @staticmethod
 +    def _tool_get_active(context, space_type, mode, with_icon=False):
 +        """
 +        Return the active Python tool definition and icon name.
 +        """
 +        cls = ToolSelectPanelHelper._tool_class_from_space_type(space_type)
 +        if cls is not None:
 +            tool_active = ToolSelectPanelHelper._tool_active_from_context(context, space_type, mode)
 +            tool_active_text = getattr(tool_active, "name", None)
 +            for item in ToolSelectPanelHelper._tools_flatten(cls.tools_from_context(context, mode)):
 +                if item is not None:
 +                    if item.text == tool_active_text:
 +                        if with_icon:
 +                            icon_value = ToolSelectPanelHelper._icon_value_from_icon_handle(item.icon)
 +                        else:
 +                            icon_value = 0
 +                        return (item, tool_active, icon_value)
 +        return None, None, 0
 +
 +    @staticmethod
 +    def _tool_get_by_name(context, space_type, text):
 +        """
 +        Return the active Python tool definition and index (if in sub-group, else -1).
 +        """
 +        cls = ToolSelectPanelHelper._tool_class_from_space_type(space_type)
 +        if cls is not None:
 +            for item, index in ToolSelectPanelHelper._tools_flatten_with_tool_index(cls.tools_from_context(context)):
 +                if item is not None:
 +                    if item.text == text:
 +                        return (cls, item, index)
 +        return None, None, -1
 +
 +    @staticmethod
 +    def _tool_active_from_context(context, space_type, mode=None, create=False):
 +        if space_type == 'VIEW_3D':
 +            if mode is None:
 +                mode = context.mode
 +            tool = context.workspace.tools.from_space_view3d_mode(mode, create=create)
 +            if tool is not None:
 +                tool.refresh_from_context()
 +                return tool
 +        elif space_type == 'IMAGE_EDITOR':
 +            space_data = context.space_data
 +            if mode is None:
 +                if space_data is None:
 +                    mode = 'VIEW'
 +                else:
 +                    mode = space_data.mode
 +            tool = context.workspace.tools.from_space_image_mode(mode, create=create)
 +            if tool is not None:
 +                tool.refresh_from_context()
 +                return tool
 +        elif space_type == 'NODE_EDITOR':
 +            space_data = context.space_data
 +            tool = context.workspace.tools.from_space_node(create=create)
 +            if tool is not None:
 +                tool.refresh_from_context()
 +                return tool
 +        return None
 +
 +    @staticmethod
 +    def _tool_text_from_button(context):
 +        return context.button_operator.name
 +
 +    @classmethod
 +    def _km_action_simple(cls, kc, context_descr, text, keymap_fn):
 +        km_idname = f"{cls.keymap_prefix:s} {context_descr:s}, {text:s}"
 +        km = kc.keymaps.get(km_idname)
 +        if km is None:
 +            km = kc.keymaps.new(km_idname, space_type=cls.bl_space_type, region_type='WINDOW', tool=True)
 +            keymap_fn[0](km)
 +        keymap_fn[0] = km.name
 +
 +    # Special internal function, gives use items that contain keymaps.
 +    @staticmethod
 +    def _tools_flatten_with_keymap(tools):
 +        for item_parent in tools:
 +            if item_parent is None:
 +                continue
 +            for item in item_parent if (type(item_parent) is tuple) else (item_parent,):
 +                # skip None or generator function
 +                if item is None or _item_is_fn(item):
 +                    continue
 +                if item.keymap is not None:
 +                    yield item
 +
 +    @classmethod
 +    def register(cls):
 +        wm = bpy.context.window_manager
 +        # Write into defaults, users may modify in preferences.
 +        kc = wm.keyconfigs.default
 +
 +        # Track which tool-group was last used for non-active groups.
 +        # Blender stores the active tool-group index.
 +        #
 +        # {tool_name_first: index_in_group, ...}
 +        cls._tool_group_active = {}
 +
 +        # ignore in background mode
 +        if kc is None:
 +            return
 +
 +        for context_mode, tools in cls.tools_all():
 +            if context_mode is None:
 +                context_descr = "All"
 +            else:
 +                context_descr = context_mode.replace("_", " ").title()
 +
 +            for item in cls._tools_flatten_with_keymap(tools):
 +                keymap_data = item.keymap
 +                if callable(keymap_data[0]):
 +                    cls._km_action_simple(kc, context_descr, item.text, keymap_data)
 +
 +    @classmethod
 +    def keymap_ui_hierarchy(cls, context_mode):
 +        # See: bpy_extras.keyconfig_utils
 +        for context_mode_test, tools in cls.tools_all():
 +            if context_mode_test == context_mode:
 +                for item in cls._tools_flatten_with_keymap(tools):
 +                    km_name = item.keymap[0]
 +                    # print((km.name, cls.bl_space_type, 'WINDOW', []))
 +                    yield (km_name, cls.bl_space_type, 'WINDOW', [])
 +
 +    # -------------------------------------------------------------------------
 +    # Layout Generators
 +    #
 +    # Meaning of recieved values:
 +    # - Bool: True for a separator, otherwise False for regular tools.
 +    # - None: Signal to finish (complete any final operations, e.g. add padding).
 +
 +    @staticmethod
 +    def _layout_generator_single_column(layout, scale_y):
 +        col = layout.column(align=True)
 +        col.scale_y = scale_y
 +        is_sep = False
 +        while True:
 +            if is_sep is True:
 +                col = layout.column(align=True)
 +                col.scale_y = scale_y
 +            elif is_sep is None:
 +                yield None
 +                return
 +            is_sep = yield col
 +
 +    @staticmethod
 +    def _layout_generator_multi_columns(layout, column_count, scale_y):
 +        scale_x = scale_y * 1.1
 +        column_last = column_count - 1
 +
 +        col = layout.column(align=True)
 +
 +        row = col.row(align=True)
 +
 +        row.scale_x = scale_x
 +        row.scale_y = scale_y
 +
 +        is_sep = False
 +        column_index = 0
 +        while True:
 +            if is_sep is True:
 +                if column_index != column_last:
 +                    row.label(text="")
 +                col = layout.column(align=True)
 +                row = col.row(align=True)
 +                row.scale_x = scale_x
 +                row.scale_y = scale_y
 +                column_index = 0
 +
 +            is_sep = yield row
 +            if is_sep is None:
 +                if column_index == column_last:
 +                    row.label(text="")
 +                    yield None
 +                    return
 +
 +            if column_index == column_count:
 +                column_index = 0
 +                row = col.row(align=True)
 +                row.scale_x = scale_x
 +                row.scale_y = scale_y
 +            column_index += 1
 +
 +    @staticmethod
 +    def _layout_generator_detect_from_region(layout, region, scale_y):
 +        """
 +        Choose an appropriate layout for the toolbar.
 +        """
 +        # Currently this just checks the width,
 +        # we could have different layouts as preferences too.
 +        system = bpy.context.user_preferences.system
 +        view2d = region.view2d
 +        view2d_scale = (
 +            view2d.region_to_view(1.0, 0.0)[0] -
 +            view2d.region_to_view(0.0, 0.0)[0]
 +        )
 +        width_scale = region.width * view2d_scale / system.ui_scale
 +
 +        if width_scale > 120.0:
 +            show_text = True
 +            column_count = 1
 +        else:
 +            show_text = False
 +            # 2 column layout, disabled
 +            if width_scale > 80.0:
 +                column_count = 2
 +            else:
 +                column_count = 1
 +
 +        if column_count == 1:
 +            ui_gen = ToolSelectPanelHelper._layout_generator_single_column(layout, scale_y=scale_y)
 +        else:
 +            ui_gen = ToolSelectPanelHelper._layout_generator_multi_columns(layout, column_count=column_count, scale_y=scale_y)
 +
 +        return ui_gen, show_text
 +
 +    @classmethod
 +    def draw_cls(cls, layout, context, detect_layout=True, scale_y=1.75):
 +        # Use a classmethod so it can be called outside of a panel context.
 +
 +        # XXX, this UI isn't very nice.
 +        # We might need to create new button types for this.
 +        # Since we probably want:
 +        # - tool-tips that include multiple key shortcuts.
 +        # - ability to click and hold to expose sub-tools.
 +
 +        space_type = context.space_data.type
 +        tool_active_text = getattr(
 +            ToolSelectPanelHelper._tool_active_from_context(context, space_type),
 +            "name", None,
 +        )
 +
 +        if detect_layout:
 +            ui_gen, show_text = cls._layout_generator_detect_from_region(layout, context.region, scale_y)
 +        else:
 +            ui_gen = ToolSelectPanelHelper._layout_generator_single_column(layout, scale_y)
 +            show_text = True
 +
 +        # Start iteration
 +        ui_gen.send(None)
 +
 +        for item in cls.tools_from_context(context):
 +            if item is None:
 +                ui_gen.send(True)
 +                continue
 +
 +            if type(item) is tuple:
 +                is_active = False
 +                i = 0
 +                for i, sub_item in enumerate(item):
 +                    if sub_item is None:
 +                        continue
 +                    is_active = (sub_item.text == tool_active_text)
 +                    if is_active:
 +                        index = i
 +                        break
 +                del i, sub_item
 +
 +                if is_active:
 +                    # not ideal, write this every time :S
 +                    cls._tool_group_active[item[0].text] = index
 +                else:
 +                    index = cls._tool_group_active.get(item[0].text, 0)
 +
 +                item = item[index]
 +                use_menu = True
 +            else:
 +                index = -1
 +                use_menu = False
 +
 +            is_active = (item.text == tool_active_text)
 +            icon_value = ToolSelectPanelHelper._icon_value_from_icon_handle(item.icon)
 +
 +            sub = ui_gen.send(False)
 +
 +            if use_menu:
 +                sub.operator_menu_hold(
 +                    "wm.tool_set_by_name",
 +                    text=item.text if show_text else "",
 +                    depress=is_active,
 +                    menu="WM_MT_toolsystem_submenu",
 +                    icon_value=icon_value,
 +                ).name = item.text
 +            else:
 +                sub.operator(
 +                    "wm.tool_set_by_name",
 +                    text=item.text if show_text else "",
 +                    depress=is_active,
 +                    icon_value=icon_value,
 +                ).name = item.text
 +        # Signal to finish any remaining layout edits.
 +        ui_gen.send(None)
 +
 +    def draw(self, context):
 +        self.draw_cls(self.layout, context)
 +
 +    @staticmethod
 +    def tool_active_from_context(context):
 +        # BAD DESIGN WARNING: last used tool
 +        workspace = context.workspace
 +        space_type = workspace.tools_space_type
 +        mode = workspace.tools_mode
 +        return ToolSelectPanelHelper._tool_active_from_context(context, space_type, mode)
 +
 +    @staticmethod
 +    def draw_active_tool_header(
 +            context, layout,
 +            *,
 +            show_tool_name=False,
 +    ):
 +        # BAD DESIGN WARNING: last used tool
 +        workspace = context.workspace
 +        space_type = workspace.tools_space_type
 +        mode = workspace.tools_mode
 +        item, tool, icon_value = ToolSelectPanelHelper._tool_get_active(context, space_type, mode, with_icon=True)
 +        if item is None:
 +            return None
 +        # Note: we could show 'item.text' here but it makes the layout jitter when switching tools.
 +        # Add some spacing since the icon is currently assuming regular small icon size.
 +        layout.label(text="    " + item.text if show_tool_name else " ", icon_value=icon_value)
 +        draw_settings = item.draw_settings
 +        if draw_settings is not None:
 +            draw_settings(context, layout, tool)
 +        return tool
 +
 +
 +# The purpose of this menu is to be a generic popup to select between tools
 +# in cases when a single tool allows to select alternative tools.
 +class WM_MT_toolsystem_submenu(Menu):
 +    bl_label = ""
 +
 +    @staticmethod
 +    def _tool_group_from_button(context):
 +        # Lookup the tool definitions based on the space-type.
 +        cls = ToolSelectPanelHelper._tool_class_from_space_type(context.space_data.type)
 +        if cls is not None:
 +            button_text = ToolSelectPanelHelper._tool_text_from_button(context)
 +            for item_group in cls.tools_from_context(context):
 +                if type(item_group) is tuple:
 +                    for sub_item in item_group:
 +                        if sub_item.text == button_text:
 +                            return cls, item_group
 +        return None, None
 +
 +    def draw(self, context):
 +        layout = self.layout
 +        layout.scale_y = 2.0
 +
 +        _cls, item_group = self._tool_group_from_button(context)
 +        if item_group is None:
 +            # Should never happen, just in case
 +            layout.label(text="Unable to find toolbar group")
 +            return
 +
 +        for item in item_group:
 +            if item is None:
 +                layout.separator()
 +                continue
 +            icon_value = ToolSelectPanelHelper._icon_value_from_icon_handle(item.icon)
 +            layout.operator(
 +                "wm.tool_set_by_name",
 +                text=item.text,
 +                icon_value=icon_value,
 +            ).name = item.text
 +
 +
 +def _activate_by_item(context, space_type, item, index):
 +    tool = ToolSelectPanelHelper._tool_active_from_context(context, space_type, create=True)
 +    tool.setup(
 +        name=item.text,
 +        keymap=item.keymap[0] if item.keymap is not None else "",
 +        cursor=item.cursor or 'DEFAULT',
 +        gizmo_group=item.widget or "",
 +        data_block=item.data_block or "",
 +        operator=item.operator or "",
 +        index=index,
 +    )
 +
 +    WindowManager = bpy.types.WindowManager
 +
 +    handle_map = _activate_by_item._cursor_draw_handle
 +    handle = handle_map.pop(space_type, None)
 +    if (handle is not None):
 +        WindowManager.draw_cursor_remove(handle)
 +    if item.draw_cursor is not None:
 +        def handle_fn(context, item, tool, xy):
 +            item.draw_cursor(context, tool, xy)
 +        handle = WindowManager.draw_cursor_add(handle_fn, (context, item, tool), space_type)
 +        handle_map[space_type] = handle
 +
 +_activate_by_item._cursor_draw_handle = {}
 +
 +
 +def activate_by_name(context, space_type, text):
 +    _cls, item, index = ToolSelectPanelHelper._tool_get_by_name(context, space_type, text)
 +    if item is None:
 +        return False
 +    _activate_by_item(context, space_type, item, index)
 +    return True
 +
 +
 +def activate_by_name_or_cycle(context, space_type, text, offset=1):
 +
 +    # Only cycle when the active tool is activated again.
 +    cls, item, _index = ToolSelectPanelHelper._tool_get_by_name(context, space_type, text)
 +    if item is None:
 +        return False
 +
 +    tool_active = ToolSelectPanelHelper._tool_active_from_context(context, space_type)
 +    text_active = getattr(tool_active, "name", None)
 +
 +    text_current = ""
 +    for item_group in cls.tools_from_context(context):
 +        if type(item_group) is tuple:
 +            index_current = cls._tool_group_active.get(item_group[0].text, 0)
 +            for sub_item in item_group:
 +                if sub_item.text == text:
 +                    text_current = item_group[index_current].text
 +                    break
 +            if text_current:
 +                break
 +
 +    if text_current == "":
 +        return activate_by_name(context, space_type, text)
 +    if text_active != text_current:
 +        return activate_by_name(context, space_type, text_current)
 +
 +    index_found = (tool_active.index + offset) % len(item_group)
 +
 +    cls._tool_group_active[item_group[0].text] = index_found
 +
 +    item_found = item_group[index_found]
 +    _activate_by_item(context, space_type, item_found, index_found)
 +    return True
 +
 +
 +def description_from_name(context, space_type, text, *, use_operator=True):
 +    # Used directly for tooltips.
 +    _cls, item, _index = ToolSelectPanelHelper._tool_get_by_name(context, space_type, text)
 +    if item is None:
 +        return False
 +
 +    # Custom description.
 +    description = item.description
 +    if description is not None:
 +        if callable(description):
 +            km = _keymap_from_item(context, item)
 +            return description(context, item, km)
 +        return description
 +
 +    # Extract from the operator.
 +    if use_operator:
 +        operator = item.operator
 +        if operator is None:
 +            if item.keymap is not None:
 +                km = _keymap_from_item(context, item)
 +                if km is not None:
 +                    for kmi in km.keymap_items:
 +                        if kmi.active:
 +                            operator = kmi.idname
 +                            break
 +
 +        if operator is not None:
 +            import _bpy
 +            return _bpy.ops.get_rna_type(operator).description
 +    return ""
 +
 +
 +def keymap_from_name(context, space_type, text):
 +    # Used directly for tooltips.
 +    _cls, item, _index = ToolSelectPanelHelper._tool_get_by_name(context, space_type, text)
 +    if item is None:
 +        return False
 +
 +    keymap = item.keymap
 +    # List container of one.
 +    if keymap:
 +        return keymap[0]
 +    return ""
 +
 +
 +def _keymap_from_item(context, item):
 +    if item.keymap is not None:
 +        wm = context.window_manager
 +        keyconf = wm.keyconfigs.active
 +        return keyconf.keymaps.get(item.keymap[0])
 +    return None
 +
 +
 +classes = (
 +    WM_MT_toolsystem_submenu,
 +)
 +
 +if __name__ == "__main__":  # only for live edit.
 +    from bpy.utils import register_class
 +    for cls in classes:
 +        register_class(cls)
index 0f8aaa8,0000000..ee5bdb3
mode 100644,000000..100644
--- /dev/null
@@@ -1,1095 -1,0 +1,1095 @@@
-         # Example of how toolsettings can be accessed as pop-overs.
 +# ##### BEGIN GPL LICENSE BLOCK #####
 +#
 +#  This program is free software; you can redistribute it and/or
 +#  modify it under the terms of the GNU General Public License
 +#  as published by the Free Software Foundation; either version 2
 +#  of the License, or (at your option) any later version.
 +#
 +#  This program is distributed in the hope that it will be useful,
 +#  but WITHOUT ANY WARRANTY; without even the implied warranty of
 +#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 +#  GNU General Public License for more details.
 +#
 +#  You should have received a copy of the GNU General Public License
 +#  along with this program; if not, write to the Free Software Foundation,
 +#  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 +#
 +# ##### END GPL LICENSE BLOCK #####
 +
 +# <pep8 compliant>
 +import bpy
 +from bpy.types import Header, Menu, Panel
 +from .properties_grease_pencil_common import (
 +    GPENCIL_UL_layer,
 +)
 +
 +
 +class TOPBAR_HT_upper_bar(Header):
 +    bl_space_type = 'TOPBAR'
 +
 +    def draw(self, context):
 +        region = context.region
 +
 +        if region.alignment == 'RIGHT':
 +            self.draw_right(context)
 +        else:
 +            self.draw_left(context)
 +
 +    def draw_left(self, context):
 +        layout = self.layout
 +
 +        window = context.window
 +        screen = context.screen
 +
 +        layout.operator("wm.splash", text="", icon='BLENDER', emboss=False)
 +
 +        TOPBAR_MT_editor_menus.draw_collapsible(context, layout)
 +
 +        layout.separator()
 +
 +        if not screen.show_fullscreen:
 +            layout.template_ID_tabs(
 +                window, "workspace",
 +                new="workspace.add",
 +                menu="TOPBAR_MT_workspace_menu",
 +            )
 +        else:
 +            layout.operator(
 +                "screen.back_to_previous",
 +                icon='SCREEN_BACK',
 +                text="Back to Previous",
 +            )
 +
 +    def draw_right(self, context):
 +        layout = self.layout
 +
 +        window = context.window
 +        screen = context.screen
 +        scene = window.scene
 +
 +        # If statusbar is hidden, still show messages at the top
 +        if not screen.show_statusbar:
 +            layout.template_reports_banner()
 +            layout.template_running_jobs()
 +
 +        # Active workspace view-layer is retrieved through window, not through workspace.
 +        layout.template_ID(window, "scene", new="scene.new", unlink="scene.delete")
 +
 +        row = layout.row(align=True)
 +        row.template_search(
 +            window, "view_layer",
 +            scene, "view_layers",
 +            new="scene.view_layer_add",
 +            unlink="scene.view_layer_remove")
 +
 +
 +class TOPBAR_HT_lower_bar(Header):
 +    bl_space_type = 'TOPBAR'
 +    bl_region_type = 'WINDOW'
 +
 +    def draw(self, context):
 +        region = context.region
 +
 +        if region.alignment == 'LEFT':
 +            self.draw_left(context)
 +        elif region.alignment == 'RIGHT':
 +            self.draw_right(context)
 +        else:
 +            self.draw_center(context)
 +
 +    def draw_left(self, context):
 +        layout = self.layout
 +
 +        # Active Tool
 +        # -----------
 +        from .space_toolsystem_common import ToolSelectPanelHelper
 +        tool = ToolSelectPanelHelper.draw_active_tool_header(context, layout)
 +        tool_space_type = 'VIEW_3D' if tool is None else tool.space_type
 +        tool_mode = context.mode if tool is None else tool.mode
 +
 +        # Object Mode Options
 +        # -------------------
 +
-             ts = context.scene.tool_settings
-             settings = ts.gpencil_paint
++        # Example of how tool_settings can be accessed as pop-overs.
 +
 +        # TODO(campbell): editing options should be after active tool options
 +        # (obviously separated for from the users POV)
 +        draw_fn = getattr(getattr(_draw_left_context_mode, tool_space_type, None), tool_mode, None)
 +        if draw_fn is not None:
 +            draw_fn(context, layout, tool)
 +
 +        if tool_space_type == 'VIEW_3D':
 +            # Note: general mode options should be added to 'draw_right'.
 +            if tool_mode == 'SCULPT':
 +                if (tool is not None) and tool.has_datablock:
 +                    layout.popover_group(space_type='PROPERTIES', region_type='WINDOW', context=".paint_common", category="")
 +            elif tool_mode == 'PAINT_VERTEX':
 +                if (tool is not None) and tool.has_datablock:
 +                    layout.popover_group(space_type='PROPERTIES', region_type='WINDOW', context=".paint_common", category="")
 +            elif tool_mode == 'PAINT_WEIGHT':
 +                if (tool is not None) and tool.has_datablock:
 +                    layout.popover_group(space_type='PROPERTIES', region_type='WINDOW', context=".paint_common", category="")
 +            elif tool_mode == 'PAINT_TEXTURE':
 +                if (tool is not None) and tool.has_datablock:
 +                    layout.popover_group(space_type='PROPERTIES', region_type='WINDOW', context=".paint_common", category="")
 +            elif tool_mode == 'EDIT_ARMATURE':
 +                pass
 +            elif tool_mode == 'EDIT_CURVE':
 +                pass
 +            elif tool_mode == 'EDIT_MESH':
 +                pass
 +            elif tool_mode == 'POSE':
 +                pass
 +            elif tool_mode == 'PARTICLE':
 +                # Disable, only shows "Brush" panel, which is already in the top-bar.
 +                # if tool.has_datablock:
 +                #     layout.popover_group(space_type='PROPERTIES', region_type='WINDOW', context=".paint_common", category="")
 +                pass
 +            elif tool_mode == 'PAINT_GPENCIL':
 +                if (tool is not None) and tool.has_datablock:
 +                    layout.popover_group(space_type='PROPERTIES', region_type='WINDOW', context=".greasepencil_paint", category="")
 +            elif tool_mode == 'SCULPT_GPENCIL':
 +                layout.popover_group(space_type='PROPERTIES', region_type='WINDOW', context=".greasepencil_sculpt", category="")
 +            elif tool_mode == 'WEIGHT_GPENCIL':
 +                layout.popover_group(space_type='PROPERTIES', region_type='WINDOW', context=".greasepencil_weight", category="")
 +        elif tool_space_type == 'IMAGE_EDITOR':
 +            if tool_mode == 'PAINT':
 +                if (tool is not None) and tool.has_datablock:
 +                    layout.popover_group(space_type='PROPERTIES', region_type='WINDOW', context=".paint_common_2d", category="")
 +            elif context.uv_sculpt_object is not None:
 +                layout.popover_group(space_type='PROPERTIES', region_type='WINDOW', context=".uv_sculpt", category="")
 +
 +    def draw_center(self, context):
 +        pass
 +
 +    def draw_right(self, context):
 +        layout = self.layout
 +
 +        # Active Tool
 +        # -----------
 +        from .space_toolsystem_common import ToolSelectPanelHelper
 +        tool = ToolSelectPanelHelper.tool_active_from_context(context)
 +        tool_space_type = 'VIEW_3D' if tool is None else tool.space_type
 +        tool_mode = context.mode if tool is None else tool.mode
 +
 +        if tool_space_type == 'VIEW_3D':
 +            if tool_mode == 'SCULPT':
 +                layout.popover_group(space_type='PROPERTIES', region_type='WINDOW', context=".sculpt_mode", category="")
 +            elif tool_mode == 'PAINT_VERTEX':
 +                layout.popover_group(space_type='PROPERTIES', region_type='WINDOW', context=".vertexpaint", category="")
 +            elif tool_mode == 'PAINT_WEIGHT':
 +                layout.popover_group(space_type='PROPERTIES', region_type='WINDOW', context=".weightpaint", category="")
 +            elif tool_mode == 'PAINT_TEXTURE':
 +                layout.popover_group(space_type='PROPERTIES', region_type='WINDOW', context=".imagepaint", category="")
 +            elif tool_mode == 'EDIT_TEXT':
 +                layout.popover_group(space_type='PROPERTIES', region_type='WINDOW', context=".text_edit", category="")
 +            elif tool_mode == 'EDIT_ARMATURE':
 +                layout.popover_group(space_type='PROPERTIES', region_type='WINDOW', context=".armature_edit", category="")
 +            elif tool_mode == 'EDIT_METABALL':
 +                layout.popover_group(space_type='PROPERTIES', region_type='WINDOW', context=".mball_edit", category="")
 +            elif tool_mode == 'EDIT_LATTICE':
 +                layout.popover_group(space_type='PROPERTIES', region_type='WINDOW', context=".lattice_edit", category="")
 +            elif tool_mode == 'EDIT_CURVE':
 +                layout.popover_group(space_type='PROPERTIES', region_type='WINDOW', context=".curve_edit", category="")
 +            elif tool_mode == 'EDIT_MESH':
 +                layout.popover_group(space_type='PROPERTIES', region_type='WINDOW', context=".mesh_edit", category="")
 +            elif tool_mode == 'POSE':
 +                layout.popover_group(space_type='PROPERTIES', region_type='WINDOW', context=".posemode", category="")
 +            elif tool_mode == 'PARTICLE':
 +                layout.popover_group(space_type='PROPERTIES', region_type='WINDOW', context=".particlemode", category="")
 +            elif tool_mode == 'OBJECT':
 +                layout.popover_group(space_type='PROPERTIES', region_type='WINDOW', context=".objectmode", category="")
 +            elif tool_mode in {'PAINT_GPENCIL', 'EDIT_GPENCIL', 'SCULPT_GPENCIL', 'WEIGHT_GPENCIL'}:
 +                # Grease pencil layer.
 +                gpl = context.active_gpencil_layer
 +                if gpl and gpl.info is not None:
 +                    text = gpl.info
 +                    maxw = 25
 +                    if len(text) > maxw:
 +                        text = text[:maxw - 5] + '..' + text[-3:]
 +                else:
 +                    text = ""
 +
 +                layout.label(text="Layer:")
 +                sub = layout.row()
 +                sub.ui_units_x = 8
 +                sub.popover(
 +                    panel="TOPBAR_PT_gpencil_layers",
 +                    text=text,
 +                )
 +        elif tool_space_type == 'IMAGE_EDITOR':
 +            if tool_mode == 'PAINT':
 +                layout.popover_group(space_type='PROPERTIES', region_type='WINDOW', context=".imagepaint_2d", category="")
 +
 +
 +class _draw_left_context_mode:
 +    class VIEW_3D:
 +        @staticmethod
 +        def SCULPT(context, layout, tool):
 +            if (tool is None) or (not tool.has_datablock):
 +                return
 +
 +            paint = context.tool_settings.sculpt
 +            layout.template_ID_preview(paint, "brush", rows=3, cols=8, hide_buttons=True)
 +
 +            brush = paint.brush
 +            if brush is None:
 +                return
 +
 +            from .properties_paint_common import UnifiedPaintPanel
 +
 +            UnifiedPaintPanel.prop_unified_size(layout, context, brush, "size", slider=True, text="Radius")
 +            UnifiedPaintPanel.prop_unified_strength(layout, context, brush, "strength", slider=True, text="Strength")
 +            layout.prop(brush, "direction", text="", expand=True)
 +
 +        @staticmethod
 +        def PAINT_TEXTURE(context, layout, tool):
 +            if (tool is None) or (not tool.has_datablock):
 +                return
 +
 +            paint = context.tool_settings.image_paint
 +            layout.template_ID_preview(paint, "brush", rows=3, cols=8, hide_buttons=True)
 +
 +            brush = paint.brush
 +            if brush is None:
 +                return
 +
 +            from .properties_paint_common import UnifiedPaintPanel
 +
 +            UnifiedPaintPanel.prop_unified_color(layout, context, brush, "color", text="")
 +            UnifiedPaintPanel.prop_unified_size(layout, context, brush, "size", slider=True, text="Radius")
 +            UnifiedPaintPanel.prop_unified_strength(layout, context, brush, "strength", slider=True, text="Strength")
 +
 +        @staticmethod
 +        def PAINT_VERTEX(context, layout, tool):
 +            if (tool is None) or (not tool.has_datablock):
 +                return
 +
 +            paint = context.tool_settings.vertex_paint
 +            layout.template_ID_preview(paint, "brush", rows=3, cols=8, hide_buttons=True)
 +
 +            brush = paint.brush
 +            if brush is None:
 +                return
 +
 +            from .properties_paint_common import UnifiedPaintPanel
 +
 +            UnifiedPaintPanel.prop_unified_color(layout, context, brush, "color", text="")
 +            UnifiedPaintPanel.prop_unified_size(layout, context, brush, "size", slider=True, text="Radius")
 +            UnifiedPaintPanel.prop_unified_strength(layout, context, brush, "strength", slider=True, text="Strength")
 +
 +        @staticmethod
 +        def PAINT_WEIGHT(context, layout, tool):
 +            if (tool is None) or (not tool.has_datablock):
 +                return
 +
 +            paint = context.tool_settings.weight_paint
 +            layout.template_ID_preview(paint, "brush", rows=3, cols=8, hide_buttons=True)
 +            brush = paint.brush
 +            if brush is None:
 +                return
 +
 +            from .properties_paint_common import UnifiedPaintPanel
 +
 +            UnifiedPaintPanel.prop_unified_weight(layout, context, brush, "weight", slider=True, text="Weight")
 +            UnifiedPaintPanel.prop_unified_size(layout, context, brush, "size", slider=True, text="Radius")
 +            UnifiedPaintPanel.prop_unified_strength(layout, context, brush, "strength", slider=True, text="Strength")
 +
 +        @staticmethod
 +        def PAINT_GPENCIL(context, layout, tool):
 +            if tool is None:
 +                return
 +
 +            is_paint = True
 +            if (tool.name in {"Line", "Box", "Circle", "Arc", "Curve"}):
 +                is_paint = False
 +            elif (not tool.has_datablock):
 +                return
 +
 +            paint = context.tool_settings.gpencil_paint
 +            brush = paint.brush
 +            if brush is None:
 +                return
 +
 +            gp_settings = brush.gpencil_settings
 +
 +            def draw_color_selector():
 +                ma = gp_settings.material
 +                row = layout.row(align=True)
 +
 +                icon_id = 0
 +                if ma:
 +                    icon_id = ma.id_data.preview.icon_id
 +                    txt_ma = ma.name
 +                    maxw = 25
 +                    if len(txt_ma) > maxw:
 +                        txt_ma = txt_ma[:maxw - 5] + '..' + txt_ma[-3:]
 +                else:
 +                    txt_ma = ""
 +
 +                row.label(text="Material:")
 +                sub = row.row()
 +                sub.ui_units_x = 8
 +                sub.popover(
 +                    panel="TOPBAR_PT_gpencil_materials",
 +                    text=txt_ma,
 +                    icon_value=icon_id,
 +                )
 +
 +                row.prop(gp_settings, "use_material_pin", text="")
 +
 +            row = layout.row(align=True)
++            tool_settings = context.scene.tool_settings
++            settings = tool_settings.gpencil_paint
 +            row.template_ID_preview(settings, "brush", rows=3, cols=8, hide_buttons=True)
 +
 +            if brush.gpencil_tool == 'ERASE':
 +                row = layout.row(align=True)
 +                row.prop(brush, "size", text="Radius")
 +                row.prop(gp_settings, "use_pressure", text="", icon='STYLUS_PRESSURE')
 +                row.prop(gp_settings, "use_occlude_eraser", text="", icon='XRAY')
 +                if gp_settings.eraser_mode == 'SOFT':
 +                    row = layout.row(align=True)
 +                    row.prop(gp_settings, "pen_strength", slider=True)
 +                    row.prop(gp_settings, "use_strength_pressure", text="", icon='STYLUS_PRESSURE')
 +            elif brush.gpencil_tool == 'FILL':
 +                row = layout.row()
 +                row.prop(gp_settings, "fill_leak", text="Leak Size")
 +                row.prop(brush, "size", text="Thickness")
 +                row.prop(gp_settings, "fill_simplify_level", text="Simplify")
 +
 +                draw_color_selector()
 +
 +                row = layout.row(align=True)
 +                row.prop(gp_settings, "fill_draw_mode", text="")
 +                row.prop(gp_settings, "show_fill_boundary", text="", icon='GRID')
 +
 +            else:  # brush.gpencil_tool == 'DRAW':
 +                row = layout.row(align=True)
 +                row.prop(brush, "size", text="Radius")
 +                if is_paint:
 +                    row.prop(gp_settings, "use_pressure", text="", icon='STYLUS_PRESSURE')
 +                row = layout.row(align=True)
 +                row.prop(gp_settings, "pen_strength", slider=True)
 +                if is_paint:
 +                    row.prop(gp_settings, "use_strength_pressure", text="", icon='STYLUS_PRESSURE')
 +
 +                draw_color_selector()
 +
 +                if tool.name in {"Arc", "Curve", "Line", "Box", "Circle"}:
 +                    settings = context.tool_settings.gpencil_sculpt
 +                    row = layout.row(align=True)
 +                    row.prop(settings, "use_thickness_curve", text="", icon='CURVE_DATA')
 +                    sub = row.row(align=True)
 +                    sub.active = settings.use_thickness_curve
 +                    sub.popover(
 +                        panel="TOPBAR_PT_gpencil_primitive",
 +                        text="Thickness Profile"
 +                    )
 +
 +        @staticmethod
 +        def SCULPT_GPENCIL(context, layout, tool):
 +            if (tool is None) or (not tool.has_datablock):
 +                return
 +            tool_settings = context.tool_settings
 +            settings = tool_settings.gpencil_sculpt
 +            tool = settings.sculpt_tool
 +            brush = settings.brush
 +
 +            row = layout.row(align=True)
 +            row.prop(brush, "size", slider=True)
 +            sub = row.row(align=True)
 +            sub.enabled = tool not in {'GRAB', 'CLONE'}
 +            sub.prop(brush, "use_pressure_radius", text="")
 +
 +            row = layout.row(align=True)
 +            row.prop(brush, "strength", slider=True)
 +            row.prop(brush, "use_pressure_strength", text="")
 +
 +            if tool in {'THICKNESS', 'STRENGTH', 'PINCH', 'TWIST'}:
 +                row.separator()
 +                row.prop(brush, "direction", expand=True, text="")
 +
 +        @staticmethod
 +        def WEIGHT_GPENCIL(context, layout, tool):
 +            if (tool is None) or (not tool.has_datablock):
 +                return
 +            tool_settings = context.tool_settings
 +            settings = tool_settings.gpencil_sculpt
 +            brush = settings.brush
 +
 +            layout.prop(brush, "size", slider=True)
 +
 +            row = layout.row(align=True)
 +            row.prop(brush, "strength", slider=True)
 +            row.prop(brush, "use_pressure_strength", text="")
 +
 +            layout.prop(brush, "target_weight", slider=True)
 +
 +        @staticmethod
 +        def PARTICLE(context, layout, tool):
 +            # See: 'VIEW3D_PT_tools_brush', basically a duplicate
 +            settings = context.tool_settings.particle_edit
 +            brush = settings.brush
 +            tool = settings.tool
 +            if tool != 'NONE':
 +                layout.prop(brush, "size", slider=True)
 +                if tool == 'ADD':
 +                    layout.prop(brush, "count")
 +
 +                    layout.prop(settings, "use_default_interpolate")
 +                    layout.prop(brush, "steps", slider=True)
 +                    layout.prop(settings, "default_key_count", slider=True)
 +                else:
 +                    layout.prop(brush, "strength", slider=True)
 +
 +                    if tool == 'LENGTH':
 +                        layout.row().prop(brush, "length_mode", expand=True)
 +                    elif tool == 'PUFF':
 +                        layout.row().prop(brush, "puff_mode", expand=True)
 +                        layout.prop(brush, "use_puff_volume")
 +                    elif tool == 'COMB':
 +                        # Note: actually in 'Options' panel,
 +                        # disabled when used in popover.
 +                        row = layout.row()
 +                        row.active = settings.is_editable
 +                        row.prop(settings, "use_emitter_deflect", text="Deflect Emitter")
 +                        sub = row.row(align=True)
 +                        sub.active = settings.use_emitter_deflect
 +                        sub.prop(settings, "emitter_distance", text="Distance")
 +
 +    class IMAGE_EDITOR:
 +        @staticmethod
 +        def VIEW(context, layout, tool):
 +            tool_settings = context.tool_settings
 +            if tool_settings.use_uv_sculpt:
 +                if context.mode == 'EDIT_MESH':
 +                    uv_sculpt = tool_settings.uv_sculpt
 +                    brush = uv_sculpt.brush
 +                    if brush:
 +                        from .properties_paint_common import UnifiedPaintPanel
 +
 +                        row = layout.row(align=True)
 +                        UnifiedPaintPanel.prop_unified_size(row, context, brush, "size", slider=True, text="Radius")
 +                        UnifiedPaintPanel.prop_unified_size(row, context, brush, "use_pressure_size")
 +
 +                        row = layout.row(align=True)
 +                        UnifiedPaintPanel.prop_unified_strength(row, context, brush, "strength", slider=True, text="Strength")
 +                        UnifiedPaintPanel.prop_unified_strength(row, context, brush, "use_pressure_strength")
 +
 +        @staticmethod
 +        def PAINT(context, layout, tool):
 +            if (tool is None) or (not tool.has_datablock):
 +                return
 +
 +            paint = context.tool_settings.image_paint
 +            layout.template_ID_preview(paint, "brush", rows=3, cols=8, hide_buttons=True)
 +
 +            brush = paint.brush
 +            if brush is None:
 +                return
 +
 +            from .properties_paint_common import UnifiedPaintPanel
 +
 +            UnifiedPaintPanel.prop_unified_color(layout, context, brush, "color", text="")
 +            UnifiedPaintPanel.prop_unified_size(layout, context, brush, "size", slider=True, text="Radius")
 +            UnifiedPaintPanel.prop_unified_strength(layout, context, brush, "strength", slider=True, text="Strength")
 +
 +
 +class TOPBAR_PT_gpencil_layers(Panel):
 +    bl_space_type = 'VIEW_3D'
 +    bl_region_type = 'HEADER'
 +    bl_label = "Layers"
 +    bl_ui_units_x = 14
 +
 +    @classmethod
 +    def poll(cls, context):
 +        if context.gpencil_data is None:
 +            return False
 +
 +        ob = context.object
 +        if ob is not None and ob.type == 'GPENCIL':
 +            return True
 +
 +        return False
 +
 +    @staticmethod
 +    def draw(self, context):
 +        layout = self.layout
 +        gpd = context.gpencil_data
 +
 +        # Grease Pencil data...
 +        if (gpd is None) or (not gpd.layers):
 +            layout.operator("gpencil.layer_add", text="New Layer")
 +        else:
 +            self.draw_layers(context, layout, gpd)
 +
 +    def draw_layers(self, context, layout, gpd):
 +        row = layout.row()
 +
 +        col = row.column()
 +        layer_rows = 10
 +        col.template_list("GPENCIL_UL_layer", "", gpd, "layers", gpd.layers, "active_index",
 +                          rows=layer_rows, reverse=True)
 +
 +        gpl = context.active_gpencil_layer
 +        if gpl:
 +            srow = col.row(align=True)
 +            srow.prop(gpl, "blend_mode", text="Blend")
 +
 +            srow = col.row(align=True)
 +            srow.prop(gpl, "opacity", text="Opacity", slider=True)
 +            srow.prop(gpl, "clamp_layer", text="",
 +                     icon='MOD_MASK' if gpl.clamp_layer else 'LAYER_ACTIVE')
 +
 +        col = row.column()
 +
 +        sub = col.column(align=True)
 +        sub.operator("gpencil.layer_add", icon='ADD', text="")
 +        sub.operator("gpencil.layer_remove", icon='REMOVE', text="")
 +
 +        gpl = context.active_gpencil_layer
 +        if gpl:
 +            sub.menu("GPENCIL_MT_layer_specials", icon='DOWNARROW_HLT', text="")
 +
 +            if len(gpd.layers) > 1:
 +                col.separator()
 +
 +                sub = col.column(align=True)
 +                sub.operator("gpencil.layer_move", icon='TRIA_UP', text="").type = 'UP'
 +                sub.operator("gpencil.layer_move", icon='TRIA_DOWN', text="").type = 'DOWN'
 +
 +                col.separator()
 +
 +                sub = col.column(align=True)
 +                sub.operator("gpencil.layer_isolate", icon='LOCKED', text="").affect_visibility = False
 +                sub.operator("gpencil.layer_isolate", icon='HIDE_OFF', text="").affect_visibility = True
 +
 +
 +class TOPBAR_MT_editor_menus(Menu):
 +    bl_idname = "TOPBAR_MT_editor_menus"
 +    bl_label = ""
 +
 +    def draw(self, context):
 +        self.draw_menus(self.layout, context)
 +
 +    @staticmethod
 +    def draw_menus(layout, context):
 +        layout.menu("TOPBAR_MT_file")
 +        layout.menu("TOPBAR_MT_edit")
 +
 +        layout.menu("TOPBAR_MT_render")
 +
 +        layout.menu("TOPBAR_MT_window")
 +        layout.menu("TOPBAR_MT_help")
 +
 +
 +class TOPBAR_MT_file(Menu):
 +    bl_label = "File"
 +
 +    def draw(self, context):
 +        layout = self.layout
 +
 +        layout.operator_context = 'INVOKE_AREA'
 +        layout.menu("TOPBAR_MT_file_new", text="New", icon='FILE_NEW')
 +        layout.operator("wm.open_mainfile", text="Open...", icon='FILE_FOLDER')
 +        layout.menu("TOPBAR_MT_file_open_recent")
 +        layout.operator("wm.revert_mainfile")
 +        layout.operator("wm.recover_last_session")
 +        layout.operator("wm.recover_auto_save", text="Recover Auto Save...")
 +
 +        layout.separator()
 +
 +        layout.operator_context = 'EXEC_AREA' if context.blend_data.is_saved else 'INVOKE_AREA'
 +        layout.operator("wm.save_mainfile", text="Save", icon='FILE_TICK')
 +
 +        layout.operator_context = 'INVOKE_AREA'
 +        layout.operator("wm.save_as_mainfile", text="Save As...")
 +        layout.operator_context = 'INVOKE_AREA'
 +        layout.operator("wm.save_as_mainfile", text="Save Copy...").copy = True
 +
 +        layout.separator()
 +        layout.operator_context = 'INVOKE_AREA'
 +
 +        if any(bpy.utils.app_template_paths()):
 +            app_template = context.user_preferences.app_template
 +        else:
 +            app_template = None
 +
 +        if app_template:
 +            layout.label(text=bpy.path.display_name(app_template))
 +            layout.operator("wm.save_homefile")
 +            layout.operator(
 +                "wm.read_factory_settings",
 +                text="Load Factory Settings",
 +            ).app_template = app_template
 +        else:
 +            layout.operator("wm.save_homefile")
 +            layout.operator("wm.read_factory_settings")
 +
 +        layout.separator()
 +
 +        layout.operator("wm.app_template_install", text="Install Application Template...")
 +
 +        layout.separator()
 +
 +        layout.operator_context = 'INVOKE_AREA'
 +        layout.operator("wm.link", text="Link...", icon='LINK_BLEND')
 +        layout.operator("wm.append", text="Append...", icon='APPEND_BLEND')
 +        layout.menu("TOPBAR_MT_file_previews")
 +
 +        layout.separator()
 +
 +        layout.menu("TOPBAR_MT_file_import", icon='IMPORT')
 +        layout.menu("TOPBAR_MT_file_export", icon='EXPORT')
 +
 +        layout.separator()
 +
 +        layout.menu("TOPBAR_MT_file_external_data")
 +
 +        layout.separator()
 +
 +        layout.operator_context = 'EXEC_AREA'
 +        if bpy.data.is_dirty and context.user_preferences.view.use_quit_dialog:
 +            layout.operator_context = 'INVOKE_SCREEN'  # quit dialog
 +        layout.operator("wm.quit_blender", text="Quit", icon='QUIT')
 +
 +
 +class TOPBAR_MT_file_new(Menu):
 +    bl_label = "New File"
 +
 +    @staticmethod
 +    def app_template_paths():
 +        import os
 +
 +        template_paths = bpy.utils.app_template_paths()
 +
 +        # expand template paths
 +        app_templates = []
 +        for path in template_paths:
 +            for d in os.listdir(path):
 +                if d.startswith(("__", ".")):
 +                    continue
 +                template = os.path.join(path, d)
 +                if os.path.isdir(template):
 +                    # template_paths_expand.append(template)
 +                    app_templates.append(d)
 +
 +        return sorted(app_templates)
 +
 +    def draw_ex(layout, context, *, use_splash=False, use_more=False):
 +        layout.operator_context = 'EXEC_DEFAULT'
 +
 +        # Limit number of templates in splash screen, spill over into more menu.
 +        paths = TOPBAR_MT_file_new.app_template_paths()
 +        splash_limit = 5
 +
 +        if use_splash:
 +            icon = 'FILE_NEW'
 +            show_more = len(paths) > (splash_limit - 1)
 +            if show_more:
 +                paths = paths[:splash_limit - 2]
 +        elif use_more:
 +            icon = 'FILE_NEW'
 +            paths = paths[splash_limit - 2:]
 +            show_more = False
 +        else:
 +            icon = 'NONE'
 +            show_more = False
 +
 +        # Draw application templates.
 +        if not use_more:
 +            props = layout.operator("wm.read_homefile", text="General", icon=icon)
 +            props.app_template = ""
 +
 +        for d in paths:
 +            props = layout.operator(
 +                "wm.read_homefile",
 +                text=bpy.path.display_name(d),
 +                icon=icon,
 +            )
 +            props.app_template = d
 +
 +        if show_more:
 +            layout.menu("TOPBAR_MT_templates_more", text="...")
 +
 +    def draw(self, context):
 +        TOPBAR_MT_file_new.draw_ex(self.layout, context)
 +
 +
 +class TOPBAR_MT_templates_more(Menu):
 +    bl_label = "Templates"
 +
 +    def draw(self, context):
 +        bpy.types.TOPBAR_MT_file_new.draw_ex(self.layout, context, use_more=True)
 +
 +
 +class TOPBAR_MT_file_import(Menu):
 +    bl_idname = "TOPBAR_MT_file_import"
 +    bl_label = "Import"
 +
 +    def draw(self, context):
 +        if bpy.app.build_options.collada:
 +            self.layout.operator("wm.collada_import", text="Collada (Default) (.dae)")
 +        if bpy.app.build_options.alembic:
 +            self.layout.operator("wm.alembic_import", text="Alembic (.abc)")
 +
 +
 +class TOPBAR_MT_file_export(Menu):
 +    bl_idname = "TOPBAR_MT_file_export"
 +    bl_label = "Export"
 +
 +    def draw(self, context):
 +        if bpy.app.build_options.collada:
 +            self.layout.operator("wm.collada_export", text="Collada (Default) (.dae)")
 +        if bpy.app.build_options.alembic:
 +            self.layout.operator("wm.alembic_export", text="Alembic (.abc)")
 +
 +
 +class TOPBAR_MT_file_external_data(Menu):
 +    bl_label = "External Data"
 +
 +    def draw(self, context):
 +        layout = self.layout
 +
 +        icon = 'CHECKBOX_HLT' if bpy.data.use_autopack else 'CHECKBOX_DEHLT'
 +        layout.operator("file.autopack_toggle", icon=icon)
 +
 +        layout.separator()
 +
 +        pack_all = layout.row()
 +        pack_all.operator("file.pack_all")
 +        pack_all.active = not bpy.data.use_autopack
 +
 +        unpack_all = layout.row()
 +        unpack_all.operator("file.unpack_all")
 +        unpack_all.active = not bpy.data.use_autopack
 +
 +        layout.separator()
 +
 +        layout.operator("file.make_paths_relative")
 +        layout.operator("file.make_paths_absolute")
 +        layout.operator("file.report_missing_files")
 +        layout.operator("file.find_missing_files")
 +
 +
 +class TOPBAR_MT_file_previews(Menu):
 +    bl_label = "Data Previews"
 +
 +    def draw(self, context):
 +        layout = self.layout
 +
 +        layout.operator("wm.previews_ensure")
 +        layout.operator("wm.previews_batch_generate")
 +
 +        layout.separator()
 +
 +        layout.operator("wm.previews_clear")
 +        layout.operator("wm.previews_batch_clear")
 +
 +
 +class TOPBAR_MT_render(Menu):
 +    bl_label = "Render"
 +
 +    def draw(self, context):
 +        layout = self.layout
 +
 +        rd = context.scene.render
 +
 +        layout.operator("render.render", text="Render Image", icon='RENDER_STILL').use_viewport = True
 +        props = layout.operator("render.render", text="Render Animation", icon='RENDER_ANIMATION')
 +        props.animation = True
 +        props.use_viewport = True
 +
 +        layout.separator()
 +
 +        layout.operator("sound.mixdown", text="Render Audio...")
 +
 +        layout.separator()
 +
 +        layout.operator("render.view_show", text="View Render")
 +        layout.operator("render.play_rendered_anim", text="View Animation")
 +        layout.prop_menu_enum(rd, "display_mode", text="Display Mode")
 +
 +        layout.separator()
 +
 +        layout.prop(rd, "use_lock_interface", text="Lock Interface")
 +
 +
 +class TOPBAR_MT_edit(Menu):
 +    bl_label = "Edit"
 +
 +    def draw(self, context):
 +        layout = self.layout
 +
 +        layout.operator("ed.undo")
 +        layout.operator("ed.redo")
 +
 +        layout.separator()
 +
 +        layout.operator("ed.undo_history", text="Undo History...")
 +
 +        layout.separator()
 +
 +        layout.operator("screen.repeat_last")
 +        layout.operator("screen.repeat_history", text="Repeat History...")
 +
 +        layout.separator()
 +
 +        layout.operator("screen.redo_last", text="Adjust Last Operation...")
 +
 +        layout.separator()
 +
 +        layout.operator("wm.search_menu", text="Operator Search...", icon='VIEWZOOM')
 +
 +        layout.separator()
 +
 +        # Should move elsewhere (impacts outliner & 3D view).
 +        tool_settings = context.tool_settings
 +        layout.prop(tool_settings, "lock_object_mode")
 +
 +        layout.separator()
 +
 +        layout.operator("screen.userpref_show", text="Preferences...", icon='PREFERENCES')
 +
 +
 +class TOPBAR_MT_window(Menu):
 +    bl_label = "Window"
 +
 +    def draw(self, context):
 +        import sys
 +
 +        layout = self.layout
 +
 +        layout.operator("wm.window_new")
 +        layout.operator("wm.window_new_main")
 +
 +        layout.separator()
 +
 +        layout.operator("wm.window_fullscreen_toggle", icon='FULLSCREEN_ENTER')
 +
 +        layout.separator()
 +
 +        layout.operator("screen.workspace_cycle", text="Next Workspace").direction = 'NEXT'
 +        layout.operator("screen.workspace_cycle", text="Previous Workspace").direction = 'PREV'
 +
 +        layout.separator()
 +
 +        layout.prop(context.screen, "show_topbar")
 +        layout.prop(context.screen, "show_statusbar")
 +
 +        layout.separator()
 +
 +        layout.operator("screen.screenshot")
 +
 +        if sys.platform[:3] == "win":
 +            layout.separator()
 +            layout.operator("wm.console_toggle", icon='CONSOLE')
 +
 +        if context.scene.render.use_multiview:
 +            layout.separator()
 +            layout.operator("wm.set_stereo_3d")
 +
 +
 +class TOPBAR_MT_help(Menu):
 +    bl_label = "Help"
 +
 +    def draw(self, context):
 +        layout = self.layout
 +
 +        show_developer = context.user_preferences.view.show_developer_ui
 +
 +        layout.operator(
 +            "wm.url_open", text="Manual", icon='HELP',
 +        ).url = "https://docs.blender.org/manual/en/dev/"
 +
 +        layout.operator(
 +            "wm.url_open", text="Report a Bug", icon='URL',
 +        ).url = "https://developer.blender.org/maniphest/task/edit/form/1"
 +
 +        layout.separator()
 +
 +        layout.operator(
 +            "wm.url_open", text="User Communities", icon='URL',
 +        ).url = "https://www.blender.org/community/"
 +        layout.operator(
 +            "wm.url_open", text="Developer Community", icon='URL',
 +        ).url = "https://www.blender.org/get-involved/developers/"
 +
 +        layout.separator()
 +
 +        layout.operator(
 +            "wm.url_open", text="Blender Website", icon='URL',
 +        ).url = "https://www.blender.org"
 +        layout.operator(
 +            "wm.url_open", text="Release Notes", icon='URL',
 +        ).url = "https://www.blender.org/download/releases/%d-%d/" % bpy.app.version[:2]
 +        layout.operator(
 +            "wm.url_open", text="Credits", icon='URL',
 +        ).url = "https://www.blender.org/about/credits/"
 +
 +        layout.separator()
 +
 +        layout.operator(
 +            "wm.url_open", text="Blender Store", icon='URL',
 +        ).url = "https://store.blender.org"
 +        layout.operator(
 +            "wm.url_open", text="Development Fund", icon='URL'
 +        ).url = "https://fund.blender.org"
 +        layout.operator(
 +            "wm.url_open", text="Donate", icon='URL',
 +        ).url = "https://www.blender.org/foundation/donation-payment/"
 +
 +        layout.separator()
 +
 +        if show_developer:
 +            layout.operator(
 +                "wm.url_open", text="Python API Reference", icon='URL',
 +            ).url = bpy.types.WM_OT_doc_view._prefix
 +
 +            layout.operator("wm.operator_cheat_sheet", icon='TEXT')
 +
 +        layout.operator("wm.sysinfo")
 +
 +        layout.separator()
 +
 +        layout.operator("wm.splash", icon='BLENDER')
 +
 +
 +class TOPBAR_MT_file_specials(Menu):
 +    bl_label = "File Context Menu"
 +
 +    def draw(self, context):
 +        layout = self.layout
 +
 +        layout.operator_context = 'INVOKE_AREA'
 +        layout.operator("wm.read_homefile", text="New", icon='FILE_NEW')
 +        layout.operator("wm.open_mainfile", text="Open...", icon='FILE_FOLDER')
 +
 +        layout.separator()
 +
 +        layout.operator("wm.link", text="Link...", icon='LINK_BLEND')
 +        layout.operator("wm.append", text="Append...", icon='APPEND_BLEND')
 +
 +        layout.separator()
 +
 +        layout.menu("TOPBAR_MT_file_import", icon='IMPORT')
 +        layout.menu("TOPBAR_MT_file_export", icon='EXPORT')
 +
 +
 +class TOPBAR_MT_window_specials(Menu):
 +    bl_label = "Window Context Menu"
 +
 +    def draw(self, context):
 +        layout = self.layout
 +
 +        layout.operator_context = 'EXEC_AREA'
 +
 +        layout.operator("wm.window_new")
 +        layout.operator("wm.window_new_main")
 +
 +        layout.operator_context = 'INVOKE_AREA'
 +
 +        layout.operator("screen.area_dupli", icon='DUPLICATE')
 +
 +        layout.separator()
 +
 +        layout.operator("screen.area_split", text="Horizontal Split").direction = 'HORIZONTAL'
 +        layout.operator("screen.area_split", text="Vertical Split").direction = 'VERTICAL'
 +
 +        layout.separator()
 +
 +        layout.operator("wm.window_fullscreen_toggle", icon='FULLSCREEN_ENTER')
 +
 +        layout.separator()
 +
 +        layout.operator("screen.userpref_show", text="Preferences...", icon='PREFERENCES')
 +
 +
 +class TOPBAR_MT_workspace_menu(Menu):
 +    bl_label = "Workspace"
 +
 +    def draw(self, context):
 +        layout = self.layout
 +
 +        layout.operator("workspace.duplicate", text="Duplicate", icon='DUPLICATE')
 +        if len(bpy.data.workspaces) > 1:
 +            layout.operator("workspace.delete", text="Delete", icon='REMOVE')
 +
 +        layout.separator()
 +
 +        layout.operator("workspace.reorder_to_front", text="Reorder to Front", icon='TRIA_LEFT_BAR')
 +        layout.operator("workspace.reorder_to_back", text="Reorder to Back", icon='TRIA_RIGHT_BAR')
 +
 +        layout.separator()
 +
 +        # For key binding discoverability.
 +        props = layout.operator("screen.workspace_cycle", text="Previous Workspace")
 +        props.direction = 'PREV'
 +        props = layout.operator("screen.workspace_cycle", text="Next Workspace")
 +        props.direction = 'NEXT'
 +
 +
 +class TOPBAR_PT_active_tool(Panel):
 +    bl_space_type = 'PROPERTIES'
 +    bl_region_type = 'WINDOW'
 +    bl_category = ""
 +    bl_context = ".active_tool"  # dot on purpose (access from tool settings)
 +    bl_label = "Active Tool"
 +    bl_options = {'HIDE_HEADER'}
 +
 +    def draw(self, context):
 +        layout = self.layout
 +
 +        # Panel display of topbar tool settings.
 +        # currently displays in tool settings, keep here since the same functionality is used for the topbar.
 +
 +        layout.use_property_split = True
 +        layout.use_property_decorate = False
 +
 +        from .space_toolsystem_common import ToolSelectPanelHelper
 +        ToolSelectPanelHelper.draw_active_tool_header(context, layout, show_tool_name=True)
 +
 +
 +# Grease Pencil Object - Primitive curve
 +class TOPBAR_PT_gpencil_primitive(Panel):
 +    bl_space_type = 'VIEW_3D'
 +    bl_region_type = 'HEADER'
 +    bl_label = "Primitives"
 +
 +    @staticmethod
 +    def draw(self, context):
 +        settings = context.tool_settings.gpencil_sculpt
 +
 +        layout = self.layout
 +        # Curve
 +        layout.template_curve_mapping(settings, "thickness_primitive_curve", brush=True)
 +
 +
 +classes = (
 +    TOPBAR_HT_upper_bar,
 +    TOPBAR_HT_lower_bar,
 +    TOPBAR_MT_file_specials,
 +    TOPBAR_MT_window_specials,
 +    TOPBAR_MT_workspace_menu,
 +    TOPBAR_MT_editor_menus,
 +    TOPBAR_MT_file,
 +    TOPBAR_MT_file_new,
 +    TOPBAR_MT_templates_more,
 +    TOPBAR_MT_file_import,
 +    TOPBAR_MT_file_export,
 +    TOPBAR_MT_file_external_data,
 +    TOPBAR_MT_file_previews,
 +    TOPBAR_MT_edit,
 +    TOPBAR_MT_render,
 +    TOPBAR_MT_window,
 +    TOPBAR_MT_help,
 +    TOPBAR_PT_active_tool,
 +    TOPBAR_PT_gpencil_layers,
 +    TOPBAR_PT_gpencil_primitive,
 +)
 +
 +if __name__ == "__main__":  # only for live edit.
 +    from bpy.utils import register_class
 +    for cls in classes:
 +        register_class(cls)
@@@ -4978,93 -3790,98 +4978,93 @@@ class VIEW3D_PT_overlay_paint(Panel)
  
      def draw(self, context):
          layout = self.layout
 -
          view = context.space_data
 -        use_multiview = context.scene.render.use_multiview
 +        overlay = view.overlay
 +        display_all = overlay.show_overlays
  
          col = layout.column()
 -        col.operator("view3d.background_image_add", text="Add Image")
 -
 -        for i, bg in enumerate(view.background_images):
 -            layout.active = view.show_background_images
 -            box = layout.box()
 -            row = box.row(align=True)
 -            row.prop(bg, "show_expanded", text="", emboss=False)
 -            if bg.source == 'IMAGE' and bg.image:
 -                row.prop(bg.image, "name", text="", emboss=False)
 -            elif bg.source == 'MOVIE_CLIP' and bg.clip:
 -                row.prop(bg.clip, "name", text="", emboss=False)
 -            else:
 -                row.label(text="Not Set")
 +        col.active = display_all
  
 -            if bg.show_background_image:
 -                row.prop(bg, "show_background_image", text="", emboss=False, icon='RESTRICT_VIEW_OFF')
 -            else:
 -                row.prop(bg, "show_background_image", text="", emboss=False, icon='RESTRICT_VIEW_ON')
 +        col.prop(overlay, {
 +            'PAINT_TEXTURE': "texture_paint_mode_opacity",
 +            'PAINT_VERTEX': "vertex_paint_mode_opacity",
 +            'PAINT_WEIGHT': "weight_paint_mode_opacity",
 +        }[context.mode], text="Opacity")
  
 -            row.operator("view3d.background_image_remove", text="", emboss=False, icon='X').index = i
 +        if context.mode == 'PAINT_WEIGHT':
 +            row = col.split(factor=0.33)
 +            row.label(text="Zero Weights")
 +            sub = row.row()
 +            sub.prop(context.tool_settings, "vertex_group_user", expand=True)
  
 -            box.prop(bg, "view_axis", text="Axis")
 +            col.prop(overlay, "show_wpaint_contours")
  
 -            if bg.show_expanded:
 -                row = box.row()
 -                row.prop(bg, "source", expand=True)
 +        if context.mode in {'PAINT_WEIGHT', 'PAINT_VERTEX'}:
 +            col.prop(overlay, "show_paint_wire")
  
 -                has_bg = False
 -                if bg.source == 'IMAGE':
 -                    row = box.row()
 -                    row.template_ID(bg, "image", open="image.open")
 -                    if bg.image is not None:
 -                        box.template_image(bg, "image", bg.image_user, compact=True)
 -                        has_bg = True
  
 -                        if use_multiview and bg.view_axis in {'CAMERA', 'ALL'}:
 -                            box.prop(bg.image, "use_multiview")
 +class VIEW3D_PT_pivot_point(Panel):
 +    bl_space_type = 'VIEW_3D'
 +    bl_region_type = 'HEADER'
 +    bl_label = "Pivot Point"
 +    bl_ui_units_x = 8
  
 -                            column = box.column()
 -                            column.active = bg.image.use_multiview
 +    def draw(self, context):
-         toolsettings = context.tool_settings
++        tool_settings = context.tool_settings
 +        obj = context.active_object
 +        mode = context.mode
  
 -                            column.label(text="Views Format:")
 -                            column.row().prop(bg.image, "views_format", expand=True)
 +        layout = self.layout
 +        col = layout.column()
 +        col.label(text="Pivot Point")
-         col.prop(toolsettings, "transform_pivot_point", expand=True)
++        col.prop(tool_settings, "transform_pivot_point", expand=True)
  
 -                            sub = column.box()
 -                            sub.active = bg.image.views_format == 'STEREO_3D'
 -                            sub.template_image_stereo_3d(bg.image.stereo_3d_format)
 +        if (obj is None) or (mode in {'OBJECT', 'POSE', 'WEIGHT_PAINT'}):
 +            col.separator()
  
 -                elif bg.source == 'MOVIE_CLIP':
 -                    box.prop(bg, "use_camera_clip")
 +            col.prop(
-                 toolsettings,
++                tool_settings,
 +                "use_transform_pivot_point_align",
 +                text="Only Origins",
 +            )
  
 -                    column = box.column()
 -                    column.active = not bg.use_camera_clip
 -                    column.template_ID(bg, "clip", open="clip.open")
  
 -                    if bg.clip:
 -                        column.template_movieclip(bg, "clip", compact=True)
 +class VIEW3D_PT_snapping(Panel):
 +    bl_space_type = 'VIEW_3D'
 +    bl_region_type = 'HEADER'
 +    bl_label = "Snapping"
  
 -                    if bg.use_camera_clip or bg.clip:
 -                        has_bg = True
 +    def draw(self, context):
-         toolsettings = context.tool_settings
-         snap_elements = toolsettings.snap_elements
++        tool_settings = context.tool_settings
++        snap_elements = tool_settings.snap_elements
 +        obj = context.active_object
 +        object_mode = 'OBJECT' if obj is None else obj.mode
  
 -                    column = box.column()
 -                    column.active = has_bg
 -                    column.prop(bg.clip_user, "proxy_render_size", text="")
 -                    column.prop(bg.clip_user, "use_render_undistorted")
 +        layout = self.layout
 +        col = layout.column()
 +        col.label(text="Snapping")
-         col.prop(toolsettings, "snap_elements", expand=True)
++        col.prop(tool_settings, "snap_elements", expand=True)
  
 -                if has_bg:
 -                    col = box.column()
 -                    col.prop(bg, "opacity", slider=True)
 -                    col.row().prop(bg, "draw_depth", expand=True)
 +        col.separator()
 +        if 'INCREMENT' in snap_elements:
-             col.prop(toolsettings, "use_snap_grid_absolute")
++            col.prop(tool_settings, "use_snap_grid_absolute")
  
 -                    if bg.view_axis in {'CAMERA', 'ALL'}:
 -                        col.row().prop(bg, "frame_method", expand=True)
 +        if snap_elements != {'INCREMENT'}:
 +            col.label(text="Target")
 +            row = col.row(align=True)
-             row.prop(toolsettings, "snap_target", expand=True)
++            row.prop(tool_settings, "snap_target", expand=True)
  
 -                    box = col.box()
 -                    row = box.row()
 -                    row.prop(bg, "offset_x", text="X")
 -                    row.prop(bg, "offset_y", text="Y")
 +            if obj:
 +                if object_mode == 'EDIT':
-                     col.prop(toolsettings, "use_snap_self")
++                    col.prop(tool_settings, "use_snap_self")
 +                if object_mode in {'OBJECT', 'POSE', 'EDIT'}:
-                     col.prop(toolsettings, "use_snap_align_rotation")
++                    col.prop(tool_settings, "use_snap_align_rotation")
  
 -                    row = box.row()
 -                    row.prop(bg, "use_flip_x")
 -                    row.prop(bg, "use_flip_y")
 +            if 'FACE' in snap_elements:
-                 col.prop(toolsettings, "use_snap_project")
++                col.prop(tool_settings, "use_snap_project")
  
 -                    row = box.row()
 -                    if bg.view_axis != 'CAMERA':
 -                        row.prop(bg, "rotation")
 -                        row.prop(bg, "size")
 +            if 'VOLUME' in snap_elements:
-                 col.prop(toolsettings, "use_snap_peel_object")
++                col.prop(tool_settings, "use_snap_peel_object")
  
  
  class VIEW3D_PT_transform_orientations(Panel):
  
      def draw(self, context):
          layout = self.layout
 +        layout.label(text="Transform Orientations")
  
 -        view = context.space_data
 -        orientation = view.current_orientation
 +        scene = context.scene
 +        orientation = scene.current_orientation
  
 -        row = layout.row(align=True)
 -        row.prop(view, "transform_orientation", text="")
 -        row.operator("transform.create_orientation", text="", icon='ZOOMIN')
 +        row = layout.row()
 +        col = row.column()
 +        col.prop(scene, "transform_orientation", expand=True)
 +        row.operator("transform.create_orientation", text="", icon='ADD', emboss=False).use = True
  
          if orientation:
 -            row = layout.row(align=True)
 -            row.prop(orientation, "name", text="")
 -            row.operator("transform.delete_orientation", text="", icon='X')
 +            row = layout.row(align=False)
 +            row.prop(orientation, "name", text="", icon="OBJECT_ORIGIN")
 +            row.operator("transform.delete_orientation", text="", icon='X', emboss=False)
  
  
 -class VIEW3D_PT_etch_a_ton(Panel):
 +class VIEW3D_PT_gpencil_origin(Panel):
      bl_space_type = 'VIEW_3D'
 -    bl_region_type = 'UI'
 -    bl_label = "Skeleton Sketching"
 -    bl_options = {'DEFAULT_CLOSED'}
 +    bl_region_type = 'HEADER'
 +    bl_label = "Stroke Placement"
 +
 +    def draw(self, context):
 +        layout = self.layout
-         ts = context.tool_settings
++        tool_settings = context.tool_settings
 +        gpd = context.gpencil_data
 +
 +        layout.label(text="Stroke Placement")
 +
 +        row = layout.row()
 +        col = row.column()
-         col.prop(ts, "gpencil_stroke_placement_view3d", expand=True)
++        col.prop(tool_settings, "gpencil_stroke_placement_view3d", expand=True)
 +
-         if ts.gpencil_stroke_placement_view3d == 'SURFACE':
++        if tool_settings.gpencil_stroke_placement_view3d == 'SURFACE':
 +            row = layout.row()
 +            row.label(text="Offset")
 +            row = layout.row()
 +            row.prop(gpd, "zdepth_offset", text="")
 +
-         if ts.gpencil_stroke_placement_view3d == 'STROKE':
++        if tool_settings.gpencil_stroke_placement_view3d == 'STROKE':
 +            row = layout.row()
 +            row.label(text="Target")
 +            row = layout.row()
-             row.prop(ts, "gpencil_stroke_snap_mode", expand=True)
++            row.prop(tool_settings, "gpencil_stroke_snap_mode", expand=True)
 +
 +
 +class VIEW3D_PT_gpencil_lock(Panel):
 +    bl_space_type = 'VIEW_3D'
 +    bl_region_type = 'HEADER'
 +    bl_label = "Drawing Plane"
 +
 +    def draw(self, context):
 +        layout = self.layout
 +        layout.label(text="Drawing Plane")
 +
 +        row = layout.row()
 +        col = row.column()
 +        col.prop(context.tool_settings.gpencil_sculpt, "lock_axis", expand=True)
 +
 +
 +class VIEW3D_PT_overlay_gpencil_options(Panel):
 +    bl_space_type = 'VIEW_3D'
 +    bl_region_type = 'HEADER'
 +    bl_parent_id = 'VIEW3D_PT_overlay'
 +    bl_label = ""
  
      @classmethod
      def poll(cls, context):
@@@ -572,11 -1306,9 +572,11 @@@ class VIEW3D_PT_stencil_projectpaint(Vi
  
      def draw(self, context):
          layout = self.layout
 +        layout.use_property_split = True
 +        layout.use_property_decorate = False
  
-         toolsettings = context.tool_settings
-         ipaint = toolsettings.image_paint
+         tool_settings = context.tool_settings
+         ipaint = tool_settings.image_paint
          ob = context.active_object
          mesh = ob.data
  
@@@ -867,11 -1590,9 +867,11 @@@ class VIEW3D_PT_sculpt_dyntopo(Panel, V
  
      def draw(self, context):
          layout = self.layout
 +        layout.use_property_split = True
 +        layout.use_property_decorate = False
  
-         toolsettings = context.tool_settings
-         sculpt = toolsettings.sculpt
+         tool_settings = context.tool_settings
+         sculpt = tool_settings.sculpt
          settings = self.paint_settings(context)
          brush = settings.brush
  
              sub.prop(sculpt, "detail_percent")
          else:
              sub.prop(sculpt, "detail_size")
 -        sub.prop(sculpt, "detail_refine_method", text="")
 -        sub.prop(sculpt, "detail_type_method", text="")
 -        col.separator()
 +        sub.prop(sculpt, "detail_refine_method", text="Refine Method")
 +        sub.prop(sculpt, "detail_type_method", text="Detailing")
 +
          col.prop(sculpt, "use_smooth_shading")
 -        col.operator("sculpt.optimize")
 -        if (sculpt.detail_type_method == 'CONSTANT'):
 -            col.operator("sculpt.detail_flood_fill")
 -        col.separator()
 +
 +
 +class VIEW3D_PT_sculpt_dyntopo_remesh(Panel, View3DPaintPanel):
 +    bl_context = ".sculpt_mode"  # dot on purpose (access from topbar)
 +    bl_label = "Remesh"
 +    bl_parent_id = "VIEW3D_PT_sculpt_dyntopo"
 +    bl_options = {'DEFAULT_CLOSED'}
 +    bl_ui_units_x = 12
 +
 +    def draw(self, context):
 +        layout = self.layout
 +        layout.use_property_split = True
 +        layout.use_property_decorate = False
 +
-         toolsettings = context.tool_settings
-         sculpt = toolsettings.sculpt
++        tool_settings = context.tool_settings
++        sculpt = tool_settings.sculpt
 +
 +        col = layout.column()
 +        col.active = context.sculpt_object.use_dynamic_topology_sculpting
 +
          col.prop(sculpt, "symmetrize_direction")
 -        col.operator("sculpt.symmetrize")
  
 +        flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False)
  
 +        col = flow.column()
 +        col.operator("sculpt.symmetrize")
 +        col = flow.column()
 +        col.operator("sculpt.optimize")
 +        if sculpt.detail_type_method in {'CONSTANT', 'MANUAL'}:
 +            col = flow.column()
 +            col.operator("sculpt.detail_flood_fill")
 +
 +# TODO, move to space_view3d.py
  class VIEW3D_PT_sculpt_options(Panel, View3DPaintPanel):
 -    bl_category = "Options"
 +    bl_context = ".sculpt_mode"  # dot on purpose (access from topbar)
      bl_label = "Options"
      bl_options = {'DEFAULT_CLOSED'}
  
  
      def draw(self, context):
          layout = self.layout
 -        # scene = context.scene
 +        layout.use_property_split = True
 +        layout.use_property_decorate = False
 +
-         toolsettings = context.tool_settings
-         sculpt = toolsettings.sculpt
++        tool_settings = context.tool_settings
++        sculpt = tool_settings.sculpt
 +
 +        flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False)
 +
 +        col = flow.column()
 +        col.prop(sculpt, "use_threaded", text="Threaded Sculpt")
 +        col = flow.column()
 +        col.prop(sculpt, "show_low_resolution")
 +        col = flow.column()
 +        col.prop(sculpt, "use_deform_only")
 +        col = flow.column()
 +        col.prop(sculpt, "show_diffuse_color")
 +        col = flow.column()
 +        col.prop(sculpt, "show_mask")
 +
 +
 +class VIEW3D_PT_sculpt_options_unified(Panel, View3DPaintPanel):
 +    bl_context = ".sculpt_mode"  # dot on purpose (access from topbar)
 +    bl_parent_id = "VIEW3D_PT_sculpt_options"
 +    bl_label = "Unified Brush"
 +
 +    @classmethod
 +    def poll(cls, context):
 +        return (context.sculpt_object and context.tool_settings.sculpt)
 +
 +    def draw(self, context):
 +        layout = self.layout
 +        layout.use_property_split = True
 +        layout.use_property_decorate = False
 +
 +        self.unified_paint_settings(layout, context)
 +
 +class VIEW3D_PT_sculpt_options_gravity(Panel, View3DPaintPanel):
 +    bl_context = ".sculpt_mode"  # dot on purpose (access from topbar)
 +    bl_parent_id = "VIEW3D_PT_sculpt_options"
 +    bl_label = "Gravity"
 +
 +    @classmethod
 +    def poll(cls, context):
 +        return (context.sculpt_object and context.tool_settings.sculpt)
 +
 +    def draw(self, context):
 +        layout = self.layout
 +        layout.use_property_split = True
 +        layout.use_property_decorate = False
  
-         toolsettings = context.tool_settings
-         sculpt = toolsettings.sculpt
+         tool_settings = context.tool_settings
+         sculpt = tool_settings.sculpt
          capabilities = sculpt.brush.sculpt_capabilities
  
 -        col = layout.column(align=True)
 +        col = layout.column()
          col.active = capabilities.has_gravity
 -        col.label(text="Gravity:")
          col.prop(sculpt, "gravity", slider=True, text="Factor")
          col.prop(sculpt, "gravity_object")
 -        col.separator()
 -
 -        layout.prop(sculpt, "use_threaded", text="Threaded Sculpt")
 -        layout.prop(sculpt, "show_low_resolution")
 -        layout.prop(sculpt, "use_deform_only")
 -        layout.prop(sculpt, "show_diffuse_color")
 -        layout.prop(sculpt, "show_mask")
 -
 -        self.unified_paint_settings(layout, context)
  
  
 +# TODO, move to space_view3d.py
  class VIEW3D_PT_sculpt_symmetry(Panel, View3DPaintPanel):
 -    bl_category = "Tools"
 +    bl_context = ".sculpt_mode"  # dot on purpose (access from topbar)
      bl_label = "Symmetry/Lock"
      bl_options = {'DEFAULT_CLOSED'}
  
@@@ -1219,22 -1838,17 +1219,22 @@@ class VIEW3D_PT_tools_imagepaint_extern
  
      def draw(self, context):
          layout = self.layout
 +        layout.use_property_split = True
 +        layout.use_property_decorate = False
  
-         toolsettings = context.tool_settings
-         ipaint = toolsettings.image_paint
+         tool_settings = context.tool_settings
+         ipaint = tool_settings.image_paint
  
 -        col = layout.column()
 -        row = col.split(align=True, percentage=0.55)
 -        row.operator("image.project_edit", text="Quick Edit")
 -        row.operator("image.project_apply", text="Apply")
 +        layout.prop(ipaint, "screen_grab_size", text="Screen Grab Size")
  
 -        col.row().prop(ipaint, "screen_grab_size", text="")
 +        layout.separator()
  
 +        flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False)
 +        col = flow.column()
 +        col.operator("image.project_edit", text="Quick Edit")
 +        col = flow.column()
 +        col.operator("image.project_apply", text="Apply")
 +        col = flow.column()
          col.operator("paint.project_image", text="Apply Camera Image")
  
  
@@@ -1247,17 -1861,10 +1247,17 @@@ class VIEW3D_PT_tools_imagepaint_symmet
      def draw(self, context):
          layout = self.layout
  
-         toolsettings = context.tool_settings
-         ipaint = toolsettings.image_paint
+         tool_settings = context.tool_settings
+         ipaint = tool_settings.image_paint
  
 -        col = layout.column(align=True)
 +        split = layout.split()
 +
 +        col = split.column()
 +        col.alignment = 'RIGHT'
 +        col.label(text="Mirror")
 +
 +        col = split.column()
 +
          row = col.row(align=True)
          row.prop(ipaint, "use_symmetry_x", text="X", toggle=True)
          row.prop(ipaint, "use_symmetry_y", text="Y", toggle=True)
@@@ -1278,12 -1884,14 +1278,12 @@@ class VIEW3D_PT_tools_projectpaint(View
      def draw(self, context):
          layout = self.layout
  
-         toolsettings = context.tool_settings
-         ipaint = toolsettings.image_paint
 +        layout.use_property_split = True
 +        layout.use_property_decorate = False
 +
+         tool_settings = context.tool_settings
+         ipaint = tool_settings.image_paint
  
 -        col = layout.column()
 -
 -        col.prop(ipaint, "use_occlude")
 -        col.prop(ipaint, "use_backface_culling")
 -
          row = layout.row()
          row.prop(ipaint, "use_normal_falloff")
  
          sub.active = (ipaint.use_normal_falloff)
          sub.prop(ipaint, "normal_angle", text="")
  
 -        layout.prop(ipaint, "use_cavity")
 -        if ipaint.use_cavity:
 -            layout.template_curve_mapping(ipaint, "cavity_curve", brush=True)
 -
          layout.prop(ipaint, "seam_bleed")
          layout.prop(ipaint, "dither")
 +
 +        flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False)
 +
 +        col = flow.column()
 +        col.prop(ipaint, "use_occlude")
 +
 +        col = flow.column()
 +        col.prop(ipaint, "use_backface_culling")
 +
 +class VIEW3D_PT_tools_projectpaint_unified(Panel, View3DPaintPanel):
 +    bl_context = ".imagepaint"  # dot on purpose (access from topbar)
 +    bl_parent_id = "VIEW3D_PT_tools_projectpaint"
 +    bl_label = "Unified Brush"
 +
 +    def draw(self, context):
 +        layout = self.layout
 +        layout.use_property_split = True
 +        layout.use_property_decorate = False
 +
          self.unified_paint_settings(layout, context)
  
-         toolsettings = context.tool_settings
-         ipaint = toolsettings.image_paint
 +class VIEW3D_PT_tools_projectpaint_cavity(View3DPaintPanel, Panel):
 +    bl_context = ".imagepaint"  # dot on purpose (access from topbar)
 +    bl_label = "Cavity Mask"
 +    bl_parent_id = "VIEW3D_PT_tools_projectpaint"
 +    bl_options = {'DEFAULT_CLOSED'}
 +
 +    def draw_header(self, context):
++        tool_settings = context.tool_settings
++        ipaint = tool_settings.image_paint
 +
 +        self.layout.prop(ipaint, "use_cavity", text="")
  
-         toolsettings = context.tool_settings
-         ipaint = toolsettings.image_paint
 +    def draw(self, context):
 +        layout = self.layout
 +
++        tool_settings = context.tool_settings
++        ipaint = tool_settings.image_paint
 +
 +        layout.active = ipaint.use_cavity
 +
 +        layout.template_curve_mapping(ipaint, "cavity_curve", brush=True)
 +
 +
 +# TODO, move to space_view3d.py
  class VIEW3D_PT_imagepaint_options(View3DPaintPanel):
 -    bl_category = "Options"
      bl_label = "Options"
  
      @classmethod
@@@ -1435,316 -2006,9 +1435,316 @@@ class VIEW3D_PT_tools_particlemode(View
              sub.prop(pe, "fade_frames", slider=True)
  
  
 -# Grease Pencil drawing tools
 -class VIEW3D_PT_tools_grease_pencil_draw(GreasePencilDrawingToolsPanel, Panel):
 -    bl_space_type = 'VIEW_3D'
 +class VIEW3D_PT_tools_normal(View3DPanel, Panel):
 +    bl_category = ""
 +    bl_context = ".mesh_edit"
 +    bl_label = "Normals"
 +    bl_options = {'DEFAULT_CLOSED'}
 +
 +    def draw(self, context):
 +        layout = self.layout
 +        layout.use_property_split = True
 +        layout.use_property_decorate = False  # No animation.
 +
-         toolsettings = context.tool_settings
++        tool_settings = context.tool_settings
 +
-         layout.prop(toolsettings, "normal_vector", text="Normal Vector")
-         layout.prop(toolsettings, "face_strength", text="Face Strength")
++        layout.prop(tool_settings, "normal_vector", text="Normal Vector")
++        layout.prop(tool_settings, "face_strength", text="Face Strength")
 +
 +# ********** grease pencil object tool panels ****************
 +
 +# Grease Pencil drawing brushes
 +class VIEW3D_PT_tools_grease_pencil_brush(View3DPanel, Panel):
 +    bl_context = ".greasepencil_paint"
 +    bl_label = "Brush"
 +
 +    @classmethod
 +    def poll(cls, context):
 +        is_3d_view = context.space_data.type == 'VIEW_3D'
 +        if is_3d_view:
 +            if context.gpencil_data is None:
 +                return False
 +
 +            gpd = context.gpencil_data
 +            return bool(gpd.is_stroke_paint_mode)
 +        else:
 +            return True
 +
 +    @staticmethod
 +    def draw(self, context):
 +        layout = self.layout
 +        layout.use_property_split = True
 +        layout.use_property_decorate = False
 +
 +        tool_settings = context.scene.tool_settings
 +        gpencil_paint = tool_settings.gpencil_paint
 +
 +        row = layout.row()
 +        col = row.column()
 +        col.template_ID_preview(gpencil_paint, "brush", new="brush.add_gpencil", rows=3, cols=8)
 +
 +        col = row.column()
 +        brush = gpencil_paint.brush
 +
 +        sub = col.column(align=True)
 +        sub.operator("gpencil.brush_presets_create", icon='HELP', text="")
 +
 +        if brush is not None:
 +            gp_settings = brush.gpencil_settings
 +
 +            # XXX: Items in "sub" currently show up beside the brush selector in a separate column
 +            if brush.gpencil_tool == 'ERASE':
 +                sub.prop(gp_settings, "use_default_eraser", text="")
 +
 +            # Brush details
 +            if brush.gpencil_tool == 'ERASE':
 +                row = layout.row(align=True)
 +                row.prop(brush, "size", text="Radius")
 +                row.prop(gp_settings, "use_pressure", text="", icon='STYLUS_PRESSURE')
 +                row.prop(gp_settings, "use_occlude_eraser", text="", icon='XRAY')
 +
 +                if gp_settings.eraser_mode == 'SOFT':
 +                    row = layout.row(align=True)
 +                    row.prop(gp_settings, "pen_strength", slider=True)
 +                    row.prop(gp_settings, "use_strength_pressure", text="", icon='STYLUS_PRESSURE')
 +                    row = layout.row(align=True)
 +                    row.prop(gp_settings, "eraser_strength_factor")
 +                    row = layout.row(align=True)
 +                    row.prop(gp_settings, "eraser_thickness_factor")
 +            elif brush.gpencil_tool == 'FILL':
 +                col = layout.column(align=True)
 +                col.prop(gp_settings, "fill_leak", text="Leak Size")
 +                col.separator()
 +                col.prop(brush, "size", text="Thickness")
 +                col.prop(gp_settings, "fill_simplify_level", text="Simplify")
 +
 +                col = layout.row(align=True)
 +                col.template_ID(gp_settings, "material")
 +
 +                row = layout.row(align=True)
 +                row.prop(gp_settings, "fill_draw_mode", text="Boundary Draw Mode")
 +                row.prop(gp_settings, "show_fill_boundary", text="", icon='GRID')
 +
 +                col = layout.column(align=True)
 +                col.enabled = gp_settings.fill_draw_mode != 'STROKE'
 +                col.prop(gp_settings, "show_fill", text="Ignore Transparent Strokes")
 +                sub = col.row(align=True)
 +                sub.enabled = not gp_settings.show_fill
 +                sub.prop(gp_settings, "fill_threshold", text="Threshold")
 +            else:  # bgpsettings.tool == 'DRAW':
 +                row = layout.row(align=True)
 +                row.prop(brush, "size", text="Radius")
 +                row.prop(gp_settings, "use_pressure", text="", icon='STYLUS_PRESSURE')
 +                row = layout.row(align=True)
 +                row.prop(gp_settings, "pen_strength", slider=True)
 +                row.prop(gp_settings, "use_strength_pressure", text="", icon='STYLUS_PRESSURE')
 +
 +                row = layout.row(align=True)
 +                row.template_ID(gp_settings, "material")
 +
 +
 +# Grease Pencil drawing brushes options
 +class VIEW3D_PT_tools_grease_pencil_brush_option(View3DPanel, Panel):
 +    bl_context = ".greasepencil_paint"
 +    bl_label = "Options"
 +    bl_options = {'DEFAULT_CLOSED'}
 +
 +    @classmethod
 +    def poll(cls, context):
 +        brush = context.tool_settings.gpencil_paint.brush
 +        return brush is not None and brush.gpencil_tool != 'ERASE'
 +
 +    def draw_header_preset(self, context):
 +        VIEW3D_PT_gpencil_brush_presets.draw_panel_header(self.layout)
 +
 +    @staticmethod
 +    def draw(self, context):
 +        layout = self.layout
 +        layout.use_property_split = True
 +        layout.use_property_decorate = False
 +
 +        brush = context.tool_settings.gpencil_paint.brush
 +        gp_settings = brush.gpencil_settings
 +
 +        if brush is not None:
 +            col = layout.column(align=True)
 +            col.prop(gp_settings, "input_samples")
 +            col.separator()
 +
 +            col.prop(gp_settings, "active_smooth_factor")
 +            col.separator()
 +
 +            col.prop(gp_settings, "angle", slider=True)
 +            col.prop(gp_settings, "angle_factor", text="Factor", slider=True)
 +            col.separator()
 +
 +
 +class VIEW3D_PT_tools_grease_pencil_brush_stabilizer(View3DPanel, Panel):
 +    bl_context = ".greasepencil_paint"
 +    bl_parent_id = 'VIEW3D_PT_tools_grease_pencil_brush_option'
 +    bl_label = "Stabilizer Settings"
 +    bl_options = {'DEFAULT_CLOSED'}
 +
 +    @classmethod
 +    def poll(cls, context):
 +        brush = context.tool_settings.gpencil_paint.brush
 +        return brush is not None and brush.gpencil_tool == 'DRAW'
 +
 +    def draw_header(self, context):
 +        brush = context.tool_settings.gpencil_paint.brush
 +        gp_settings = brush.gpencil_settings
 +        self.layout.prop(gp_settings, "use_settings_stabilizer", text="")
 +
 +    @staticmethod
 +    def draw(self, context):
 +        layout = self.layout
 +        layout.use_property_split = True
 +        layout.use_property_decorate = False
 +
 +        brush = context.tool_settings.gpencil_paint.brush
 +        gp_settings = brush.gpencil_settings
 +        layout.active = gp_settings.use_settings_stabilizer
 +
 +        layout.prop(brush, "smooth_stroke_radius", text="Radius", slider=True)
 +        layout.prop(brush, "smooth_stroke_factor", text="Factor", slider=True)
 +
 +
 +class VIEW3D_PT_tools_grease_pencil_brush_settings(View3DPanel, Panel):
 +    bl_context = ".greasepencil_paint"
 +    bl_parent_id = 'VIEW3D_PT_tools_grease_pencil_brush_option'
 +    bl_label = "Post-processing Settings"
 +    bl_options = {'DEFAULT_CLOSED'}
 +
 +    @classmethod
 +    def poll(cls, context):
 +        brush = context.tool_settings.gpencil_paint.brush
 +        return brush is not None and brush.gpencil_tool != 'ERASE'
 +
 +    def draw_header(self, context):
 +        brush = context.tool_settings.gpencil_paint.brush
 +        gp_settings = brush.gpencil_settings
 +        self.layout.prop(gp_settings, "use_settings_postprocess", text="")
 +
 +    @staticmethod
 +    def draw(self, context):
 +        layout = self.layout
 +        layout.use_property_split = True
 +        layout.use_property_decorate = False
 +
 +        brush = context.tool_settings.gpencil_paint.brush
 +        gp_settings = brush.gpencil_settings
 +        layout.active = gp_settings.use_settings_postprocess
 +
 +        col = layout.column(align=True)
 +        col.prop(gp_settings, "pen_smooth_factor")
 +        col.prop(gp_settings, "pen_smooth_steps")
 +
 +        col = layout.column(align=True)
 +        col.prop(gp_settings, "pen_thick_smooth_factor")
 +        col.prop(gp_settings, "pen_thick_smooth_steps", text="Iterations")
 +
 +        col = layout.column(align=True)
 +        col.prop(gp_settings, "pen_subdivision_steps")
 +        col.prop(gp_settings, "random_subdiv", text="Randomness", slider=True)
 +
 +
 +class VIEW3D_PT_tools_grease_pencil_brush_random(View3DPanel, Panel):
 +    bl_context = ".greasepencil_paint"
 +    bl_parent_id = 'VIEW3D_PT_tools_grease_pencil_brush_option'
 +    bl_label = "Random Settings"
 +    bl_options = {'DEFAULT_CLOSED'}
 +
 +    @classmethod
 +    def poll(cls, context):
 +        brush = context.tool_settings.gpencil_paint.brush
 +        return brush is not None and brush.gpencil_tool != 'ERASE'
 +
 +    def draw_header(self, context):
 +        brush = context.tool_settings.gpencil_paint.brush
 +        gp_settings = brush.gpencil_settings
 +        self.layout.prop(gp_settings, "use_settings_random", text="")
 +
 +    @staticmethod
 +    def draw(self, context):
 +        layout = self.layout
 +        layout.use_property_split = True
 +        layout.use_property_decorate = False
 +
 +        brush = context.tool_settings.gpencil_paint.brush
 +        gp_settings = brush.gpencil_settings
 +        layout.active = gp_settings.use_settings_random
 +
 +        layout.prop(gp_settings, "random_pressure", text="Pressure", slider=True)
 +        layout.prop(gp_settings, "random_strength", text="Strength", slider=True)
 +        layout.prop(gp_settings, "uv_random", text="UV", slider=True)
 +
 +        row = layout.row(align=True)
 +        row.prop(gp_settings, "pen_jitter", slider=True)
 +        row.prop(gp_settings, "use_jitter_pressure", text="", icon='STYLUS_PRESSURE')
 +
 +
 +# Grease Pencil drawingcurves
 +class VIEW3D_PT_tools_grease_pencil_brushcurves(View3DPanel, Panel):
 +    bl_context = ".greasepencil_paint"
 +    bl_label = "Curves"
 +    bl_options = {'DEFAULT_CLOSED'}
 +
 +    @classmethod
 +    def poll(cls, context):
 +        brush = context.tool_settings.gpencil_paint.brush
 +        return brush is not None and brush.gpencil_tool != 'ERASE'
 +
 +    @staticmethod
 +    def draw(self, context):
 +        layout = self.layout
 +
 +
 +class VIEW3D_PT_tools_grease_pencil_brushcurves_sensitivity(View3DPanel, Panel):
 +    bl_context = ".greasepencil_paint"
 +    bl_label = "Sensitivity"
 +    bl_parent_id ="VIEW3D_PT_tools_grease_pencil_brushcurves"
 +
 +    @staticmethod
 +    def draw(self, context):
 +        layout = self.layout
 +        layout.use_property_split = True
 +
 +        brush = context.tool_settings.gpencil_paint.brush
 +        gp_settings = brush.gpencil_settings
 +
 +        layout.template_curve_mapping(gp_settings, "curve_sensitivity", brush=True)
 +
 +
 +class VIEW3D_PT_tools_grease_pencil_brushcurves_strength(View3DPanel, Panel):
 +    bl_context = ".greasepencil_paint"
 +    bl_label = "Strength"
 +    bl_parent_id ="VIEW3D_PT_tools_grease_pencil_brushcurves"
 +
 +    @staticmethod
 +    def draw(self, context):
 +        layout = self.layout
 +        layout.use_property_split = True
 +
 +        brush = context.tool_settings.gpencil_paint.brush
 +        gp_settings = brush.gpencil_settings
 +
 +        layout.template_curve_mapping(gp_settings, "curve_strength", brush=True)
 +
 +
 +class VIEW3D_PT_tools_grease_pencil_brushcurves_jitter(View3DPanel, Panel):
 +    bl_context = ".greasepencil_paint"
 +    bl_label = "Jitter"
 +    bl_parent_id ="VIEW3D_PT_tools_grease_pencil_brushcurves"
 +
 +    @staticmethod
 +    def draw(self, context):
 +        layout = self.layout
 +        layout.use_property_split = True
 +
 +        brush = context.tool_settings.gpencil_paint.brush
 +        gp_settings = brush.gpencil_settings
 +
 +        layout.template_curve_mapping(gp_settings, "curve_jitter", brush=True)
  
  
  # Grease Pencil stroke editing tools