OpenGLContext 3.0.0a1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (417) hide show
  1. OpenGLContext/__init__.py +151 -0
  2. OpenGLContext/arrays.py +2 -0
  3. OpenGLContext/atlas.py +300 -0
  4. OpenGLContext/audio/__init__.py +32 -0
  5. OpenGLContext/audio/scene.py +130 -0
  6. OpenGLContext/audio/settings.py +82 -0
  7. OpenGLContext/bin/__init__.py +0 -0
  8. OpenGLContext/bin/choosecontext.py +224 -0
  9. OpenGLContext/bin/choosefonts.py +209 -0
  10. OpenGLContext/bin/gltest.py +121 -0
  11. OpenGLContext/bin/gltf_demo.py +492 -0
  12. OpenGLContext/bin/gltf_regression.py +645 -0
  13. OpenGLContext/bin/gltf_view.py +30 -0
  14. OpenGLContext/bin/keyboardevents.py +93 -0
  15. OpenGLContext/bin/lorentz.py +109 -0
  16. OpenGLContext/bin/physics_cook.py +81 -0
  17. OpenGLContext/bin/profile_view.py +38 -0
  18. OpenGLContext/bin/terrain_view.py +581 -0
  19. OpenGLContext/bin/tiles_view.py +39 -0
  20. OpenGLContext/bin/ui_demo.py +228 -0
  21. OpenGLContext/bin/view.py +341 -0
  22. OpenGLContext/bin/vrml_view.py +42 -0
  23. OpenGLContext/capture.py +125 -0
  24. OpenGLContext/context.py +1253 -0
  25. OpenGLContext/contextconfig.py +177 -0
  26. OpenGLContext/contextdefinition.py +275 -0
  27. OpenGLContext/debug/__init__.py +6 -0
  28. OpenGLContext/debug/bufferimage.py +80 -0
  29. OpenGLContext/debug/leaks.py +13 -0
  30. OpenGLContext/debug/logcontext.py +9 -0
  31. OpenGLContext/debug/logs.py +15 -0
  32. OpenGLContext/debug/state.py +329 -0
  33. OpenGLContext/displaylist.py +69 -0
  34. OpenGLContext/doinchildmatrix.py +34 -0
  35. OpenGLContext/drawcube.py +58 -0
  36. OpenGLContext/events/__init__.py +10 -0
  37. OpenGLContext/events/default_config.py +29 -0
  38. OpenGLContext/events/event.py +65 -0
  39. OpenGLContext/events/eventhandlermixin.py +214 -0
  40. OpenGLContext/events/eventmanager.py +269 -0
  41. OpenGLContext/events/fxevents.py +90 -0
  42. OpenGLContext/events/glfwevents.py +424 -0
  43. OpenGLContext/events/glutevents.py +184 -0
  44. OpenGLContext/events/inputstate.py +129 -0
  45. OpenGLContext/events/internaltime.py +161 -0
  46. OpenGLContext/events/keyboardevents.py +153 -0
  47. OpenGLContext/events/mouseevents.py +473 -0
  48. OpenGLContext/events/pygameevents.py +162 -0
  49. OpenGLContext/events/systemtime.py +12 -0
  50. OpenGLContext/events/timeeventgeneratormanager.py +40 -0
  51. OpenGLContext/events/timeevents.py +143 -0
  52. OpenGLContext/events/timer.py +170 -0
  53. OpenGLContext/events/tkevents.py +172 -0
  54. OpenGLContext/events/wxevents.py +185 -0
  55. OpenGLContext/extensionmanager.py +158 -0
  56. OpenGLContext/framecounter.py +74 -0
  57. OpenGLContext/frustum.py +135 -0
  58. OpenGLContext/glfwcontext.py +395 -0
  59. OpenGLContext/glfwinteractivecontext.py +34 -0
  60. OpenGLContext/glfwvrmlcontext.py +11 -0
  61. OpenGLContext/glutcontext.py +224 -0
  62. OpenGLContext/glutinteractivecontext.py +24 -0
  63. OpenGLContext/gluttestingcontext.py +11 -0
  64. OpenGLContext/glutvrmlcontext.py +25 -0
  65. OpenGLContext/glutvrmltestingcontext.py +48 -0
  66. OpenGLContext/hud.py +404 -0
  67. OpenGLContext/interactivecontext.py +29 -0
  68. OpenGLContext/loaders/__init__.py +1 -0
  69. OpenGLContext/loaders/base.py +84 -0
  70. OpenGLContext/loaders/cc0.py +137 -0
  71. OpenGLContext/loaders/gltf/__init__.py +71 -0
  72. OpenGLContext/loaders/gltf/accessors.py +255 -0
  73. OpenGLContext/loaders/gltf/animation.py +611 -0
  74. OpenGLContext/loaders/gltf/draco.py +166 -0
  75. OpenGLContext/loaders/gltf/environment_sky.py +416 -0
  76. OpenGLContext/loaders/gltf/loader.py +80 -0
  77. OpenGLContext/loaders/gltf/materials.py +298 -0
  78. OpenGLContext/loaders/gltf/meshes.py +292 -0
  79. OpenGLContext/loaders/gltf/samples.py +162 -0
  80. OpenGLContext/loaders/gltf/scene.py +673 -0
  81. OpenGLContext/loaders/gltf/specular_glossiness.py +154 -0
  82. OpenGLContext/loaders/gltf/textures.py +155 -0
  83. OpenGLContext/loaders/gltf/transforms.py +261 -0
  84. OpenGLContext/loaders/gltf_demos.py +511 -0
  85. OpenGLContext/loaders/hdr.py +228 -0
  86. OpenGLContext/loaders/hdri.py +83 -0
  87. OpenGLContext/loaders/loader.py +255 -0
  88. OpenGLContext/loaders/obj.py +299 -0
  89. OpenGLContext/loaders/resolver.py +536 -0
  90. OpenGLContext/loaders/tiles3d/__init__.py +9 -0
  91. OpenGLContext/loaders/tiles3d/boundingvolume.py +120 -0
  92. OpenGLContext/loaders/tiles3d/dem.py +71 -0
  93. OpenGLContext/loaders/tiles3d/fetch.py +102 -0
  94. OpenGLContext/loaders/tiles3d/foliage.py +786 -0
  95. OpenGLContext/loaders/tiles3d/frustum.py +70 -0
  96. OpenGLContext/loaders/tiles3d/geomorph.py +57 -0
  97. OpenGLContext/loaders/tiles3d/gltf_uploader.py +246 -0
  98. OpenGLContext/loaders/tiles3d/loadmanager.py +121 -0
  99. OpenGLContext/loaders/tiles3d/physics_colliders.py +78 -0
  100. OpenGLContext/loaders/tiles3d/procedural.py +355 -0
  101. OpenGLContext/loaders/tiles3d/residency.py +102 -0
  102. OpenGLContext/loaders/tiles3d/runtime.py +168 -0
  103. OpenGLContext/loaders/tiles3d/sample.py +212 -0
  104. OpenGLContext/loaders/tiles3d/scatter.py +77 -0
  105. OpenGLContext/loaders/tiles3d/screenspaceerror.py +31 -0
  106. OpenGLContext/loaders/tiles3d/tileset.py +390 -0
  107. OpenGLContext/loaders/tiles3d/traversal.py +108 -0
  108. OpenGLContext/loaders/tiles3d/vegetation.py +361 -0
  109. OpenGLContext/loaders/vrml97.py +133 -0
  110. OpenGLContext/looptrace.py +307 -0
  111. OpenGLContext/move/__init__.py +0 -0
  112. OpenGLContext/move/bindingstore.py +232 -0
  113. OpenGLContext/move/direct.py +156 -0
  114. OpenGLContext/move/dragwatcher.py +42 -0
  115. OpenGLContext/move/examinemanager.py +100 -0
  116. OpenGLContext/move/followcam.py +121 -0
  117. OpenGLContext/move/modes.py +522 -0
  118. OpenGLContext/move/movementmanager.py +74 -0
  119. OpenGLContext/move/navigation.py +137 -0
  120. OpenGLContext/move/physicsplatform.py +203 -0
  121. OpenGLContext/move/physicswalk.py +469 -0
  122. OpenGLContext/move/smooth.py +68 -0
  123. OpenGLContext/move/terrainwalk.py +451 -0
  124. OpenGLContext/move/trackball.py +92 -0
  125. OpenGLContext/move/viewplatform.py +283 -0
  126. OpenGLContext/move/viewplatformmixin.py +335 -0
  127. OpenGLContext/nav/__init__.py +13 -0
  128. OpenGLContext/nav/navmesh.py +476 -0
  129. OpenGLContext/passes/__init__.py +0 -0
  130. OpenGLContext/passes/_flat.py +1317 -0
  131. OpenGLContext/passes/asyncpick.py +216 -0
  132. OpenGLContext/passes/bloom.py +257 -0
  133. OpenGLContext/passes/flatcompat.py +246 -0
  134. OpenGLContext/passes/flatcore.py +198 -0
  135. OpenGLContext/passes/flateffects.py +372 -0
  136. OpenGLContext/passes/ibl.py +711 -0
  137. OpenGLContext/passes/instancing.py +775 -0
  138. OpenGLContext/passes/pbrpass.py +829 -0
  139. OpenGLContext/passes/renderpass.py +79 -0
  140. OpenGLContext/passes/renderstats.py +55 -0
  141. OpenGLContext/passes/rendervisitor.py +60 -0
  142. OpenGLContext/passes/selection.py +450 -0
  143. OpenGLContext/passes/selectionbuffers.py +442 -0
  144. OpenGLContext/passes/shaderpass.py +1018 -0
  145. OpenGLContext/passes/shaderpass_shadow.py +197 -0
  146. OpenGLContext/passes/shadersource.py +121 -0
  147. OpenGLContext/passes/shadowcaps.py +182 -0
  148. OpenGLContext/passes/shadowmap.py +330 -0
  149. OpenGLContext/passes/shadowmath.py +290 -0
  150. OpenGLContext/passes/shadowmixin.py +906 -0
  151. OpenGLContext/passes/shadowpool.py +186 -0
  152. OpenGLContext/passes/transmission.py +124 -0
  153. OpenGLContext/physics/__init__.py +16 -0
  154. OpenGLContext/physics/debugdraw.py +370 -0
  155. OpenGLContext/physics/demo.py +157 -0
  156. OpenGLContext/physics/gltf_world.py +185 -0
  157. OpenGLContext/physics/manager.py +84 -0
  158. OpenGLContext/physics/threaded.py +93 -0
  159. OpenGLContext/plugins.py +61 -0
  160. OpenGLContext/pygamecontext.py +178 -0
  161. OpenGLContext/pygameinteractivecontext.py +36 -0
  162. OpenGLContext/pygametestingcontext.py +10 -0
  163. OpenGLContext/pygamevrmlcontext.py +13 -0
  164. OpenGLContext/quaternion.py +190 -0
  165. OpenGLContext/renderoptions.py +280 -0
  166. OpenGLContext/resources/__init__.py +36 -0
  167. OpenGLContext/resources/available_contexts.txt +4 -0
  168. OpenGLContext/resources/available_contexts_txt.py +9 -0
  169. OpenGLContext/resources/context_icon_png.py +46 -0
  170. OpenGLContext/resources/context_icon_small_png.py +21 -0
  171. OpenGLContext/resources/disk_icon_hi_png.py +14 -0
  172. OpenGLContext/resources/disk_icon_png.py +15 -0
  173. OpenGLContext/resources/environment/pimbackground_BK.jpg +0 -0
  174. OpenGLContext/resources/environment/pimbackground_DN.jpg +0 -0
  175. OpenGLContext/resources/environment/pimbackground_FR.jpg +0 -0
  176. OpenGLContext/resources/environment/pimbackground_LF.jpg +0 -0
  177. OpenGLContext/resources/environment/pimbackground_RT.jpg +0 -0
  178. OpenGLContext/resources/environment/pimbackground_UP.jpg +0 -0
  179. OpenGLContext/resources/environment/studio_BK.jpg +0 -0
  180. OpenGLContext/resources/environment/studio_DN.jpg +0 -0
  181. OpenGLContext/resources/environment/studio_FR.jpg +0 -0
  182. OpenGLContext/resources/environment/studio_LF.jpg +0 -0
  183. OpenGLContext/resources/environment/studio_RT.jpg +0 -0
  184. OpenGLContext/resources/environment/studio_UP.jpg +0 -0
  185. OpenGLContext/resources/environment/studiobright_BK.jpg +0 -0
  186. OpenGLContext/resources/environment/studiobright_DN.jpg +0 -0
  187. OpenGLContext/resources/environment/studiobright_FR.jpg +0 -0
  188. OpenGLContext/resources/environment/studiobright_LF.jpg +0 -0
  189. OpenGLContext/resources/environment/studiobright_RT.jpg +0 -0
  190. OpenGLContext/resources/environment/studiobright_UP.jpg +0 -0
  191. OpenGLContext/resources/green_arrow_right_png.py +47 -0
  192. OpenGLContext/resources/legacy_lighting.vert.txt +37 -0
  193. OpenGLContext/resources/legacy_lighting_vert_txt.py +18 -0
  194. OpenGLContext/resources/lights.vert.txt +107 -0
  195. OpenGLContext/resources/lights_vert_txt.py +23 -0
  196. OpenGLContext/resources/phongprecalc_vert.py +30 -0
  197. OpenGLContext/resources/phongweights_frag.py +47 -0
  198. OpenGLContext/resources/pygame_icon_png.py +47 -0
  199. OpenGLContext/resources/red_arrow_right_png.py +50 -0
  200. OpenGLContext/resources/simpleshader.frag.txt +3 -0
  201. OpenGLContext/resources/simpleshader.vert.txt +3 -0
  202. OpenGLContext/resources/simpleshader_frag_txt.py +9 -0
  203. OpenGLContext/resources/simpleshader_vert_txt.py +10 -0
  204. OpenGLContext/scenegraph/__init__.py +19 -0
  205. OpenGLContext/scenegraph/appearance.py +107 -0
  206. OpenGLContext/scenegraph/arraygeometry.py +238 -0
  207. OpenGLContext/scenegraph/audio.py +632 -0
  208. OpenGLContext/scenegraph/background.py +108 -0
  209. OpenGLContext/scenegraph/basenodes.py +50 -0
  210. OpenGLContext/scenegraph/billboard.py +14 -0
  211. OpenGLContext/scenegraph/boundingvolume.py +494 -0
  212. OpenGLContext/scenegraph/box.py +180 -0
  213. OpenGLContext/scenegraph/collision.py +14 -0
  214. OpenGLContext/scenegraph/coordinate.py +20 -0
  215. OpenGLContext/scenegraph/coordinatebounded.py +18 -0
  216. OpenGLContext/scenegraph/cubebackground.py +207 -0
  217. OpenGLContext/scenegraph/extrusions.py +275 -0
  218. OpenGLContext/scenegraph/fog.py +123 -0
  219. OpenGLContext/scenegraph/gear.py +323 -0
  220. OpenGLContext/scenegraph/group.py +20 -0
  221. OpenGLContext/scenegraph/grouping.py +140 -0
  222. OpenGLContext/scenegraph/hdrbackground.py +384 -0
  223. OpenGLContext/scenegraph/ifscompiler.py +633 -0
  224. OpenGLContext/scenegraph/imagetexture.py +365 -0
  225. OpenGLContext/scenegraph/indexedfaceset.py +238 -0
  226. OpenGLContext/scenegraph/indexedlineset.py +418 -0
  227. OpenGLContext/scenegraph/indexedpolygons.py +422 -0
  228. OpenGLContext/scenegraph/inline.py +79 -0
  229. OpenGLContext/scenegraph/instancedgl.py +187 -0
  230. OpenGLContext/scenegraph/interpolators.py +122 -0
  231. OpenGLContext/scenegraph/light.py +213 -0
  232. OpenGLContext/scenegraph/lod.py +22 -0
  233. OpenGLContext/scenegraph/material.py +96 -0
  234. OpenGLContext/scenegraph/material_fields.py +50 -0
  235. OpenGLContext/scenegraph/mouseover.py +43 -0
  236. OpenGLContext/scenegraph/nodepath.py +52 -0
  237. OpenGLContext/scenegraph/nurbs.py +637 -0
  238. OpenGLContext/scenegraph/nurbssampling.py +101 -0
  239. OpenGLContext/scenegraph/nurbstess.py +316 -0
  240. OpenGLContext/scenegraph/nurbstrim.py +54 -0
  241. OpenGLContext/scenegraph/particles.py +786 -0
  242. OpenGLContext/scenegraph/pbrmaterial.py +229 -0
  243. OpenGLContext/scenegraph/pbrmesh.py +558 -0
  244. OpenGLContext/scenegraph/physicsbody.py +72 -0
  245. OpenGLContext/scenegraph/pointset.py +257 -0
  246. OpenGLContext/scenegraph/polygon.py +166 -0
  247. OpenGLContext/scenegraph/polygonsort.py +119 -0
  248. OpenGLContext/scenegraph/polygontessellator.py +213 -0
  249. OpenGLContext/scenegraph/quadrics.py +494 -0
  250. OpenGLContext/scenegraph/scenegraph.py +21 -0
  251. OpenGLContext/scenegraph/shadergeometry.py +403 -0
  252. OpenGLContext/scenegraph/shaders.py +812 -0
  253. OpenGLContext/scenegraph/shape.py +243 -0
  254. OpenGLContext/scenegraph/simplebackground.py +34 -0
  255. OpenGLContext/scenegraph/spherebackground.py +462 -0
  256. OpenGLContext/scenegraph/switch.py +75 -0
  257. OpenGLContext/scenegraph/teapot.py +439 -0
  258. OpenGLContext/scenegraph/teapot_nurbs.py +291 -0
  259. OpenGLContext/scenegraph/teapot_nurbs_data.py +384 -0
  260. OpenGLContext/scenegraph/terrain/__init__.py +12 -0
  261. OpenGLContext/scenegraph/terrain/heightfield.py +172 -0
  262. OpenGLContext/scenegraph/terrain/splat.py +224 -0
  263. OpenGLContext/scenegraph/tessellationlod.py +97 -0
  264. OpenGLContext/scenegraph/text/__init__.py +1 -0
  265. OpenGLContext/scenegraph/text/_toolsfont.py +200 -0
  266. OpenGLContext/scenegraph/text/font.py +452 -0
  267. OpenGLContext/scenegraph/text/fontprovider.py +193 -0
  268. OpenGLContext/scenegraph/text/fonts/__init__.py +75 -0
  269. OpenGLContext/scenegraph/text/fonts/font_atlas_10.py +133 -0
  270. OpenGLContext/scenegraph/text/fonts/font_atlas_12.py +158 -0
  271. OpenGLContext/scenegraph/text/fonts/font_atlas_14.py +176 -0
  272. OpenGLContext/scenegraph/text/fonts/font_atlas_16.py +204 -0
  273. OpenGLContext/scenegraph/text/fonts/font_atlas_18.py +222 -0
  274. OpenGLContext/scenegraph/text/fonts/font_atlas_20.py +244 -0
  275. OpenGLContext/scenegraph/text/fonts/font_atlas_24.py +294 -0
  276. OpenGLContext/scenegraph/text/fonts/font_atlas_28.py +339 -0
  277. OpenGLContext/scenegraph/text/fonts/font_atlas_32.py +390 -0
  278. OpenGLContext/scenegraph/text/fontstyle3d.py +62 -0
  279. OpenGLContext/scenegraph/text/glutfont.py +186 -0
  280. OpenGLContext/scenegraph/text/pygamefont.py +213 -0
  281. OpenGLContext/scenegraph/text/shaderfont.py +340 -0
  282. OpenGLContext/scenegraph/text/shadertext.py +433 -0
  283. OpenGLContext/scenegraph/text/text.py +75 -0
  284. OpenGLContext/scenegraph/text/toolsfont.py +819 -0
  285. OpenGLContext/scenegraph/text/ttfregistry.py +135 -0
  286. OpenGLContext/scenegraph/text/wglfont.py +228 -0
  287. OpenGLContext/scenegraph/text/wglfontprovider.py +37 -0
  288. OpenGLContext/scenegraph/text/wxfont.py +314 -0
  289. OpenGLContext/scenegraph/texturetransform.py +66 -0
  290. OpenGLContext/scenegraph/tilesterrain.py +95 -0
  291. OpenGLContext/scenegraph/timesensor.py +51 -0
  292. OpenGLContext/scenegraph/transform.py +180 -0
  293. OpenGLContext/scenegraph/vegetation/__init__.py +20 -0
  294. OpenGLContext/scenegraph/vegetation/base.py +96 -0
  295. OpenGLContext/scenegraph/vegetation/billboards.py +145 -0
  296. OpenGLContext/scenegraph/vegetation/clumps.py +260 -0
  297. OpenGLContext/scenegraph/vegetation/grid.py +112 -0
  298. OpenGLContext/scenegraph/vegetation/nearmesh.py +185 -0
  299. OpenGLContext/scenegraph/vertex.py +54 -0
  300. OpenGLContext/scenegraph/viewpoint.py +42 -0
  301. OpenGLContext/scenegraph/winding.py +49 -0
  302. OpenGLContext/shaders/_brdf_inc.glsl +124 -0
  303. OpenGLContext/shaders/_common_inc.glsl +24 -0
  304. OpenGLContext/shaders/_cubemap_inc.glsl +26 -0
  305. OpenGLContext/shaders/_lights_inc.glsl +32 -0
  306. OpenGLContext/shaders/_objectid_inc.glsl +14 -0
  307. OpenGLContext/shaders/_shadow_inc.glsl +254 -0
  308. OpenGLContext/shaders/_vrml97_lighting_inc.glsl +78 -0
  309. OpenGLContext/shaders/hdr_background.frag +32 -0
  310. OpenGLContext/shaders/hdr_background.vert +15 -0
  311. OpenGLContext/shaders/ibl_brdf.frag +78 -0
  312. OpenGLContext/shaders/ibl_env.frag +53 -0
  313. OpenGLContext/shaders/ibl_equirect.frag +19 -0
  314. OpenGLContext/shaders/ibl_fullscreen.vert +11 -0
  315. OpenGLContext/shaders/ibl_irradiance.frag +40 -0
  316. OpenGLContext/shaders/ibl_prefilter.frag +56 -0
  317. OpenGLContext/shaders/particle.frag +42 -0
  318. OpenGLContext/shaders/particle.vert +52 -0
  319. OpenGLContext/shaders/pbr.frag +906 -0
  320. OpenGLContext/shaders/pbr.vert +70 -0
  321. OpenGLContext/shaders/shadow_depth.frag +4 -0
  322. OpenGLContext/shaders/shadow_depth.vert +18 -0
  323. OpenGLContext/shaders/terrain_splat.frag +80 -0
  324. OpenGLContext/shaders/terrain_splat.vert +16 -0
  325. OpenGLContext/shaders/veg_billboard.frag +35 -0
  326. OpenGLContext/shaders/veg_billboard.vert +17 -0
  327. OpenGLContext/shaders/veg_clump.frag +27 -0
  328. OpenGLContext/shaders/veg_mesh.frag +26 -0
  329. OpenGLContext/shaders/veg_mesh.vert +17 -0
  330. OpenGLContext/shaders/vrml97_background.frag +17 -0
  331. OpenGLContext/shaders/vrml97_background.vert +24 -0
  332. OpenGLContext/shaders/vrml97_lighting.frag +111 -0
  333. OpenGLContext/shaders/vrml97_lighting.vert +50 -0
  334. OpenGLContext/shaders/vrml97_line.frag +23 -0
  335. OpenGLContext/shaders/vrml97_line.vert +24 -0
  336. OpenGLContext/shaders/vrml97_point.frag +36 -0
  337. OpenGLContext/shaders/vrml97_point.vert +21 -0
  338. OpenGLContext/shaders/vrml97_unlit.frag +59 -0
  339. OpenGLContext/shaders/vrml97_unlit.vert +18 -0
  340. OpenGLContext/shaders/vrml97_vertex_color.frag +85 -0
  341. OpenGLContext/shaders/vrml97_vertex_color.vert +34 -0
  342. OpenGLContext/stalltrace.py +637 -0
  343. OpenGLContext/testing/__init__.py +67 -0
  344. OpenGLContext/testing/display.py +30 -0
  345. OpenGLContext/testing/event_injector.py +564 -0
  346. OpenGLContext/testing/framebuffer_comparison.py +842 -0
  347. OpenGLContext/testing/gl_env.py +39 -0
  348. OpenGLContext/testing/paths.py +23 -0
  349. OpenGLContext/testing/process_exit.py +30 -0
  350. OpenGLContext/testing/report_generator.py +594 -0
  351. OpenGLContext/testing/subprocess_runner.py +411 -0
  352. OpenGLContext/testingcontext.py +36 -0
  353. OpenGLContext/tests/test_atlas.py +62 -0
  354. OpenGLContext/tests/test_configs.py +27 -0
  355. OpenGLContext/tests/test_polygonsort.py +45 -0
  356. OpenGLContext/tests/test_utilities.py +84 -0
  357. OpenGLContext/tests/test_viewplatform.py +39 -0
  358. OpenGLContext/texture.py +449 -0
  359. OpenGLContext/texturecache.py +37 -0
  360. OpenGLContext/triangleutilities.py +89 -0
  361. OpenGLContext/ui/__init__.py +39 -0
  362. OpenGLContext/ui/bindings.py +243 -0
  363. OpenGLContext/ui/console.py +339 -0
  364. OpenGLContext/ui/debugoverlay.py +519 -0
  365. OpenGLContext/ui/dialogs.py +124 -0
  366. OpenGLContext/ui/draw.py +617 -0
  367. OpenGLContext/ui/gallery.py +425 -0
  368. OpenGLContext/ui/generate.py +195 -0
  369. OpenGLContext/ui/geometry.py +88 -0
  370. OpenGLContext/ui/hudwidgets.py +1053 -0
  371. OpenGLContext/ui/layout.py +263 -0
  372. OpenGLContext/ui/metrics.py +218 -0
  373. OpenGLContext/ui/overlay.py +412 -0
  374. OpenGLContext/ui/panel.py +459 -0
  375. OpenGLContext/ui/pictures.py +261 -0
  376. OpenGLContext/ui/screen.py +177 -0
  377. OpenGLContext/ui/scroll.py +297 -0
  378. OpenGLContext/ui/session.py +329 -0
  379. OpenGLContext/ui/settings.py +273 -0
  380. OpenGLContext/ui/skin.py +330 -0
  381. OpenGLContext/ui/widgets.py +1199 -0
  382. OpenGLContext/userpaths.py +48 -0
  383. OpenGLContext/utilities.py +94 -0
  384. OpenGLContext/vectorutilities.py +101 -0
  385. OpenGLContext/viewer/__init__.py +60 -0
  386. OpenGLContext/viewer/adapters/__init__.py +150 -0
  387. OpenGLContext/viewer/adapters/base.py +157 -0
  388. OpenGLContext/viewer/adapters/gltf.py +31 -0
  389. OpenGLContext/viewer/adapters/obj.py +20 -0
  390. OpenGLContext/viewer/adapters/scenegraph.py +71 -0
  391. OpenGLContext/viewer/adapters/tiles.py +296 -0
  392. OpenGLContext/viewer/adapters/vrml.py +25 -0
  393. OpenGLContext/viewer/asyncscene.py +117 -0
  394. OpenGLContext/viewer/caption.py +126 -0
  395. OpenGLContext/viewer/capture.py +77 -0
  396. OpenGLContext/viewer/debug.py +78 -0
  397. OpenGLContext/viewer/environment.py +131 -0
  398. OpenGLContext/viewer/framing.py +111 -0
  399. OpenGLContext/viewer/library.py +235 -0
  400. OpenGLContext/viewer/menu.py +218 -0
  401. OpenGLContext/viewer/options.py +150 -0
  402. OpenGLContext/viewer/overlay.py +63 -0
  403. OpenGLContext/viewer/sceneviewer.py +1056 -0
  404. OpenGLContext/viewer/screens.py +172 -0
  405. OpenGLContext/viewer/source.py +56 -0
  406. OpenGLContext/visitor.py +68 -0
  407. OpenGLContext/vrmlcontext.py +72 -0
  408. OpenGLContext/wxcontext.py +424 -0
  409. OpenGLContext/wxinteractivecontext.py +25 -0
  410. OpenGLContext/wxtestingcontext.py +9 -0
  411. OpenGLContext/wxvrmlcontext.py +13 -0
  412. openglcontext-3.0.0a1.dist-info/METADATA +650 -0
  413. openglcontext-3.0.0a1.dist-info/RECORD +417 -0
  414. openglcontext-3.0.0a1.dist-info/WHEEL +5 -0
  415. openglcontext-3.0.0a1.dist-info/entry_points.txt +11 -0
  416. openglcontext-3.0.0a1.dist-info/licenses/license.txt +89 -0
  417. openglcontext-3.0.0a1.dist-info/top_level.txt +1 -0
