add_curve_extra_objects: fix invalid identity checks
[blender-addons.git] / development_icon_get.py
blob37fae00a0150f57dcc9da258b5e087b0b631cdf5
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 #####
19 # <pep8 compliant>
22 bl_info = {
23 "name": "Icon Viewer",
24 "description": "Click an icon to copy its name to the clipboard",
25 "author": "roaoao",
26 "version": (1, 4, 0),
27 "blender": (2, 80, 0),
28 "location": "Text Editor > Dev Tab > Icon Viewer",
29 "wiki_url": "https://docs.blender.org/manual/en/dev/addons/"
30 "development/icon_viewer.html",
31 "category": "Development"
34 import bpy
35 import math
36 from bpy.props import (
37 BoolProperty,
38 StringProperty,
41 DPI = 72
42 POPUP_PADDING = 10
43 PANEL_PADDING = 44
44 WIN_PADDING = 32
45 ICON_SIZE = 20
46 HISTORY_SIZE = 100
47 HISTORY = []
50 def ui_scale():
51 prefs = bpy.context.preferences.system
52 return prefs.dpi * prefs.pixel_size / DPI
55 def prefs():
56 return bpy.context.preferences.addons[__name__].preferences
59 class Icons:
60 def __init__(self, is_popup=False):
61 self._filtered_icons = None
62 self._filter = ""
63 self.filter = ""
64 self.selected_icon = ""
65 self.is_popup = is_popup
67 @property
68 def filter(self):
69 return self._filter
71 @filter.setter
72 def filter(self, value):
73 if self._filter == value:
74 return
76 self._filter = value
77 self.update()
79 @property
80 def filtered_icons(self):
81 if self._filtered_icons is None:
82 self._filtered_icons = []
83 icon_filter = self._filter.upper()
84 self.filtered_icons.clear()
85 pr = prefs()
87 icons = bpy.types.UILayout.bl_rna.functions[
88 "prop"].parameters["icon"].enum_items.keys()
89 for icon in icons:
90 if icon == 'NONE' or \
91 icon_filter and icon_filter not in icon or \
92 not pr.show_brush_icons and "BRUSH_" in icon and \
93 icon != 'BRUSH_DATA' or \
94 not pr.show_matcap_icons and "MATCAP_" in icon or \
95 not pr.show_event_icons and (
96 "EVENT_" in icon or "MOUSE_" in icon
97 ) or \
98 not pr.show_colorset_icons and "COLORSET_" in icon:
99 continue
100 self._filtered_icons.append(icon)
102 return self._filtered_icons
104 @property
105 def num_icons(self):
106 return len(self.filtered_icons)
108 def update(self):
109 if self._filtered_icons is not None:
110 self._filtered_icons.clear()
111 self._filtered_icons = None
113 def draw(self, layout, num_cols=0, icons=None):
114 if icons:
115 filtered_icons = reversed(icons)
116 else:
117 filtered_icons = self.filtered_icons
119 column = layout.column(align=True)
120 row = column.row(align=True)
121 row.alignment = 'CENTER'
123 selected_icon = self.selected_icon if self.is_popup else \
124 bpy.context.window_manager.clipboard
125 col_idx = 0
126 for i, icon in enumerate(filtered_icons):
127 p = row.operator(
128 IV_OT_icon_select.bl_idname, text="",
129 icon=icon, emboss=icon == selected_icon)
130 p.icon = icon
131 p.force_copy_on_select = not self.is_popup
133 col_idx += 1
134 if col_idx > num_cols - 1:
135 if icons:
136 break
137 col_idx = 0
138 if i < len(filtered_icons) - 1:
139 row = column.row(align=True)
140 row.alignment = 'CENTER'
142 if col_idx != 0 and not icons and i >= num_cols:
143 for _ in range(num_cols - col_idx):
144 row.label(text="", icon='BLANK1')
146 if not filtered_icons:
147 row.label(text="No icons were found")
150 class IV_Preferences(bpy.types.AddonPreferences):
151 bl_idname = __name__
153 panel_icons = Icons()
154 popup_icons = Icons(is_popup=True)
156 def update_icons(self, context):
157 self.panel_icons.update()
158 self.popup_icons.update()
160 def set_panel_filter(self, value):
161 self.panel_icons.filter = value
163 panel_filter: StringProperty(
164 description="Filter",
165 default="",
166 get=lambda s: s.panel_icons.filter,
167 set=set_panel_filter,
168 options={'TEXTEDIT_UPDATE'})
169 show_panel_icons: BoolProperty(
170 name="Show Icons",
171 description="Show icons", default=True)
172 show_history: BoolProperty(
173 name="Show History",
174 description="Show history", default=True)
175 show_brush_icons: BoolProperty(
176 name="Show Brush Icons",
177 description="Show brush icons", default=True,
178 update=update_icons)
179 show_matcap_icons: BoolProperty(
180 name="Show Matcap Icons",
181 description="Show matcap icons", default=True,
182 update=update_icons)
183 show_event_icons: BoolProperty(
184 name="Show Event Icons",
185 description="Show event icons", default=True,
186 update=update_icons)
187 show_colorset_icons: BoolProperty(
188 name="Show Colorset Icons",
189 description="Show colorset icons", default=True,
190 update=update_icons)
191 copy_on_select: BoolProperty(
192 name="Copy Icon On Click",
193 description="Copy icon on click", default=True)
194 close_on_select: BoolProperty(
195 name="Close Popup On Click",
196 description=(
197 "Close the popup on click.\n"
198 "Not supported by some windows (User Preferences, Render)"
200 default=False)
201 auto_focus_filter: BoolProperty(
202 name="Auto Focus Input Field",
203 description="Auto focus input field", default=True)
204 show_panel: BoolProperty(
205 name="Show Panel",
206 description="Show the panel in the Text Editor", default=True)
207 show_header: BoolProperty(
208 name="Show Header",
209 description="Show the header in the Python Console",
210 default=True)
212 def draw(self, context):
213 layout = self.layout
214 row = layout.row()
215 row.scale_y = 1.5
216 row.operator(IV_OT_icons_show.bl_idname)
218 row = layout.row()
220 col = row.column(align=True)
221 col.label(text="Icons:")
222 col.prop(self, "show_matcap_icons")
223 col.prop(self, "show_brush_icons")
224 col.prop(self, "show_colorset_icons")
225 col.prop(self, "show_event_icons")
226 col.separator()
227 col.prop(self, "show_history")
229 col = row.column(align=True)
230 col.label(text="Popup:")
231 col.prop(self, "auto_focus_filter")
232 col.prop(self, "copy_on_select")
233 if self.copy_on_select:
234 col.prop(self, "close_on_select")
236 col = row.column(align=True)
237 col.label(text="Panel:")
238 col.prop(self, "show_panel")
239 if self.show_panel:
240 col.prop(self, "show_panel_icons")
242 col.separator()
243 col.label(text="Header:")
244 col.prop(self, "show_header")
247 class IV_PT_icons(bpy.types.Panel):
248 bl_space_type = "TEXT_EDITOR"
249 bl_region_type = "UI"
250 bl_label = "Icon Viewer"
251 bl_category = "Dev"
252 bl_options = {'DEFAULT_CLOSED'}
254 @staticmethod
255 def tag_redraw():
256 wm = bpy.context.window_manager
257 if not wm:
258 return
260 for w in wm.windows:
261 for a in w.screen.areas:
262 if a.type == 'TEXT_EDITOR':
263 for r in a.regions:
264 if r.type == 'UI':
265 r.tag_redraw()
267 def draw(self, context):
268 pr = prefs()
269 row = self.layout.row(align=True)
270 if pr.show_panel_icons:
271 row.prop(pr, "panel_filter", text="", icon='VIEWZOOM')
272 else:
273 row.operator(IV_OT_icons_show.bl_idname)
274 row.operator(
275 IV_OT_panel_menu_call.bl_idname, text="", icon='COLLAPSEMENU')
277 _, y0 = context.region.view2d.region_to_view(0, 0)
278 _, y1 = context.region.view2d.region_to_view(0, 10)
279 region_scale = 10 / abs(y0 - y1)
281 num_cols = max(
283 (context.region.width - PANEL_PADDING) //
284 math.ceil(ui_scale() * region_scale * ICON_SIZE))
286 col = None
287 if HISTORY and pr.show_history:
288 col = self.layout.column(align=True)
289 pr.panel_icons.draw(col.box(), num_cols, HISTORY)
291 if pr.show_panel_icons:
292 col = col or self.layout.column(align=True)
293 pr.panel_icons.draw(col.box(), num_cols)
295 @classmethod
296 def poll(cls, context):
297 return prefs().show_panel
300 class IV_HT_icons(bpy.types.Header):
301 bl_space_type = 'CONSOLE'
303 def draw(self, context):
304 if not prefs().show_header:
305 return
306 layout = self.layout
307 layout.separator()
308 layout.operator(IV_OT_icons_show.bl_idname)
311 class IV_OT_panel_menu_call(bpy.types.Operator):
312 bl_idname = "iv.panel_menu_call"
313 bl_label = ""
314 bl_description = "Menu"
315 bl_options = {'INTERNAL'}
317 def menu(self, menu, context):
318 pr = prefs()
319 layout = menu.layout
320 layout.prop(pr, "show_panel_icons")
321 layout.prop(pr, "show_history")
323 if not pr.show_panel_icons:
324 return
326 layout.separator()
327 layout.prop(pr, "show_matcap_icons")
328 layout.prop(pr, "show_brush_icons")
329 layout.prop(pr, "show_colorset_icons")
330 layout.prop(pr, "show_event_icons")
332 def execute(self, context):
333 context.window_manager.popup_menu(self.menu, title="Icon Viewer")
334 return {'FINISHED'}
337 class IV_OT_icon_select(bpy.types.Operator):
338 bl_idname = "iv.icon_select"
339 bl_label = ""
340 bl_description = "Select the icon"
341 bl_options = {'INTERNAL'}
343 icon: StringProperty()
344 force_copy_on_select: BoolProperty()
346 def execute(self, context):
347 pr = prefs()
348 pr.popup_icons.selected_icon = self.icon
349 if pr.copy_on_select or self.force_copy_on_select:
350 context.window_manager.clipboard = self.icon
351 self.report({'INFO'}, self.icon)
353 if pr.close_on_select and IV_OT_icons_show.instance:
354 IV_OT_icons_show.instance.close()
356 if pr.show_history:
357 if self.icon in HISTORY:
358 HISTORY.remove(self.icon)
359 if len(HISTORY) >= HISTORY_SIZE:
360 HISTORY.pop(0)
361 HISTORY.append(self.icon)
362 return {'FINISHED'}
365 class IV_OT_icons_show(bpy.types.Operator):
366 bl_idname = "iv.icons_show"
367 bl_label = "Icon Viewer"
368 bl_description = "Icon viewer"
369 bl_property = "filter_auto_focus"
371 instance = None
373 def set_filter(self, value):
374 prefs().popup_icons.filter = value
376 def set_selected_icon(self, value):
377 if IV_OT_icons_show.instance:
378 IV_OT_icons_show.instance.auto_focusable = False
380 filter_auto_focus: StringProperty(
381 description="Filter",
382 get=lambda s: prefs().popup_icons.filter,
383 set=set_filter,
384 options={'TEXTEDIT_UPDATE', 'SKIP_SAVE'})
385 filter: StringProperty(
386 description="Filter",
387 get=lambda s: prefs().popup_icons.filter,
388 set=set_filter,
389 options={'TEXTEDIT_UPDATE'})
390 selected_icon: StringProperty(
391 description="Selected Icon",
392 get=lambda s: prefs().popup_icons.selected_icon,
393 set=set_selected_icon)
395 def get_num_cols(self, num_icons):
396 return round(1.3 * math.sqrt(num_icons))
398 def draw_header(self, layout):
399 pr = prefs()
400 header = layout.box()
401 header = header.split(factor=0.75) if self.selected_icon else \
402 header.row()
403 row = header.row(align=True)
404 row.prop(pr, "show_matcap_icons", text="", icon='SHADING_RENDERED')
405 row.prop(pr, "show_brush_icons", text="", icon='BRUSH_DATA')
406 row.prop(pr, "show_colorset_icons", text="", icon='COLOR')
407 row.prop(pr, "show_event_icons", text="", icon='HAND')
408 row.separator()
410 row.prop(
411 pr, "copy_on_select", text="",
412 icon='COPYDOWN', toggle=True)
413 if pr.copy_on_select:
414 sub = row.row(align=True)
415 if bpy.context.window.screen.name == "temp":
416 sub.alert = True
417 sub.prop(
418 pr, "close_on_select", text="",
419 icon='RESTRICT_SELECT_OFF', toggle=True)
420 row.prop(
421 pr, "auto_focus_filter", text="",
422 icon='OUTLINER_DATA_FONT', toggle=True)
423 row.separator()
425 if self.auto_focusable and pr.auto_focus_filter:
426 row.prop(self, "filter_auto_focus", text="", icon='VIEWZOOM')
427 else:
428 row.prop(self, "filter", text="", icon='VIEWZOOM')
430 if self.selected_icon:
431 row = header.row()
432 row.prop(self, "selected_icon", text="", icon=self.selected_icon)
434 def draw(self, context):
435 pr = prefs()
436 col = self.layout
437 self.draw_header(col)
439 history_num_cols = int(
440 (self.width - POPUP_PADDING) / (ui_scale() * ICON_SIZE))
441 num_cols = min(
442 self.get_num_cols(len(pr.popup_icons.filtered_icons)),
443 history_num_cols)
445 subcol = col.column(align=True)
447 if HISTORY and pr.show_history:
448 pr.popup_icons.draw(subcol.box(), history_num_cols, HISTORY)
450 pr.popup_icons.draw(subcol.box(), num_cols)
452 def close(self):
453 bpy.context.window.screen = bpy.context.window.screen
455 def check(self, context):
456 return True
458 def cancel(self, context):
459 IV_OT_icons_show.instance = None
460 IV_PT_icons.tag_redraw()
462 def execute(self, context):
463 if not IV_OT_icons_show.instance:
464 return {'CANCELLED'}
465 IV_OT_icons_show.instance = None
467 pr = prefs()
468 if self.selected_icon and not pr.copy_on_select:
469 context.window_manager.clipboard = self.selected_icon
470 self.report({'INFO'}, self.selected_icon)
471 pr.popup_icons.selected_icon = ""
473 IV_PT_icons.tag_redraw()
474 return {'FINISHED'}
476 def invoke(self, context, event):
477 pr = prefs()
478 pr.popup_icons.selected_icon = ""
479 pr.popup_icons.filter = ""
480 IV_OT_icons_show.instance = self
481 self.auto_focusable = True
483 num_cols = self.get_num_cols(len(pr.popup_icons.filtered_icons))
484 self.width = min(
485 ui_scale() * (num_cols * ICON_SIZE + POPUP_PADDING),
486 context.window.width - WIN_PADDING)
488 return context.window_manager.invoke_props_dialog(
489 self, width=self.width)
492 classes = (
493 IV_PT_icons,
494 IV_HT_icons,
495 IV_OT_panel_menu_call,
496 IV_OT_icon_select,
497 IV_OT_icons_show,
498 IV_Preferences,
502 def register():
503 if bpy.app.background:
504 return
506 for cls in classes:
507 bpy.utils.register_class(cls)
510 def unregister():
511 if bpy.app.background:
512 return
514 for cls in classes:
515 bpy.utils.unregister_class(cls)