Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions android/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ Label = "toga_android.widgets.label:Label"
MapView = "toga_android.widgets.mapview:MapView"
MultilineTextInput = "toga_android.widgets.multilinetextinput:MultilineTextInput"
NumberInput = "toga_android.widgets.numberinput:NumberInput"
OpenGLView = "toga_android.widgets.openglview:OpenGLView"
OptionContainer = "toga_android.widgets.optioncontainer:OptionContainer"
PasswordInput = "toga_android.widgets.passwordinput:PasswordInput"
ProgressBar = "toga_android.widgets.progressbar:ProgressBar"
Expand Down
67 changes: 67 additions & 0 deletions android/src/toga_android/widgets/openglview.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
from android.opengl import GLES20 as GL, GLSurfaceView
from java import dynamic_proxy

from toga.widgets.gl.openglcontext import OpenGLContext

from .base import Widget


class AndroidOpenGLContext(OpenGLContext):
"""The implementation layer of a Qt OpenGL context."""

def clear_color(self, r: float, g: float, b: float, a: float):
GL.glClearColor(r, g, b, a)

def clear(self, mask: int):
GL.glClear(mask)

def create_shader(self, type: int):
return GL.glCreateShader(type)

def shader_source(self, shader: int, source: str):
GL.glShaderSource(shader, source)

def compile_shader(self, shader: int):
GL.glCompileShader(shader)

def delete_shader(self, shader):
GL.glDeleteShader(shader)

def create_program(self) -> int:
return GL.glCreateProgram()

def attach_shader(self, program: int, shader: int):
GL.glAttachShader(program, shader)

def link_program(self, program: int):
GL.glLinkProgram(program)

def delete_program(self, program):
GL.glDeleteProgram(program)


class TogaGLRenderer(dynamic_proxy(GLSurfaceView.Renderer)):
def onSurfaceCreated(self, unused, config):
pass

def onDrawFrame(self, unused):
self._redraw()

def onSurfaceChanged(self, unused, width, height):
self._redraw()

def _redraw(self):
context = AndroidOpenGLContext()
self.interface.on_render(context)


class OpenGLView(Widget):
def create(self):
self.native = GLSurfaceView(self._native_activity)
self.renderer = TogaGLRenderer()
self.renderer.interface = self.interface
self.native.setEGLContextClientVersion(2)
self.native.setRenderer(self.renderer)

def redraw(self):
self.native.invalidate()
1 change: 1 addition & 0 deletions cocoa/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ Label = "toga_cocoa.widgets.label:Label"
MapView = "toga_cocoa.widgets.mapview:MapView"
MultilineTextInput = "toga_cocoa.widgets.multilinetextinput:MultilineTextInput"
NumberInput = "toga_cocoa.widgets.numberinput:NumberInput"
OpenGLView = "toga_cocoa.widgets.openglview:OpenGLView"
OptionContainer = "toga_cocoa.widgets.optioncontainer:OptionContainer"
PasswordInput = "toga_cocoa.widgets.passwordinput:PasswordInput"
ProgressBar = "toga_cocoa.widgets.progressbar:ProgressBar"
Expand Down
11 changes: 11 additions & 0 deletions cocoa/src/toga_cocoa/libs/appkit.py
Original file line number Diff line number Diff line change
Expand Up @@ -518,10 +518,21 @@ class NSLayoutPriority(Enum):
NSOpenGLPFARemotePixelBuffer = 91 # can be used to render offline to a pbuffer
NSOpenGLPFAAllowOfflineRenderers = 96 # allow use of offline renderers
NSOpenGLPFAAcceleratedCompute = 97 # choose a hardware accelerated compute device
NSOpenGLPFAOpenGLProfile = 99 # specify an OpenGL Profile to use
NSOpenGLPFAVirtualScreenCount = 128 # number of virtual screens in this format

NSOpenGLProfileVersionLegacy = 0x1000 # choose a Legacy/Pre-OpenGL 3.0 Implementation
NSOpenGLProfileVersion3_2Core = 0x3200 # choose an OpenGL 3.2 Core Implementation
NSOpenGLProfileVersion4_1Core = 0x4100 # choose an OpenGL 4.1 Core Implementation

NSOpenGLCPSwapInterval = 222

