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