sync pyglet update (fix TextInput

This commit is contained in:
shenjack 2023-05-05 21:58:38 +08:00
parent b2818c0cd6
commit da2958de1e
10 changed files with 144 additions and 69 deletions

View File

@ -9,7 +9,7 @@ import sys
from typing import TYPE_CHECKING
#: The release version
version = '2.0.5'
version = '2.0.6'
__version__ = version
MIN_PYTHON_VERSION = 3, 8

View File

@ -3,6 +3,7 @@ from .base import Display, Screen, ScreenMode, Canvas
from pyglet.libs.win32 import _user32
from pyglet.libs.win32.constants import *
from pyglet.libs.win32.types import *
from pyglet.libs.win32.context_managers import device_context
class Win32Display(Display):
@ -30,13 +31,13 @@ class Win32Screen(Screen):
self._handle = handle
def get_matching_configs(self, template):
hdc = _user32.GetDC(0)
canvas = Win32Canvas(self.display, 0, hdc)
configs = template.match(canvas)
# XXX deprecate config's being screen-specific
for config in configs:
config.screen = self
_user32.ReleaseDC(0, hdc)
with device_context(None) as hdc:
canvas = Win32Canvas(self.display, 0, hdc)
configs = template.match(canvas)
# XXX deprecate config's being screen-specific
for config in configs:
config.screen = self
return configs
def get_device_name(self):

View File

@ -991,7 +991,7 @@ def unpack_rows(rows):
to being a sequence of bytes.
"""
for row in rows:
fmt = f'!{len(row)}'
fmt = f'!{len(row)}H'
yield bytearray(struct.pack(fmt, *row))
@ -1722,7 +1722,7 @@ class Reader:
"PLTE chunk is required before bKGD chunk.")
self.background = struct.unpack('B', data)
else:
self.background = struct.unpack(f"!{self.color_planes}",
self.background = struct.unpack(f"!{self.color_planes}H",
data)
except struct.error:
raise FormatError("bKGD chunk has incorrect length.")
@ -1744,7 +1744,7 @@ class Reader:
f"tRNS chunk is not valid with colour type {self.color_type}.")
try:
self.transparent = \
struct.unpack(f"!{self.color_planes}", data)
struct.unpack(f"!{self.color_planes}H", data)
except struct.error:
raise FormatError("tRNS chunk has incorrect length.")
@ -1977,7 +1977,7 @@ class Reader:
pixels = itertrns(pixels)
targetbitdepth = None
if self.sbit:
sbit = struct.unpack(f'{len(self.sbit)}', self.sbit)
sbit = struct.unpack(f'{len(self.sbit)}B', self.sbit)
targetbitdepth = max(sbit)
if targetbitdepth > info['bitdepth']:
raise Error(f'sBIT chunk {sbit!r} exceeds bitdepth {self.bitdepth}')

View File

@ -367,16 +367,16 @@ class TruetypeInfo:
# a fuckwit.
header = _read_cmap_format4Header(self._data, offset)
seg_count = header.seg_count_x2 // 2
array_size = struct.calcsize(f'>{seg_count}')
end_count = self._read_array(f'>{seg_count}',
array_size = struct.calcsize(f'>{seg_count}H')
end_count = self._read_array(f'>{seg_count}H',
offset + header.size)
start_count = self._read_array(f'>{seg_count}',
start_count = self._read_array(f'>{seg_count}H',
offset + header.size + array_size + 2)
id_delta = self._read_array(f'>{seg_count}',
id_delta = self._read_array(f'>{seg_count}H',
offset + header.size + array_size + 2 + array_size)
id_range_offset_address = \
offset + header.size + array_size + 2 + array_size + array_size
id_range_offset = self._read_array(f'>{seg_count}',
id_range_offset = self._read_array(f'>{seg_count}H',
id_range_offset_address)
character_map = {}
for i in range(0, seg_count):

View File

@ -9,6 +9,7 @@ from pyglet.font import base
from pyglet.font import win32query
import pyglet.image
from pyglet.libs.win32.constants import *
from pyglet.libs.win32.context_managers import device_context
from pyglet.libs.win32.types import *
from pyglet.libs.win32 import _gdi32 as gdi32, _user32 as user32
from pyglet.libs.win32 import _kernel32 as kernel32
@ -195,14 +196,13 @@ class Win32Font(base.Font):
self.hfont = gdi32.CreateFontIndirectW(byref(self.logfont))
# Create a dummy DC for coordinate mapping
dc = user32.GetDC(0)
metrics = TEXTMETRIC()
gdi32.SelectObject(dc, self.hfont)
gdi32.GetTextMetricsA(dc, byref(metrics))
self.ascent = metrics.tmAscent
self.descent = -metrics.tmDescent
self.max_glyph_width = metrics.tmMaxCharWidth
user32.ReleaseDC(0, dc)
with device_context(None) as dc:
metrics = TEXTMETRIC()
gdi32.SelectObject(dc, self.hfont)
gdi32.GetTextMetricsA(dc, byref(metrics))
self.ascent = metrics.tmAscent
self.descent = -metrics.tmDescent
self.max_glyph_width = metrics.tmMaxCharWidth
def __del__(self):
gdi32.DeleteObject(self.hfont)
@ -210,22 +210,22 @@ class Win32Font(base.Font):
@staticmethod
def get_logfont(name, size, bold, italic, dpi):
# Create a dummy DC for coordinate mapping
dc = user32.GetDC(0)
if dpi is None:
dpi = 96
logpixelsy = dpi
with device_context(None) as dc:
if dpi is None:
dpi = 96
logpixelsy = dpi
logfont = LOGFONTW()
# Conversion of point size to device pixels
logfont.lfHeight = int(-size * logpixelsy // 72)
if bold:
logfont.lfWeight = FW_BOLD
else:
logfont.lfWeight = FW_NORMAL
logfont.lfItalic = italic
logfont.lfFaceName = name
logfont.lfQuality = ANTIALIASED_QUALITY
logfont = LOGFONTW()
# Conversion of point size to device pixels
logfont.lfHeight = int(-size * logpixelsy // 72)
if bold:
logfont.lfWeight = FW_BOLD
else:
logfont.lfWeight = FW_NORMAL
logfont.lfItalic = italic
logfont.lfFaceName = name
logfont.lfQuality = ANTIALIASED_QUALITY
user32.ReleaseDC(0, dc)
return logfont
@classmethod

View File

@ -71,6 +71,7 @@ appropriate typeface name and create the font using CreateFont or
CreateFontIndirect.
"""
from pyglet.libs.win32.context_managers import device_context
DEBUG = False
@ -385,36 +386,33 @@ def query(charset=DEFAULT_CHARSET):
global FONTDB
# 1. Get device context of the entire screen
hdc = user32.GetDC(None)
with device_context(None) as hdc:
# 2. Call EnumFontFamiliesExA (ANSI version)
# 2. Call EnumFontFamiliesExA (ANSI version)
# 2a. Call with empty font name to query all available fonts
# (or fonts for the specified charset)
#
# NOTES:
#
# * there are fonts that don't support ANSI charset
# * for DEFAULT_CHARSET font is passed to callback function as
# many times as charsets it supports
# 2a. Call with empty font name to query all available fonts
# (or fonts for the specified charset)
#
# NOTES:
#
# * there are fonts that don't support ANSI charset
# * for DEFAULT_CHARSET font is passed to callback function as
# many times as charsets it supports
# [ ] font name should be less than 32 symbols with terminating \0
# [ ] check double purpose - enumerate all available font names
# - enumerate all available charsets for a single font
# - other params?
# [ ] font name should be less than 32 symbols with terminating \0
# [ ] check double purpose - enumerate all available font names
# - enumerate all available charsets for a single font
# - other params?
logfont = LOGFONTW(0, 0, 0, 0, 0, 0, 0, 0, charset, 0, 0, 0, 0, '')
FONTDB = [] # clear cached FONTDB for enum_font_names callback
res = gdi32.EnumFontFamiliesExW(
hdc, # handle to device context
ctypes.byref(logfont),
enum_font_names, # pointer to callback function
0, # lParam - application-supplied data
0) # dwFlags - reserved = 0
# res here is the last value returned by callback function
# 3. Release DC
user32.ReleaseDC(None, hdc)
logfont = LOGFONTW(0, 0, 0, 0, 0, 0, 0, 0, charset, 0, 0, 0, 0, '')
FONTDB = [] # clear cached FONTDB for enum_font_names callback
res = gdi32.EnumFontFamiliesExW(
hdc, # handle to device context
ctypes.byref(logfont),
enum_font_names, # pointer to callback function
0, # lParam - application-supplied data
0) # dwFlags - reserved = 0
# res here is the last value returned by callback function
return FONTDB

View File

@ -438,6 +438,7 @@ class TextEntry(WidgetBase):
def _set_focus(self, value):
self._focus = value
self._caret.visible = value
self._caret.layout = self._layout
def update_groups(self, order):
self._outline.group = Group(order=order + 1, parent=self._user_group)

View File

@ -0,0 +1,57 @@
"""
Win32 resources as handy context managers.
These are intended to help keep loader code clean & stable at the price
of a tiny bit of execution speed. Performance-critical code should avoid
using these helpers in favor of direct win32 API calls.
Before::
def loader_function(arg):
context_handle = user32.GetResource(None)
result = calculation(arg)
# Easily forgotten!
user32.ReleaseResource(context_handle)
return result
After::
def loader_function(arg):
with resource_context() as context_handle:
result = calculation(arg)
return result
"""
from contextlib import contextmanager
from typing import Optional, Generator
from ctypes.wintypes import HANDLE
from ctypes import WinError
from pyglet.libs.win32 import _user32 as user32
@contextmanager
def device_context(window_handle: Optional[int] = None) -> Generator[HANDLE, None, None]:
"""
A Windows device context wrapped as a context manager.
Args:
window_handle: A specific window handle to use, if any.
Raises:
WinError: Raises if a device context cannot be acquired or released
Yields:
HANDLE: the managed drawing context handle to auto-close.
"""
if not (_dc := user32.GetDC(window_handle)):
raise WinError()
try:
yield _dc
finally:
if not user32.ReleaseDC(None, _dc):
raise WinError()

View File

@ -85,8 +85,10 @@ class Caret:
"""
from pyglet import gl
self._layout = layout
batch = batch or layout.batch
group = layout.foreground_decoration_group
self._custom_batch = True if batch else False
self._batch = batch or layout.batch
self._group = layout.foreground_decoration_group
# Handle both 3 and 4 byte colors
r, g, b, *a = color
@ -96,7 +98,7 @@ class Caret:
colors = r, g, b, self._visible_alpha, r, g, b, self._visible_alpha
self._list = group.program.vertex_list(2, gl.GL_LINES, batch, group, colors=('Bn', colors))
self._list = self._group.program.vertex_list(2, gl.GL_LINES, batch, self._group, colors=('Bn', colors))
self._ideal_x = None
self._ideal_line = None
self._next_attributes = {}
@ -105,6 +107,21 @@ class Caret:
layout.push_handlers(self)
@property
def layout(self):
return self._layout
@layout.setter
def layout(self, layout):
if self._layout == layout and self._group == layout.group:
return
from pyglet import gl
self._layout = layout
batch = self._batch if self._custom_batch else layout.batch
self._group = layout.foreground_decoration_group
self._batch.migrate(self._list, gl.GL_LINES, self._group, batch)
def delete(self):
"""Remove the caret from its batch.

View File

@ -958,6 +958,7 @@ class TextLayout:
def group(self, group):
self._user_group = group
self._initialize_groups()
self.group_cache.clear()
self._update()
@property