Newer
Older
# ##### 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>
bl_info = {
"name": "Icon Viewer",
"description": "Click an icon to copy its name to the clipboard",
"author": "roaoao",
"location": "Text Editor > Dev Tab > Icon Viewer",
"wiki_url": "https://docs.blender.org/manual/en/dev/addons/"
"development/icon_viewer.html",
from bpy.props import (
BoolProperty,
StringProperty,
)
DPI = 72
POPUP_PADDING = 10
PANEL_PADDING = 44
WIN_PADDING = 32
ICON_SIZE = 20
HISTORY_SIZE = 100
HISTORY = []
def ui_scale():
prefs = bpy.context.preferences.system
Brecht Van Lommel
committed
return prefs.dpi * prefs.pixel_size / DPI
return bpy.context.preferences.addons[__name__].preferences
self._filtered_icons = None
self._filter = ""
self.filter = ""
self.selected_icon = ""
@property
def filter(self):
return self._filter
@filter.setter
def filter(self, value):
if self._filter == value:
return
self._filter = value
self.update()
@property
def filtered_icons(self):
if self._filtered_icons is None:
self._filtered_icons = []
icon_filter = self._filter.upper()
self.filtered_icons.clear()
pr = prefs()
icons = bpy.types.UILayout.bl_rna.functions[
"prop"].parameters["icon"].enum_items.keys()
for icon in icons:
if icon == 'NONE' or \
icon_filter and icon_filter not in icon or \
not pr.show_brush_icons and "BRUSH_" in icon and \
icon != 'BRUSH_DATA' or \
not pr.show_matcap_icons and "MATCAP_" in icon or \
not pr.show_event_icons and (
"EVENT_" in icon or "MOUSE_" in icon
) or \
not pr.show_colorset_icons and "COLORSET_" in icon:
continue
self._filtered_icons.append(icon)
return self._filtered_icons
@property
def num_icons(self):
return len(self.filtered_icons)
def update(self):
if self._filtered_icons is not None:
self._filtered_icons.clear()
self._filtered_icons = None
def draw(self, layout, num_cols=0, icons=None):
if icons:
filtered_icons = reversed(icons)
else:
filtered_icons = self.filtered_icons
column = layout.column(align=True)
row = column.row(align=True)
selected_icon = self.selected_icon if self.is_popup else \
bpy.context.window_manager.clipboard
col_idx = 0
for i, icon in enumerate(filtered_icons):
p = row.operator(
IV_OT_icon_select.bl_idname, text="",
icon=icon, emboss=icon == selected_icon)
p.force_copy_on_select = not self.is_popup
col_idx += 1
if col_idx > num_cols - 1:
if icons:
break
col_idx = 0
if i < len(filtered_icons) - 1:
row.alignment = 'CENTER'
if col_idx != 0 and not icons and i >= num_cols:
for _ in range(num_cols - col_idx):
row.label(text="No icons were found")
class IV_Preferences(bpy.types.AddonPreferences):
bl_idname = __name__
panel_icons = Icons()
def update_icons(self, context):
self.panel_icons.update()
self.popup_icons.update()
def set_panel_filter(self, value):
self.panel_icons.filter = value
panel_filter: StringProperty(
description="Filter",
default="",
get=lambda s: s.panel_icons.filter,
set=set_panel_filter,
options={'TEXTEDIT_UPDATE'})
show_panel_icons: BoolProperty(
description="Show icons", default=True)
show_history: BoolProperty(
description="Show history", default=True)
show_brush_icons: BoolProperty(
name="Show Brush Icons",
description="Show brush icons", default=True,
update=update_icons)
show_matcap_icons: BoolProperty(
name="Show Matcap Icons",
description="Show matcap icons", default=True,
update=update_icons)
show_event_icons: BoolProperty(
name="Show Event Icons",
description="Show event icons", default=True,
update=update_icons)
show_colorset_icons: BoolProperty(
name="Show Colorset Icons",
description="Show colorset icons", default=True,
update=update_icons)
copy_on_select: BoolProperty(
description="Copy icon on click", default=True)
close_on_select: BoolProperty(
name="Close Popup On Click",
description=(
"Close the popup on click.\n"
Sybren A. Stüvel
committed
"Not supported by some windows (User Preferences, Render)"
),
default=False)
auto_focus_filter: BoolProperty(
description="Auto focus input field", default=True)
show_panel: BoolProperty(
description="Show the panel in the Text Editor", default=True)
show_header: BoolProperty(
description="Show the header in the Python Console",
def draw(self, context):
layout = self.layout
row = layout.row()
row.scale_y = 1.5
row.operator(IV_OT_icons_show.bl_idname)
row = layout.row()
col = row.column(align=True)
col.label(text="Icons:")
col.prop(self, "show_matcap_icons")
col.prop(self, "show_brush_icons")
col.prop(self, "show_colorset_icons")
col.prop(self, "show_event_icons")
col.separator()
col.prop(self, "show_history")
col = row.column(align=True)
col.label(text="Popup:")
col.prop(self, "auto_focus_filter")
col.prop(self, "copy_on_select")
if self.copy_on_select:
col.prop(self, "close_on_select")
col = row.column(align=True)
col.label(text="Panel:")
col.prop(self, "show_panel")
if self.show_panel:
col.prop(self, "show_panel_icons")
col.separator()
class IV_PT_icons(bpy.types.Panel):
bl_space_type = "TEXT_EDITOR"
bl_region_type = "UI"
bl_label = "Icon Viewer"
bl_category = "Dev"
bl_options = {'DEFAULT_CLOSED'}
@staticmethod
def tag_redraw():
wm = bpy.context.window_manager
if not wm:
return
for w in wm.windows:
for a in w.screen.areas:
if a.type == 'TEXT_EDITOR':
for r in a.regions:
if r.type == 'UI':
r.tag_redraw()
def draw(self, context):
pr = prefs()
row.prop(pr, "panel_filter", text="", icon='VIEWZOOM')
else:
row.operator(IV_OT_icons_show.bl_idname)
row.operator(
IV_OT_panel_menu_call.bl_idname, text="", icon='COLLAPSEMENU')
_, y0 = context.region.view2d.region_to_view(0, 0)
_, y1 = context.region.view2d.region_to_view(0, 10)
region_scale = 10 / abs(y0 - y1)
num_cols = max(
1,
(context.region.width - PANEL_PADDING) //
math.ceil(ui_scale() * region_scale * ICON_SIZE))
col = None
if HISTORY and pr.show_history:
pr.panel_icons.draw(col.box(), num_cols, HISTORY)
col = col or self.layout.column(align=True)
pr.panel_icons.draw(col.box(), num_cols)
@classmethod
def poll(cls, context):
return prefs().show_panel
class IV_HT_icons(bpy.types.Header):
bl_space_type = 'CONSOLE'
def draw(self, context):
if not prefs().show_header:
return
layout = self.layout
layout.separator()
layout.operator(IV_OT_icons_show.bl_idname)
class IV_OT_panel_menu_call(bpy.types.Operator):
bl_idname = "iv.panel_menu_call"
bl_label = ""
bl_description = "Menu"
bl_options = {'INTERNAL'}
def menu(self, menu, context):
pr = prefs()
layout = menu.layout
layout.prop(pr, "show_panel_icons")
layout.prop(pr, "show_history")
if not pr.show_panel_icons:
return
layout.separator()
layout.prop(pr, "show_matcap_icons")
layout.prop(pr, "show_brush_icons")
layout.prop(pr, "show_colorset_icons")
layout.prop(pr, "show_event_icons")
context.window_manager.popup_menu(self.menu, title="Icon Viewer")
return {'FINISHED'}
class IV_OT_icon_select(bpy.types.Operator):
bl_idname = "iv.icon_select"
bl_label = ""
bl_description = "Select the icon"
bl_options = {'INTERNAL'}
icon: StringProperty()
force_copy_on_select: BoolProperty()
def execute(self, context):
pr = prefs()
pr.popup_icons.selected_icon = self.icon
if pr.copy_on_select or self.force_copy_on_select:
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
context.window_manager.clipboard = self.icon
self.report({'INFO'}, self.icon)
if pr.close_on_select and IV_OT_icons_show.instance:
IV_OT_icons_show.instance.close()
if pr.show_history:
if self.icon in HISTORY:
HISTORY.remove(self.icon)
if len(HISTORY) >= HISTORY_SIZE:
HISTORY.pop(0)
HISTORY.append(self.icon)
return {'FINISHED'}
class IV_OT_icons_show(bpy.types.Operator):
bl_idname = "iv.icons_show"
bl_label = "Icon Viewer"
bl_description = "Icon viewer"
bl_property = "filter_auto_focus"
instance = None
def set_filter(self, value):
prefs().popup_icons.filter = value
def set_selected_icon(self, value):
if IV_OT_icons_show.instance:
IV_OT_icons_show.instance.auto_focusable = False
filter_auto_focus: StringProperty(
description="Filter",
get=lambda s: prefs().popup_icons.filter,
set=set_filter,
options={'TEXTEDIT_UPDATE', 'SKIP_SAVE'})
description="Filter",
get=lambda s: prefs().popup_icons.filter,
set=set_filter,
options={'TEXTEDIT_UPDATE'})
selected_icon: StringProperty(
description="Selected Icon",
get=lambda s: prefs().popup_icons.selected_icon,
set=set_selected_icon)
def get_num_cols(self, num_icons):
return round(1.3 * math.sqrt(num_icons))
def draw_header(self, layout):
pr = prefs()
header = layout.box()
header = header.split(factor=0.75) if self.selected_icon else \
header.row()
row = header.row(align=True)
row.prop(pr, "show_matcap_icons", text="", icon='SHADING_RENDERED')
row.prop(pr, "show_brush_icons", text="", icon='BRUSH_DATA')
row.prop(pr, "show_colorset_icons", text="", icon='COLOR')
row.prop(pr, "show_event_icons", text="", icon='HAND')
if bpy.context.window.screen.name == "temp":
sub.alert = True
sub.prop(
icon='RESTRICT_SELECT_OFF', toggle=True)
row.prop(
icon='OUTLINER_DATA_FONT', toggle=True)
row.separator()
if self.auto_focusable and pr.auto_focus_filter:
row.prop(self, "filter_auto_focus", text="", icon='VIEWZOOM')
row.prop(self, "filter", text="", icon='VIEWZOOM')
if self.selected_icon:
row = header.row()
row.prop(self, "selected_icon", text="", icon=self.selected_icon)
pr = prefs()
col = self.layout
self.draw_header(col)
history_num_cols = int(
(self.width - POPUP_PADDING) / (ui_scale() * ICON_SIZE))
num_cols = min(
self.get_num_cols(len(pr.popup_icons.filtered_icons)),
pr.popup_icons.draw(subcol.box(), history_num_cols, HISTORY)
pr.popup_icons.draw(subcol.box(), num_cols)
def close(self):
bpy.context.window.screen = bpy.context.window.screen
def check(self, context):
return True
def cancel(self, context):
IV_OT_icons_show.instance = None
IV_PT_icons.tag_redraw()
def execute(self, context):
if not IV_OT_icons_show.instance:
return {'CANCELLED'}
IV_OT_icons_show.instance = None
pr = prefs()
if self.selected_icon and not pr.copy_on_select:
context.window_manager.clipboard = self.selected_icon
self.report({'INFO'}, self.selected_icon)
pr.popup_icons.selected_icon = ""
IV_PT_icons.tag_redraw()
return {'FINISHED'}
def invoke(self, context, event):
pr = prefs()
pr.popup_icons.selected_icon = ""
pr.popup_icons.filter = ""
IV_OT_icons_show.instance = self
self.auto_focusable = True
num_cols = self.get_num_cols(len(pr.popup_icons.filtered_icons))
self.width = min(
ui_scale() * (num_cols * ICON_SIZE + POPUP_PADDING),
context.window.width - WIN_PADDING)
return context.window_manager.invoke_props_dialog(
self, width=self.width)
IV_PT_icons,
IV_HT_icons,
IV_OT_panel_menu_call,
IV_OT_icon_select,
IV_OT_icons_show,
IV_Preferences,
if bpy.app.background:
return
for cls in classes:
bpy.utils.register_class(cls)
if bpy.app.background:
return
for cls in classes:
bpy.utils.unregister_class(cls)