Source code for enamlnative.ios.app
"""
Copyright (c) 2017-2022, CodeLV.
Distributed under the terms of the MIT License.
The full license is in the file LICENSE, distributed with this software.
"""
import ctypes
from ctypes.util import find_library
from atom.api import Float, Typed, Value
from enaml.application import ProxyResolver
from enamlnative.core.app import BridgedApplication
from . import factories
from .bridge import ObjcBridgeObject, ObjcMethod
[docs]class ENBridge(ObjcBridgeObject):
"""Access ENBridge.m using ctypes.
Based on:
https://stackoverflow.com/questions/1490039/
calling-objective-c-functions-from-python#1490644
"""
#: Objc library
objc = Value()
#: Bridge.m access via ctypes
bridge = Value()
def _default_objc(self):
"""Load the objc library using ctypes."""
objc = ctypes.cdll.LoadLibrary(find_library("objc"))
objc.objc_getClass.restype = ctypes.c_void_p
objc.sel_registerName.restype = ctypes.c_void_p
objc.objc_msgSend.restype = ctypes.c_void_p
objc.objc_msgSend.argtypes = [ctypes.c_void_p, ctypes.c_void_p]
return objc
def _default_bridge(self):
"""Get an instance of the ENBridge object using ctypes."""
objc = self.objc
ENBridge = objc.objc_getClass("ENBridge")
return objc.objc_msgSend(ENBridge, objc.sel_registerName("instance"))
[docs] def processEvents(self, data):
"""Sends msgpack data to the ENBridge instance
by calling the processEvents method via ctypes."""
objc = self.objc
bridge = self.bridge
#: This must come after the above as it changes the arguments!
objc.objc_msgSend.argtypes = [
ctypes.c_void_p,
ctypes.c_void_p,
ctypes.c_char_p,
ctypes.c_int,
]
objc.objc_msgSend(
bridge, objc.sel_registerName("processEvents:length:"), data, len(data)
)
#: Add a target to a UIControl that invokes a python callback
addTarget = ObjcMethod(
"UIControl",
dict(forControlEvents="enum"), # ""UIControlEvents"),
dict(andCallback=int),
dict(usingMethod="NSString"),
dict(withValues="NSArray"),
)
[docs]class AppDelegate(ObjcBridgeObject):
pass
[docs]class ViewController(ObjcBridgeObject):
displayView = ObjcMethod("UIView")
[docs]class IPhoneApplication(BridgedApplication):
"""An iPhone implementation of an Enaml Native BridgedApplication.
An IPhoneApplication uses the native iOS widget toolkit to implement an
Enaml UI that runs in the local process.
Since Objective-C can easily use the Python C-API, much if this classes
implementation is done directly. For instance, the AppEventListener API is
implemented directly in Objective-C (in Bridge.m) and invokes methods
on this directly.
"""
#: AppDelegate widget
app_delegate = Typed(AppDelegate)
#: ViewControler
view_controller = Typed(ViewController)
#: ENBridge
bridge = Typed(ENBridge)
#: Pixel density of the device
#: Loaded immediately as this is used often.
dp = Float()
# --------------------------------------------------------------------------
# Defaults
# --------------------------------------------------------------------------
def _default_app_delegate(self):
"""Return a bridge object reference to the AppDelegate
this bridge sets this using a special id of -1
"""
return AppDelegate(__id__=-1)
def _default_view_controller(self):
"""Return a bridge object reference to the ViewController
the bridge sets this using a special id of -2
"""
return ViewController(__id__=-2)
def _default_bridge(self):
"""Access the bridge using ctypes. Everything else should use
bridge objects.
"""
return ENBridge(__id__=-4)
def _default_dp(self):
return 1.0
# -------------------------------------------------------------------------
# IPhoneApplication Constructor
# -------------------------------------------------------------------------
[docs] def __init__(self, *args, **kwargs):
"""Initialize a IPhoneApplication."""
super().__init__(*args, **kwargs)
self.resolver = ProxyResolver(factories=factories.IOS_FACTORIES)
# -------------------------------------------------------------------------
# Bridge API Implementation
# -------------------------------------------------------------------------
[docs] def show_view(self):
"""Show the current `app.view`. This will fade out the previous
with the new view.
"""
self.view_controller.displayView(self.get_view())
[docs] def dispatch_events(self, data):
"""Send the data to the Native application for processing"""
self.bridge.processEvents(data)
# -------------------------------------------------------------------------
# Plugin API Implementation
# -------------------------------------------------------------------------
[docs] def load_plugin_factories(self):
"""Add any plugin toolkit widgets to the ANDROID_FACTORIES"""
for plugin in self.get_plugins(group="enaml_native_ios_factories"):
get_factories = plugin.load()
PLUGIN_FACTORIES = get_factories()
factories.IOS_FACTORIES.update(PLUGIN_FACTORIES)