c36a0f7a354a1e39a5737f615e070c648abba74e
[blender.git] / release / scripts / startup / bl_ui / space_userpref.py
1 # ##### BEGIN GPL LICENSE BLOCK #####
2 #
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.
7 #
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.
12 #
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.
16 #
17 # ##### END GPL LICENSE BLOCK #####
18
19 # <pep8 compliant>
20 import bpy
21 from bpy.types import (
22     Header,
23     Menu,
24     Panel,
25 )
26 from bpy.app.translations import pgettext_iface as iface_
27
28
29 class USERPREF_HT_header(Header):
30     bl_space_type = 'PREFERENCES'
31
32     def draw(self, _context):
33         layout = self.layout
34         layout.operator_context = 'EXEC_AREA'
35
36         layout.template_header()
37
38         layout.separator_spacer()
39
40         layout.operator("wm.save_userpref")
41
42
43 class USERPREF_PT_navigation_bar(Panel):
44     bl_label = "Preferences Navigation"
45     bl_space_type = 'PREFERENCES'
46     bl_region_type = 'NAVIGATION_BAR'
47     bl_options = {'HIDE_HEADER'}
48
49     def draw(self, context):
50         layout = self.layout
51
52         prefs = context.preferences
53
54         col = layout.column()
55
56         col.scale_x = 1.3
57         col.scale_y = 1.3
58         col.prop(prefs, "active_section", expand=True)
59
60
61 class USERPREF_PT_save_preferences(Panel):
62     bl_label = "Save Preferences"
63     bl_space_type = 'PREFERENCES'
64     bl_region_type = 'EXECUTE'
65     bl_options = {'HIDE_HEADER'}
66
67     @classmethod
68     def poll(cls, context):
69         # Hide when header is visible
70         for region in context.area.regions:
71             if region.type == 'HEADER' and region.height <= 1:
72                 return True
73
74         return False
75
76     def draw(self, _context):
77         layout = self.layout
78         layout.operator_context = 'EXEC_AREA'
79
80         layout.scale_x = 1.3
81         layout.scale_y = 1.3
82
83         layout.operator("wm.save_userpref")
84
85
86 class PreferencePanel(Panel):
87     """
88     Base class for panels to center align contents with some horizontal margin.
89     Deriving classes need to implement a ``draw_props(context, layout)`` function.
90     """
91
92     bl_space_type = 'PREFERENCES'
93     bl_region_type = 'WINDOW'
94
95     def draw(self, context):
96         layout = self.layout
97         width = context.region.width
98         ui_scale = context.preferences.system.ui_scale
99
100         layout.use_property_split = True
101         layout.use_property_decorate = False  # No animation.
102
103         row = layout.row()
104         if width > (350 * ui_scale):  # No horizontal margin if region is rather small.
105             row.label()  # Needed so col below is centered.
106
107         col = row.column()
108         col.ui_units_x = 50
109
110         # draw_props implemented by deriving classes.
111         self.draw_props(context, col)
112
113         if width > (350 * ui_scale):  # No horizontal margin if region is rather small.
114             row.label()  # Needed so col above is centered.
115
116
117 class USERPREF_PT_interface_display(PreferencePanel):
118     bl_label = "Display"
119
120     @classmethod
121     def poll(cls, context):
122         prefs = context.preferences
123         return (prefs.active_section == 'INTERFACE')
124
125     def draw_props(self, context, layout):
126         prefs = context.preferences
127         view = prefs.view
128
129         flow = layout.grid_flow(row_major=False, columns=0, even_columns=True, even_rows=False, align=False)
130
131         flow.prop(view, "ui_scale", text="Resolution Scale")
132         flow.prop(view, "ui_line_width", text="Line Width")
133
134         layout.separator()
135
136         flow = layout.grid_flow(row_major=False, columns=0, even_columns=True, even_rows=False, align=False)
137
138         flow.prop(view, "show_splash", text="Splash Screen")
139         flow.prop(view, "show_tooltips")
140         flow.prop(view, "show_tooltips_python")
141         flow.prop(view, "show_developer_ui")
142         flow.prop(view, "show_large_cursors")
143
144
145 class USERPREF_PT_interface_text(PreferencePanel):
146     bl_label = "Text Rendering"
147     bl_options = {'DEFAULT_CLOSED'}
148
149     @classmethod
150     def poll(cls, context):
151         prefs = context.preferences
152         return (prefs.active_section == 'INTERFACE')
153
154     def draw_props(self, context, layout):
155         prefs = context.preferences
156         view = prefs.view
157
158         flow = layout.grid_flow(row_major=False, columns=0, even_columns=True, even_rows=False, align=False)
159
160         flow.prop(view, "use_text_antialiasing", text="Anti-aliasing")
161         sub = flow.column()
162         sub.active = view.use_text_antialiasing
163         sub.prop(view, "text_hinting", text="Hinting")
164
165         flow.prop(view, "font_path_ui")
166         flow.prop(view, "font_path_ui_mono")
167
168
169 class USERPREF_PT_interface_translation(PreferencePanel):
170     bl_label = "Translation"
171
172     @classmethod
173     def poll(cls, context):
174         prefs = context.preferences
175         return (prefs.active_section == 'INTERFACE') and bpy.app.build_options.international
176
177     def draw_header(self, context):
178         prefs = context.preferences
179         view = prefs.view
180
181         self.layout.prop(view, "use_international_fonts", text="")
182
183     def draw_props(self, context, layout):
184         prefs = context.preferences
185         view = prefs.view
186
187         layout.active = view.use_international_fonts
188
189         layout.prop(view, "language")
190
191         flow = layout.grid_flow(row_major=False, columns=0, even_columns=True, even_rows=False, align=False)
192
193         flow.prop(view, "use_translate_tooltips", text="Tooltips")
194         flow.prop(view, "use_translate_interface", text="Interface")
195         flow.prop(view, "use_translate_new_dataname", text="New Data")
196
197
198 class USERPREF_PT_interface_editors(PreferencePanel):
199     bl_label = "Editors"
200
201     @classmethod
202     def poll(cls, context):
203         prefs = context.preferences
204         return (prefs.active_section == 'INTERFACE')
205
206     def draw_props(self, context, layout):
207         prefs = context.preferences
208         view = prefs.view
209         system = prefs.system
210
211         flow = layout.grid_flow(row_major=False, columns=0, even_columns=True, even_rows=False, align=False)
212
213         flow.prop(system, "use_region_overlap")
214         flow.prop(view, "show_layout_ui", text="Corner Splitting")
215         flow.prop(view, "color_picker_type")
216         flow.row().prop(view, "header_align")
217
218
219 class USERPREF_PT_interface_menus(Panel):
220     bl_space_type = 'PREFERENCES'
221     bl_region_type = 'WINDOW'
222     bl_label = "Menus"
223     bl_options = {'DEFAULT_CLOSED'}
224
225     @classmethod
226     def poll(cls, context):
227         prefs = context.preferences
228         return (prefs.active_section == 'INTERFACE')
229
230     def draw(self, context):
231         pass
232
233
234 class USERPREF_PT_interface_menus_mouse_over(PreferencePanel):
235     bl_label = "Open on Mouse Over"
236     bl_parent_id = "USERPREF_PT_interface_menus"
237
238     def draw_header(self, context):
239         prefs = context.preferences
240         view = prefs.view
241
242         self.layout.prop(view, "use_mouse_over_open", text="")
243
244     def draw_props(self, context, layout):
245         prefs = context.preferences
246         view = prefs.view
247
248         layout.active = view.use_mouse_over_open
249
250         flow = layout.grid_flow(row_major=False, columns=0, even_columns=True, even_rows=False, align=False)
251
252         flow.prop(view, "open_toplevel_delay", text="Top Level")
253         flow.prop(view, "open_sublevel_delay", text="Sub Level")
254
255
256 class USERPREF_PT_interface_menus_pie(PreferencePanel):
257     bl_label = "Pie Menus"
258     bl_parent_id = "USERPREF_PT_interface_menus"
259
260     def draw_props(self, context, layout):
261         prefs = context.preferences
262         view = prefs.view
263
264         flow = layout.grid_flow(row_major=False, columns=0, even_columns=True, even_rows=False, align=False)
265
266         flow.prop(view, "pie_animation_timeout")
267         flow.prop(view, "pie_tap_timeout")
268         flow.prop(view, "pie_initial_timeout")
269         flow.prop(view, "pie_menu_radius")
270         flow.prop(view, "pie_menu_threshold")
271         flow.prop(view, "pie_menu_confirm")
272
273
274 class USERPREF_PT_edit_objects(Panel):
275     bl_label = "Objects"
276     bl_space_type = 'PREFERENCES'
277     bl_region_type = 'WINDOW'
278
279     @classmethod
280     def poll(cls, context):
281         prefs = context.preferences
282         return (prefs.active_section == 'EDITING')
283
284     def draw(self, context):
285         pass
286
287 class USERPREF_PT_edit_objects_new(PreferencePanel):
288     bl_label = "New Objects"
289     bl_parent_id = "USERPREF_PT_edit_objects"
290
291     def draw_props(self, context, layout):
292         prefs = context.preferences
293         edit = prefs.edit
294
295         flow = layout.grid_flow(row_major=False, columns=0, even_columns=True, even_rows=False, align=False)
296
297         flow.prop(edit, "material_link", text="Link Materials to")
298         flow.prop(edit, "object_align", text="Align to")
299         flow.prop(edit, "use_enter_edit_mode", text="Enter Edit Mode")
300
301
302 class USERPREF_PT_edit_objects_duplicate_data(PreferencePanel):
303     bl_label = "Duplicate Data"
304     bl_parent_id = "USERPREF_PT_edit_objects"
305
306     def draw_props(self, context, layout):
307         prefs = context.preferences
308         edit = prefs.edit
309
310         flow = layout.grid_flow(row_major=False, columns=0, even_columns=True, even_rows=False, align=True)
311
312         col = flow.column()
313         col.prop(edit, "use_duplicate_action", text="Action")
314         col.prop(edit, "use_duplicate_armature", text="Armature")
315         col.prop(edit, "use_duplicate_curve", text="Curve")
316         # col.prop(edit, "use_duplicate_fcurve", text="F-Curve")
317         col.prop(edit, "use_duplicate_light", text="Light")
318         col = flow.column()
319         col.prop(edit, "use_duplicate_material", text="Material")
320         col.prop(edit, "use_duplicate_mesh", text="Mesh")
321         col.prop(edit, "use_duplicate_metaball", text="Metaball")
322         col.prop(edit, "use_duplicate_particle", text="Particle")
323         col = flow.column()
324         col.prop(edit, "use_duplicate_surface", text="Surface")
325         col.prop(edit, "use_duplicate_text", text="Text")
326         col.prop(edit, "use_duplicate_texture", text="Texture")
327
328
329 class USERPREF_PT_edit_cursor(PreferencePanel):
330     bl_label = "3D Cursor"
331
332     @classmethod
333     def poll(cls, context):
334         prefs = context.preferences
335         return (prefs.active_section == 'EDITING')
336
337     def draw_props(self, context, layout):
338         prefs = context.preferences
339         edit = prefs.edit
340
341         flow = layout.grid_flow(row_major=False, columns=0, even_columns=True, even_rows=False, align=False)
342
343         flow.prop(edit, "use_mouse_depth_cursor")
344         flow.prop(edit, "use_cursor_lock_adjust")
345
346
347 class USERPREF_PT_edit_gpencil(PreferencePanel):
348     bl_label = "Grease Pencil"
349     bl_options = {'DEFAULT_CLOSED'}
350
351     @classmethod
352     def poll(cls, context):
353         prefs = context.preferences
354         return (prefs.active_section == 'EDITING')
355
356     def draw_props(self, context, layout):
357         prefs = context.preferences
358         edit = prefs.edit
359
360         flow = layout.grid_flow(row_major=False, columns=0, even_columns=True, even_rows=False, align=False)
361
362         flow.prop(edit, "grease_pencil_manhattan_distance", text="Manhattan Distance")
363         flow.prop(edit, "grease_pencil_euclidean_distance", text="Euclidean Distance")
364
365
366 class USERPREF_PT_edit_annotations(PreferencePanel):
367     bl_label = "Annotations"
368
369     @classmethod
370     def poll(cls, context):
371         prefs = context.preferences
372         return (prefs.active_section == 'EDITING')
373
374     def draw_props(self, context, layout):
375         prefs = context.preferences
376         edit = prefs.edit
377
378         flow = layout.grid_flow(row_major=False, columns=0, even_columns=True, even_rows=False, align=False)
379
380         flow.prop(edit, "grease_pencil_default_color", text="Default Color")
381         flow.prop(edit, "grease_pencil_eraser_radius", text="Eraser Radius")
382         flow.prop(edit, "use_grease_pencil_simplify_stroke", text="Simplify Stroke")
383
384 class USERPREF_PT_edit_weight_paint(PreferencePanel):
385     bl_label = "Weight Paint"
386     bl_options = {'DEFAULT_CLOSED'}
387
388     @classmethod
389     def poll(cls, context):
390         prefs = context.preferences
391         return (prefs.active_section == 'EDITING')
392
393     def draw_props(self, context, layout):
394         prefs = context.preferences
395         view = prefs.view
396
397         layout.prop(view, "use_weight_color_range", text="Use Custom Colors")
398
399         col = layout.column()
400         col.active = view.use_weight_color_range
401         col.template_color_ramp(view, "weight_color_range", expand=True)
402
403
404 class USERPREF_PT_edit_misc(PreferencePanel):
405     bl_label = "Miscellaneous"
406     bl_options = {'DEFAULT_CLOSED'}
407
408     @classmethod
409     def poll(cls, context):
410         prefs = context.preferences
411         return (prefs.active_section == 'EDITING')
412
413     def draw_props(self, context, layout):
414         prefs = context.preferences
415         edit = prefs.edit
416
417         flow = layout.grid_flow(row_major=False, columns=0, even_columns=True, even_rows=False, align=False)
418
419         flow.prop(edit, "sculpt_paint_overlay_color", text="Sculpt Overlay Color")
420         flow.prop(edit, "node_margin", text="Node Auto-offset Margin")
421
422
423 class USERPREF_PT_animation_timeline(PreferencePanel):
424     bl_label = "Timeline"
425
426     @classmethod
427     def poll(cls, context):
428         prefs = context.preferences
429         return (prefs.active_section == 'ANIMATION')
430
431     def draw_props(self, context, layout):
432         prefs = context.preferences
433         view = prefs.view
434         edit = prefs.edit
435
436         flow = layout.grid_flow(row_major=False, columns=0, even_columns=True, even_rows=False, align=False)
437         flow.prop(edit, "use_negative_frames")
438
439         layout.separator()
440
441         flow = layout.grid_flow(row_major=False, columns=0, even_columns=True, even_rows=False, align=False)
442
443         flow.prop(view, "view2d_grid_spacing_min", text="Minimum Grid Spacing")
444         flow.prop(view, "timecode_style")
445         flow.prop(view, "view_frame_type")
446         if view.view_frame_type == 'SECONDS':
447             flow.prop(view, "view_frame_seconds")
448         elif view.view_frame_type == 'KEYFRAMES':
449             flow.prop(view, "view_frame_keyframes")
450
451
452 class USERPREF_PT_animation_keyframes(PreferencePanel):
453     bl_label = "Keyframes"
454
455     @classmethod
456     def poll(cls, context):
457         prefs = context.preferences
458         return (prefs.active_section == 'ANIMATION')
459
460     def draw_props(self, context, layout):
461         prefs = context.preferences
462         edit = prefs.edit
463
464         flow = layout.grid_flow(row_major=False, columns=0, even_columns=True, even_rows=False, align=False)
465
466         flow.prop(edit, "use_visual_keying")
467         flow.prop(edit, "use_keyframe_insert_needed", text="Only Insert Needed")
468
469
470 class USERPREF_PT_animation_autokey(PreferencePanel):
471     bl_label = "Auto-Keyframing"
472     bl_parent_id = "USERPREF_PT_animation_keyframes"
473
474     def draw_props(self, context, layout):
475         prefs = context.preferences
476         edit = prefs.edit
477
478         flow = layout.grid_flow(row_major=False, columns=0, even_columns=True, even_rows=False, align=False)
479
480         flow.prop(edit, "use_auto_keying_warning", text="Show Warning")
481         flow.prop(edit, "use_keyframe_insert_available", text="Only Insert Available")
482         flow.prop(edit, "use_auto_keying", text="Enable in New Scenes")
483
484
485 class USERPREF_PT_animation_fcurves(PreferencePanel):
486     bl_label = "F-Curves"
487
488     @classmethod
489     def poll(cls, context):
490         prefs = context.preferences
491         return (prefs.active_section == 'ANIMATION')
492
493     def draw_props(self, context, layout):
494         prefs = context.preferences
495         edit = prefs.edit
496
497         flow = layout.grid_flow(row_major=False, columns=0, even_columns=True, even_rows=False, align=False)
498
499         flow.prop(edit, "fcurve_unselected_alpha", text="F-Curve Visibility")
500         flow.prop(edit, "keyframe_new_interpolation_type", text="Default Interpolation")
501         flow.prop(edit, "keyframe_new_handle_type", text="Default Handles")
502         flow.prop(edit, "use_insertkey_xyz_to_rgb", text="XYZ to RGB")
503
504
505 class USERPREF_PT_system_sound(PreferencePanel):
506     bl_label = "Sound"
507
508     @classmethod
509     def poll(cls, context):
510         prefs = context.preferences
511         return (prefs.active_section == 'SYSTEM')
512
513     def draw_props(self, context, layout):
514         prefs = context.preferences
515         system = prefs.system
516
517         layout.prop(system, "audio_device", expand=False)
518
519         sub = layout.grid_flow(row_major=False, columns=0, even_columns=False, even_rows=False, align=False)
520         sub.active = system.audio_device not in {'NONE', 'Null'}
521         sub.prop(system, "audio_channels", text="Channels")
522         sub.prop(system, "audio_mixing_buffer", text="Mixing Buffer")
523         sub.prop(system, "audio_sample_rate", text="Sample Rate")
524         sub.prop(system, "audio_sample_format", text="Sample Format")
525
526
527 class USERPREF_PT_system_cycles_devices(PreferencePanel):
528     bl_label = "Cycles Render Devices"
529
530     @classmethod
531     def poll(cls, context):
532         prefs = context.preferences
533         return (prefs.active_section == 'SYSTEM')
534
535     def draw_props(self, context, layout):
536         prefs = context.preferences
537
538         col = layout.column()
539         col.use_property_split = False
540
541         if bpy.app.build_options.cycles:
542             addon = prefs.addons.get("cycles")
543             if addon is not None:
544                 addon.preferences.draw_impl(col, context)
545             del addon
546
547         # NOTE: Disabled for until GPU side of OpenSubdiv is brought back.
548         # system = prefs.system
549         # if hasattr(system, "opensubdiv_compute_type"):
550         #     col.label(text="OpenSubdiv compute:")
551         #     col.row().prop(system, "opensubdiv_compute_type", text="")
552
553
554 class USERPREF_PT_viewport_display(PreferencePanel):
555     bl_label = "Display"
556
557     @classmethod
558     def poll(cls, context):
559         prefs = context.preferences
560         return (prefs.active_section == 'VIEWPORT')
561
562     def draw_props(self, context, layout):
563         prefs = context.preferences
564         view = prefs.view
565
566         flow = layout.grid_flow(row_major=False, columns=0, even_columns=True, even_rows=False, align=False)
567
568         flow.prop(view, "show_object_info", text="Object Info")
569         flow.prop(view, "show_view_name", text="View Name")
570         flow.prop(view, "show_playback_fps", text="Playback FPS")
571
572         layout.separator()
573
574         flow = layout.grid_flow(row_major=False, columns=0, even_columns=True, even_rows=False, align=False)
575
576         col = flow.column()
577         col.prop(view, "gizmo_size", text="Gizmo Size")
578         col.prop(view, "object_origin_size")
579         col.separator()
580
581         flow.separator()
582
583         col = flow.column()
584         col.prop(view, "mini_axis_type", text="3D Viewport Axis")
585
586         if view.mini_axis_type == 'MINIMAL':
587             sub = col.column()
588             sub.active = view.mini_axis_type == 'MINIMAL'
589             sub.prop(view, "mini_axis_size", text="Size")
590             sub.prop(view, "mini_axis_brightness", text="Brightness")
591
592
593 class USERPREF_PT_viewport_quality(PreferencePanel):
594     bl_label = "Quality"
595
596     @classmethod
597     def poll(cls, context):
598         prefs = context.preferences
599         return (prefs.active_section == 'VIEWPORT')
600
601     def draw_props(self, context, layout):
602         import sys
603         prefs = context.preferences
604         system = prefs.system
605
606         flow = layout.grid_flow(row_major=False, columns=0, even_columns=True, even_rows=False, align=False)
607
608         flow.prop(system, "gpu_viewport_quality")
609         flow.prop(system, "multi_sample", text="Multisampling")
610         flow.prop(system, "gpencil_multi_sample", text="Grease Pencil Multisampling")
611
612         if sys.platform == "linux" and system.multi_sample != 'NONE':
613             layout.label(text="Might fail for Mesh editing selection!")
614
615
616 class USERPREF_PT_viewport_textures(PreferencePanel):
617     bl_label = "Textures"
618
619     @classmethod
620     def poll(cls, context):
621         prefs = context.preferences
622         return (prefs.active_section == 'VIEWPORT')
623
624     def draw_props(self, context, layout):
625         prefs = context.preferences
626         system = prefs.system
627
628         flow = layout.grid_flow(row_major=False, columns=0, even_columns=True, even_rows=False, align=False)
629
630         flow.prop(system, "gl_texture_limit", text="Limit Size")
631         flow.prop(system, "anisotropic_filter")
632         flow.prop(system, "gl_clip_alpha", slider=True)
633         flow.prop(system, "image_draw_method", text="Image Display Method")
634
635
636 class USERPREF_PT_viewport_selection(PreferencePanel):
637     bl_label = "Selection"
638     bl_options = {'DEFAULT_CLOSED'}
639
640     @classmethod
641     def poll(cls, context):
642         prefs = context.preferences
643         return (prefs.active_section == 'VIEWPORT')
644
645     def draw_props(self, context, layout):
646         prefs = context.preferences
647         system = prefs.system
648
649         flow = layout.grid_flow(row_major=False, columns=0, even_columns=True, even_rows=False, align=False)
650
651         flow.prop(system, "use_select_pick_depth")
652
653
654 class USERPREF_PT_system_memory(PreferencePanel):
655     bl_label = "Memory & Limits"
656
657     @classmethod
658     def poll(cls, context):
659         prefs = context.preferences
660         return (prefs.active_section == 'SYSTEM')
661
662     def draw_props(self, context, layout):
663         prefs = context.preferences
664         system = prefs.system
665         edit = prefs.edit
666
667         flow = layout.grid_flow(row_major=False, columns=0, even_columns=True, even_rows=False, align=False)
668
669         flow.prop(edit, "undo_steps", text="Undo Steps")
670         flow.prop(edit, "undo_memory_limit", text="Undo Memory Limit")
671         flow.prop(edit, "use_global_undo")
672
673         layout.separator()
674
675         flow = layout.grid_flow(row_major=False, columns=0, even_columns=True, even_rows=False, align=False)
676
677         flow.prop(system, "memory_cache_limit", text="Sequencer Cache Limit")
678         flow.prop(system, "scrollback", text="Console Scrollback Lines")
679
680         layout.separator()
681
682         flow = layout.grid_flow(row_major=False, columns=0, even_columns=True, even_rows=False, align=False)
683
684         flow.prop(system, "texture_time_out", text="Texture Time Out")
685         flow.prop(system, "texture_collection_rate", text="Garbage Collection Rate")
686
687
688 class USERPREF_MT_interface_theme_presets(Menu):
689     bl_label = "Presets"
690     preset_subdir = "interface_theme"
691     preset_operator = "script.execute_preset"
692     preset_type = 'XML'
693     preset_xml_map = (
694         ("preferences.themes[0]", "Theme"),
695         ("preferences.ui_styles[0]", "ThemeStyle"),
696     )
697     draw = Menu.draw_preset
698
699     def reset_cb(context):
700         bpy.ops.ui.reset_default_theme()
701
702
703 class USERPREF_PT_theme(Panel):
704     bl_space_type = 'PREFERENCES'
705     bl_label = "Themes"
706     bl_region_type = 'WINDOW'
707     bl_options = {'HIDE_HEADER'}
708
709     @classmethod
710     def poll(cls, context):
711         prefs = context.preferences
712         return (prefs.active_section == 'THEMES')
713
714     def draw(self, _context):
715         layout = self.layout
716
717         split = layout.split(factor=0.6)
718
719         row = split.row(align=True)
720         row.menu("USERPREF_MT_interface_theme_presets", text=USERPREF_MT_interface_theme_presets.bl_label)
721         row.operator("wm.interface_theme_preset_add", text="", icon='ADD')
722         row.operator("wm.interface_theme_preset_add", text="", icon='REMOVE').remove_active = True
723
724         row = split.row(align=True)
725         row.operator("wm.theme_install", text="Install...", icon='IMPORT')
726         row.operator("ui.reset_default_theme", text="Reset", icon='LOOP_BACK')
727
728
729 class USERPREF_PT_theme_user_interface(PreferencePanel):
730     bl_space_type = 'PREFERENCES'
731     bl_region_type = 'WINDOW'
732     bl_label = "User Interface"
733     bl_options = {'DEFAULT_CLOSED'}
734
735     @classmethod
736     def poll(cls, context):
737         prefs = context.preferences
738         return (prefs.active_section == 'THEMES')
739
740     def draw_header(self, _context):
741         layout = self.layout
742
743         layout.label(icon='WORKSPACE')
744
745     def draw(self, context):
746         pass
747
748
749 # Base class for dynamically defined widget color panels.
750 class PreferenceThemeWidgetColorPanel(Panel):
751     bl_space_type = 'PREFERENCES'
752     bl_region_type = 'WINDOW'
753     bl_parent_id = "USERPREF_PT_theme_user_interface"
754
755     @staticmethod
756     def draw(self, context):
757         theme = context.preferences.themes[0]
758         ui = theme.user_interface
759         widget_style = getattr(ui, self.wcol)
760         layout = self.layout
761
762         layout.use_property_split = True
763
764         flow = layout.grid_flow(row_major=False, columns=0, even_columns=True, even_rows=False, align=False)
765
766         col = flow.column()
767         col.prop(widget_style, "outline")
768         col.prop(widget_style, "item", slider=True)
769         col.prop(widget_style, "inner", slider=True)
770         col.prop(widget_style, "inner_sel", slider=True)
771
772         col = flow.column()
773         col.prop(widget_style, "text")
774         col.prop(widget_style, "text_sel")
775         col.prop(widget_style, "roundness")
776
777         col = flow.column()
778         col.prop(widget_style, "show_shaded")
779
780         colsub = col.column()
781         colsub.active = widget_style.show_shaded
782         colsub.prop(widget_style, "shadetop")
783         colsub.prop(widget_style, "shadedown")
784
785     @classmethod
786     def poll(cls, context):
787         prefs = context.preferences
788         return (prefs.active_section == 'THEMES')
789
790
791 class USERPREF_PT_theme_interface_state(PreferencePanel):
792     bl_label = "State"
793     bl_options = {'DEFAULT_CLOSED'}
794     bl_parent_id = "USERPREF_PT_theme_user_interface"
795
796     def draw_props(self, context, layout):
797         theme = context.preferences.themes[0]
798         ui_state = theme.user_interface.wcol_state
799
800         flow = layout.grid_flow(row_major=False, columns=0, even_columns=True, even_rows=False, align=False)
801
802         col = flow.column(align=True)
803         col.prop(ui_state, "inner_anim")
804         col.prop(ui_state, "inner_anim_sel")
805
806         col = flow.column(align=True)
807         col.prop(ui_state, "inner_driven")
808         col.prop(ui_state, "inner_driven_sel")
809
810         col = flow.column(align=True)
811         col.prop(ui_state, "inner_key")
812         col.prop(ui_state, "inner_key_sel")
813
814         col = flow.column(align=True)
815         col.prop(ui_state, "inner_overridden")
816         col.prop(ui_state, "inner_overridden_sel")
817
818         col = flow.column(align=True)
819         col.prop(ui_state, "inner_changed")
820         col.prop(ui_state, "inner_changed_sel")
821
822         col = flow.column(align=True)
823         col.prop(ui_state, "blend")
824
825
826 class USERPREF_PT_theme_interface_styles(PreferencePanel):
827     bl_label = "Styles"
828     bl_options = {'DEFAULT_CLOSED'}
829     bl_parent_id = "USERPREF_PT_theme_user_interface"
830
831     def draw_props(self, context, layout):
832         theme = context.preferences.themes[0]
833         ui = theme.user_interface
834
835         flow = layout.grid_flow(row_major=False, columns=0, even_columns=True, even_rows=False, align=False)
836
837         flow.prop(ui, "menu_shadow_fac")
838         flow.prop(ui, "icon_alpha")
839         flow.prop(ui, "icon_saturation")
840         flow.prop(ui, "editor_outline")
841         flow.prop(ui, "menu_shadow_width")
842         flow.prop(ui, "widget_emboss")
843
844
845 class USERPREF_PT_theme_interface_gizmos(PreferencePanel):
846     bl_label = "Axis & Gizmo Colors"
847     bl_options = {'DEFAULT_CLOSED'}
848     bl_parent_id = "USERPREF_PT_theme_user_interface"
849
850     def draw_props(self, context, layout):
851         theme = context.preferences.themes[0]
852         ui = theme.user_interface
853
854         flow = layout.grid_flow(row_major=False, columns=0, even_columns=True, even_rows=True, align=False)
855
856         col = flow.column(align=True)
857         col.prop(ui, "axis_x", text="Axis X")
858         col.prop(ui, "axis_y", text="Y")
859         col.prop(ui, "axis_z", text="Z")
860
861         col = flow.column()
862         col.prop(ui, "gizmo_primary")
863         col.prop(ui, "gizmo_secondary")
864
865         col = flow.column()
866         col.prop(ui, "gizmo_a")
867         col.prop(ui, "gizmo_b")
868
869
870 class USERPREF_PT_theme_interface_icons(PreferencePanel):
871     bl_label = "Icon Colors"
872     bl_options = {'DEFAULT_CLOSED'}
873     bl_parent_id = "USERPREF_PT_theme_user_interface"
874
875     def draw_props(self, context, layout):
876         theme = context.preferences.themes[0]
877         ui = theme.user_interface
878
879         flow = layout.grid_flow(row_major=False, columns=0, even_columns=True, even_rows=False, align=False)
880
881         flow.prop(ui, "icon_collection")
882         flow.prop(ui, "icon_object")
883         flow.prop(ui, "icon_object_data")
884         flow.prop(ui, "icon_modifier")
885         flow.prop(ui, "icon_shading")
886
887
888 class USERPREF_PT_theme_text_style(PreferencePanel):
889     bl_label = "Text Style"
890     bl_options = {'DEFAULT_CLOSED'}
891
892     @classmethod
893     def poll(cls, context):
894         prefs = context.preferences
895         return (prefs.active_section == 'THEMES')
896
897     @staticmethod
898     def _ui_font_style(layout, font_style):
899         layout.use_property_split = True
900         flow = layout.grid_flow(row_major=False, columns=0, even_columns=True, even_rows=False, align=True)
901
902         col = flow.column()
903         col.row().prop(font_style, "font_kerning_style", expand=True)
904         col.prop(font_style, "points")
905
906         col = flow.column(align=True)
907         col.prop(font_style, "shadow_offset_x", text="Shadow Offset X")
908         col.prop(font_style, "shadow_offset_y", text="Y")
909
910         col = flow.column()
911         col.prop(font_style, "shadow")
912         col.prop(font_style, "shadow_alpha")
913         col.prop(font_style, "shadow_value")
914
915     def draw_header(self, _context):
916         layout = self.layout
917
918         layout.label(icon='FONTPREVIEW')
919
920     def draw_props(self, context, layout):
921         style = context.preferences.ui_styles[0]
922
923         layout.label(text="Panel Title")
924         self._ui_font_style(layout, style.panel_title)
925
926         layout.separator()
927
928         layout.label(text="Widget")
929         self._ui_font_style(layout, style.widget)
930
931         layout.separator()
932
933         layout.label(text="Widget Label")
934         self._ui_font_style(layout, style.widget_label)
935
936
937 class USERPREF_PT_theme_bone_color_sets(PreferencePanel):
938     bl_label = "Bone Color Sets"
939     bl_options = {'DEFAULT_CLOSED'}
940
941     @classmethod
942     def poll(cls, context):
943         prefs = context.preferences
944         return (prefs.active_section == 'THEMES')
945
946     def draw_header(self, _context):
947         layout = self.layout
948
949         layout.label(icon='COLOR')
950
951     def draw_props(self, context, layout):
952         theme = context.preferences.themes[0]
953
954         layout.use_property_split = True
955
956         for i, ui in enumerate(theme.bone_color_sets, 1):
957             layout.label(text=iface_(f"Color Set {i:d}"), translate=False)
958
959             flow = layout.grid_flow(row_major=False, columns=0, even_columns=True, even_rows=False, align=False)
960
961             flow.prop(ui, "normal")
962             flow.prop(ui, "select")
963             flow.prop(ui, "active")
964             flow.prop(ui, "show_colored_constraints")
965
966
967 # Base class for dynamically defined theme-space panels.
968 class PreferenceThemeSpacePanel(Panel):
969     bl_space_type = 'PREFERENCES'
970     bl_region_type = 'WINDOW'
971
972     # not essential, hard-coded UI delimiters for the theme layout
973     ui_delimiters = {
974         'VIEW_3D': {
975             "text_grease_pencil",
976             "text_keyframe",
977             "speaker",
978             "freestyle_face_mark",
979             "split_normal",
980             "bone_solid",
981             "paint_curve_pivot",
982         },
983         'GRAPH_EDITOR': {
984             "handle_vertex_select",
985         },
986         'IMAGE_EDITOR': {
987             "paint_curve_pivot",
988         },
989         'NODE_EDITOR': {
990             "layout_node",
991         },
992         'CLIP_EDITOR': {
993             "handle_vertex_select",
994         }
995     }
996
997     # TODO theme_area should be deprecated
998     @staticmethod
999     def _theme_generic(layout, themedata, theme_area):
1000
1001         layout.use_property_split = True
1002
1003         flow = layout.grid_flow(row_major=False, columns=0, even_columns=True, even_rows=False, align=False)
1004
1005         props_type = {}
1006
1007         for prop in themedata.rna_type.properties:
1008             if prop.identifier == "rna_type":
1009                 continue
1010
1011             props_type.setdefault((prop.type, prop.subtype), []).append(prop)
1012
1013         th_delimiters = PreferenceThemeSpacePanel.ui_delimiters.get(theme_area)
1014         for props_type, props_ls in sorted(props_type.items()):
1015             if props_type[0] == 'POINTER':
1016                 continue
1017
1018             if th_delimiters is None:
1019                 # simple, no delimiters
1020                 for prop in props_ls:
1021                     flow.prop(themedata, prop.identifier)
1022             else:
1023
1024                 for prop in props_ls:
1025                     flow.prop(themedata, prop.identifier)
1026
1027     @staticmethod
1028     def draw_header(self, _context):
1029         if hasattr(self, "icon") and self.icon != 'NONE':
1030             layout = self.layout
1031             layout.label(icon=self.icon)
1032
1033     @staticmethod
1034     def draw(self, context):
1035         layout = self.layout
1036         theme = context.preferences.themes[0]
1037
1038         datapath_list = self.datapath.split(".")
1039         data = theme
1040         for datapath_item in datapath_list:
1041             data = getattr(data, datapath_item)
1042         PreferenceThemeSpacePanel._theme_generic(layout, data, self.theme_area)
1043
1044     @classmethod
1045     def poll(cls, context):
1046         prefs = context.preferences
1047         return (prefs.active_section == 'THEMES')
1048
1049
1050 class ThemeGenericClassGenerator():
1051     generated_classes = []
1052
1053     @staticmethod
1054     def generate_panel_classes_for_wcols():
1055         wcols = [
1056             ("Regular", "wcol_regular"),
1057             ("Tool", "wcol_tool"),
1058             ("Toolbar Item", "wcol_toolbar_item"),
1059             ("Radio Buttons", "wcol_radio"),
1060             ("Text", "wcol_text"),
1061             ("Option", "wcol_option"),
1062             ("Toggle", "wcol_toggle"),
1063             ("Number Field", "wcol_num"),
1064             ("Value Slider", "wcol_numslider"),
1065             ("Box", "wcol_box"),
1066             ("Menu", "wcol_menu"),
1067             ("Pie Menu", "wcol_pie_menu"),
1068             ("Pulldown", "wcol_pulldown"),
1069             ("Menu Back", "wcol_menu_back"),
1070             ("Tooltip", "wcol_tooltip"),
1071             ("Menu Item", "wcol_menu_item"),
1072             ("Scroll Bar", "wcol_scroll"),
1073             ("Progress Bar", "wcol_progress"),
1074             ("List Item", "wcol_list_item"),
1075             ("Tab", "wcol_tab"),
1076         ]
1077
1078         for (name, wcol) in wcols:
1079             panel_id = "USERPREF_PT_theme_interface_" + wcol
1080             paneltype = type(panel_id, (PreferenceThemeWidgetColorPanel,), {
1081                 "bl_label": name,
1082                 "bl_options": {'DEFAULT_CLOSED'},
1083                 "draw": PreferenceThemeWidgetColorPanel.draw,
1084                 "wcol": wcol,
1085             })
1086
1087             ThemeGenericClassGenerator.generated_classes.append(paneltype)
1088
1089     @staticmethod
1090     def generate_theme_area_child_panel_classes(parent_id, rna_type, theme_area, datapath):
1091         def generate_child_panel_classes_recurse(parent_id, rna_type, theme_area, datapath):
1092             props_type = {}
1093
1094             for prop in rna_type.properties:
1095                 if prop.identifier == "rna_type":
1096                     continue
1097
1098                 props_type.setdefault((prop.type, prop.subtype), []).append(prop)
1099
1100             for props_type, props_ls in sorted(props_type.items()):
1101                 if props_type[0] == 'POINTER':
1102                     for prop in props_ls:
1103                         new_datapath = datapath + "." + prop.identifier if datapath else prop.identifier
1104                         panel_id = parent_id + "_" + prop.identifier
1105                         paneltype = type(panel_id, (PreferenceThemeSpacePanel,), {
1106                             "bl_label": rna_type.properties[prop.identifier].name,
1107                             "bl_parent_id": parent_id,
1108                             "bl_options": {'DEFAULT_CLOSED'},
1109                             "draw": PreferenceThemeSpacePanel.draw,
1110                             "theme_area": theme_area.identifier,
1111                             "datapath": new_datapath,
1112                         })
1113
1114                         ThemeGenericClassGenerator.generated_classes.append(paneltype)
1115                         generate_child_panel_classes_recurse(panel_id, prop.fixed_type, theme_area, new_datapath)
1116
1117         generate_child_panel_classes_recurse(parent_id, rna_type, theme_area, datapath)
1118
1119     @staticmethod
1120     def generate_panel_classes_from_theme_areas():
1121         from bpy.types import Theme
1122
1123         for theme_area in Theme.bl_rna.properties['theme_area'].enum_items_static:
1124             if theme_area.identifier in {'USER_INTERFACE', 'STYLE', 'BONE_COLOR_SETS'}:
1125                 continue
1126
1127             panel_id = "USERPREF_PT_theme_" + theme_area.identifier.lower()
1128             # Generate panel-class from theme_area
1129             paneltype = type(panel_id, (PreferenceThemeSpacePanel,), {
1130                 "bl_label": theme_area.name,
1131                 "bl_options": {'DEFAULT_CLOSED'},
1132                 "draw_header": PreferenceThemeSpacePanel.draw_header,
1133                 "draw": PreferenceThemeSpacePanel.draw,
1134                 "theme_area": theme_area.identifier,
1135                 "icon": theme_area.icon,
1136                 "datapath": theme_area.identifier.lower(),
1137             })
1138
1139             ThemeGenericClassGenerator.generated_classes.append(paneltype)
1140             ThemeGenericClassGenerator.generate_theme_area_child_panel_classes(
1141                 panel_id, Theme.bl_rna.properties[theme_area.identifier.lower()].fixed_type,
1142                 theme_area, theme_area.identifier.lower())
1143
1144
1145 class FilePathsPanel(Panel):
1146     bl_space_type = 'PREFERENCES'
1147     bl_region_type = 'WINDOW'
1148
1149     @classmethod
1150     def poll(cls, context):
1151         prefs = context.preferences
1152         return (prefs.active_section == 'FILE_PATHS')
1153
1154     def draw(self, context):
1155         layout = self.layout
1156
1157         layout.use_property_split = True
1158         layout.use_property_decorate = False
1159
1160         self.draw_props(context, layout)
1161
1162
1163 class USERPREF_PT_file_paths_data(FilePathsPanel):
1164     bl_label = "Data"
1165
1166     def draw_props(self, context, layout):
1167         paths = context.preferences.filepaths
1168
1169         col = self.layout.column()
1170         col.prop(paths, "font_directory", text="Fonts")
1171         col.prop(paths, "texture_directory", text="Textures")
1172         col.prop(paths, "script_directory", text="Scripts")
1173         col.prop(paths, "sound_directory", text="Sounds")
1174         col.prop(paths, "temporary_directory", text="Temporary Files")
1175
1176
1177 class USERPREF_PT_file_paths_render(FilePathsPanel):
1178     bl_label = "Render"
1179
1180     def draw_props(self, context, layout):
1181         paths = context.preferences.filepaths
1182
1183         col = self.layout.column()
1184         col.prop(paths, "render_output_directory", text="Render Output")
1185         col.prop(paths, "render_cache_directory", text="Render Cache")
1186
1187
1188 class USERPREF_PT_file_paths_applications(FilePathsPanel):
1189     bl_label = "Applications"
1190
1191     def draw_props(self, context, layout):
1192         paths = context.preferences.filepaths
1193
1194         col = layout.column()
1195         col.prop(paths, "image_editor", text="Image Editor")
1196         col.prop(paths, "animation_player_preset", text="Animation Player")
1197         if paths.animation_player_preset == 'CUSTOM':
1198             col.prop(paths, "animation_player", text="Player")
1199
1200
1201 class USERPREF_PT_file_paths_development(FilePathsPanel):
1202     bl_label = "Development"
1203
1204     @classmethod
1205     def poll(cls, context):
1206         prefs = context.preferences
1207         return (prefs.active_section == 'FILE_PATHS') and prefs.view.show_developer_ui
1208
1209     def draw_props(self, context, layout):
1210         paths = context.preferences.filepaths
1211         layout.prop(paths, "i18n_branches_directory", text="I18n Branches")
1212
1213
1214 class USERPREF_PT_saveload_autorun(PreferencePanel):
1215     bl_label = "Auto Run Python Scripts"
1216     bl_parent_id = "USERPREF_PT_saveload_blend"
1217
1218     def draw_header(self, context):
1219         prefs = context.preferences
1220         paths = prefs.filepaths
1221
1222         self.layout.prop(paths, "use_scripts_auto_execute", text="")
1223
1224     def draw(self, context):
1225         layout = self.layout
1226         prefs = context.preferences
1227         paths = prefs.filepaths
1228
1229         layout.use_property_split = True
1230         layout.use_property_decorate = False  # No animation.
1231
1232         layout.active = paths.use_scripts_auto_execute
1233
1234         box = layout.box()
1235         row = box.row()
1236         row.label(text="Excluded Paths:")
1237         row.operator("wm.userpref_autoexec_path_add", text="", icon='ADD', emboss=False)
1238         for i, path_cmp in enumerate(prefs.autoexec_paths):
1239             row = box.row()
1240             row.prop(path_cmp, "path", text="")
1241             row.prop(path_cmp, "use_glob", text="", icon='FILTER')
1242             row.operator("wm.userpref_autoexec_path_remove", text="", icon='X', emboss=False).index = i
1243
1244
1245 class USERPREF_PT_saveload_blend(PreferencePanel):
1246     bl_label = "Blend Files"
1247
1248     @classmethod
1249     def poll(cls, context):
1250         prefs = context.preferences
1251         return (prefs.active_section == 'SAVE_LOAD')
1252
1253     def draw_props(self, context, layout):
1254         prefs = context.preferences
1255         paths = prefs.filepaths
1256         view = prefs.view
1257
1258         flow = layout.grid_flow(row_major=False, columns=0, even_columns=True, even_rows=False, align=False)
1259
1260         flow.prop(paths, "use_relative_paths")
1261         flow.prop(paths, "use_file_compression")
1262         flow.prop(paths, "use_load_ui")
1263         flow.prop(paths, "use_save_preview_images")
1264         flow.prop(paths, "use_tabs_as_spaces")
1265         flow.prop(view, "use_quit_dialog")
1266
1267         layout.separator()
1268
1269         flow = layout.grid_flow(row_major=False, columns=0, even_columns=True, even_rows=False, align=False)
1270
1271         flow.prop(paths, "save_version")
1272         flow.prop(paths, "recent_files")
1273
1274
1275 class USERPREF_PT_saveload_blend_autosave(PreferencePanel):
1276     bl_label = "Auto Save"
1277     bl_parent_id = "USERPREF_PT_saveload_blend"
1278
1279     def draw_props(self, context, layout):
1280         prefs = context.preferences
1281         paths = prefs.filepaths
1282
1283         flow = layout.grid_flow(row_major=False, columns=0, even_columns=True, even_rows=False, align=False)
1284
1285         flow.prop(paths, "use_auto_save_temporary_files")
1286         sub = flow.column()
1287         sub.active = paths.use_auto_save_temporary_files
1288         sub.prop(paths, "auto_save_time", text="Timer (mins)")
1289
1290
1291 class USERPREF_PT_saveload_file_browser(PreferencePanel):
1292     bl_label = "File Browser"
1293
1294     @classmethod
1295     def poll(cls, context):
1296         prefs = context.preferences
1297         return (prefs.active_section == 'SAVE_LOAD')
1298
1299     def draw_props(self, context, layout):
1300         prefs = context.preferences
1301         paths = prefs.filepaths
1302
1303         flow = layout.grid_flow(row_major=False, columns=0, even_columns=True, even_rows=False, align=False)
1304
1305         flow.prop(paths, "use_filter_files")
1306         flow.prop(paths, "show_hidden_files_datablocks")
1307         flow.prop(paths, "hide_recent_locations")
1308         flow.prop(paths, "hide_system_bookmarks")
1309         flow.prop(paths, "show_thumbnails")
1310
1311
1312 class USERPREF_MT_ndof_settings(Menu):
1313     # accessed from the window key-bindings in C (only)
1314     bl_label = "3D Mouse Settings"
1315
1316     def draw(self, context):
1317         layout = self.layout
1318
1319         input_prefs = context.preferences.inputs
1320
1321         is_view3d = context.space_data.type == 'VIEW_3D'
1322
1323         layout.prop(input_prefs, "ndof_sensitivity")
1324         layout.prop(input_prefs, "ndof_orbit_sensitivity")
1325         layout.prop(input_prefs, "ndof_deadzone")
1326
1327         if is_view3d:
1328             layout.separator()
1329             layout.prop(input_prefs, "ndof_show_guide")
1330
1331             layout.separator()
1332             layout.label(text="Orbit Style")
1333             layout.row().prop(input_prefs, "ndof_view_navigate_method", text="")
1334             layout.row().prop(input_prefs, "ndof_view_rotate_method", text="")
1335             layout.separator()
1336             layout.label(text="Orbit Options")
1337             layout.prop(input_prefs, "ndof_rotx_invert_axis")
1338             layout.prop(input_prefs, "ndof_roty_invert_axis")
1339             layout.prop(input_prefs, "ndof_rotz_invert_axis")
1340
1341         # view2d use pan/zoom
1342         layout.separator()
1343         layout.label(text="Pan Options")
1344         layout.prop(input_prefs, "ndof_panx_invert_axis")
1345         layout.prop(input_prefs, "ndof_pany_invert_axis")
1346         layout.prop(input_prefs, "ndof_panz_invert_axis")
1347         layout.prop(input_prefs, "ndof_pan_yz_swap_axis")
1348
1349         layout.label(text="Zoom Options")
1350         layout.prop(input_prefs, "ndof_zoom_invert")
1351
1352         if is_view3d:
1353             layout.separator()
1354             layout.label(text="Fly/Walk Options")
1355             layout.prop(input_prefs, "ndof_fly_helicopter", icon='NDOF_FLY')
1356             layout.prop(input_prefs, "ndof_lock_horizon", icon='NDOF_DOM')
1357
1358
1359 class USERPREF_PT_input_keyboard(PreferencePanel):
1360     bl_label = "Keyboard"
1361
1362     @classmethod
1363     def poll(cls, context):
1364         prefs = context.preferences
1365         return (prefs.active_section == 'INPUT')
1366
1367     def draw_props(self, context, layout):
1368         prefs = context.preferences
1369         inputs = prefs.inputs
1370
1371         layout.prop(inputs, "use_emulate_numpad")
1372         layout.prop(inputs, "use_numeric_input_advanced")
1373
1374
1375 class USERPREF_PT_input_mouse(PreferencePanel):
1376     bl_label = "Mouse"
1377
1378     @classmethod
1379     def poll(cls, context):
1380         prefs = context.preferences
1381         return (prefs.active_section == 'INPUT')
1382
1383     def draw_props(self, context, layout):
1384         prefs = context.preferences
1385         inputs = prefs.inputs
1386
1387         flow = layout.grid_flow(row_major=False, columns=0, even_columns=True, even_rows=False, align=False)
1388
1389         flow.prop(inputs, "use_mouse_emulate_3_button")
1390         flow.prop(inputs, "use_mouse_continuous")
1391         flow.prop(inputs, "use_drag_immediately")
1392         flow.prop(inputs, "drag_threshold")
1393         flow.prop(inputs, "mouse_double_click_time", text="Double Click Speed")
1394
1395
1396 class USERPREF_PT_navigation_orbit(PreferencePanel):
1397     bl_label = "Orbit & Pan"
1398
1399     @classmethod
1400     def poll(cls, context):
1401         prefs = context.preferences
1402         return (prefs.active_section == 'NAVIGATION')
1403
1404     def draw_props(self, context, layout):
1405         import sys
1406         prefs = context.preferences
1407         inputs = prefs.inputs
1408         view = prefs.view
1409
1410         flow = layout.grid_flow(row_major=False, columns=0, even_columns=True, even_rows=False, align=False)
1411
1412         flow.row().prop(inputs, "view_rotate_method", expand=True)
1413         flow.prop(inputs, "use_rotate_around_active")
1414         flow.prop(inputs, "use_auto_perspective")
1415         flow.prop(inputs, "use_mouse_depth_navigate")
1416         if sys.platform == "darwin":
1417             flow.prop(inputs, "use_trackpad_natural", text="Natural Trackpad Direction")
1418
1419         flow.separator()
1420
1421         flow.prop(view, "smooth_view")
1422         flow.prop(view, "rotation_angle")
1423
1424
1425 class USERPREF_PT_navigation_zoom(PreferencePanel):
1426     bl_label = "Zoom"
1427
1428     @classmethod
1429     def poll(cls, context):
1430         prefs = context.preferences
1431         return (prefs.active_section == 'NAVIGATION')
1432
1433     def draw_props(self, context, layout):
1434         prefs = context.preferences
1435         inputs = prefs.inputs
1436
1437         flow = layout.grid_flow(row_major=False, columns=0, even_columns=True, even_rows=False, align=False)
1438
1439         flow.row().prop(inputs, "view_zoom_method", text="Zoom Method", expand=True)
1440         if inputs.view_zoom_method in {'DOLLY', 'CONTINUE'}:
1441             flow.row().prop(inputs, "view_zoom_axis", expand=True)
1442             flow.prop(inputs, "invert_mouse_zoom", text="Invert Mouse Zoom Direction")
1443
1444         flow.prop(inputs, "invert_zoom_wheel", text="Invert Wheel Zoom Direction")
1445         # sub.prop(view, "wheel_scroll_lines", text="Scroll Lines")
1446         flow.prop(inputs, "use_zoom_to_mouse")
1447
1448
1449 class USERPREF_PT_navigation_fly_walk(PreferencePanel):
1450     bl_label = "Fly & Walk"
1451
1452     @classmethod
1453     def poll(cls, context):
1454         prefs = context.preferences
1455         return (prefs.active_section == 'NAVIGATION')
1456
1457     def draw_props(self, context, layout):
1458         prefs = context.preferences
1459         inputs = prefs.inputs
1460
1461         layout.row().prop(inputs, "navigation_mode", expand=True)
1462
1463         flow = layout.grid_flow(row_major=False, columns=0, even_columns=True, even_rows=False, align=False)
1464         flow.prop(inputs, "use_camera_lock_parent")
1465
1466
1467 class USERPREF_PT_navigation_fly_walk_navigation(PreferencePanel):
1468     bl_label = "Walk"
1469     bl_parent_id = "USERPREF_PT_navigation_fly_walk"
1470     bl_options = {'DEFAULT_CLOSED'}
1471
1472     @classmethod
1473     def poll(cls, context):
1474         prefs = context.preferences
1475         return prefs.inputs.navigation_mode == 'WALK'
1476
1477     def draw_props(self, context, layout):
1478         prefs = context.preferences
1479         inputs = prefs.inputs
1480         walk = inputs.walk_navigation
1481
1482         flow = layout.grid_flow(row_major=False, columns=0, even_columns=True, even_rows=False, align=False)
1483
1484         flow.prop(walk, "use_mouse_reverse")
1485         flow.prop(walk, "mouse_speed")
1486         flow.prop(walk, "teleport_time")
1487
1488         sub = flow.column(align=True)
1489         sub.prop(walk, "walk_speed")
1490         sub.prop(walk, "walk_speed_factor")
1491
1492
1493 class USERPREF_PT_navigation_fly_walk_gravity(PreferencePanel):
1494     bl_label = "Gravity"
1495     bl_parent_id = "USERPREF_PT_navigation_fly_walk"
1496     bl_options = {'DEFAULT_CLOSED'}
1497
1498     @classmethod
1499     def poll(cls, context):
1500         prefs = context.preferences
1501         return prefs.inputs.navigation_mode == 'WALK'
1502
1503     def draw_header(self, context):
1504         prefs = context.preferences
1505         inputs = prefs.inputs
1506         walk = inputs.walk_navigation
1507
1508         self.layout.prop(walk, "use_gravity", text="")
1509
1510     def draw_props(self, context, layout):
1511         prefs = context.preferences
1512         inputs = prefs.inputs
1513         walk = inputs.walk_navigation
1514
1515         layout.active = walk.use_gravity
1516
1517         flow = layout.grid_flow(row_major=False, columns=0, even_columns=True, even_rows=False, align=False)
1518
1519         flow.prop(walk, "view_height")
1520         flow.prop(walk, "jump_height")
1521
1522
1523 class USERPREF_PT_input_tablet(PreferencePanel):
1524     bl_label = "Tablet"
1525
1526     @classmethod
1527     def poll(cls, context):
1528         prefs = context.preferences
1529         inputs = prefs.inputs
1530         return prefs.active_section == 'INPUT'
1531
1532     def draw_props(self, context, layout):
1533         prefs = context.preferences
1534         inputs = prefs.inputs
1535
1536         import sys
1537         if sys.platform[:3] == "win":
1538             layout.prop(inputs, "tablet_api")
1539             layout.separator()
1540
1541         flow = layout.grid_flow(row_major=False, columns=0, even_columns=True, even_rows=False, align=False)
1542
1543         flow.prop(inputs, "pressure_threshold_max")
1544         flow.prop(inputs, "pressure_softness")
1545
1546
1547 class USERPREF_PT_input_ndof(PreferencePanel):
1548     bl_label = "NDOF"
1549     bl_options = {'DEFAULT_CLOSED'}
1550
1551     @classmethod
1552     def poll(cls, context):
1553         prefs = context.preferences
1554         inputs = prefs.inputs
1555         return prefs.active_section == 'INPUT' and inputs.use_ndof
1556
1557     def draw_props(self, context, layout):
1558         prefs = context.preferences
1559         inputs = prefs.inputs
1560
1561         flow = layout.grid_flow(row_major=False, columns=0, even_columns=True, even_rows=False, align=False)
1562
1563         flow.prop(inputs, "ndof_sensitivity", text="Pan Sensitivity")
1564         flow.prop(inputs, "ndof_orbit_sensitivity", text="Orbit Sensitivity")
1565         flow.prop(inputs, "ndof_deadzone", text="Deadzone")
1566
1567         layout.separator()
1568
1569         flow.row().prop(inputs, "ndof_view_navigate_method", expand=True)
1570         flow.row().prop(inputs, "ndof_view_rotate_method", expand=True)
1571
1572
1573 class USERPREF_MT_keyconfigs(Menu):
1574     bl_label = "KeyPresets"
1575     preset_subdir = "keyconfig"
1576     preset_operator = "wm.keyconfig_activate"
1577
1578     def draw(self, context):
1579         Menu.draw_preset(self, context)
1580
1581
1582 class USERPREF_PT_keymap(Panel):
1583     bl_space_type = 'PREFERENCES'
1584     bl_label = "Keymap"
1585     bl_region_type = 'WINDOW'
1586     bl_options = {'HIDE_HEADER'}
1587
1588     @classmethod
1589     def poll(cls, context):
1590         prefs = context.preferences
1591         return (prefs.active_section == 'KEYMAP')
1592
1593     def draw(self, context):
1594         from rna_keymap_ui import draw_keymaps
1595
1596         layout = self.layout
1597
1598         # import time
1599
1600         # start = time.time()
1601
1602         # Keymap Settings
1603         draw_keymaps(context, layout)
1604
1605         # print("runtime", time.time() - start)
1606
1607
1608 class USERPREF_PT_addons(Panel):
1609     bl_space_type = 'PREFERENCES'
1610     bl_label = "Add-ons"
1611     bl_region_type = 'WINDOW'
1612     bl_options = {'HIDE_HEADER'}
1613
1614     _support_icon_mapping = {
1615         'OFFICIAL': 'FILE_BLEND',
1616         'COMMUNITY': 'COMMUNITY',
1617         'TESTING': 'EXPERIMENTAL',
1618     }
1619
1620     @classmethod
1621     def poll(cls, context):
1622         prefs = context.preferences
1623         return (prefs.active_section == 'ADDONS')
1624
1625     @staticmethod
1626     def is_user_addon(mod, user_addon_paths):
1627         import os
1628
1629         if not user_addon_paths:
1630             for path in (
1631                     bpy.utils.script_path_user(),
1632                     bpy.utils.script_path_pref(),
1633             ):
1634                 if path is not None:
1635                     user_addon_paths.append(os.path.join(path, "addons"))
1636
1637         for path in user_addon_paths:
1638             if bpy.path.is_subdir(mod.__file__, path):
1639                 return True
1640         return False
1641
1642     @staticmethod
1643     def draw_error(layout, message):
1644         lines = message.split("\n")
1645         box = layout.box()
1646         sub = box.row()
1647         sub.label(text=lines[0])
1648         sub.label(icon='ERROR')
1649         for l in lines[1:]:
1650             box.label(text=l)
1651
1652     def draw(self, context):
1653         import os
1654         import addon_utils
1655
1656         layout = self.layout
1657
1658         prefs = context.preferences
1659         used_ext = {ext.module for ext in prefs.addons}
1660
1661         addon_user_dirs = tuple(
1662             p for p in (
1663                 os.path.join(prefs.filepaths.script_directory, "addons"),
1664                 bpy.utils.user_resource('SCRIPTS', "addons"),
1665             )
1666             if p
1667         )
1668
1669         # Development option for 2.8x, don't show users bundled addons
1670         # unless they have been updated for 2.8x.
1671         # Developers can turn them on with '--debug'
1672         show_official_27x_addons = bpy.app.debug
1673
1674         # collect the categories that can be filtered on
1675         addons = [
1676             (mod, addon_utils.module_bl_info(mod))
1677             for mod in addon_utils.modules(refresh=False)
1678         ]
1679
1680         split = layout.split(factor=0.6)
1681
1682         row = split.row()
1683         row.prop(context.window_manager, "addon_support", expand=True)
1684
1685         row = split.row(align=True)
1686         row.operator("wm.addon_install", icon='IMPORT', text="Install...")
1687         row.operator("wm.addon_refresh", icon='FILE_REFRESH', text="Refresh")
1688
1689         row = layout.row()
1690         row.prop(context.window_manager, "addon_filter", text="")
1691         row.prop(context.window_manager, "addon_search", text="", icon='VIEWZOOM')
1692
1693         col = layout.column()
1694
1695         # set in addon_utils.modules_refresh()
1696         if addon_utils.error_duplicates:
1697             box = col.box()
1698             row = box.row()
1699             row.label(text="Multiple add-ons with the same name found!")
1700             row.label(icon='ERROR')
1701             box.label(text="Delete one of each pair to resolve:")
1702             for (addon_name, addon_file, addon_path) in addon_utils.error_duplicates:
1703                 box.separator()
1704                 sub_col = box.column(align=True)
1705                 sub_col.label(text=addon_name + ":")
1706                 sub_col.label(text="    " + addon_file)
1707                 sub_col.label(text="    " + addon_path)
1708
1709         if addon_utils.error_encoding:
1710             self.draw_error(
1711                 col,
1712                 "One or more addons do not have UTF-8 encoding\n"
1713                 "(see console for details)",
1714             )
1715
1716         filter = context.window_manager.addon_filter
1717         search = context.window_manager.addon_search.lower()
1718         support = context.window_manager.addon_support
1719
1720         # initialized on demand
1721         user_addon_paths = []
1722
1723         for mod, info in addons:
1724             module_name = mod.__name__
1725
1726             is_enabled = module_name in used_ext
1727
1728             if info["support"] not in support:
1729                 continue
1730
1731             # check if addon should be visible with current filters
1732             if (
1733                     (filter == "All") or
1734                     (filter == info["category"]) or
1735                     (filter == "Enabled" and is_enabled) or
1736                     (filter == "Disabled" and not is_enabled) or
1737                     (filter == "User" and (mod.__file__.startswith(addon_user_dirs)))
1738             ):
1739                 if search and search not in info["name"].lower():
1740                     if info["author"]:
1741                         if search not in info["author"].lower():
1742                             continue
1743                     else:
1744                         continue
1745
1746                 # Skip 2.7x add-ons included with Blender, unless in debug mode.
1747                 is_addon_27x = info.get("blender", (0,)) < (2, 80)
1748                 if (
1749                         is_addon_27x and
1750                         (not show_official_27x_addons) and
1751                         (not mod.__file__.startswith(addon_user_dirs))
1752                 ):
1753                     continue
1754
1755                 # Addon UI Code
1756                 col_box = col.column()
1757                 box = col_box.box()
1758                 colsub = box.column()
1759                 row = colsub.row(align=True)
1760
1761                 row.operator(
1762                     "wm.addon_expand",
1763                     icon='DISCLOSURE_TRI_DOWN' if info["show_expanded"] else 'DISCLOSURE_TRI_RIGHT',
1764                     emboss=False,
1765                 ).module = module_name
1766
1767                 row.operator(
1768                     "wm.addon_disable" if is_enabled else "wm.addon_enable",
1769                     icon='CHECKBOX_HLT' if is_enabled else 'CHECKBOX_DEHLT', text="",
1770                     emboss=False,
1771                 ).module = module_name
1772
1773                 sub = row.row()
1774                 sub.active = is_enabled
1775                 sub.label(text="%s: %s" % (info["category"], info["name"]))
1776
1777                 # WARNING: 2.8x exception, may be removed
1778                 # use disabled state for old add-ons, chances are they are broken.
1779                 if is_addon_27x:
1780                     sub.label(text="upgrade to 2.8x required")
1781                     sub.label(icon='ERROR')
1782                 # Remove code above after 2.8x migration is complete.
1783                 elif info["warning"]:
1784                     sub.label(icon='ERROR')
1785
1786                 # icon showing support level.
1787                 sub.label(icon=self._support_icon_mapping.get(info["support"], 'QUESTION'))
1788
1789                 # Expanded UI (only if additional info is available)
1790                 if info["show_expanded"]:
1791                     if info["description"]:
1792                         split = colsub.row().split(factor=0.15)
1793                         split.label(text="Description:")
1794                         split.label(text=info["description"])
1795                     if info["location"]:
1796                         split = colsub.row().split(factor=0.15)
1797                         split.label(text="Location:")
1798                         split.label(text=info["location"])
1799                     if mod:
1800                         split = colsub.row().split(factor=0.15)
1801                         split.label(text="File:")
1802                         split.label(text=mod.__file__, translate=False)
1803                     if info["author"]:
1804                         split = colsub.row().split(factor=0.15)
1805                         split.label(text="Author:")
1806                         split.label(text=info["author"], translate=False)
1807                     if info["version"]:
1808                         split = colsub.row().split(factor=0.15)
1809                         split.label(text="Version:")
1810                         split.label(text=".".join(str(x) for x in info["version"]), translate=False)
1811                     if info["warning"]:
1812                         split = colsub.row().split(factor=0.15)
1813                         split.label(text="Warning:")
1814                         split.label(text="  " + info["warning"], icon='ERROR')
1815
1816                     user_addon = USERPREF_PT_addons.is_user_addon(mod, user_addon_paths)
1817                     tot_row = bool(info["wiki_url"]) + bool(user_addon)
1818
1819                     if tot_row:
1820                         split = colsub.row().split(factor=0.15)
1821                         split.label(text="Internet:")
1822                         sub = split.row()
1823                         if info["wiki_url"]:
1824                             sub.operator(
1825                                 "wm.url_open", text="Documentation", icon='HELP',
1826                             ).url = info["wiki_url"]
1827                         # Only add "Report a Bug" button if tracker_url is set
1828                         # or the add-on is bundled (use official tracker then).
1829                         if info.get("tracker_url") or not user_addon:
1830                             sub.operator(
1831                                 "wm.url_open", text="Report a Bug", icon='URL',
1832                             ).url = info.get(
1833                                 "tracker_url",
1834                                 "https://developer.blender.org/maniphest/task/edit/form/2",
1835                             )
1836                         if user_addon:
1837                             sub.operator(
1838                                 "wm.addon_remove", text="Remove", icon='CANCEL',
1839                             ).module = mod.__name__
1840
1841                     # Show addon user preferences
1842                     if is_enabled:
1843                         addon_preferences = prefs.addons[module_name].preferences
1844                         if addon_preferences is not None:
1845                             draw = getattr(addon_preferences, "draw", None)
1846                             if draw is not None:
1847                                 addon_preferences_class = type(addon_preferences)
1848                                 box_prefs = col_box.box()
1849                                 box_prefs.label(text="Preferences:")
1850                                 addon_preferences_class.layout = box_prefs
1851                                 try:
1852                                     draw(context)
1853                                 except:
1854                                     import traceback
1855                                     traceback.print_exc()
1856                                     box_prefs.label(text="Error (see console)", icon='ERROR')
1857                                 del addon_preferences_class.layout
1858
1859         # Append missing scripts
1860         # First collect scripts that are used but have no script file.
1861         module_names = {mod.__name__ for mod, info in addons}
1862         missing_modules = {ext for ext in used_ext if ext not in module_names}
1863
1864         if missing_modules and filter in {"All", "Enabled"}:
1865             col.column().separator()
1866             col.column().label(text="Missing script files")
1867
1868             module_names = {mod.__name__ for mod, info in addons}
1869             for module_name in sorted(missing_modules):
1870                 is_enabled = module_name in used_ext
1871                 # Addon UI Code
1872                 box = col.column().box()
1873                 colsub = box.column()
1874                 row = colsub.row(align=True)
1875
1876                 row.label(text="", icon='ERROR')
1877
1878                 if is_enabled:
1879                     row.operator(
1880                         "wm.addon_disable", icon='CHECKBOX_HLT', text="", emboss=False,
1881                     ).module = module_name
1882
1883                 row.label(text=module_name, translate=False)
1884
1885
1886 class StudioLightPanelMixin():
1887     bl_space_type = 'PREFERENCES'
1888     bl_region_type = 'WINDOW'
1889
1890     @classmethod
1891     def poll(cls, context):
1892         prefs = context.preferences
1893         return (prefs.active_section == 'LIGHTS')
1894
1895     def _get_lights(self, prefs):
1896         return [light for light in prefs.studio_lights if light.is_user_defined and light.type == self.sl_type]
1897
1898     def draw(self, context):
1899         layout = self.layout
1900         prefs = context.preferences
1901         lights = self._get_lights(prefs)
1902
1903         self.draw_light_list(layout, lights)
1904
1905     def draw_light_list(self, layout, lights):
1906         if lights:
1907             flow = layout.grid_flow(row_major=False, columns=4, even_columns=True, even_rows=True, align=False)
1908             for studio_light in lights:
1909                 self.draw_studio_light(flow, studio_light)
1910         else:
1911             layout.label(text="No custom {} configured".format(self.bl_label))
1912
1913     def draw_studio_light(self, layout, studio_light):
1914         box = layout.box()
1915         row = box.row()
1916
1917         row.template_icon(layout.icon(studio_light), scale=3.0)
1918         col = row.column()
1919         op = col.operator("wm.studiolight_uninstall", text="", icon='REMOVE')
1920         op.index = studio_light.index
1921
1922         if studio_light.type == 'STUDIO':
1923             op = col.operator("wm.studiolight_copy_settings", text="", icon='IMPORT')
1924             op.index = studio_light.index
1925
1926         box.label(text=studio_light.name)
1927
1928
1929 class USERPREF_PT_studiolight_matcaps(Panel, StudioLightPanelMixin):
1930     bl_label = "MatCaps"
1931     sl_type = 'MATCAP'
1932
1933     def draw_header_preset(self, context):
1934         layout = self.layout
1935         layout.operator("wm.studiolight_install", icon='IMPORT', text="Install...").type = 'MATCAP'
1936         layout.separator()
1937
1938
1939 class USERPREF_PT_studiolight_world(Panel, StudioLightPanelMixin):
1940     bl_label = "LookDev HDRIs"
1941     sl_type = 'WORLD'
1942
1943     def draw_header_preset(self, context):
1944         layout = self.layout
1945         layout.operator("wm.studiolight_install", icon='IMPORT', text="Install...").type = 'WORLD'
1946         layout.separator()
1947
1948
1949 class USERPREF_PT_studiolight_lights(Panel, StudioLightPanelMixin):
1950     bl_label = "Studio Lights"
1951     sl_type = 'STUDIO'
1952
1953     def draw_header_preset(self, context):
1954         layout = self.layout
1955         op = layout.operator("wm.studiolight_install", icon='IMPORT', text="Install...")
1956         op.type = 'STUDIO'
1957         op.filter_glob = ".sl"
1958         layout.separator()
1959
1960
1961 class USERPREF_PT_studiolight_light_editor(Panel):
1962     bl_label = "Editor"
1963     bl_parent_id = "USERPREF_PT_studiolight_lights"
1964     bl_space_type = 'PREFERENCES'
1965     bl_region_type = 'WINDOW'
1966     bl_options = {'DEFAULT_CLOSED'}
1967
1968     def opengl_light_buttons(self, layout, light):
1969
1970         col = layout.column()
1971         col.active = light.use
1972
1973         col.prop(light, "use", text="Use Light")
1974         col.prop(light, "diffuse_color", text="Diffuse")
1975         col.prop(light, "specular_color", text="Specular")
1976         col.prop(light, "smooth")
1977         col.prop(light, "direction")
1978
1979     def draw(self, context):
1980         layout = self.layout
1981
1982         prefs = context.preferences
1983         system = prefs.system
1984
1985         row = layout.row()
1986         row.prop(system, "edit_studio_light", toggle=True)
1987         row.operator("wm.studiolight_new", text="Save as Studio light", icon='FILE_TICK')
1988
1989         layout.separator()
1990
1991         layout.use_property_split = True
1992         column = layout.split()
1993         column.active = system.edit_studio_light
1994
1995         light = system.solid_lights[0]
1996         colsplit = column.split(factor=0.85)
1997         self.opengl_light_buttons(colsplit, light)
1998
1999         light = system.solid_lights[1]
2000         colsplit = column.split(factor=0.85)
2001         self.opengl_light_buttons(colsplit, light)
2002
2003         light = system.solid_lights[2]
2004         colsplit = column.split(factor=0.85)
2005         self.opengl_light_buttons(colsplit, light)
2006
2007         light = system.solid_lights[3]
2008         self.opengl_light_buttons(column, light)
2009
2010         layout.separator()
2011
2012         layout.prop(system, "light_ambient")
2013
2014
2015 ThemeGenericClassGenerator.generate_panel_classes_for_wcols()
2016
2017 # Order of registration defines order in UI, so dynamically generated classes are 'injected' in the intended order.
2018 classes = (USERPREF_PT_theme_user_interface,) + tuple(ThemeGenericClassGenerator.generated_classes)
2019
2020 classes += (
2021     USERPREF_HT_header,
2022     USERPREF_PT_navigation_bar,
2023     USERPREF_PT_save_preferences,
2024
2025     USERPREF_PT_interface_display,
2026     USERPREF_PT_interface_editors,
2027     USERPREF_PT_interface_translation,
2028     USERPREF_PT_interface_text,
2029     USERPREF_PT_interface_menus,
2030     USERPREF_PT_interface_menus_mouse_over,
2031     USERPREF_PT_interface_menus_pie,
2032
2033     USERPREF_PT_viewport_display,
2034     USERPREF_PT_viewport_quality,
2035     USERPREF_PT_viewport_textures,
2036     USERPREF_PT_viewport_selection,
2037
2038     USERPREF_PT_edit_objects,
2039     USERPREF_PT_edit_objects_new,
2040     USERPREF_PT_edit_objects_duplicate_data,
2041     USERPREF_PT_edit_cursor,
2042     USERPREF_PT_edit_annotations,
2043     USERPREF_PT_edit_weight_paint,
2044     USERPREF_PT_edit_gpencil,
2045     USERPREF_PT_edit_misc,
2046
2047     USERPREF_PT_animation_timeline,
2048     USERPREF_PT_animation_keyframes,
2049     USERPREF_PT_animation_autokey,
2050     USERPREF_PT_animation_fcurves,
2051
2052     USERPREF_PT_system_cycles_devices,
2053     USERPREF_PT_system_memory,
2054     USERPREF_PT_system_sound,
2055
2056     USERPREF_MT_interface_theme_presets,
2057     USERPREF_PT_theme,
2058     USERPREF_PT_theme_interface_state,
2059     USERPREF_PT_theme_interface_styles,
2060     USERPREF_PT_theme_interface_gizmos,
2061     USERPREF_PT_theme_interface_icons,
2062     USERPREF_PT_theme_text_style,
2063     USERPREF_PT_theme_bone_color_sets,
2064
2065     USERPREF_PT_file_paths_data,
2066     USERPREF_PT_file_paths_render,
2067     USERPREF_PT_file_paths_applications,
2068     USERPREF_PT_file_paths_development,
2069
2070     USERPREF_PT_saveload_blend,
2071     USERPREF_PT_saveload_blend_autosave,
2072     USERPREF_PT_saveload_autorun,
2073     USERPREF_PT_saveload_file_browser,
2074
2075     USERPREF_MT_ndof_settings,
2076     USERPREF_MT_keyconfigs,
2077
2078     USERPREF_PT_input_keyboard,
2079     USERPREF_PT_input_mouse,
2080     USERPREF_PT_input_tablet,
2081     USERPREF_PT_input_ndof,
2082     USERPREF_PT_navigation_orbit,
2083     USERPREF_PT_navigation_zoom,
2084     USERPREF_PT_navigation_fly_walk,
2085     USERPREF_PT_navigation_fly_walk_navigation,
2086     USERPREF_PT_navigation_fly_walk_gravity,
2087
2088     USERPREF_PT_keymap,
2089     USERPREF_PT_addons,
2090
2091     USERPREF_PT_studiolight_lights,
2092     USERPREF_PT_studiolight_light_editor,
2093     USERPREF_PT_studiolight_matcaps,
2094     USERPREF_PT_studiolight_world,
2095 )
2096
2097 # Add dynamically generated editor theme panels last, so they show up last in the theme section.
2098 ThemeGenericClassGenerator.generated_classes.clear()
2099 ThemeGenericClassGenerator.generate_panel_classes_from_theme_areas()
2100 classes += tuple(ThemeGenericClassGenerator.generated_classes)
2101
2102 if __name__ == "__main__":  # only for live edit.
2103     from bpy.utils import register_class
2104     for cls in classes:
2105         register_class(cls)