1 # ##### BEGIN GPL LICENSE BLOCK #####
3 # This program is free software; you can redistribute it and/or
4 # modify it under the terms of the GNU General Public License
5 # as published by the Free Software Foundation; either version 2
6 # of the License, or (at your option) any later version.
8 # This program is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # GNU General Public License for more details.
13 # You should have received a copy of the GNU General Public License
14 # along with this program; if not, write to the Free Software Foundation,
15 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 # ##### END GPL LICENSE BLOCK #####
23 "name": "Icon Viewer",
24 "description": "Click an icon to copy its name to the clipboard",
27 "blender": (2, 80, 0),
28 "location": "Text Editor > Dev Tab > Icon Viewer",
29 "doc_url": "{BLENDER_MANUAL_URL}/addons/development/icon_viewer.html",
30 "category": "Development",
35 from bpy
.props
import (
50 prefs
= bpy
.context
.preferences
.system
51 return prefs
.dpi
* prefs
.pixel_size
/ DPI
55 return bpy
.context
.preferences
.addons
[__name__
].preferences
59 def __init__(self
, is_popup
=False):
60 self
._filtered
_icons
= None
63 self
.selected_icon
= ""
64 self
.is_popup
= is_popup
71 def filter(self
, value
):
72 if self
._filter
== value
:
79 def filtered_icons(self
):
80 if self
._filtered
_icons
is None:
81 self
._filtered
_icons
= []
82 icon_filter
= self
._filter
.upper()
83 self
.filtered_icons
.clear()
86 icons
= bpy
.types
.UILayout
.bl_rna
.functions
[
87 "prop"].parameters
["icon"].enum_items
.keys()
89 if icon
== 'NONE' or \
90 icon_filter
and icon_filter
not in icon
or \
91 not pr
.show_brush_icons
and "BRUSH_" in icon
and \
92 icon
!= 'BRUSH_DATA' or \
93 not pr
.show_matcap_icons
and "MATCAP_" in icon
or \
94 not pr
.show_event_icons
and (
95 "EVENT_" in icon
or "MOUSE_" in icon
97 not pr
.show_colorset_icons
and "COLORSET_" in icon
:
99 self
._filtered
_icons
.append(icon
)
101 return self
._filtered
_icons
105 return len(self
.filtered_icons
)
108 if self
._filtered
_icons
is not None:
109 self
._filtered
_icons
.clear()
110 self
._filtered
_icons
= None
112 def draw(self
, layout
, num_cols
=0, icons
=None):
114 filtered_icons
= reversed(icons
)
116 filtered_icons
= self
.filtered_icons
118 column
= layout
.column(align
=True)
119 row
= column
.row(align
=True)
120 row
.alignment
= 'CENTER'
122 selected_icon
= self
.selected_icon
if self
.is_popup
else \
123 bpy
.context
.window_manager
.clipboard
125 for i
, icon
in enumerate(filtered_icons
):
127 IV_OT_icon_select
.bl_idname
, text
="",
128 icon
=icon
, emboss
=icon
== selected_icon
)
130 p
.force_copy_on_select
= not self
.is_popup
133 if col_idx
> num_cols
- 1:
137 if i
< len(filtered_icons
) - 1:
138 row
= column
.row(align
=True)
139 row
.alignment
= 'CENTER'
141 if col_idx
!= 0 and not icons
and i
>= num_cols
:
142 for _
in range(num_cols
- col_idx
):
143 row
.label(text
="", icon
='BLANK1')
145 if not filtered_icons
:
146 row
.label(text
="No icons were found")
149 class IV_Preferences(bpy
.types
.AddonPreferences
):
152 panel_icons
= Icons()
153 popup_icons
= Icons(is_popup
=True)
155 def update_icons(self
, context
):
156 self
.panel_icons
.update()
157 self
.popup_icons
.update()
159 def set_panel_filter(self
, value
):
160 self
.panel_icons
.filter = value
162 panel_filter
: StringProperty(
163 description
="Filter",
165 get
=lambda s
: s
.panel_icons
.filter,
166 set=set_panel_filter
,
167 options
={'TEXTEDIT_UPDATE'})
168 show_panel_icons
: BoolProperty(
170 description
="Show icons", default
=True)
171 show_history
: BoolProperty(
173 description
="Show history", default
=True)
174 show_brush_icons
: BoolProperty(
175 name
="Show Brush Icons",
176 description
="Show brush icons", default
=True,
178 show_matcap_icons
: BoolProperty(
179 name
="Show Matcap Icons",
180 description
="Show matcap icons", default
=True,
182 show_event_icons
: BoolProperty(
183 name
="Show Event Icons",
184 description
="Show event icons", default
=True,
186 show_colorset_icons
: BoolProperty(
187 name
="Show Colorset Icons",
188 description
="Show colorset icons", default
=True,
190 copy_on_select
: BoolProperty(
191 name
="Copy Icon On Click",
192 description
="Copy icon on click", default
=True)
193 close_on_select
: BoolProperty(
194 name
="Close Popup On Click",
196 "Close the popup on click.\n"
197 "Not supported by some windows (User Preferences, Render)"
200 auto_focus_filter
: BoolProperty(
201 name
="Auto Focus Input Field",
202 description
="Auto focus input field", default
=True)
203 show_panel
: BoolProperty(
205 description
="Show the panel in the Text Editor", default
=True)
206 show_header
: BoolProperty(
208 description
="Show the header in the Python Console",
211 def draw(self
, context
):
215 row
.operator(IV_OT_icons_show
.bl_idname
)
219 col
= row
.column(align
=True)
220 col
.label(text
="Icons:")
221 col
.prop(self
, "show_matcap_icons")
222 col
.prop(self
, "show_brush_icons")
223 col
.prop(self
, "show_colorset_icons")
224 col
.prop(self
, "show_event_icons")
226 col
.prop(self
, "show_history")
228 col
= row
.column(align
=True)
229 col
.label(text
="Popup:")
230 col
.prop(self
, "auto_focus_filter")
231 col
.prop(self
, "copy_on_select")
232 if self
.copy_on_select
:
233 col
.prop(self
, "close_on_select")
235 col
= row
.column(align
=True)
236 col
.label(text
="Panel:")
237 col
.prop(self
, "show_panel")
239 col
.prop(self
, "show_panel_icons")
242 col
.label(text
="Header:")
243 col
.prop(self
, "show_header")
246 class IV_PT_icons(bpy
.types
.Panel
):
247 bl_space_type
= "TEXT_EDITOR"
248 bl_region_type
= "UI"
249 bl_label
= "Icon Viewer"
251 bl_options
= {'DEFAULT_CLOSED'}
255 wm
= bpy
.context
.window_manager
260 for a
in w
.screen
.areas
:
261 if a
.type == 'TEXT_EDITOR':
266 def draw(self
, context
):
268 row
= self
.layout
.row(align
=True)
269 if pr
.show_panel_icons
:
270 row
.prop(pr
, "panel_filter", text
="", icon
='VIEWZOOM')
272 row
.operator(IV_OT_icons_show
.bl_idname
)
274 IV_OT_panel_menu_call
.bl_idname
, text
="", icon
='COLLAPSEMENU')
276 _
, y0
= context
.region
.view2d
.region_to_view(0, 0)
277 _
, y1
= context
.region
.view2d
.region_to_view(0, 10)
278 region_scale
= 10 / abs(y0
- y1
)
282 (context
.region
.width
- PANEL_PADDING
) //
283 math
.ceil(ui_scale() * region_scale
* ICON_SIZE
))
286 if HISTORY
and pr
.show_history
:
287 col
= self
.layout
.column(align
=True)
288 pr
.panel_icons
.draw(col
.box(), num_cols
, HISTORY
)
290 if pr
.show_panel_icons
:
291 col
= col
or self
.layout
.column(align
=True)
292 pr
.panel_icons
.draw(col
.box(), num_cols
)
295 def poll(cls
, context
):
296 return prefs().show_panel
299 class IV_OT_panel_menu_call(bpy
.types
.Operator
):
300 bl_idname
= "iv.panel_menu_call"
302 bl_description
= "Menu"
303 bl_options
= {'INTERNAL'}
305 def menu(self
, menu
, context
):
308 layout
.prop(pr
, "show_panel_icons")
309 layout
.prop(pr
, "show_history")
311 if not pr
.show_panel_icons
:
315 layout
.prop(pr
, "show_matcap_icons")
316 layout
.prop(pr
, "show_brush_icons")
317 layout
.prop(pr
, "show_colorset_icons")
318 layout
.prop(pr
, "show_event_icons")
320 def execute(self
, context
):
321 context
.window_manager
.popup_menu(self
.menu
, title
="Icon Viewer")
325 class IV_OT_icon_select(bpy
.types
.Operator
):
326 bl_idname
= "iv.icon_select"
328 bl_description
= "Select the icon"
329 bl_options
= {'INTERNAL'}
331 icon
: StringProperty()
332 force_copy_on_select
: BoolProperty()
334 def execute(self
, context
):
336 pr
.popup_icons
.selected_icon
= self
.icon
337 if pr
.copy_on_select
or self
.force_copy_on_select
:
338 context
.window_manager
.clipboard
= self
.icon
339 self
.report({'INFO'}, self
.icon
)
341 if pr
.close_on_select
and IV_OT_icons_show
.instance
:
342 IV_OT_icons_show
.instance
.close()
345 if self
.icon
in HISTORY
:
346 HISTORY
.remove(self
.icon
)
347 if len(HISTORY
) >= HISTORY_SIZE
:
349 HISTORY
.append(self
.icon
)
353 class IV_OT_icons_show(bpy
.types
.Operator
):
354 bl_idname
= "iv.icons_show"
355 bl_label
= "Icon Viewer"
356 bl_description
= "Icon viewer"
357 bl_property
= "filter_auto_focus"
361 def set_filter(self
, value
):
362 prefs().popup_icons
.filter = value
364 def set_selected_icon(self
, value
):
365 if IV_OT_icons_show
.instance
:
366 IV_OT_icons_show
.instance
.auto_focusable
= False
368 filter_auto_focus
: StringProperty(
369 description
="Filter",
370 get
=lambda s
: prefs().popup_icons
.filter,
372 options
={'TEXTEDIT_UPDATE', 'SKIP_SAVE'})
373 filter: StringProperty(
374 description
="Filter",
375 get
=lambda s
: prefs().popup_icons
.filter,
377 options
={'TEXTEDIT_UPDATE'})
378 selected_icon
: StringProperty(
379 description
="Selected Icon",
380 get
=lambda s
: prefs().popup_icons
.selected_icon
,
381 set=set_selected_icon
)
383 def get_num_cols(self
, num_icons
):
384 return round(1.3 * math
.sqrt(num_icons
))
386 def draw_header(self
, layout
):
388 header
= layout
.box()
389 header
= header
.split(factor
=0.75) if self
.selected_icon
else \
391 row
= header
.row(align
=True)
392 row
.prop(pr
, "show_matcap_icons", text
="", icon
='SHADING_RENDERED')
393 row
.prop(pr
, "show_brush_icons", text
="", icon
='BRUSH_DATA')
394 row
.prop(pr
, "show_colorset_icons", text
="", icon
='COLOR')
395 row
.prop(pr
, "show_event_icons", text
="", icon
='HAND')
399 pr
, "copy_on_select", text
="",
400 icon
='COPYDOWN', toggle
=True)
401 if pr
.copy_on_select
:
402 sub
= row
.row(align
=True)
403 if bpy
.context
.window
.screen
.name
== "temp":
406 pr
, "close_on_select", text
="",
407 icon
='RESTRICT_SELECT_OFF', toggle
=True)
409 pr
, "auto_focus_filter", text
="",
410 icon
='OUTLINER_DATA_FONT', toggle
=True)
413 if self
.auto_focusable
and pr
.auto_focus_filter
:
414 row
.prop(self
, "filter_auto_focus", text
="", icon
='VIEWZOOM')
416 row
.prop(self
, "filter", text
="", icon
='VIEWZOOM')
418 if self
.selected_icon
:
420 row
.prop(self
, "selected_icon", text
="", icon
=self
.selected_icon
)
422 def draw(self
, context
):
425 self
.draw_header(col
)
427 history_num_cols
= int(
428 (self
.width
- POPUP_PADDING
) / (ui_scale() * ICON_SIZE
))
430 self
.get_num_cols(len(pr
.popup_icons
.filtered_icons
)),
433 subcol
= col
.column(align
=True)
435 if HISTORY
and pr
.show_history
:
436 pr
.popup_icons
.draw(subcol
.box(), history_num_cols
, HISTORY
)
438 pr
.popup_icons
.draw(subcol
.box(), num_cols
)
441 bpy
.context
.window
.screen
= bpy
.context
.window
.screen
443 def check(self
, context
):
446 def cancel(self
, context
):
447 IV_OT_icons_show
.instance
= None
448 IV_PT_icons
.tag_redraw()
450 def execute(self
, context
):
451 if not IV_OT_icons_show
.instance
:
453 IV_OT_icons_show
.instance
= None
456 if self
.selected_icon
and not pr
.copy_on_select
:
457 context
.window_manager
.clipboard
= self
.selected_icon
458 self
.report({'INFO'}, self
.selected_icon
)
459 pr
.popup_icons
.selected_icon
= ""
461 IV_PT_icons
.tag_redraw()
464 def invoke(self
, context
, event
):
466 pr
.popup_icons
.selected_icon
= ""
467 pr
.popup_icons
.filter = ""
468 IV_OT_icons_show
.instance
= self
469 self
.auto_focusable
= True
471 num_cols
= self
.get_num_cols(len(pr
.popup_icons
.filtered_icons
))
472 self
.width
= int(min(
473 ui_scale() * (num_cols
* ICON_SIZE
+ POPUP_PADDING
),
474 context
.window
.width
- WIN_PADDING
))
476 return context
.window_manager
.invoke_props_dialog(
477 self
, width
=self
.width
)
479 def draw_console_header(self
, context
):
480 if not prefs().show_header
:
482 self
.layout
.operator(IV_OT_icons_show
.bl_idname
)
486 IV_OT_panel_menu_call
,
494 if bpy
.app
.background
:
498 bpy
.utils
.register_class(cls
)
500 bpy
.types
.CONSOLE_HT_header
.append(draw_console_header
)
504 if bpy
.app
.background
:
507 bpy
.types
.CONSOLE_HT_header
.remove(draw_console_header
)
510 bpy
.utils
.unregister_class(cls
)