1 # SPDX-FileCopyrightText: 2010-2022 Blender Foundation
3 # SPDX-License-Identifier: GPL-2.0-or-later
8 "description": "Click an icon to copy its name to the clipboard",
12 "location": "Text Editor > Dev Tab > Icon Viewer",
13 "doc_url": "{BLENDER_MANUAL_URL}/addons/development/icon_viewer.html",
14 "category": "Development",
19 from bpy
.props
import (
34 prefs
= bpy
.context
.preferences
.system
35 return prefs
.dpi
/ DPI
39 return bpy
.context
.preferences
.addons
[__name__
].preferences
43 def __init__(self
, is_popup
=False):
44 self
._filtered
_icons
= None
47 self
.selected_icon
= ""
48 self
.is_popup
= is_popup
55 def filter(self
, value
):
56 if self
._filter
== value
:
63 def filtered_icons(self
):
64 if self
._filtered
_icons
is None:
65 self
._filtered
_icons
= []
66 icon_filter
= self
._filter
.upper()
67 self
.filtered_icons
.clear()
70 icons
= bpy
.types
.UILayout
.bl_rna
.functions
[
71 "prop"].parameters
["icon"].enum_items
.keys()
73 if icon
== 'NONE' or \
74 icon_filter
and icon_filter
not in icon
or \
75 not pr
.show_brush_icons
and "BRUSH_" in icon
and \
76 icon
!= 'BRUSH_DATA' or \
77 not pr
.show_matcap_icons
and "MATCAP_" in icon
or \
78 not pr
.show_event_icons
and (
79 "EVENT_" in icon
or "MOUSE_" in icon
81 not pr
.show_colorset_icons
and "COLORSET_" in icon
:
83 self
._filtered
_icons
.append(icon
)
85 return self
._filtered
_icons
89 return len(self
.filtered_icons
)
92 if self
._filtered
_icons
is not None:
93 self
._filtered
_icons
.clear()
94 self
._filtered
_icons
= None
96 def draw(self
, layout
, num_cols
=0, icons
=None):
98 filtered_icons
= reversed(icons
)
100 filtered_icons
= self
.filtered_icons
102 column
= layout
.column(align
=True)
103 row
= column
.row(align
=True)
104 row
.alignment
= 'CENTER'
106 selected_icon
= self
.selected_icon
if self
.is_popup
else \
107 bpy
.context
.window_manager
.clipboard
109 for i
, icon
in enumerate(filtered_icons
):
111 IV_OT_icon_select
.bl_idname
, text
="",
112 icon
=icon
, emboss
=icon
== selected_icon
)
114 p
.force_copy_on_select
= not self
.is_popup
117 if col_idx
> num_cols
- 1:
121 if i
< len(filtered_icons
) - 1:
122 row
= column
.row(align
=True)
123 row
.alignment
= 'CENTER'
125 if col_idx
!= 0 and not icons
and i
>= num_cols
:
126 for _
in range(num_cols
- col_idx
):
127 row
.label(text
="", icon
='BLANK1')
129 if not filtered_icons
:
130 row
.label(text
="No icons were found")
133 class IV_Preferences(bpy
.types
.AddonPreferences
):
136 panel_icons
= Icons()
137 popup_icons
= Icons(is_popup
=True)
139 def update_icons(self
, context
):
140 self
.panel_icons
.update()
141 self
.popup_icons
.update()
143 def set_panel_filter(self
, value
):
144 self
.panel_icons
.filter = value
146 panel_filter
: StringProperty(
147 description
="Filter",
149 get
=lambda s
: s
.panel_icons
.filter,
150 set=set_panel_filter
,
151 options
={'TEXTEDIT_UPDATE'})
152 show_panel_icons
: BoolProperty(
154 description
="Show icons", default
=True)
155 show_history
: BoolProperty(
157 description
="Show history", default
=True)
158 show_brush_icons
: BoolProperty(
159 name
="Show Brush Icons",
160 description
="Show brush icons", default
=True,
162 show_matcap_icons
: BoolProperty(
163 name
="Show Matcap Icons",
164 description
="Show matcap icons", default
=True,
166 show_event_icons
: BoolProperty(
167 name
="Show Event Icons",
168 description
="Show event icons", default
=True,
170 show_colorset_icons
: BoolProperty(
171 name
="Show Colorset Icons",
172 description
="Show colorset icons", default
=True,
174 copy_on_select
: BoolProperty(
175 name
="Copy Icon On Click",
176 description
="Copy icon on click", default
=True)
177 close_on_select
: BoolProperty(
178 name
="Close Popup On Click",
180 "Close the popup on click.\n"
181 "Not supported by some windows (User Preferences, Render)"
184 auto_focus_filter
: BoolProperty(
185 name
="Auto Focus Input Field",
186 description
="Auto focus input field", default
=True)
187 show_panel
: BoolProperty(
189 description
="Show the panel in the Text Editor", default
=True)
190 show_header
: BoolProperty(
192 description
="Show the header in the Python Console",
195 def draw(self
, context
):
199 row
.operator(IV_OT_icons_show
.bl_idname
)
203 col
= row
.column(align
=True)
204 col
.label(text
="Icons:")
205 col
.prop(self
, "show_matcap_icons")
206 col
.prop(self
, "show_brush_icons")
207 col
.prop(self
, "show_colorset_icons")
208 col
.prop(self
, "show_event_icons")
210 col
.prop(self
, "show_history")
212 col
= row
.column(align
=True)
213 col
.label(text
="Popup:")
214 col
.prop(self
, "auto_focus_filter")
215 col
.prop(self
, "copy_on_select")
216 if self
.copy_on_select
:
217 col
.prop(self
, "close_on_select")
219 col
= row
.column(align
=True)
220 col
.label(text
="Panel:")
221 col
.prop(self
, "show_panel")
223 col
.prop(self
, "show_panel_icons")
226 col
.label(text
="Header:")
227 col
.prop(self
, "show_header")
230 class IV_PT_icons(bpy
.types
.Panel
):
231 bl_space_type
= "TEXT_EDITOR"
232 bl_region_type
= "UI"
233 bl_label
= "Icon Viewer"
235 bl_options
= {'DEFAULT_CLOSED'}
239 wm
= bpy
.context
.window_manager
244 for a
in w
.screen
.areas
:
245 if a
.type == 'TEXT_EDITOR':
250 def draw(self
, context
):
252 row
= self
.layout
.row(align
=True)
253 if pr
.show_panel_icons
:
254 row
.prop(pr
, "panel_filter", text
="", icon
='VIEWZOOM')
256 row
.operator(IV_OT_icons_show
.bl_idname
)
258 IV_OT_panel_menu_call
.bl_idname
, text
="", icon
='COLLAPSEMENU')
260 _
, y0
= context
.region
.view2d
.region_to_view(0, 0)
261 _
, y1
= context
.region
.view2d
.region_to_view(0, 10)
262 region_scale
= 10 / abs(y0
- y1
)
266 (context
.region
.width
- PANEL_PADDING
) //
267 math
.ceil(ui_scale() * region_scale
* ICON_SIZE
))
270 if HISTORY
and pr
.show_history
:
271 col
= self
.layout
.column(align
=True)
272 pr
.panel_icons
.draw(col
.box(), num_cols
, HISTORY
)
274 if pr
.show_panel_icons
:
275 col
= col
or self
.layout
.column(align
=True)
276 pr
.panel_icons
.draw(col
.box(), num_cols
)
279 def poll(cls
, context
):
280 return prefs().show_panel
283 class IV_OT_panel_menu_call(bpy
.types
.Operator
):
284 bl_idname
= "iv.panel_menu_call"
286 bl_description
= "Menu"
287 bl_options
= {'INTERNAL'}
289 def menu(self
, menu
, context
):
292 layout
.prop(pr
, "show_panel_icons")
293 layout
.prop(pr
, "show_history")
295 if not pr
.show_panel_icons
:
299 layout
.prop(pr
, "show_matcap_icons")
300 layout
.prop(pr
, "show_brush_icons")
301 layout
.prop(pr
, "show_colorset_icons")
302 layout
.prop(pr
, "show_event_icons")
304 def execute(self
, context
):
305 context
.window_manager
.popup_menu(self
.menu
, title
="Icon Viewer")
309 class IV_OT_icon_select(bpy
.types
.Operator
):
310 bl_idname
= "iv.icon_select"
312 bl_description
= "Select the icon"
313 bl_options
= {'INTERNAL'}
315 icon
: StringProperty()
316 force_copy_on_select
: BoolProperty()
318 def execute(self
, context
):
320 pr
.popup_icons
.selected_icon
= self
.icon
321 if pr
.copy_on_select
or self
.force_copy_on_select
:
322 context
.window_manager
.clipboard
= self
.icon
323 self
.report({'INFO'}, self
.icon
)
325 if pr
.close_on_select
and IV_OT_icons_show
.instance
:
326 IV_OT_icons_show
.instance
.close()
329 if self
.icon
in HISTORY
:
330 HISTORY
.remove(self
.icon
)
331 if len(HISTORY
) >= HISTORY_SIZE
:
333 HISTORY
.append(self
.icon
)
337 class IV_OT_icons_show(bpy
.types
.Operator
):
338 bl_idname
= "iv.icons_show"
339 bl_label
= "Icon Viewer"
340 bl_description
= "Icon viewer"
341 bl_property
= "filter_auto_focus"
345 def set_filter(self
, value
):
346 prefs().popup_icons
.filter = value
348 def set_selected_icon(self
, value
):
349 if IV_OT_icons_show
.instance
:
350 IV_OT_icons_show
.instance
.auto_focusable
= False
352 filter_auto_focus
: StringProperty(
353 description
="Filter",
354 get
=lambda s
: prefs().popup_icons
.filter,
356 options
={'TEXTEDIT_UPDATE', 'SKIP_SAVE'})
357 filter: StringProperty(
358 description
="Filter",
359 get
=lambda s
: prefs().popup_icons
.filter,
361 options
={'TEXTEDIT_UPDATE'})
362 selected_icon
: StringProperty(
363 description
="Selected Icon",
364 get
=lambda s
: prefs().popup_icons
.selected_icon
,
365 set=set_selected_icon
)
367 def get_num_cols(self
, num_icons
):
368 return round(1.3 * math
.sqrt(num_icons
))
370 def draw_header(self
, layout
):
372 header
= layout
.box()
373 header
= header
.split(factor
=0.75) if self
.selected_icon
else \
375 row
= header
.row(align
=True)
376 row
.prop(pr
, "show_matcap_icons", text
="", icon
='SHADING_RENDERED')
377 row
.prop(pr
, "show_brush_icons", text
="", icon
='BRUSH_DATA')
378 row
.prop(pr
, "show_colorset_icons", text
="", icon
='COLOR')
379 row
.prop(pr
, "show_event_icons", text
="", icon
='HAND')
383 pr
, "copy_on_select", text
="",
384 icon
='COPYDOWN', toggle
=True)
385 if pr
.copy_on_select
:
386 sub
= row
.row(align
=True)
387 if bpy
.context
.window
.screen
.name
== "temp":
390 pr
, "close_on_select", text
="",
391 icon
='RESTRICT_SELECT_OFF', toggle
=True)
393 pr
, "auto_focus_filter", text
="",
394 icon
='OUTLINER_DATA_FONT', toggle
=True)
397 if self
.auto_focusable
and pr
.auto_focus_filter
:
398 row
.prop(self
, "filter_auto_focus", text
="", icon
='VIEWZOOM')
400 row
.prop(self
, "filter", text
="", icon
='VIEWZOOM')
402 if self
.selected_icon
:
404 row
.prop(self
, "selected_icon", text
="", icon
=self
.selected_icon
)
406 def draw(self
, context
):
409 self
.draw_header(col
)
411 history_num_cols
= int(
412 (self
.width
- POPUP_PADDING
) / (ui_scale() * ICON_SIZE
))
414 self
.get_num_cols(len(pr
.popup_icons
.filtered_icons
)),
417 subcol
= col
.column(align
=True)
419 if HISTORY
and pr
.show_history
:
420 pr
.popup_icons
.draw(subcol
.box(), history_num_cols
, HISTORY
)
422 pr
.popup_icons
.draw(subcol
.box(), num_cols
)
425 bpy
.context
.window
.screen
= bpy
.context
.window
.screen
427 def check(self
, context
):
430 def cancel(self
, context
):
431 IV_OT_icons_show
.instance
= None
432 IV_PT_icons
.tag_redraw()
434 def execute(self
, context
):
435 if not IV_OT_icons_show
.instance
:
437 IV_OT_icons_show
.instance
= None
440 if self
.selected_icon
and not pr
.copy_on_select
:
441 context
.window_manager
.clipboard
= self
.selected_icon
442 self
.report({'INFO'}, self
.selected_icon
)
443 pr
.popup_icons
.selected_icon
= ""
445 IV_PT_icons
.tag_redraw()
448 def invoke(self
, context
, event
):
450 pr
.popup_icons
.selected_icon
= ""
451 pr
.popup_icons
.filter = ""
452 IV_OT_icons_show
.instance
= self
453 self
.auto_focusable
= True
455 num_cols
= self
.get_num_cols(len(pr
.popup_icons
.filtered_icons
))
456 self
.width
= int(min(
457 ui_scale() * (num_cols
* ICON_SIZE
+ POPUP_PADDING
),
458 context
.window
.width
- WIN_PADDING
))
460 return context
.window_manager
.invoke_props_dialog(
461 self
, width
=self
.width
)
463 def draw_console_header(self
, context
):
464 if not prefs().show_header
:
466 self
.layout
.operator(IV_OT_icons_show
.bl_idname
)
470 IV_OT_panel_menu_call
,
478 if bpy
.app
.background
:
482 bpy
.utils
.register_class(cls
)
484 bpy
.types
.CONSOLE_HT_header
.append(draw_console_header
)
488 if bpy
.app
.background
:
491 bpy
.types
.CONSOLE_HT_header
.remove(draw_console_header
)
494 bpy
.utils
.unregister_class(cls
)