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