######################################################################
# NSOpenGLView.h
NSOpenGLView = ObjCClass("NSOpenGLView")
NSOpenGLContext = ObjCClass("NSOpenGLContext")
NSOpenGLPixelFormat = ObjCClass("NSOpenGLPixelFormat")

######################################################################
# NSOpenPanel.h
NSOpenPanel = ObjCClass("NSOpenPanel")
Expand Down
113 changes: 113 additions & 0 deletions cocoa/src/toga_cocoa/widgets/openglview.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
import ctypes
from ctypes import c_float, cdll
from ctypes.util import find_library

from rubicon.objc import objc_method, objc_property
from travertino.size import at_least

from toga.widgets.gl.openglcontext import OpenGLContext
from toga_cocoa.libs import (
NSOpenGLPFAAccelerated,
NSOpenGLPFAAccumSize,
NSOpenGLPFAAlphaSize,
NSOpenGLPFAColorSize,
NSOpenGLPFADepthSize,
NSOpenGLPFADoubleBuffer,
NSOpenGLPFANoRecovery,
NSOpenGLPFAStencilSize,
NSOpenGLPFAWindow,
NSOpenGLPixelFormat,
NSOpenGLView,
NSRect,
)

from .base import Widget

# possibly use PyOpenGL instead?
GL = cdll.LoadLibrary(find_library("OpenGL"))


class CocoaOpenGLContext(OpenGLContext):
def clear_color(self, r: float, g: float, b: float, a: float):
GL.glClearColor(c_float(r), c_float(g), c_float(b), c_float(a))

def clear(self, mask: int):
GL.glClear(mask)

def create_shader(self, type: int):
return GL.glCreateShader(type)

def shader_source(self, shader: int, source: str):
GL.glShaderSource(shader, source)

def compile_shader(self, shader: int):
GL.glCompileShader(shader)

def delete_shader(self, shader: int):
GL.glDeleteShader(shader)

def create_program(self) -> int:
return GL.glCreateProgram()

def attach_shader(self, program: int, shader: int):
GL.glAttachShader(program, shader)

def link_program(self, program: int):
GL.glLinkProgram(program)

def delete_program(self, program):
GL.glDeleteProgram(program)


class TogaOpenGLView(NSOpenGLView):
interface = objc_property(object, weak=True)
impl = objc_property(object, weak=True)

@objc_method
def drawRect_(self, rect: NSRect) -> None:
self.openGLContext.makeCurrentContext()
context = self.openGLContext
GL.glViewport(0, 0, int(rect.size.width), int(rect.size.height))
self.interface.on_render(CocoaOpenGLContext())
context.flushBuffer()

@objc_method
def initWithFrame_(self, frame: NSRect):
a = (
NSOpenGLPFANoRecovery,
NSOpenGLPFAWindow,
NSOpenGLPFAAccelerated,
NSOpenGLPFADoubleBuffer,
NSOpenGLPFAColorSize,
24,
NSOpenGLPFAAlphaSize,
8,
NSOpenGLPFADepthSize,
24,
NSOpenGLPFAStencilSize,
8,
NSOpenGLPFAAccumSize,
0,
)
attributes = (ctypes.c_uint32 * len(a))(*a)
pixel_format = NSOpenGLPixelFormat.alloc().initWithAttributes_(attributes)
return self.initWithFrame_pixelFormat_(frame, pixel_format)


class OpenGLView(Widget):
def create(self):
self.native = TogaOpenGLView.alloc().init()
self.native.interface = self.interface
self.native.impl = self

# Add the layout constraints
self.add_constraints()

def redraw(self):
self.native.needsDisplay = True

# Rehint
def rehint(self):
fitting_size = self.native.fittingSize()
self.interface.intrinsic.height = at_least(fitting_size.height)
self.interface.intrinsic.width = at_least(fitting_size.width)
1 change: 1 addition & 0 deletions core/src/toga/__init__.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ from toga.widgets.multilinetextinput import MultilineTextInput as MultilineTextI
from toga.widgets.numberinput import NumberInput as NumberInput
from toga.widgets.optioncontainer import OptionContainer as OptionContainer
from toga.widgets.optioncontainer import OptionItem as OptionItem
from toga.widgets.gl.openglview import OpenGLView as OpenGLView
from toga.widgets.passwordinput import PasswordInput as PasswordInput
from toga.widgets.progressbar import ProgressBar as ProgressBar
from toga.widgets.box import Row as Row
Expand Down
Empty file.
Loading
Loading