pyglet 2.1.13__py3-none-any.whl → 3.0.dev1__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.
- pyglet/__init__.py +67 -61
- pyglet/__init__.pyi +15 -8
- pyglet/app/__init__.py +22 -13
- pyglet/app/async_app.py +212 -0
- pyglet/app/base.py +2 -1
- pyglet/app/{xlib.py → linux.py} +3 -3
- pyglet/config/__init__.py +101 -0
- pyglet/config/gl/__init__.py +30 -0
- pyglet/config/gl/egl.py +120 -0
- pyglet/config/gl/macos.py +262 -0
- pyglet/config/gl/windows.py +267 -0
- pyglet/config/gl/x11.py +142 -0
- pyglet/customtypes.py +43 -2
- pyglet/display/__init__.py +8 -6
- pyglet/display/base.py +3 -63
- pyglet/display/cocoa.py +12 -17
- pyglet/display/emscripten.py +39 -0
- pyglet/display/headless.py +23 -30
- pyglet/display/wayland.py +157 -0
- pyglet/display/win32.py +5 -21
- pyglet/display/xlib.py +19 -27
- pyglet/display/xlib_vidmoderestore.py +2 -2
- pyglet/enums.py +183 -0
- pyglet/event.py +0 -1
- pyglet/experimental/geoshader_sprite.py +15 -13
- pyglet/experimental/hidraw.py +6 -15
- pyglet/experimental/multitexture_sprite.py +31 -19
- pyglet/experimental/particles.py +13 -35
- pyglet/font/__init__.py +251 -85
- pyglet/font/base.py +116 -61
- pyglet/font/dwrite/__init__.py +349 -204
- pyglet/font/dwrite/dwrite_lib.py +27 -5
- pyglet/font/fontconfig.py +14 -6
- pyglet/font/freetype.py +138 -87
- pyglet/font/freetype_lib.py +19 -0
- pyglet/font/group.py +179 -0
- pyglet/font/harfbuzz/__init__.py +3 -3
- pyglet/font/pyodide_js.py +310 -0
- pyglet/font/quartz.py +319 -126
- pyglet/font/ttf.py +45 -3
- pyglet/font/user.py +14 -19
- pyglet/font/win32.py +45 -21
- pyglet/graphics/__init__.py +8 -787
- pyglet/graphics/allocation.py +115 -1
- pyglet/graphics/api/__init__.py +77 -0
- pyglet/graphics/api/base.py +299 -0
- pyglet/graphics/api/gl/__init__.py +58 -0
- pyglet/graphics/api/gl/base.py +24 -0
- pyglet/graphics/{vertexbuffer.py → api/gl/buffer.py} +104 -159
- pyglet/graphics/api/gl/cocoa/context.py +76 -0
- pyglet/graphics/api/gl/context.py +391 -0
- pyglet/graphics/api/gl/default_shaders.py +0 -0
- pyglet/graphics/api/gl/draw.py +627 -0
- pyglet/graphics/api/gl/egl/__init__.py +0 -0
- pyglet/graphics/api/gl/egl/context.py +92 -0
- pyglet/graphics/api/gl/enums.py +76 -0
- pyglet/graphics/api/gl/framebuffer.py +315 -0
- pyglet/graphics/api/gl/gl.py +5463 -0
- pyglet/graphics/api/gl/gl_info.py +188 -0
- pyglet/graphics/api/gl/global_opengl.py +226 -0
- pyglet/{gl → graphics/api/gl}/lib.py +34 -18
- pyglet/graphics/api/gl/shader.py +1476 -0
- pyglet/graphics/api/gl/shapes.py +55 -0
- pyglet/graphics/api/gl/sprite.py +102 -0
- pyglet/graphics/api/gl/state.py +219 -0
- pyglet/graphics/api/gl/text.py +190 -0
- pyglet/graphics/api/gl/texture.py +1526 -0
- pyglet/graphics/{vertexarray.py → api/gl/vertexarray.py} +11 -13
- pyglet/graphics/api/gl/vertexdomain.py +751 -0
- pyglet/graphics/api/gl/win32/__init__.py +0 -0
- pyglet/graphics/api/gl/win32/context.py +108 -0
- pyglet/graphics/api/gl/win32/wgl_info.py +24 -0
- pyglet/graphics/api/gl/xlib/__init__.py +0 -0
- pyglet/graphics/api/gl/xlib/context.py +174 -0
- pyglet/{gl → graphics/api/gl/xlib}/glx_info.py +26 -31
- pyglet/graphics/api/gl1/__init__.py +0 -0
- pyglet/{gl → graphics/api/gl1}/gl_compat.py +3 -2
- pyglet/graphics/api/gl2/__init__.py +0 -0
- pyglet/graphics/api/gl2/buffer.py +320 -0
- pyglet/graphics/api/gl2/draw.py +600 -0
- pyglet/graphics/api/gl2/global_opengl.py +122 -0
- pyglet/graphics/api/gl2/shader.py +200 -0
- pyglet/graphics/api/gl2/shapes.py +51 -0
- pyglet/graphics/api/gl2/sprite.py +79 -0
- pyglet/graphics/api/gl2/text.py +175 -0
- pyglet/graphics/api/gl2/vertexdomain.py +364 -0
- pyglet/graphics/api/webgl/__init__.py +233 -0
- pyglet/graphics/api/webgl/buffer.py +302 -0
- pyglet/graphics/api/webgl/context.py +234 -0
- pyglet/graphics/api/webgl/draw.py +590 -0
- pyglet/graphics/api/webgl/enums.py +76 -0
- pyglet/graphics/api/webgl/framebuffer.py +360 -0
- pyglet/graphics/api/webgl/gl.py +1537 -0
- pyglet/graphics/api/webgl/gl_info.py +130 -0
- pyglet/graphics/api/webgl/shader.py +1346 -0
- pyglet/graphics/api/webgl/shapes.py +92 -0
- pyglet/graphics/api/webgl/sprite.py +102 -0
- pyglet/graphics/api/webgl/state.py +227 -0
- pyglet/graphics/api/webgl/text.py +187 -0
- pyglet/graphics/api/webgl/texture.py +1227 -0
- pyglet/graphics/api/webgl/vertexarray.py +54 -0
- pyglet/graphics/api/webgl/vertexdomain.py +616 -0
- pyglet/graphics/api/webgl/webgl_js.pyi +307 -0
- pyglet/{image → graphics}/atlas.py +33 -32
- pyglet/graphics/base.py +10 -0
- pyglet/graphics/buffer.py +245 -0
- pyglet/graphics/draw.py +578 -0
- pyglet/graphics/framebuffer.py +26 -0
- pyglet/graphics/instance.py +178 -69
- pyglet/graphics/shader.py +267 -1553
- pyglet/graphics/state.py +83 -0
- pyglet/graphics/texture.py +703 -0
- pyglet/graphics/vertexdomain.py +695 -538
- pyglet/gui/ninepatch.py +10 -10
- pyglet/gui/widgets.py +120 -10
- pyglet/image/__init__.py +20 -1973
- pyglet/image/animation.py +12 -12
- pyglet/image/base.py +730 -0
- pyglet/image/codecs/__init__.py +9 -0
- pyglet/image/codecs/bmp.py +53 -30
- pyglet/image/codecs/dds.py +53 -31
- pyglet/image/codecs/gdiplus.py +38 -14
- pyglet/image/codecs/gdkpixbuf2.py +0 -2
- pyglet/image/codecs/js_image.py +99 -0
- pyglet/image/codecs/ktx2.py +161 -0
- pyglet/image/codecs/pil.py +1 -1
- pyglet/image/codecs/png.py +1 -1
- pyglet/image/codecs/wic.py +11 -2
- pyglet/info.py +26 -24
- pyglet/input/__init__.py +8 -0
- pyglet/input/base.py +163 -105
- pyglet/input/controller.py +13 -19
- pyglet/input/controller_db.py +39 -24
- pyglet/input/emscripten/__init__.py +18 -0
- pyglet/input/emscripten/gamepad_js.py +397 -0
- pyglet/input/linux/__init__.py +11 -5
- pyglet/input/linux/evdev.py +10 -11
- pyglet/input/linux/x11_xinput.py +2 -2
- pyglet/input/linux/x11_xinput_tablet.py +1 -1
- pyglet/input/macos/__init__.py +7 -2
- pyglet/input/macos/darwin_gc.py +559 -0
- pyglet/input/win32/__init__.py +1 -1
- pyglet/input/win32/directinput.py +34 -29
- pyglet/input/win32/xinput.py +11 -61
- pyglet/lib.py +3 -3
- pyglet/libs/__init__.py +1 -1
- pyglet/{gl → libs/darwin}/agl.py +1 -1
- pyglet/libs/darwin/cocoapy/__init__.py +2 -2
- pyglet/libs/darwin/cocoapy/cocoahelpers.py +181 -0
- pyglet/libs/darwin/cocoapy/cocoalibs.py +31 -0
- pyglet/libs/darwin/cocoapy/cocoatypes.py +27 -0
- pyglet/libs/darwin/cocoapy/runtime.py +81 -45
- pyglet/libs/darwin/coreaudio.py +4 -4
- pyglet/{gl → libs/darwin}/lib_agl.py +9 -8
- pyglet/libs/darwin/quartzkey.py +1 -3
- pyglet/libs/egl/__init__.py +2 -0
- pyglet/libs/egl/egl_lib.py +576 -0
- pyglet/libs/egl/eglext.py +51 -5
- pyglet/libs/linux/__init__.py +0 -0
- pyglet/libs/linux/egl/__init__.py +0 -0
- pyglet/libs/linux/egl/eglext.py +22 -0
- pyglet/libs/linux/glx/__init__.py +0 -0
- pyglet/{gl → libs/linux/glx}/glx.py +13 -14
- pyglet/{gl → libs/linux/glx}/glxext_arb.py +408 -192
- pyglet/{gl → libs/linux/glx}/glxext_mesa.py +1 -1
- pyglet/{gl → libs/linux/glx}/glxext_nv.py +345 -164
- pyglet/{gl → libs/linux/glx}/lib_glx.py +3 -2
- pyglet/libs/linux/wayland/__init__.py +0 -0
- pyglet/libs/linux/wayland/client.py +1068 -0
- pyglet/libs/linux/wayland/lib_wayland.py +207 -0
- pyglet/libs/linux/wayland/wayland_egl.py +38 -0
- pyglet/libs/{wayland → linux/wayland}/xkbcommon.py +26 -0
- pyglet/libs/{x11 → linux/x11}/xf86vmode.py +4 -4
- pyglet/libs/{x11 → linux/x11}/xinerama.py +2 -2
- pyglet/libs/{x11 → linux/x11}/xinput.py +10 -10
- pyglet/libs/linux/x11/xrandr.py +0 -0
- pyglet/libs/{x11 → linux/x11}/xrender.py +1 -1
- pyglet/libs/shared/__init__.py +0 -0
- pyglet/libs/shared/spirv/__init__.py +0 -0
- pyglet/libs/shared/spirv/lib_shaderc.py +85 -0
- pyglet/libs/shared/spirv/lib_spirv_cross.py +126 -0
- pyglet/libs/win32/__init__.py +28 -8
- pyglet/libs/win32/constants.py +59 -48
- pyglet/libs/win32/context_managers.py +20 -3
- pyglet/libs/win32/dinput.py +105 -88
- pyglet/{gl → libs/win32}/lib_wgl.py +52 -26
- pyglet/libs/win32/types.py +58 -23
- pyglet/{gl → libs/win32}/wgl.py +32 -25
- pyglet/{gl → libs/win32}/wglext_arb.py +364 -2
- pyglet/media/__init__.py +9 -10
- pyglet/media/codecs/__init__.py +12 -1
- pyglet/media/codecs/base.py +99 -96
- pyglet/media/codecs/ffmpeg.py +2 -2
- pyglet/media/codecs/ffmpeg_lib/libavformat.py +3 -8
- pyglet/media/codecs/webaudio_pyodide.py +111 -0
- pyglet/media/drivers/__init__.py +9 -4
- pyglet/media/drivers/base.py +4 -4
- pyglet/media/drivers/openal/__init__.py +1 -1
- pyglet/media/drivers/openal/adaptation.py +3 -3
- pyglet/media/drivers/pulse/__init__.py +1 -1
- pyglet/media/drivers/pulse/adaptation.py +3 -3
- pyglet/media/drivers/pyodide_js/__init__.py +8 -0
- pyglet/media/drivers/pyodide_js/adaptation.py +288 -0
- pyglet/media/drivers/xaudio2/adaptation.py +3 -3
- pyglet/media/player.py +276 -193
- pyglet/media/player_worker_thread.py +1 -1
- pyglet/model/__init__.py +39 -29
- pyglet/model/codecs/base.py +4 -4
- pyglet/model/codecs/gltf.py +3 -3
- pyglet/model/codecs/obj.py +71 -43
- pyglet/resource.py +129 -78
- pyglet/shapes.py +154 -194
- pyglet/sprite.py +47 -164
- pyglet/text/__init__.py +44 -54
- pyglet/text/caret.py +12 -7
- pyglet/text/document.py +19 -17
- pyglet/text/formats/html.py +2 -2
- pyglet/text/formats/structured.py +10 -40
- pyglet/text/layout/__init__.py +20 -13
- pyglet/text/layout/base.py +176 -287
- pyglet/text/layout/incremental.py +9 -10
- pyglet/text/layout/scrolling.py +7 -95
- pyglet/window/__init__.py +183 -172
- pyglet/window/cocoa/__init__.py +62 -51
- pyglet/window/cocoa/pyglet_delegate.py +2 -25
- pyglet/window/cocoa/pyglet_view.py +9 -8
- pyglet/window/dialog/__init__.py +184 -0
- pyglet/window/dialog/base.py +99 -0
- pyglet/window/dialog/darwin.py +121 -0
- pyglet/window/dialog/linux.py +72 -0
- pyglet/window/dialog/windows.py +194 -0
- pyglet/window/emscripten/__init__.py +779 -0
- pyglet/window/headless/__init__.py +44 -28
- pyglet/window/key.py +2 -0
- pyglet/window/mouse.py +2 -2
- pyglet/window/wayland/__init__.py +377 -0
- pyglet/window/win32/__init__.py +101 -46
- pyglet/window/xlib/__init__.py +104 -66
- {pyglet-2.1.13.dist-info → pyglet-3.0.dev1.dist-info}/METADATA +2 -3
- pyglet-3.0.dev1.dist-info/RECORD +322 -0
- {pyglet-2.1.13.dist-info → pyglet-3.0.dev1.dist-info}/WHEEL +1 -1
- pyglet/gl/__init__.py +0 -208
- pyglet/gl/base.py +0 -499
- pyglet/gl/cocoa.py +0 -309
- pyglet/gl/gl.py +0 -4625
- pyglet/gl/gl.pyi +0 -2320
- pyglet/gl/gl_compat.pyi +0 -3097
- pyglet/gl/gl_info.py +0 -190
- pyglet/gl/headless.py +0 -166
- pyglet/gl/wgl_info.py +0 -36
- pyglet/gl/wglext_nv.py +0 -1096
- pyglet/gl/win32.py +0 -268
- pyglet/gl/xlib.py +0 -295
- pyglet/image/buffer.py +0 -274
- pyglet/image/codecs/s3tc.py +0 -354
- pyglet/libs/x11/xrandr.py +0 -166
- pyglet-2.1.13.dist-info/RECORD +0 -234
- /pyglet/{libs/wayland → graphics/api/gl/cocoa}/__init__.py +0 -0
- /pyglet/libs/{egl → linux/egl}/egl.py +0 -0
- /pyglet/libs/{egl → linux/egl}/lib.py +0 -0
- /pyglet/libs/{ioctl.py → linux/ioctl.py} +0 -0
- /pyglet/libs/{wayland → linux/wayland}/gbm.py +0 -0
- /pyglet/libs/{x11 → linux/x11}/__init__.py +0 -0
- /pyglet/libs/{x11 → linux/x11}/cursorfont.py +0 -0
- /pyglet/libs/{x11 → linux/x11}/xlib.py +0 -0
- /pyglet/libs/{x11 → linux/x11}/xsync.py +0 -0
- {pyglet-2.1.13.dist-info/licenses → pyglet-3.0.dev1.dist-info}/LICENSE +0 -0
pyglet/window/cocoa/__init__.py
CHANGED
|
@@ -4,7 +4,6 @@ from ctypes import c_void_p
|
|
|
4
4
|
from typing import TYPE_CHECKING, Sequence
|
|
5
5
|
|
|
6
6
|
import pyglet
|
|
7
|
-
from pyglet.display.cocoa import CocoaCanvas
|
|
8
7
|
from pyglet.event import EventDispatcher
|
|
9
8
|
from pyglet.libs.darwin import AutoReleasePool, CGPoint, cocoapy
|
|
10
9
|
from pyglet.window import BaseWindow, DefaultMouseCursor, MouseCursor
|
|
@@ -17,7 +16,7 @@ from .pyglet_window import PygletToolWindow, PygletWindow
|
|
|
17
16
|
from .systemcursor import SystemCursor
|
|
18
17
|
|
|
19
18
|
if TYPE_CHECKING:
|
|
20
|
-
from pyglet.gl.cocoa import CocoaContext
|
|
19
|
+
from pyglet.graphics.api.gl.cocoa.context import CocoaContext
|
|
21
20
|
|
|
22
21
|
NSApplication = cocoapy.ObjCClass('NSApplication')
|
|
23
22
|
NSCursor = cocoapy.ObjCClass('NSCursor')
|
|
@@ -30,9 +29,11 @@ NSPasteboard = cocoapy.ObjCClass('NSPasteboard')
|
|
|
30
29
|
quartz = cocoapy.quartz
|
|
31
30
|
cf = cocoapy.cf
|
|
32
31
|
|
|
32
|
+
CAMetalLayer = cocoapy.ObjCClass('CAMetalLayer')
|
|
33
|
+
|
|
33
34
|
|
|
34
35
|
class CocoaMouseCursor(MouseCursor):
|
|
35
|
-
|
|
36
|
+
api_drawable = False
|
|
36
37
|
|
|
37
38
|
def __init__(self, cursorName: str) -> None:
|
|
38
39
|
# cursorName is a string identifying one of the named default NSCursors
|
|
@@ -93,26 +94,20 @@ class CocoaWindow(BaseWindow):
|
|
|
93
94
|
def _create(self) -> None:
|
|
94
95
|
with AutoReleasePool():
|
|
95
96
|
if self._nswindow:
|
|
96
|
-
# The window is about
|
|
97
|
+
# The window is about to be recreated so destroy everything
|
|
97
98
|
# associated with the old window, then destroy the window itself.
|
|
98
|
-
nsview = self.canvas.nsview
|
|
99
|
-
self.canvas = None
|
|
100
99
|
self._nswindow.orderOut_(None)
|
|
101
100
|
self._nswindow.close()
|
|
102
101
|
self.context.detach()
|
|
103
102
|
self._nswindow.release()
|
|
104
103
|
self._nswindow = None
|
|
105
|
-
|
|
104
|
+
self._nsview.release()
|
|
105
|
+
self._nsview = None
|
|
106
106
|
self._delegate.release()
|
|
107
107
|
self._delegate = None
|
|
108
108
|
|
|
109
109
|
# Determine window parameters.
|
|
110
|
-
|
|
111
|
-
screen_scale = self.screen.get_scale()
|
|
112
|
-
w, h = self.get_requested_size()
|
|
113
|
-
width, height = w / screen_scale, h / screen_scale
|
|
114
|
-
else:
|
|
115
|
-
width, height = self._width, self._height
|
|
110
|
+
width, height = self._width, self._height
|
|
116
111
|
|
|
117
112
|
content_rect = cocoapy.NSMakeRect(0, 0, width, height)
|
|
118
113
|
WindowClass = PygletWindow
|
|
@@ -165,25 +160,29 @@ class CocoaWindow(BaseWindow):
|
|
|
165
160
|
|
|
166
161
|
# Then create a view and set it as our NSWindow's content view.
|
|
167
162
|
self._nsview = PygletView.alloc().initWithFrame_cocoaWindow_(content_rect, self)
|
|
168
|
-
self.
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
self.
|
|
174
|
-
self.
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
163
|
+
self._metal_layer = None
|
|
164
|
+
if not self._shadow:
|
|
165
|
+
if "gl" in pyglet.options.backend:
|
|
166
|
+
self._nsview.setWantsBestResolutionOpenGLSurface_(True)
|
|
167
|
+
elif pyglet.options.backend == "vulkan":
|
|
168
|
+
self._metal_layer = CAMetalLayer.alloc().init()
|
|
169
|
+
#self._metal_layer.setFramebufferOnly_(True) # Layer can only be used as a FB. More performant?
|
|
170
|
+
transparent = self.style == 'transparent' or self.style == 'overlay'
|
|
171
|
+
self._metal_layer.setOpaque_(not transparent)
|
|
172
|
+
|
|
173
|
+
# Attach the CAMetalLayer to the NSView
|
|
174
|
+
self._nsview.setLayer_(self._metal_layer)
|
|
175
|
+
self._nsview.setWantsLayer_(True)
|
|
176
|
+
else:
|
|
177
|
+
print(f"Unsupported backend found. '{pyglet.options.backend}'")
|
|
178
|
+
|
|
179
|
+
self._assign_config()
|
|
180
|
+
self.context.attach(self)
|
|
181
|
+
if self._metal_layer:
|
|
182
|
+
self.context._nscontext = self._metal_layer # noqa: SLF001
|
|
180
183
|
self._nswindow.setContentView_(self._nsview)
|
|
181
184
|
self._nswindow.makeFirstResponder_(self._nsview)
|
|
182
185
|
|
|
183
|
-
# Create a canvas with the view as its drawable and attach context to it.
|
|
184
|
-
self.canvas = CocoaCanvas(self.display, self.screen, self._nsview)
|
|
185
|
-
self.context.attach(self.canvas)
|
|
186
|
-
|
|
187
186
|
# Configure the window.
|
|
188
187
|
self._nswindow.setAcceptsMouseMovedEvents_(True)
|
|
189
188
|
|
|
@@ -208,13 +207,29 @@ class CocoaWindow(BaseWindow):
|
|
|
208
207
|
array = NSArray.arrayWithObject_(cocoapy.NSPasteboardTypeURL)
|
|
209
208
|
self._nsview.registerForDraggedTypes_(array)
|
|
210
209
|
|
|
211
|
-
self.
|
|
210
|
+
self._update_geometry()
|
|
212
211
|
self.switch_to()
|
|
213
212
|
self.set_vsync(self._vsync)
|
|
214
213
|
self.set_visible(self._visible)
|
|
215
214
|
|
|
215
|
+
if not self._fullscreen:
|
|
216
|
+
if self._style in ("transparent", "overlay"):
|
|
217
|
+
self._nswindow.setOpaque_(False)
|
|
218
|
+
self._nswindow.setBackgroundColor_(NSColor.clearColor())
|
|
219
|
+
self._nswindow.setHasShadow_(False)
|
|
220
|
+
|
|
221
|
+
if self._style == "overlay":
|
|
222
|
+
self.set_mouse_passthrough(True)
|
|
223
|
+
self._nswindow.setLevel_(cocoapy.NSStatusWindowLevel)
|
|
224
|
+
|
|
225
|
+
def _update_geometry(self):
|
|
226
|
+
if self._metal_layer:
|
|
227
|
+
pass
|
|
228
|
+
else:
|
|
229
|
+
self.context.update_geometry()
|
|
230
|
+
|
|
216
231
|
def _get_dpi_desc(self) -> int:
|
|
217
|
-
if
|
|
232
|
+
if self._nswindow:
|
|
218
233
|
desc = self._nswindow.deviceDescription()
|
|
219
234
|
rsize = desc.objectForKey_(darwin.NSDeviceResolution).sizeValue()
|
|
220
235
|
return int(rsize.width)
|
|
@@ -227,7 +242,7 @@ class CocoaWindow(BaseWindow):
|
|
|
227
242
|
|
|
228
243
|
Read only.
|
|
229
244
|
"""
|
|
230
|
-
if
|
|
245
|
+
if self._nswindow:
|
|
231
246
|
return self._nswindow.backingScaleFactor()
|
|
232
247
|
|
|
233
248
|
return 1.0
|
|
@@ -271,7 +286,7 @@ class CocoaWindow(BaseWindow):
|
|
|
271
286
|
|
|
272
287
|
with AutoReleasePool():
|
|
273
288
|
# Restore cursor visibility
|
|
274
|
-
self.
|
|
289
|
+
self.set_mouse_cursor_platform_visible(True)
|
|
275
290
|
self.set_exclusive_mouse(False)
|
|
276
291
|
self.set_exclusive_keyboard(False)
|
|
277
292
|
|
|
@@ -288,11 +303,7 @@ class CocoaWindow(BaseWindow):
|
|
|
288
303
|
self._delegate.release()
|
|
289
304
|
self._delegate = None
|
|
290
305
|
|
|
291
|
-
# Remove view
|
|
292
|
-
if self.canvas:
|
|
293
|
-
self.canvas.nsview = None
|
|
294
|
-
self.canvas = None
|
|
295
|
-
|
|
306
|
+
# Remove view.
|
|
296
307
|
if self._nsview:
|
|
297
308
|
self._nswindow.setContentView_(None)
|
|
298
309
|
self._nsview.release()
|
|
@@ -314,6 +325,10 @@ class CocoaWindow(BaseWindow):
|
|
|
314
325
|
if self.context:
|
|
315
326
|
self.context.set_current()
|
|
316
327
|
|
|
328
|
+
def before_draw(self) -> None:
|
|
329
|
+
if self.context:
|
|
330
|
+
self.context.before_draw()
|
|
331
|
+
|
|
317
332
|
def flip(self) -> None:
|
|
318
333
|
self.draw_mouse_cursor()
|
|
319
334
|
if self.context:
|
|
@@ -381,7 +396,7 @@ class CocoaWindow(BaseWindow):
|
|
|
381
396
|
image = max_image.get_image_data()
|
|
382
397
|
fmt = 'ARGB'
|
|
383
398
|
bytesPerRow = len(fmt) * image.width
|
|
384
|
-
data = image.
|
|
399
|
+
data = image.get_bytes(fmt, -bytesPerRow)
|
|
385
400
|
|
|
386
401
|
# Use image data to create a data provider.
|
|
387
402
|
# Using CGDataProviderCreateWithData crashes PyObjC 2.2b3, so we create
|
|
@@ -444,7 +459,7 @@ class CocoaWindow(BaseWindow):
|
|
|
444
459
|
return self._width, self._height
|
|
445
460
|
|
|
446
461
|
def get_framebuffer_size(self) -> tuple[int, int]:
|
|
447
|
-
view = self.
|
|
462
|
+
view = self._nsview
|
|
448
463
|
bounds = view.bounds()
|
|
449
464
|
bounds = view.convertRectToBacking_(bounds)
|
|
450
465
|
return int(bounds.size.width), int(bounds.size.height)
|
|
@@ -452,11 +467,7 @@ class CocoaWindow(BaseWindow):
|
|
|
452
467
|
def set_size(self, width: int, height: int) -> None:
|
|
453
468
|
super().set_size(width, height)
|
|
454
469
|
|
|
455
|
-
|
|
456
|
-
screen_scale = self._nswindow.backingScaleFactor()
|
|
457
|
-
frame_width, frame_height = width // screen_scale, height // screen_scale
|
|
458
|
-
else:
|
|
459
|
-
frame_width, frame_height = width, height
|
|
470
|
+
frame_width, frame_height = width, height
|
|
460
471
|
|
|
461
472
|
self._set_frame_size(frame_width, frame_height)
|
|
462
473
|
self.dispatch_event('_on_internal_resize', width, height)
|
|
@@ -516,8 +527,8 @@ class CocoaWindow(BaseWindow):
|
|
|
516
527
|
self._nswindow.zoom_(None)
|
|
517
528
|
|
|
518
529
|
def set_vsync(self, vsync: bool) -> None:
|
|
519
|
-
if pyglet.options
|
|
520
|
-
vsync = pyglet.options
|
|
530
|
+
if pyglet.options.vsync is not None:
|
|
531
|
+
vsync = pyglet.options.vsync
|
|
521
532
|
|
|
522
533
|
super().set_vsync(vsync)
|
|
523
534
|
self.context.set_vsync(vsync)
|
|
@@ -531,7 +542,7 @@ class CocoaWindow(BaseWindow):
|
|
|
531
542
|
rect = self._nswindow.contentRectForFrameRect_(window_frame)
|
|
532
543
|
return cocoapy.foundation.NSMouseInRect(point, rect, False)
|
|
533
544
|
|
|
534
|
-
def
|
|
545
|
+
def set_mouse_cursor_platform_visible(self, platform_visible: int | None = None) -> None:
|
|
535
546
|
# When the platform_visible argument is supplied with a boolean, then this
|
|
536
547
|
# method simply sets whether or not the platform mouse cursor is visible.
|
|
537
548
|
if platform_visible is not None:
|
|
@@ -555,7 +566,7 @@ class CocoaWindow(BaseWindow):
|
|
|
555
566
|
# If we are in the window, then what we do depends on both
|
|
556
567
|
# the current pyglet-set visibility setting for the mouse and
|
|
557
568
|
# the type of the mouse cursor. If the cursor has been hidden
|
|
558
|
-
# in the window with
|
|
569
|
+
# in the window with set_mouse_cursor_visible() then don't show it.
|
|
559
570
|
elif not self._mouse_visible:
|
|
560
571
|
SystemCursor.hide()
|
|
561
572
|
# If the mouse is set as a system-defined cursor, then we
|
|
@@ -566,7 +577,7 @@ class CocoaWindow(BaseWindow):
|
|
|
566
577
|
SystemCursor.unhide()
|
|
567
578
|
# If the mouse cursor is OpenGL drawable, then it we need to hide
|
|
568
579
|
# the system mouse cursor, so that the cursor can draw itself.
|
|
569
|
-
elif self._mouse_cursor.
|
|
580
|
+
elif self._mouse_cursor.api_drawable:
|
|
570
581
|
SystemCursor.hide()
|
|
571
582
|
# Otherwise, show the default cursor.
|
|
572
583
|
else:
|
|
@@ -639,7 +650,7 @@ class CocoaWindow(BaseWindow):
|
|
|
639
650
|
quartz.CGAssociateMouseAndMouseCursorPosition(True)
|
|
640
651
|
|
|
641
652
|
# Update visibility of mouse cursor.
|
|
642
|
-
self.
|
|
653
|
+
self.set_mouse_cursor_platform_visible()
|
|
643
654
|
|
|
644
655
|
def set_exclusive_keyboard(self, exclusive: bool = True) -> None:
|
|
645
656
|
# http://developer.apple.com/mac/library/technotes/tn2002/tn2062.html
|
|
@@ -3,11 +3,9 @@ from __future__ import annotations
|
|
|
3
3
|
from ctypes import c_void_p
|
|
4
4
|
from typing import TYPE_CHECKING
|
|
5
5
|
|
|
6
|
-
import pyglet
|
|
7
6
|
from pyglet.libs.darwin.cocoapy import (
|
|
8
7
|
NSApplicationDidHideNotification,
|
|
9
8
|
NSApplicationDidUnhideNotification,
|
|
10
|
-
NSMakeRect,
|
|
11
9
|
ObjCClass,
|
|
12
10
|
ObjCInstance,
|
|
13
11
|
ObjCSubclass,
|
|
@@ -101,7 +99,7 @@ class PygletDelegate_Implementation:
|
|
|
101
99
|
self.did_pause_exclusive_mouse = False
|
|
102
100
|
self._window._nswindow.setMovable_(True) # Mac OS 10.6 # noqa: SLF001
|
|
103
101
|
# Restore previous mouse visibility settings.
|
|
104
|
-
self._window.
|
|
102
|
+
self._window.set_mouse_cursor_platform_visible()
|
|
105
103
|
self._window.dispatch_event('on_activate')
|
|
106
104
|
|
|
107
105
|
@PygletDelegate.method('v@')
|
|
@@ -115,7 +113,7 @@ class PygletDelegate_Implementation:
|
|
|
115
113
|
# the window is reactivated by clicking on its title bar.
|
|
116
114
|
self._window._nswindow.setMovable_(False) # Mac OS X 10.6 # noqa: SLF001
|
|
117
115
|
# Make sure that cursor is visible.
|
|
118
|
-
self._window.
|
|
116
|
+
self._window.set_mouse_cursor_platform_visible(True)
|
|
119
117
|
self._window.dispatch_event('on_deactivate')
|
|
120
118
|
|
|
121
119
|
@PygletDelegate.method('v@')
|
|
@@ -157,27 +155,6 @@ class PygletDelegate_Implementation:
|
|
|
157
155
|
new_scale = self._window._nswindow.backingScaleFactor()
|
|
158
156
|
if old_scale != new_scale:
|
|
159
157
|
self._window.switch_to()
|
|
160
|
-
|
|
161
|
-
currentFrame = self._window._nswindow.frame()
|
|
162
|
-
|
|
163
|
-
if pyglet.options.dpi_scaling == "real":
|
|
164
|
-
screen_scale = new_scale
|
|
165
|
-
w, h = self._window.get_requested_size()
|
|
166
|
-
width, height = int(w / screen_scale), int(h / screen_scale)
|
|
167
|
-
|
|
168
|
-
# Force Window back to correct size.
|
|
169
|
-
self._window._set_frame_size(width, height)
|
|
170
|
-
else:
|
|
171
|
-
# MacOS seems to cache the state of the window size, even between different DPI scales/monitors.
|
|
172
|
-
# This means that the screen will refuse to refresh until we resize the window to a different size.
|
|
173
|
-
# Force a refresh by setting a temporary frame, then forcing it back.
|
|
174
|
-
if pyglet.options.dpi_scaling == "scaled":
|
|
175
|
-
tempRect = NSMakeRect(currentFrame.origin.x, currentFrame.origin.y,
|
|
176
|
-
currentFrame.size.width + 1, currentFrame.size.height + 1)
|
|
177
|
-
# TODO: Add variable to ignore the next two on-resize events?
|
|
178
|
-
self._window._nswindow.setFrame_display_(tempRect, True)
|
|
179
|
-
self._window._nswindow.setFrame_display_(currentFrame, True)
|
|
180
|
-
|
|
181
158
|
self._window.context.update_geometry()
|
|
182
159
|
self._window.dispatch_event("_on_internal_scale", new_scale, self._window._get_dpi_desc())
|
|
183
160
|
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
import ctypes
|
|
4
3
|
from typing import TYPE_CHECKING
|
|
5
4
|
|
|
6
5
|
import pyglet
|
|
@@ -11,12 +10,13 @@ from pyglet.libs.darwin import (
|
|
|
11
10
|
NSLeftCommandKeyMask,
|
|
12
11
|
NSLeftControlKeyMask,
|
|
13
12
|
NSLeftShiftKeyMask,
|
|
13
|
+
NSMakeRect,
|
|
14
14
|
NSPasteboardURLReadingFileURLsOnlyKey,
|
|
15
15
|
NSRightAlternateKeyMask,
|
|
16
16
|
NSRightCommandKeyMask,
|
|
17
17
|
NSRightControlKeyMask,
|
|
18
18
|
NSRightShiftKeyMask,
|
|
19
|
-
cocoapy,
|
|
19
|
+
cocoapy,
|
|
20
20
|
)
|
|
21
21
|
from pyglet.libs.darwin.quartzkey import charmap, keymap
|
|
22
22
|
from pyglet.window import key, mouse
|
|
@@ -24,6 +24,7 @@ from pyglet.window import key, mouse
|
|
|
24
24
|
from .pyglet_textview import PygletTextView
|
|
25
25
|
|
|
26
26
|
if TYPE_CHECKING:
|
|
27
|
+
import ctypes
|
|
27
28
|
from . import CocoaWindow
|
|
28
29
|
|
|
29
30
|
NSTrackingArea = cocoapy.ObjCClass('NSTrackingArea')
|
|
@@ -193,18 +194,18 @@ class PygletView_Implementation:
|
|
|
193
194
|
# This method is called when view is first installed as the
|
|
194
195
|
# contentView of window. Don't do anything on first call.
|
|
195
196
|
# This also helps ensure correct window creation event ordering.
|
|
196
|
-
if not self._window.context.
|
|
197
|
+
if not self._window.context.window or self._window._shadow: # noqa: SLF001
|
|
197
198
|
return
|
|
198
199
|
|
|
199
200
|
width, height = int(size.width), int(size.height)
|
|
200
201
|
self._window.switch_to()
|
|
201
|
-
self._window.
|
|
202
|
+
self._window._update_geometry()
|
|
202
203
|
self._window._width, self._window._height = width, height # noqa: SLF001
|
|
203
204
|
self._window.dispatch_event('_on_internal_resize', width, height)
|
|
204
205
|
self._window.dispatch_event('on_expose')
|
|
205
206
|
# Can't get app.event_loop.enter_blocking() working with Cocoa, because
|
|
206
207
|
# when mouse clicks on the window's resize control, Cocoa enters into a
|
|
207
|
-
# mini-event loop that only responds to mouseDragged and
|
|
208
|
+
# mini-event loop that only responds to mouseDragged and mouseUp events.
|
|
208
209
|
# This means that using NSTimer to call idle() won't work. Our kludge
|
|
209
210
|
# is to override NSWindow's nextEventMatchingMask_etc method and call
|
|
210
211
|
# idle() from there.
|
|
@@ -340,7 +341,7 @@ class PygletView_Implementation:
|
|
|
340
341
|
def mouseEntered_(self, nsevent: cocoapy.ObjCInstance) -> None:
|
|
341
342
|
x, y = getMousePosition(self, nsevent)
|
|
342
343
|
self._window._mouse_in_window = True # noqa: SLF001
|
|
343
|
-
# Don't call self._window.
|
|
344
|
+
# Don't call self._window.set_mouse_cursor_platform_visible() from here.
|
|
344
345
|
# Better to do it from cursorUpdate:
|
|
345
346
|
self._window.dispatch_event('on_mouse_enter', x, y)
|
|
346
347
|
|
|
@@ -349,7 +350,7 @@ class PygletView_Implementation:
|
|
|
349
350
|
x, y = getMousePosition(self, nsevent)
|
|
350
351
|
self._window._mouse_in_window = False # noqa: SLF001
|
|
351
352
|
if not self._window._mouse_exclusive: # noqa: SLF001
|
|
352
|
-
self._window.
|
|
353
|
+
self._window.set_mouse_cursor_platform_visible()
|
|
353
354
|
self._window.dispatch_event('on_mouse_leave', x, y)
|
|
354
355
|
|
|
355
356
|
@PygletView.method('v@')
|
|
@@ -363,7 +364,7 @@ class PygletView_Implementation:
|
|
|
363
364
|
# to the default arrow and screw up our cursor tracking.
|
|
364
365
|
self._window._mouse_in_window = True # noqa: SLF001
|
|
365
366
|
if not self._window._mouse_exclusive: # noqa: SLF001
|
|
366
|
-
self._window.
|
|
367
|
+
self._window.set_mouse_cursor_platform_visible()
|
|
367
368
|
|
|
368
369
|
@PygletView.method('Q@')
|
|
369
370
|
def draggingEntered_(self, draginfo: cocoapy.ObjCInstance) -> int:
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
import sys
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
import pyglet
|
|
6
|
+
from pyglet.event import EventDispatcher as _EventDispatcher, EVENT_HANDLE_STATE
|
|
7
|
+
|
|
8
|
+
"""File dialog classes for opening and saving files.
|
|
9
|
+
|
|
10
|
+
This module provides example Dialog Windows for opening and
|
|
11
|
+
saving files. These are made using the `tkinter` module, which
|
|
12
|
+
is part of the Python standard library. Dialog Windows run in a
|
|
13
|
+
background process to prevent any interference with your main
|
|
14
|
+
application, and integrate using the standard pyglet Event
|
|
15
|
+
framework.
|
|
16
|
+
|
|
17
|
+
Note that these dialogs do not actually open or save any data
|
|
18
|
+
to disk. They simply return one or more strings, which contain
|
|
19
|
+
the final file paths that were selected or entered. You can
|
|
20
|
+
then use this information in your main application to handle
|
|
21
|
+
the disk IO. This is done by attaching an event handler to the
|
|
22
|
+
dialog, which will receive the file path(s) as an argument.
|
|
23
|
+
|
|
24
|
+
Create a `FileOpenDialog` instance, and attach a handler to it::
|
|
25
|
+
|
|
26
|
+
# Restrict to only showing ".png" and ".bmp" file types,
|
|
27
|
+
# and allow selecting more than one file
|
|
28
|
+
open_dialog = FileOpenDialog(filetypes=[("PNG", ".png"), ("24-bit Bitmap", ".bmp")], multiple=True)
|
|
29
|
+
|
|
30
|
+
# Multiple filetypes can be specified in the same string as long as a space is used. Wildcards are also accepted.
|
|
31
|
+
open_dialog = FileOpenDialog(filetypes=[("Images", "*.png *.bmp *.jpg")], multiple=True)
|
|
32
|
+
|
|
33
|
+
@open_dialog.event
|
|
34
|
+
def on_dialog_open(filenames):
|
|
35
|
+
print("list of selected filenames:", filenames)
|
|
36
|
+
# Your own code here to handle loading the file name(s).
|
|
37
|
+
|
|
38
|
+
# Show the Dialog whenever you need. This is non-blocking:
|
|
39
|
+
open_dialog.show()
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
The `FileSaveDialog` works similarly::
|
|
43
|
+
|
|
44
|
+
# Add a default file extension ".sav" to the file
|
|
45
|
+
save_as = FileSaveDialog(default_ext='.sav')
|
|
46
|
+
|
|
47
|
+
@save_as.event
|
|
48
|
+
def on_dialog_save(filename):
|
|
49
|
+
print("FILENAMES ON SAVE!", filename)
|
|
50
|
+
# Your own code here to handle saving the file name(s).
|
|
51
|
+
|
|
52
|
+
# Show the Dialog whenever you need. This is non-blocking:
|
|
53
|
+
open_dialog.show()
|
|
54
|
+
"""
|
|
55
|
+
|
|
56
|
+
_is_pyglet_doc_run = hasattr(sys, "is_pyglet_doc_run") and sys.is_pyglet_doc_run
|
|
57
|
+
|
|
58
|
+
class _OpenDialogMeta(type):
|
|
59
|
+
def __new__(cls, name: str, bases: tuple, attrs: dict) -> type:
|
|
60
|
+
if _is_pyglet_doc_run:
|
|
61
|
+
return super().__new__(cls, name, bases, attrs)
|
|
62
|
+
|
|
63
|
+
if pyglet.compat_platform == "win32":
|
|
64
|
+
from pyglet.window.dialog.windows import WindowsFileOpenDialog # noqa: PLC0415
|
|
65
|
+
return WindowsFileOpenDialog
|
|
66
|
+
if pyglet.compat_platform == "darwin":
|
|
67
|
+
from pyglet.window.dialog.darwin import MacOSFileOpenDialog # noqa: PLC0415
|
|
68
|
+
return MacOSFileOpenDialog
|
|
69
|
+
if pyglet.compat_platform == "linux":
|
|
70
|
+
from pyglet.window.dialog.linux import TkFileOpenDialog # noqa: PLC0415
|
|
71
|
+
return TkFileOpenDialog
|
|
72
|
+
|
|
73
|
+
return super().__new__(cls, name, bases, attrs)
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
class FileOpenDialog(_EventDispatcher, metaclass=_OpenDialogMeta):
|
|
77
|
+
"""Opens a system file dialog window for opening files.
|
|
78
|
+
|
|
79
|
+
.. versionadded:: 3.0
|
|
80
|
+
"""
|
|
81
|
+
def __init__(
|
|
82
|
+
self, title: str="Open File", initial_dir: str | Path | None= None, initial_file: str | None=None,
|
|
83
|
+
filetypes: list[tuple[str, str]] | None=None, multiple: bool=False) -> None:
|
|
84
|
+
"""Establish how the open file dialog will behave.
|
|
85
|
+
|
|
86
|
+
Args:
|
|
87
|
+
title:
|
|
88
|
+
The Dialog Window name. Defaults to "Open File".
|
|
89
|
+
initial_dir:
|
|
90
|
+
The directory to start in. If a path is not given, it is up the OS to determine behavior.
|
|
91
|
+
On Windows, if None is passed, it will open to the last used directory.
|
|
92
|
+
initial_file:
|
|
93
|
+
The filename to prepopulate with when opening. Not supported on Mac OS.
|
|
94
|
+
filetypes:
|
|
95
|
+
An optional list of tuples containing (name, extension) to filter by.
|
|
96
|
+
If none are given, all files will be shown and selectable.
|
|
97
|
+
For example: `[("PNG", ".png"), ("24-bit Bitmap", ".bmp")]`
|
|
98
|
+
For multiple file types in the same selection, separate by a semicolon.
|
|
99
|
+
For example: [("Images", ".png;.bmp")]`
|
|
100
|
+
multiple: bool
|
|
101
|
+
True if multiple files can be selected. Defaults to False.
|
|
102
|
+
"""
|
|
103
|
+
self.title = title
|
|
104
|
+
self.initial_dir = initial_dir
|
|
105
|
+
self.filetypes = filetypes
|
|
106
|
+
self.multiple = multiple
|
|
107
|
+
self.initial_file = initial_file
|
|
108
|
+
|
|
109
|
+
def open(self) -> None:
|
|
110
|
+
"""Open a file dialog window to select files according to the configuration."""
|
|
111
|
+
raise NotImplementedError
|
|
112
|
+
|
|
113
|
+
def on_dialog_open(self, filenames: list[str]) -> EVENT_HANDLE_STATE:
|
|
114
|
+
"""Event for filename choice.
|
|
115
|
+
|
|
116
|
+
Args:
|
|
117
|
+
filenames:
|
|
118
|
+
The selected filename paths chosen by the user. May be empty if nothing is chosen.
|
|
119
|
+
"""
|
|
120
|
+
|
|
121
|
+
class _SaveDialogMeta(type):
|
|
122
|
+
def __new__(cls, name: str, bases: tuple, attrs: dict) -> type:
|
|
123
|
+
if _is_pyglet_doc_run:
|
|
124
|
+
return super().__new__(cls, name, bases, attrs)
|
|
125
|
+
|
|
126
|
+
if pyglet.compat_platform == "win32":
|
|
127
|
+
from pyglet.window.dialog.windows import WindowsFileSaveDialog # noqa: PLC0415
|
|
128
|
+
return WindowsFileSaveDialog
|
|
129
|
+
if pyglet.compat_platform == "darwin":
|
|
130
|
+
from pyglet.window.dialog.darwin import MacOSFileSaveDialog # noqa: PLC0415
|
|
131
|
+
return MacOSFileSaveDialog
|
|
132
|
+
if pyglet.compat_platform == "linux":
|
|
133
|
+
from pyglet.window.dialog.linux import TkFileSaveDialog # noqa: PLC0415
|
|
134
|
+
return TkFileSaveDialog
|
|
135
|
+
|
|
136
|
+
return super().__new__(cls, name, bases, attrs)
|
|
137
|
+
|
|
138
|
+
class FileSaveDialog(_EventDispatcher, metaclass=_SaveDialogMeta):
|
|
139
|
+
"""Opens a system file dialog window for saving files.
|
|
140
|
+
|
|
141
|
+
.. versionadded:: 3.0
|
|
142
|
+
"""
|
|
143
|
+
def __init__(self, title: str="Save As", initial_dir: str | Path | None=None, initial_file: str | None=None,
|
|
144
|
+
filetypes: list[tuple[str, str]] | None=None, default_ext: str="") -> None:
|
|
145
|
+
"""Establish how the save file dialog will behave.
|
|
146
|
+
|
|
147
|
+
Args:
|
|
148
|
+
title:
|
|
149
|
+
The Dialog Window name. Defaults to "Save As".
|
|
150
|
+
initial_dir:
|
|
151
|
+
The directory to start in. If a path is not given, it is up the OS to determine behavior.
|
|
152
|
+
On Windows, if None is passed, it will open to the last used directory.
|
|
153
|
+
initial_file:
|
|
154
|
+
A default file name to be filled in. Defaults to None.
|
|
155
|
+
filetypes:
|
|
156
|
+
An optional list of tuples containing (name, extension) to
|
|
157
|
+
filter to. If the `default_ext` argument is not given, this list
|
|
158
|
+
also dictates the extension that will be added to the entered
|
|
159
|
+
file name. If a list of `filetypes` are not give, you can enter
|
|
160
|
+
any file name to save as.
|
|
161
|
+
For example: `[("PNG", ".png"), ("24-bit Bitmap", ".bmp")]`
|
|
162
|
+
default_ext:
|
|
163
|
+
A default file extension to add to the file. This will override
|
|
164
|
+
the `filetypes` list if given, but will not override a manually
|
|
165
|
+
entered extension.
|
|
166
|
+
"""
|
|
167
|
+
self.title = title
|
|
168
|
+
self.initial_dir = initial_dir or Path.cwd()
|
|
169
|
+
self.filetypes = filetypes
|
|
170
|
+
self.initial_file = initial_file
|
|
171
|
+
self.default_ext = default_ext
|
|
172
|
+
|
|
173
|
+
def open(self) -> None:
|
|
174
|
+
"""Open the file dialog window to save files according to the configuration."""
|
|
175
|
+
raise NotImplementedError
|
|
176
|
+
|
|
177
|
+
def on_dialog_save(self, filename: str) -> EVENT_HANDLE_STATE:
|
|
178
|
+
"""Event for filename choice.
|
|
179
|
+
|
|
180
|
+
Args:
|
|
181
|
+
filename:
|
|
182
|
+
The resulting filename a user input. The string may be empty if user input nothing or cancelled
|
|
183
|
+
the operation.
|
|
184
|
+
"""
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from abc import abstractmethod
|
|
4
|
+
|
|
5
|
+
from pyglet.event import EventDispatcher as _EventDispatcher
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class _Dialog(_EventDispatcher):
|
|
10
|
+
"""Dialog base class
|
|
11
|
+
|
|
12
|
+
This base class sets up a ProcessPoolExecutor with a single
|
|
13
|
+
background Process. This allows the Dialog to display in
|
|
14
|
+
the background without blocking or interfering with the main
|
|
15
|
+
application Process. This also limits to a single open Dialog
|
|
16
|
+
at a time.
|
|
17
|
+
"""
|
|
18
|
+
_dialog = None
|
|
19
|
+
|
|
20
|
+
@abstractmethod
|
|
21
|
+
def open(self):
|
|
22
|
+
...
|
|
23
|
+
|
|
24
|
+
def _dispatch_event(self, future):
|
|
25
|
+
raise NotImplementedError
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class FileOpenDialogBase(_Dialog):
|
|
29
|
+
def __init__(
|
|
30
|
+
self, title: str="Open File", initial_dir: str | Path | None = None, initial_file: str | None=None,
|
|
31
|
+
filetypes: list[tuple[str, str]] | None=None, multiple: bool=False,
|
|
32
|
+
):
|
|
33
|
+
"""Establish how the file dialog will behave.
|
|
34
|
+
|
|
35
|
+
title:
|
|
36
|
+
The Dialog Window name. Defaults to "Open File".
|
|
37
|
+
initial_dir:
|
|
38
|
+
The directory to start in. If a path is not given, it is up the OS to determine behavior.
|
|
39
|
+
On Windows, if None is passed, it will open to the last used directory.
|
|
40
|
+
initial_file:
|
|
41
|
+
The filename to prepopulate with when opening. Not supported on Mac OS.
|
|
42
|
+
filetypes:
|
|
43
|
+
An optional list of tuples containing (name, extension) to filter by.
|
|
44
|
+
If none are given, all files will be shown and selectable.
|
|
45
|
+
For example: `[("PNG", ".png"), ("24-bit Bitmap", ".bmp")]`
|
|
46
|
+
For multiple file types in the same selection, separate by a semicolon.
|
|
47
|
+
For example: [("Images", ".png;.bmp")]`
|
|
48
|
+
multiple: bool
|
|
49
|
+
True if multiple files can be selected. Defaults to False.
|
|
50
|
+
"""
|
|
51
|
+
self.title = title
|
|
52
|
+
self.initial_dir = initial_dir
|
|
53
|
+
self.filetypes = filetypes
|
|
54
|
+
self.multiple = multiple
|
|
55
|
+
self.initial_file = initial_file
|
|
56
|
+
|
|
57
|
+
def open(self):
|
|
58
|
+
raise NotImplementedError
|
|
59
|
+
|
|
60
|
+
FileOpenDialogBase.register_event_type('on_dialog_open')
|
|
61
|
+
|
|
62
|
+
class FileSaveDialogBase(_Dialog):
|
|
63
|
+
|
|
64
|
+
def __init__(self, title="Save As", initial_dir: str | Path | None=None, initial_file=None, filetypes=None, default_ext=""):
|
|
65
|
+
"""Establish how the save file dialog will behave.
|
|
66
|
+
|
|
67
|
+
title:
|
|
68
|
+
The Dialog Window name. Defaults to "Save As".
|
|
69
|
+
initial_dir:
|
|
70
|
+
The directory to start in. If a path is not given, it is up the OS to determine behavior.
|
|
71
|
+
On Windows, if None is passed, it will open to the last used directory.
|
|
72
|
+
initial_file:
|
|
73
|
+
A default file name to be filled in. Defaults to None.
|
|
74
|
+
filetypes:
|
|
75
|
+
An optional list of tuples containing (name, extension) to
|
|
76
|
+
filter to. If the `default_ext` argument is not given, this list
|
|
77
|
+
also dictates the extension that will be added to the entered
|
|
78
|
+
file name. If a list of `filetypes` are not give, you can enter
|
|
79
|
+
any file name to save as.
|
|
80
|
+
For example: `[("PNG", ".png"), ("24-bit Bitmap", ".bmp")]`
|
|
81
|
+
default_ext:
|
|
82
|
+
A default file extension to add to the file. This will override
|
|
83
|
+
the `filetypes` list if given, but will not override a manually
|
|
84
|
+
entered extension.
|
|
85
|
+
"""
|
|
86
|
+
self.title = title
|
|
87
|
+
self.initial_dir = initial_dir
|
|
88
|
+
self.filetypes = filetypes
|
|
89
|
+
self.initial_file = initial_file
|
|
90
|
+
self.default_ext = default_ext
|
|
91
|
+
|
|
92
|
+
def open(self) -> None:
|
|
93
|
+
raise NotImplementedError
|
|
94
|
+
|
|
95
|
+
def on_dialog_save(self, filename):
|
|
96
|
+
"""Event for filename choice"""
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
FileSaveDialogBase.register_event_type('on_dialog_save')
|