Extensions: split extensions by category Enabled/Installed/Available
Each section (if not empty) now shows a panel header. Now there is no need to include "installed/disabled" text next to each extension. This also removes the need for most of the filtering options. See design task #123660.
This commit is contained in:
@@ -585,17 +585,24 @@ def register():
|
||||
description="Show extensions by type",
|
||||
default='ADDON',
|
||||
)
|
||||
WindowManager.extension_enabled_only = BoolProperty(
|
||||
name="Show Enabled Extensions",
|
||||
description="Only show enabled extensions",
|
||||
)
|
||||
WindowManager.extension_updates_only = BoolProperty(
|
||||
name="Show Updates Available",
|
||||
description="Only show extensions with updates available",
|
||||
)
|
||||
WindowManager.extension_installed_only = BoolProperty(
|
||||
WindowManager.extension_show_panel_enabled = BoolProperty(
|
||||
name="Show Enabled Extensions",
|
||||
description="Only show enabled extensions",
|
||||
default=True,
|
||||
)
|
||||
WindowManager.extension_show_panel_installed = BoolProperty(
|
||||
name="Show Installed Extensions",
|
||||
description="Only show installed extensions",
|
||||
default=True,
|
||||
)
|
||||
WindowManager.extension_show_panel_available = BoolProperty(
|
||||
name="Show Installed Extensions",
|
||||
description="Only show installed extensions",
|
||||
default=True,
|
||||
)
|
||||
|
||||
from bl_ui.space_userpref import USERPREF_MT_interface_theme_presets
|
||||
@@ -630,8 +637,9 @@ def unregister():
|
||||
del WindowManager.extension_tags
|
||||
del WindowManager.extension_search
|
||||
del WindowManager.extension_type
|
||||
del WindowManager.extension_enabled_only
|
||||
del WindowManager.extension_installed_only
|
||||
del WindowManager.extension_show_panel_enabled
|
||||
del WindowManager.extension_show_panel_installed
|
||||
del WindowManager.extension_show_panel_available
|
||||
|
||||
for cls in classes:
|
||||
bpy.utils.unregister_class(cls)
|
||||
|
||||
@@ -3098,7 +3098,8 @@ class EXTENSIONS_OT_userpref_show_for_update(Operator):
|
||||
prefs.view.show_addons_enabled_only = False
|
||||
|
||||
# Show only extensions that will be updated.
|
||||
wm.extension_installed_only = False
|
||||
wm.extension_show_panel_enabled = True
|
||||
wm.extension_show_panel_installed = True
|
||||
wm.extension_updates_only = True
|
||||
|
||||
bpy.ops.screen.userpref_show('INVOKE_DEFAULT')
|
||||
|
||||
@@ -733,6 +733,28 @@ class notify_info:
|
||||
notify_info._update_state = False
|
||||
|
||||
|
||||
# Simple data-storage while drawing.
|
||||
# NOTE: could be a named-tuple except the `layout_panel` needs to be mutable.
|
||||
class ExtensionPanelPropData:
|
||||
__slots__ = (
|
||||
"layout_base",
|
||||
"label",
|
||||
"prop_id",
|
||||
|
||||
# `False`: uninitialized.
|
||||
# `None`: initialized & hidden.
|
||||
# `UILayout`: initialized & visible.
|
||||
"layout_panel",
|
||||
)
|
||||
|
||||
def __init__(self, layout_base, label, prop_id):
|
||||
self.layout_base = layout_base
|
||||
self.label = label
|
||||
self.prop_id = prop_id
|
||||
# Initialized as needed.
|
||||
self.layout_panel = False
|
||||
|
||||
|
||||
def extensions_panel_draw_online_extensions_request_impl(
|
||||
self,
|
||||
_context,
|
||||
@@ -810,9 +832,7 @@ def extensions_panel_draw_impl(
|
||||
search_casefold, # `str`
|
||||
filter_by_type, # `str`
|
||||
extension_tags_exclude, # `Set[str]`
|
||||
enabled_only, # `bool`
|
||||
updates_only, # `bool`
|
||||
installed_only, # `bool`
|
||||
operation_in_progress, # `bool`
|
||||
show_development, # `bool`
|
||||
):
|
||||
@@ -843,13 +863,17 @@ def extensions_panel_draw_impl(
|
||||
|
||||
prefs = context.preferences
|
||||
|
||||
if updates_only:
|
||||
installed_only = True
|
||||
|
||||
# Define a top-most column to place warnings (if-any).
|
||||
# Needed so the warnings aren't mixed in with other content.
|
||||
layout_topmost = layout.column()
|
||||
|
||||
SECTION_ENABLED, SECTION_INSTALLED, SECTION_AVAILABLE = 0, 1, 2
|
||||
layout_sections = (
|
||||
ExtensionPanelPropData(layout.column(), iface_("Enabled"), "extension_show_panel_enabled"),
|
||||
ExtensionPanelPropData(layout.column(), iface_("Installed"), "extension_show_panel_installed"),
|
||||
ExtensionPanelPropData(layout.column(), iface_("Available"), "extension_show_panel_available"),
|
||||
)
|
||||
|
||||
repos_all = extension_repos_read()
|
||||
|
||||
if bpy.app.online_access:
|
||||
@@ -1004,9 +1028,6 @@ def extensions_panel_draw_impl(
|
||||
|
||||
is_installed = item_local is not None
|
||||
|
||||
if installed_only and (is_installed == 0):
|
||||
continue
|
||||
|
||||
if extension_tags_exclude:
|
||||
if tags_exclude_match(item.tags, extension_tags_exclude):
|
||||
continue
|
||||
@@ -1038,9 +1059,6 @@ def extensions_panel_draw_impl(
|
||||
is_enabled = is_installed
|
||||
addon_module_name = None
|
||||
|
||||
if enabled_only and (not is_enabled):
|
||||
continue
|
||||
|
||||
item_version = item.version
|
||||
if item_local is None:
|
||||
item_local_version = None
|
||||
@@ -1053,18 +1071,36 @@ def extensions_panel_draw_impl(
|
||||
if not is_outdated:
|
||||
continue
|
||||
|
||||
key = (pkg_id, repo_index)
|
||||
if show_development:
|
||||
mark = key in blender_extension_mark
|
||||
show = key in blender_extension_show
|
||||
del key
|
||||
if is_enabled:
|
||||
section_type = SECTION_ENABLED
|
||||
elif is_installed:
|
||||
section_type = SECTION_INSTALLED
|
||||
else:
|
||||
section_type = SECTION_AVAILABLE
|
||||
|
||||
box = layout.box()
|
||||
if (layout_panel := layout_sections[section_type].layout_panel) is False:
|
||||
section = layout_sections[section_type]
|
||||
layout_header, layout_panel = section.layout_base.panel_prop(
|
||||
context.window_manager,
|
||||
section.prop_id,
|
||||
)
|
||||
layout_header.label(text=section.label, translate=False)
|
||||
section.layout_panel = layout_panel
|
||||
del section, layout_header
|
||||
if layout_panel is None:
|
||||
continue
|
||||
|
||||
box = layout_panel.box()
|
||||
del layout_panel, section_type
|
||||
|
||||
# Left align so the operator text isn't centered.
|
||||
colsub = box.column()
|
||||
row = colsub.row(align=True)
|
||||
# row.label
|
||||
|
||||
if show_development:
|
||||
mark = (pkg_id, repo_index) in blender_extension_mark
|
||||
show = (pkg_id, repo_index) in blender_extension_show
|
||||
|
||||
if show:
|
||||
props = row.operator("extensions.package_show_clear", text="", icon='DOWNARROW_HLT', emboss=False)
|
||||
else:
|
||||
@@ -1087,13 +1123,8 @@ def extensions_panel_draw_impl(
|
||||
# Without checking `is_enabled` here, there is no way for the user to know if an extension
|
||||
# is enabled or not, which is useful to show - when they may be considering removing/updating
|
||||
# extensions based on them being used or not.
|
||||
sub.label(
|
||||
text=(
|
||||
item.name if (is_enabled or is_installed is False) else
|
||||
item.name + iface_(" (disabled)")
|
||||
),
|
||||
translate=False,
|
||||
)
|
||||
sub.label(text=item.name, translate=False)
|
||||
|
||||
del sub
|
||||
|
||||
# Add a top-level row so `row_right` can have a grayed out button/label
|
||||
@@ -1113,10 +1144,6 @@ def extensions_panel_draw_impl(
|
||||
props.repo_index = repo_index
|
||||
props.pkg_id = pkg_id
|
||||
del props
|
||||
else:
|
||||
# Right space for alignment with the button.
|
||||
row_right.label(text="Installed ")
|
||||
row_right.active = False
|
||||
else:
|
||||
props = row_right.operator("extensions.package_install", text="Install")
|
||||
props.repo_index = repo_index
|
||||
@@ -1127,8 +1154,6 @@ def extensions_panel_draw_impl(
|
||||
if has_remote and (item_remote is None):
|
||||
# There is a local item with no remote
|
||||
row_right.label(text="Orphan ")
|
||||
else:
|
||||
row_right.label(text="Installed ")
|
||||
|
||||
row_right.active = False
|
||||
|
||||
@@ -1224,11 +1249,7 @@ class USERPREF_PT_extensions_filter(Panel):
|
||||
|
||||
col = layout.column(heading="Show Only")
|
||||
col.use_property_split = True
|
||||
col.prop(wm, "extension_enabled_only", text="Enabled Extensions")
|
||||
col.prop(wm, "extension_updates_only", text="Updates Available")
|
||||
sub = col.column()
|
||||
sub.active = (not wm.extension_enabled_only) and (not wm.extension_updates_only)
|
||||
sub.prop(wm, "extension_installed_only", text="Installed Extensions")
|
||||
|
||||
|
||||
class USERPREF_PT_addons_tags(Panel):
|
||||
@@ -1587,9 +1608,7 @@ def extensions_panel_draw(panel, context):
|
||||
wm.extension_search.casefold(),
|
||||
blender_filter_by_type_map[wm.extension_type],
|
||||
extension_tags_exclude,
|
||||
wm.extension_enabled_only,
|
||||
wm.extension_updates_only,
|
||||
wm.extension_installed_only,
|
||||
operation_in_progress,
|
||||
show_development,
|
||||
)
|
||||
@@ -1649,17 +1668,23 @@ def tags_current(wm, tags_attr):
|
||||
|
||||
if tags_attr == "addon_tags":
|
||||
filter_by_type = "add-on"
|
||||
only_enabled = prefs.view.show_addons_enabled_only
|
||||
show_enabled = prefs.view.show_addons_enabled_only
|
||||
show_installed = True
|
||||
show_available = False
|
||||
else:
|
||||
filter_by_type = blender_filter_by_type_map[wm.extension_type]
|
||||
only_enabled = wm.extension_enabled_only
|
||||
|
||||
# Currently only add-ons can make use of enabled by type (usefully) for tags.
|
||||
if only_enabled and (filter_by_type == "add-on"):
|
||||
addons_enabled = {addon.module for addon in prefs.addons}
|
||||
show_enabled = wm.extension_show_panel_enabled
|
||||
show_installed = wm.extension_show_panel_installed
|
||||
show_available = wm.extension_show_panel_available
|
||||
|
||||
repos_all = extension_repos_read()
|
||||
|
||||
# Currently only add-ons can make use of enabled by type (usefully) for tags.
|
||||
if filter_by_type == "add-on":
|
||||
addons_enabled = {addon.module for addon in prefs.addons}
|
||||
elif filter_by_type == "theme":
|
||||
active_theme_info = pkg_repo_and_id_from_theme_path(repos_all, prefs.themes[0].filepath)
|
||||
|
||||
tags = set()
|
||||
|
||||
for repo_index, (
|
||||
@@ -1673,9 +1698,8 @@ def tags_current(wm, tags_attr):
|
||||
((None,) * len(repos_all)),
|
||||
strict=True,
|
||||
)):
|
||||
if only_enabled:
|
||||
if filter_by_type == "add-on":
|
||||
repo_module_prefix = pkg_repo_module_prefix(repos_all[repo_index])
|
||||
if filter_by_type == "add-on":
|
||||
repo_module_prefix = pkg_repo_module_prefix(repos_all[repo_index])
|
||||
|
||||
for pkg_id, (item_local, item_remote) in pkg_manifest_zip_all_items(pkg_manifest_local, pkg_manifest_remote):
|
||||
item = item_local or item_remote
|
||||
@@ -1684,16 +1708,31 @@ def tags_current(wm, tags_attr):
|
||||
if filter_by_type != item.type:
|
||||
continue
|
||||
|
||||
# Filter using `Only Enabled`.
|
||||
# NOTE: this is only supported by add-ons currently.
|
||||
# This could be made to work for themes too however there is only ever one enabled theme at a time.
|
||||
# The use case for that is weak at best. "Only Enabled" can be supported by other types as needed.
|
||||
if only_enabled:
|
||||
if filter_by_type == "add-on":
|
||||
if item_local is not None:
|
||||
addon_module_name = repo_module_prefix + pkg_id
|
||||
if addon_module_name not in addons_enabled:
|
||||
continue
|
||||
is_installed = item_local is not None
|
||||
|
||||
# Filter using panel toggles.
|
||||
if filter_by_type == "add-on":
|
||||
is_enabled = False
|
||||
if item_local is not None:
|
||||
addon_module_name = repo_module_prefix + pkg_id
|
||||
if addon_module_name in addons_enabled:
|
||||
is_enabled = True
|
||||
elif filter_by_type == "theme":
|
||||
is_enabled = (repo_index, pkg_id) == active_theme_info
|
||||
else:
|
||||
assert False, "Unreachable"
|
||||
continue
|
||||
|
||||
if is_enabled:
|
||||
if not show_enabled:
|
||||
continue
|
||||
elif is_installed:
|
||||
if not show_installed:
|
||||
continue
|
||||
else:
|
||||
if not show_available:
|
||||
continue
|
||||
|
||||
if pkg_tags := item.tags:
|
||||
tags.update(pkg_tags)
|
||||
|
||||
@@ -1707,7 +1746,7 @@ def tags_current(wm, tags_attr):
|
||||
is_extension = addon_utils.check_extension(module_name)
|
||||
if is_extension:
|
||||
continue
|
||||
if only_enabled: # No need to check `filter_by_type` here.
|
||||
if show_enabled: # No need to check `filter_by_type` here.
|
||||
if module_name not in addons_enabled:
|
||||
continue
|
||||
bl_info = addon_utils.module_bl_info(mod)
|
||||
|
||||
Reference in New Issue
Block a user