@@ -0,0 +1,519 @@
1
+ """The developer overlay: everything a player should never see, in one place.
2
+
3
+ **One panel, fed by registered providers.** A provider is a callable that
4
+ returns name/value pairs and knows nothing about drawing; a subsystem appears in
5
+ the overlay by registering one, not by anybody editing this module. That is the
6
+ whole reason this is engine machinery rather than a game's own screen: the
7
+ renderer, the physics world, the navigation mesh and the application each own
8
+ their numbers, and each hands them over the same way::
9
+
10
+ context.debugOverlay.register('Map', lambda: [
11
+ ('name', loaded.name), ('family', loaded.family),
12
+ ])
13
+
14
+ Values are formatted here, so a provider returns whatever it has -- a float, a
15
+ flag, a vector, ``None`` -- and never a pre-rendered string.
16
+
17
+ **It is a HUD layer, not a panel.** It takes no input, blocks nothing and is
18
+ drawn under any screen that is open, so it can be left up while playing. A
19
+ provider that raises turns into an ``error`` row rather than taking the frame
20
+ down with it: this is diagnostic equipment, and a diagnostic that breaks the
21
+ thing it is measuring is worse than none.
22
+
23
+ This is where the frame rate is drawn.
24
+ :class:`~OpenGLContext.framecounter.FrameCounter` measures it and does not draw
25
+ it: doing that would mean fixed-function calls, which mean nothing in a
26
+ core-profile context. ``OPENGLCONTEXT_DISABLE_FPS_DISPLAY`` starts the overlay
27
+ hidden, so a captured frame has nothing over it.
28
+ """
29
+
30
+ from __future__ import annotations
31
+
32
+ import logging
33
+ import os
34
+ from dataclasses import dataclass
35
+ from typing import (
36
+ Any, Callable, Dict, Iterable, List, NamedTuple, Optional, Tuple,
37
+ )
38
+
39
+ from vrml import field
40
+
41
+ from OpenGLContext.ui.geometry import Rect
42
+ from OpenGLContext.ui.hudwidgets import HUDLayer, HUDWidget
43
+ from OpenGLContext.ui.metrics import FontMetrics
44
+
45
+ log = logging.getLogger(__name__)
46
+
47
+ __all__ = [
48
+ 'DebugOverlay', 'DebugPanel', 'DebugSection', 'Fixed', 'LaidOutRow',
49
+ 'audio_provider', 'format_value', 'frame_provider', 'loop_provider',
50
+ 'render_provider', 'platform_provider', 'physics_provider',
51
+ 'simulation_provider', 'install_default_providers',
52
+ ]
53
+
54
+ #: What a provider returns: pairs of name and value, or a mapping of the same.
55
+ Rows = Iterable[Tuple[str, Any]]
56
+ Provider = Callable[[], Any]
57
+
58
+
59
+ class DebugSection(NamedTuple):
60
+ """One labelled group of rows, as a provider last answered it."""
61
+
62
+ title: str
63
+ rows: List[Tuple[str, str]]
64
+
65
+
66
+ class LaidOutRow(NamedTuple):
67
+ """One line of the overlay, placed."""
68
+
69
+ rect: Rect
70
+ label: str
71
+ value: str
72
+ heading: bool
73
+
74
+
75
+ @dataclass(frozen=True)
76
+ class Fixed:
77
+ """A number that keeps its decimals however round it happens to be.
78
+
79
+ For the rows that change every frame. :func:`_number` drops trailing
80
+ zeros, which is right for a position — ``512.00`` is three characters of
81
+ nothing on a crowded line — and wrong for a frame rate: 60, 59.97 and 60.1
82
+ are three different widths in three consecutive frames, and a number that
83
+ changes width sixty times a second is a number nobody can read.
84
+ """
85
+
86
+ value: float
87
+ decimals: int = 2
88
+
89
+ def __str__(self) -> str:
90
+ return '%.*f' % (self.decimals, self.value)
91
+
92
+
93
+ def format_value(value: Any) -> str:
94
+ """A provider's value as the one line that will be drawn for it.
95
+
96
+ Formatting here rather than in the provider is what keeps the providers
97
+ trivial -- a provider hands over the number it has -- and what keeps the
98
+ overlay's columns reading consistently when a dozen unrelated subsystems
99
+ write into them.
100
+ """
101
+ if value is None:
102
+ return '-'
103
+ if isinstance(value, Fixed):
104
+ return str(value)
105
+ if isinstance(value, bool):
106
+ return 'yes' if value else 'no'
107
+ if isinstance(value, float):
108
+ return _number(value)
109
+ if isinstance(value, int):
110
+ return str(value)
111
+ if isinstance(value, str):
112
+ return value
113
+ if isinstance(value, (tuple, list)) or hasattr(value, '__len__'):
114
+ try:
115
+ return ', '.join(_number(float(component)) for component in value)
116
+ except (TypeError, ValueError):
117
+ pass
118
+ return str(value)
119
+
120
+
121
+ def _number(value: float) -> str:
122
+ """A float with two decimals, and none at all when it is whole.
123
+
124
+ Trailing zeros on a frame time are noise, and a position that reads
125
+ ``512.00`` is three characters of nothing on a line where width is scarce.
126
+ """
127
+ text = '%.2f' % (value,)
128
+ if text.endswith('.00'):
129
+ return text[:-3]
130
+ return text.rstrip('0').rstrip('.') if '.' in text else text
131
+
132
+
133
+ class DebugPanel(HUDWidget):
134
+ """The plate the sections are drawn on: two columns, one line per row.
135
+
136
+ Its size follows its contents, which change every frame, so it is measured
137
+ and placed each time the overlay is drawn rather than once at layout.
138
+ """
139
+
140
+ PROTO = 'DebugPanel'
141
+ anchor = field.newField('anchor', 'SFString', 1, 'top-left')
142
+
143
+ def __init__(self, **named: Any) -> None:
144
+ super(DebugPanel, self).__init__(**named)
145
+ #: What the providers last said. Not a field: it is this frame's
146
+ #: reading, not authored data.
147
+ self.sections: List[DebugSection] = []
148
+
149
+ # -- measurement ------------------------------------------------------
150
+ def columnWidths(self, metrics: FontMetrics) -> Tuple[int, int]:
151
+ """Pixels for the name column and for the value column.
152
+
153
+ One pair for the whole overlay rather than per section, so the values
154
+ of every subsystem line up in one column and the eye runs down it.
155
+ """
156
+ labels = 0
157
+ values = 0
158
+ for section in self.sections:
159
+ labels = max(labels, metrics.text_width(section.title))
160
+ for name, value in section.rows:
161
+ labels = max(labels, metrics.text_width(name))
162
+ values = max(values, metrics.text_width(value))
163
+ return (labels, values)
164
+
165
+ def lineCount(self) -> int:
166
+ """Headings and rows together -- how many lines will be drawn."""
167
+ return sum(1 + len(section.rows) for section in self.sections)
168
+
169
+ def content_size(self, metrics: FontMetrics,
170
+ available: Optional[int] = None) -> Tuple[int, int]:
171
+ lines = self.lineCount()
172
+ if not lines:
173
+ return (0, 0)
174
+ skin = self.activeSkin()
175
+ padding = int(skin.hudPadding)
176
+ labels, values = self.columnWidths(metrics)
177
+ gap = int(skin.hudSpacing) if values else 0
178
+ return (labels + gap + values + padding * 2,
179
+ lines * metrics.line_height + padding * 2)
180
+
181
+ def laidOutRows(self, metrics: FontMetrics) -> List[LaidOutRow]:
182
+ """Every heading and row, placed inside this plate, top down.
183
+
184
+ The whole of the overlay's layout, and pure arithmetic: hand it a font
185
+ and a set of sections and the rectangles come back, with no window
186
+ anywhere in sight.
187
+ """
188
+ if not self.sections or self.rect.empty:
189
+ return []
190
+ padding = int(self.activeSkin().hudPadding)
191
+ inner = self.rect.inset(padding)
192
+ line_height = metrics.line_height
193
+ cursor = inner.top - metrics.char_height
194
+ laid: List[LaidOutRow] = []
195
+ for section in self.sections:
196
+ laid.append(LaidOutRow(
197
+ Rect(inner.x, cursor, inner.width, metrics.char_height),
198
+ section.title, '', True))
199
+ cursor -= line_height
200
+ for name, value in section.rows:
201
+ laid.append(LaidOutRow(
202
+ Rect(inner.x, cursor, inner.width, metrics.char_height),
203
+ name, value, False))
204
+ cursor -= line_height
205
+ return laid
206
+
207
+ # -- drawing ----------------------------------------------------------
208
+ def paint(self, renderer: Any) -> None:
209
+ if not self.sections:
210
+ return
211
+ metrics = renderer.metrics
212
+ skin = renderer.skin
213
+ renderer.rect(self.rect, self.tinted(skin.debugFill))
214
+ for row in self.laidOutRows(metrics):
215
+ if row.heading:
216
+ renderer.textIn(row.rect, row.label, skin.debugTitle)
217
+ continue
218
+ renderer.textIn(row.rect, row.label, skin.debugLabel)
219
+ renderer.textIn(row.rect, row.value, skin.debugValue,
220
+ align='right')
221
+
222
+
223
+ class DebugOverlay(HUDLayer):
224
+ """The overlay itself: a registry of providers and the plate they fill.
225
+
226
+ Held by a context (``context.debugOverlay``) and drawn with the rest of its
227
+ HUD. Registration is by title, so registering the same title twice
228
+ replaces the provider rather than growing a second section with the same
229
+ heading -- which is what a reloaded subsystem would otherwise do.
230
+ """
231
+
232
+ PROTO = 'DebugOverlay'
233
+ margin = field.newField('margin', 'SFFloat', 1, 12.0)
234
+
235
+ def __init__(self, **named: Any) -> None:
236
+ super(DebugOverlay, self).__init__(**named)
237
+ self.panel = DebugPanel()
238
+ self.children = [self.panel]
239
+ #: Title to (order, provider), in registration order within an order.
240
+ self._providers: Dict[str, Tuple[int, Provider]] = {}
241
+ self._sequence = 0
242
+
243
+ # -- the registry -----------------------------------------------------
244
+ def register(self, title: str, provider: Provider,
245
+ order: int = 50) -> Provider:
246
+ """Add (or replace) a section, and hand the provider back.
247
+
248
+ ``order`` sorts the sections: low numbers first, and providers given
249
+ the same order keep the order they registered in, so a subsystem that
250
+ does not care where it lands simply lands after whatever was there.
251
+ """
252
+ self._sequence += 1
253
+ self._providers[title] = (order * 1000 + self._sequence, provider)
254
+ return provider
255
+
256
+ def unregister(self, title: str) -> None:
257
+ """Take a section away -- a subsystem that has been shut down."""
258
+ self._providers.pop(title, None)
259
+
260
+ def sections(self) -> List[DebugSection]:
261
+ """Ask every provider, in order, for the rows it has right now.
262
+
263
+ A provider with nothing to say is left out entirely rather than
264
+ appearing as an empty heading: a physics section on a map with no
265
+ physics is noise on a screen that is short of room.
266
+ """
267
+ found: List[DebugSection] = []
268
+ for title, (_order, provider) in sorted(self._providers.items(),
269
+ key=lambda item: item[1][0]):
270
+ rows = self._rowsFrom(title, provider)
271
+ if rows:
272
+ found.append(DebugSection(title, rows))
273
+ return found
274
+
275
+ @staticmethod
276
+ def _rowsFrom(title: str, provider: Provider) -> List[Tuple[str, str]]:
277
+ """One provider's rows, formatted, with a failure shown rather than raised."""
278
+ try:
279
+ answer = provider()
280
+ except Exception as error: # noqa: BLE001 - diagnostic
281
+ log.debug('debug overlay provider %s failed', title, exc_info=True)
282
+ return [('error', str(error) or type(error).__name__)]
283
+ if hasattr(answer, 'items'):
284
+ answer = list(answer.items())
285
+ return [(str(name), format_value(value)) for name, value in answer]
286
+
287
+ # -- the frame --------------------------------------------------------
288
+ def refresh(self) -> None:
289
+ """Read every provider into the plate, ready to be measured and drawn."""
290
+ self.panel.sections = self.sections()
291
+
292
+ def tick(self, now: Optional[float] = None) -> None:
293
+ """Refresh from the providers; the numbers are this frame's, not last's."""
294
+ self.refresh()
295
+ super(DebugOverlay, self).tick(now)
296
+
297
+ # -- visibility -------------------------------------------------------
298
+ @staticmethod
299
+ def startsVisible() -> bool:
300
+ """Whether a new overlay should be on screen straight away.
301
+
302
+ ``OPENGLCONTEXT_DISABLE_FPS_DISPLAY`` is what the capture harness sets
303
+ to get a clean frame, and it has always meant "no numbers over my
304
+ screenshot"; here it means the overlay starts hidden. A key still
305
+ brings it up, because a capture run is not the only thing that sets it.
306
+ """
307
+ return not os.environ.get('OPENGLCONTEXT_DISABLE_FPS_DISPLAY')
308
+
309
+ def toggle(self) -> bool:
310
+ """Show it if it is hidden, hide it if it is up; True if now shown."""
311
+ self.visible = not self.visible
312
+ return bool(self.visible)
313
+
314
+
315
+ # -- the providers shipped with it ----------------------------------------
316
+ # Each takes what it needs and returns a callable, so a context registers one
317
+ # by naming it rather than by writing a lambda that closes over itself.
318
+
319
+ def frame_provider(context: Any) -> Provider:
320
+ """Frame rate, frame time and the window this is all being drawn in.
321
+
322
+ The rate is the **windowed median**, not the lifetime average: a cumulative
323
+ average bakes in the first frame's shader compile and every synchronous
324
+ load after it, and then reads permanently below what the renderer is
325
+ actually doing.
326
+ """
327
+ def rows() -> Rows:
328
+ counter = getattr(context, 'frameCounter', None)
329
+ width, height = context.getViewPort()
330
+ if counter is None:
331
+ return [('fps', 0), ('viewport', '%dx%d' % (width, height))]
332
+ count, _average, last = counter.summary()
333
+ return [
334
+ # Fixed decimals: these two change every frame, and a value that
335
+ # changes *width* every frame twitches on the screen.
336
+ ('fps', Fixed(counter.recentFps())),
337
+ ('frame ms', Fixed(last * 1000.0)),
338
+ ('frames', count),
339
+ ('viewport', '%dx%d' % (width, height)),
340
+ ]
341
+ return rows
342
+
343
+
344
+ def loop_provider(context: Any) -> Provider:
345
+ """What the whole main loop costs, and which part of it costs that.
346
+
347
+ The section to read when the Frame section looks healthy and the game does
348
+ not. ``fps`` above measures the inside of ``OnDraw`` and only for frames
349
+ that changed something; ``loop fps`` here measures iterations of wall
350
+ clock, which is the rate a player's hands feel. The two disagreeing is the
351
+ diagnosis: the renderer is keeping up and something outside it is not.
352
+
353
+ ``loop ms`` beside ``worst ms`` says the same thing about a single hitch
354
+ that a median cannot -- a healthy median next to a worst ten times larger
355
+ is a loop that stutters -- and the phase rows below say where the worst one
356
+ went. Phases divide the iteration rather than overlapping it, so they add
357
+ up to ``loop ms`` and the largest is the culprit by construction.
358
+
359
+ Left out entirely by a backend that runs its own loop and never opens an
360
+ iteration: rows of zeroes would read as a loop doing nothing at all, which
361
+ is a worse answer than no rows.
362
+ """
363
+ def rows() -> Rows:
364
+ trace = getattr(context, 'loopTrace', None)
365
+ if trace is None:
366
+ return []
367
+ summary = trace.summary()
368
+ if not summary['iterations']:
369
+ return []
370
+ found: List[Tuple[str, Any]] = [
371
+ ('loop fps', Fixed(summary['rate'])),
372
+ ('loop ms', Fixed(summary['median_ms'])),
373
+ ('worst ms', Fixed(summary['worst_ms'])),
374
+ ('stalls', summary['stalls']),
375
+ ]
376
+ culprit = trace.worst_phase()
377
+ if culprit is not None:
378
+ found.append(('last stall', '%s %.0fms' % culprit))
379
+ # Worst first, which `phases_ms` already orders them by: a panel that
380
+ # reads top-down as most-to-least expensive answers "where did it go"
381
+ # without the reader comparing every row against every other.
382
+ found.extend((name, Fixed(milliseconds))
383
+ for name, milliseconds in trace.phases_ms().items())
384
+ return found
385
+ return rows
386
+
387
+
388
+ def render_provider(context: Any) -> Provider:
389
+ """Which renderer features are on, and what the last frame cost in draws."""
390
+ def rows() -> Rows:
391
+ definition = getattr(context, 'contextDefinition', None)
392
+ found: List[Tuple[str, Any]] = [
393
+ ('profile', 'core' if getattr(context, 'coreProfile', False)
394
+ else 'compatibility'),
395
+ ]
396
+ for name in ('shadows', 'ibl', 'bloom', 'transmission', 'instancing',
397
+ 'vsync'):
398
+ if definition is not None and hasattr(definition, name):
399
+ found.append((name, bool(getattr(definition, name))))
400
+ stats = getattr(context, 'renderStats', None)
401
+ if stats is not None:
402
+ found.extend([
403
+ ('shapes', stats.shapes),
404
+ ('draws', stats.draws),
405
+ ('instanced', '%d in %d groups' % (stats.instances,
406
+ stats.instanceGroups)),
407
+ ])
408
+ return found
409
+ return rows
410
+
411
+
412
+ def platform_provider(context: Any) -> Provider:
413
+ """Where the camera is, and what the character controller under it is doing.
414
+
415
+ Everything is read defensively: a context may have no view platform yet, a
416
+ platform may be a plain camera with no controller, and neither is a reason
417
+ for the overlay to stop reporting the rest.
418
+ """
419
+ def rows() -> Rows:
420
+ platform = context.getViewPlatform()
421
+ if platform is None:
422
+ return []
423
+ found: List[Tuple[str, Any]] = [
424
+ ('position', tuple(float(value)
425
+ for value in platform.position[:3])),
426
+ ]
427
+ controller = getattr(platform, 'controller', None)
428
+ if controller is not None:
429
+ for name, label in (('grounded', 'grounded'),
430
+ ('velocity', 'velocity'),
431
+ ('mode', 'mode')):
432
+ if hasattr(controller, name):
433
+ found.append((label, getattr(controller, name)))
434
+ return found
435
+ return rows
436
+
437
+
438
+ def physics_provider(world: Callable[[], Any]) -> Provider:
439
+ """Body and contact counts from a physics world, fetched when asked for.
440
+
441
+ Takes a callable rather than the world itself because a world is built when
442
+ a level loads and replaced when the next one does; a provider holding the
443
+ first would report on it forever.
444
+ """
445
+ def rows() -> Rows:
446
+ found_world = world()
447
+ if found_world is None:
448
+ return []
449
+ found: List[Tuple[str, Any]] = []
450
+ for name, label in (('bodies', 'bodies'), ('contacts', 'contacts')):
451
+ collection = getattr(found_world, name, None)
452
+ if collection is not None:
453
+ found.append((label, len(collection)))
454
+ return found
455
+ return rows
456
+
457
+
458
+ def simulation_provider(simulation: Callable[[], Any]) -> Provider:
459
+ """Whether a background physics thread is getting the turns it asked for.
460
+
461
+ The companion to :func:`physics_provider`, which reports on the *world*;
462
+ this reports on the thread stepping it. A simulation that falls behind
463
+ resets its schedule rather than running the missed ticks back to back --
464
+ the only alternative is a spiral -- so being starved costs simulated time
465
+ silently, and everything in the world moves slowly. That is
466
+ indistinguishable from wrong gravity, wrong units or a wrong timestep until
467
+ ``sim hz`` is on the panel beside the rate that was asked for.
468
+
469
+ Takes a callable for the same reason :func:`physics_provider` does: a
470
+ simulation is built when a level loads and replaced when the next one does.
471
+ """
472
+ def rows() -> Rows:
473
+ found_sim = simulation()
474
+ if found_sim is None:
475
+ return []
476
+ found: List[Tuple[str, Any]] = [
477
+ ('sim hz', Fixed(found_sim.rate())),
478
+ ('asked', Fixed(found_sim.sim_hz)),
479
+ ('steps', found_sim.steps),
480
+ ]
481
+ # Zero is the healthy answer and a healthy row is a row not worth the
482
+ # space; a non-zero one is the whole reason the section exists.
483
+ dropped = found_sim.dropped
484
+ if dropped:
485
+ found.append(('dropped', dropped))
486
+ return found
487
+ return rows
488
+
489
+
490
+ def audio_provider(context: Any) -> Provider:
491
+ """What this context's sound is doing, and whether it is doing it at all.
492
+
493
+ The one subsystem whose state cannot be seen, which is exactly why it wants
494
+ a place on the overlay: a sound that is not audible may be off, may have no
495
+ device, may have lost its voice to a louder one, or may simply be too far
496
+ away, and no amount of listening tells those four apart.
497
+
498
+ Reported even when there is no engine, because "audio: idle" -- nothing in
499
+ this scene has asked to make a noise -- is itself the answer to the most
500
+ common question.
501
+ """
502
+ def rows() -> Rows:
503
+ from OpenGLContext.audio import scene as audioscene
504
+ if getattr(context, 'contextDefinition', None) is None:
505
+ return []
506
+ return list(audioscene.describe(context).items())
507
+ return rows
508
+
509
+
510
+ def install_default_providers(overlay: DebugOverlay, context: Any) -> None:
511
+ """Register the sections every context can answer for itself."""
512
+ overlay.register('Frame', frame_provider(context), order=10)
513
+ # Directly under Frame, because the two are read against each other: a
514
+ # healthy `fps` over a poor `loop fps` is the whole diagnosis, and a reader
515
+ # who has to hunt down the panel for the second number will not compare it.
516
+ overlay.register('Loop', loop_provider(context), order=15)
517
+ overlay.register('Render', render_provider(context), order=20)
518
+ overlay.register('View', platform_provider(context), order=30)
519
+ overlay.register('Audio', audio_provider(context), order=35)
@@ -0,0 +1,124 @@
1
+ """Ready-made dialogs: a question, a message, and a wall of text.
2
+
3
+ These are the shapes an application asks for often enough that building them
4
+ out of widgets every time would be busywork. Each returns a
5
+ :class:`~OpenGLContext.ui.panel.Panel` for the caller to push onto a context's
6
+ overlay stack::
7
+
8
+ context.pushOverlay(dialogs.confirm(
9
+ 'Download the core textures?',
10
+ detail='About 40MB, cached for next time.',
11
+ on_answer=self.texturesAnswered))
12
+
13
+ The buttons are **real measured rectangles**, hovered and clicked with the
14
+ pointer; the keys are accelerators for anyone who prefers them, not the only
15
+ way in. A question that can only be dismissed by guessing a key is a bad
16
+ question.
17
+ """
18
+
19
+ from __future__ import annotations
20
+
21
+ from gettext import gettext as _
22
+ from typing import Callable, Optional, Sequence
23
+
24
+ from OpenGLContext.ui.layout import Column, Row
25
+ from OpenGLContext.ui.panel import Panel
26
+ from OpenGLContext.ui.widgets import Button, Label, Spacer, DANGER, PRIMARY
27
+
28
+ __all__ = ['confirm', 'message', 'notice']
29
+
30
+ #: Content width, in characters, that a text dialog aims for. Roughly a
31
+ #: newspaper column: long enough not to hyphenate, short enough to read.
32
+ DIALOG_COLUMNS = 56
33
+ #: Keys that answer a question, unless the caller names others.
34
+ YES_KEYS = ('y',)
35
+ NO_KEYS = ('n',)
36
+
37
+
38
+ def confirm(question: str, detail: str = '',
39
+ on_answer: Optional[Callable[[bool], None]] = None,
40
+ yes: Optional[str] = None, no: Optional[str] = None,
41
+ title: str = '',
42
+ danger: bool = False,
43
+ yes_keys: Sequence[str] = YES_KEYS,
44
+ no_keys: Sequence[str] = NO_KEYS) -> Panel:
45
+ """A modal yes/no question.
46
+
47
+ ``on_answer`` is called once, with True or False; Escape and the panel's
48
+ Cancel both answer False, because a question dismissed without an answer is
49
+ a No in every case this is used for. ``danger`` marks the affirmative as
50
+ the destructive one -- resetting bindings, discarding changes.
51
+ """
52
+ yes_button = Button(text=yes or _('Yes'), name='yes',
53
+ role=DANGER if danger else PRIMARY,
54
+ accelerator=yes_keys[0] if yes_keys else '')
55
+ no_button = Button(text=no or _('No'), name='no',
56
+ accelerator=no_keys[0] if no_keys else '')
57
+ body = [Label(text=question, wrap=True, name='question')]
58
+ if detail:
59
+ body.append(Label(text=detail, wrap=True, name='detail', top=4))
60
+ body.append(Row(children=[Spacer(), no_button, yes_button], spacing=8,
61
+ top=8, name='buttons'))
62
+ panel = Panel(title=title, scrim=True, modal=True,
63
+ preferredColumns=DIALOG_COLUMNS,
64
+ children=[Column(children=body, spacing=4)])
65
+ answered: list = []
66
+
67
+ def answer(value: bool) -> None:
68
+ if answered:
69
+ return
70
+ answered.append(value)
71
+ panel.close(value)
72
+ if on_answer is not None:
73
+ on_answer(value)
74
+
75
+ yes_button.on_activate = lambda widget: answer(True)
76
+ no_button.on_activate = lambda widget: answer(False)
77
+ # Escape is a No, so a prompt closed any other way still reports one answer.
78
+ panel.on_close = lambda closing: answer(False)
79
+ for extra in yes_keys[1:]:
80
+ panel.accelerators[extra] = lambda dialog: answer(True)
81
+ for extra in no_keys[1:]:
82
+ panel.accelerators[extra] = lambda dialog: answer(False)
83
+ return panel
84
+
85
+
86
+ def message(text: str, title: str = '', button: Optional[str] = None,
87
+ on_close: Optional[Callable[[Panel], None]] = None) -> Panel:
88
+ """Something the user has to acknowledge, with one button."""
89
+ ok = Button(text=button or _('OK'), role=PRIMARY, name='ok')
90
+ panel = Panel(title=title, scrim=True, modal=True,
91
+ preferredColumns=DIALOG_COLUMNS,
92
+ children=[Column(children=[
93
+ Label(text=text, wrap=True, name='message'),
94
+ Row(children=[Spacer(), ok], top=8),
95
+ ], spacing=4)])
96
+ ok.on_activate = lambda widget: panel.close(True)
97
+ if on_close is not None:
98
+ panel.on_close = on_close
99
+ return panel
100
+
101
+
102
+ def notice(title: str, text: str, columns: int = 78,
103
+ on_close: Optional[Callable[[Panel], None]] = None) -> Panel:
104
+ """A wall of text nobody can style away -- a copyright or licence notice.
105
+
106
+ Filling the window and scrolling rather than sizing to the text: these run
107
+ to thousands of words, and a panel that grew to fit one would be taller
108
+ than any screen.
109
+ """
110
+ from OpenGLContext.ui.scroll import ScrollViewport
111
+
112
+ close = Button(text=_('Close'), role=PRIMARY, name='close')
113
+ body = ScrollViewport(name='body', flex=1, children=[
114
+ Label(text=text, wrap=True, name='text')])
115
+ panel = Panel(title=title, modal=True, scrim=True, fill=True,
116
+ preferredColumns=columns,
117
+ children=[Column(children=[
118
+ body,
119
+ Row(children=[Spacer(), close], top=8),
120
+ ], spacing=4)])
121
+ close.on_activate = lambda widget: panel.close(True)
122
+ if on_close is not None:
123
+ panel.on_close = on_close
124
+ return panel