Skip to content
This repository was archived by the owner on Aug 31, 2021. It is now read-only.

Commit 3a87ac4

Browse files
authored
Merge pull request #6199 from livecodeali/feature-objc_protocol_proxy
[[ ObjcDelegates ]] Enable creation of objc delegates implementing protocol methods
2 parents a0280dc + 87345c6 commit 3a87ac4

File tree

8 files changed

+1621
-2
lines changed

8 files changed

+1621
-2
lines changed
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
# LiveCode Builder Language
2+
## Objective-C delegate support
3+
Handlers `CreateObjcDelegate` and `CreateObjcDelegateWithContext` have
4+
been added to the objc module which allow the creation of custom
5+
delegate objects with LCB implementations of protocol methods.
6+
7+
In order to create a delegate to handle a particular protocol method,
8+
pass in the protocol name as the first argument and the mapping from
9+
method names to LCB handlers as the second argument. For example, to
10+
create a selectionChanged message for an `NSTextView`, we need to create
11+
a handler
12+
13+
private handler DidChangeSelection(in pNotification as ObjcObject) returns nothing
14+
post "selectionChanged"
15+
end handler
16+
17+
and create a `NSTextViewDelegate`:
18+
19+
variable tDelegate as optional ObjcObject
20+
put CreateObjcDelegate( \
21+
"NSTextViewDelegate", \
22+
{"textViewDidChangeSelection:": DidChangeSelection}, \
23+
) into tDelegate
24+
if tDelegate is not nothing then
25+
put tDelegate into mTextViewDelegate
26+
end if
27+
28+
Optionally, a context parameter can be passed in at delegate creation
29+
time:
30+
31+
put CreateObjcDelegateWithContext( \
32+
"NSTextViewDelegate", \
33+
{"textViewDidChangeSelection:": DidChangeSelectionContext}, \
34+
tContext) into tDelegate
35+
36+
if tDelegate is not nothing then
37+
put tDelegate into mTextViewDelegate
38+
end if
39+
40+
In this case the context variable will be passed as first argument of
41+
the corresponding LCB callback:
42+
43+
private handler DidChangeSelectionContext(in pContext, in pNotification as ObjcObject) returns nothing
44+
post "selectionChanged" with [pContext]
45+
end handler
46+
47+
Some protocols consist of purely optional methods. In this case the
48+
information about the protocol's methods are not available from the
49+
objective-c runtime API. For this eventuality there are also handlers
50+
`CreateObjcInformalDelegate` and `CreateObjcInformalDelegateWithContext`.
51+
52+
These handlers take a list of foreign handlers as their first argument
53+
instead of a protocol name. The foreign handlers' information is used to
54+
resolve incoming selectors so that the desired LCB callback is called.
55+
For example the `NSSoundDelegate` protocol has only one method, and it
56+
is optional,
57+
58+
- (void)sound:(NSSound *)sound didFinishPlaying:(BOOL)aBool;
59+
60+
So in order to create an `NSSoundDelegate`, we need to create a list of
61+
foreign handlers, in this case just the following:
62+
63+
foreign handler NSSoundDidFinishPlaying(in pSound as ObjcId, in pDidFinish as CSChar) binds to "objc:.-sound:didFinishPlaying:"
64+
65+
and create the informal delegate
66+
67+
handler DidSoundFinish(in pSound as ObjcId, in pDidFinish as Boolean) returns nothing
68+
if pDidFinish then
69+
post "soundFinished"
70+
end if
71+
end handler
72+
73+
foreign handler Objc_SetSoundDelegate(in pSound as ObjcId, in pDelegate as ObjcId) returns nothing \
74+
binds to "objc:NSSound.-setDelegate:"
75+
...
76+
77+
variable tDelegate as optional ObjcObject
78+
put CreateObjcInformalDelegate( \
79+
[NSSoundDidFinishPlaying], \
80+
{"textViewDidChangeSelection:": DidChangeSelection}) \
81+
into tDelegate
82+
end if
83+
if tDelegate is not nothing then
84+
put tDelegate into mSoundDelegate
85+
Objc_SetSoundDelegate(tSound, tDelegate)
86+
end if
87+
88+
> *Note:* Delegate properties are usually 'assigned' rather than
89+
> 'retained', so it is necessary to store them in module variables
90+
> until they are no longer needed. Generally the pattern required is
91+
> as follows:
92+
93+
handler OnOpen()
94+
-- Create native view and set native layer
95+
-- Set native view delegate property
96+
-- Store view and delegate in module vars
97+
end handler
98+
99+
handler OnClose()
100+
-- Set native view delegate property to nothing
101+
-- Put nothing into view and delegate module vars
102+
-- Set native layer to nothing
103+
end handler

libfoundation/include/foundation-objc.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,4 +30,6 @@ NSString *MCNameConvertToAutoreleasedNSString(MCNameRef name);
3030
NSData *MCDataConvertToAutoreleasedNSData(MCDataRef data);
3131
#endif
3232

33+
extern MCTypeInfoRef kMCObjcDelegateCallbackSignatureErrorTypeInfo;
34+
extern MCTypeInfoRef kMCObjcDelegateMappingErrorTypeInfo;
3335
#endif

libfoundation/include/foundation.h

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3060,12 +3060,19 @@ MC_DLLEXPORT bool MCRecordIterate(MCRecordRef record, uintptr_t& x_iterator, MCN
30603060
// HANDLER DEFINITIONS
30613061
//
30623062

3063+
enum MCHandlerQueryType
3064+
{
3065+
kMCHandlerQueryTypeNone,
3066+
kMCHandlerQueryTypeObjcSelector,
3067+
};
3068+
30633069
struct MCHandlerCallbacks
30643070
{
30653071
size_t size;
30663072
void (*release)(void *context);
30673073
bool (*invoke)(void *context, MCValueRef *arguments, uindex_t argument_count, MCValueRef& r_value);
30683074
bool (*describe)(void *context, MCStringRef& r_desc);
3075+
bool (*query)(void *context, MCHandlerQueryType type, void *r_info);
30693076
};
30703077

30713078
MC_DLLEXPORT bool MCHandlerCreate(MCTypeInfoRef typeinfo, const MCHandlerCallbacks *callbacks, void *context, MCHandlerRef& r_handler);
@@ -3551,6 +3558,24 @@ MC_DLLEXPORT void *MCObjcObjectGetRetainedId(MCObjcObjectRef obj);
35513558
* an ObjcObject. The id is autoreleased before being returned. */
35523559
MC_DLLEXPORT void *MCObjcObjectGetAutoreleasedId(MCObjcObjectRef obj);
35533560

3561+
/* Create an ObjcObject containing an instance of the com_livecode_MCObjcFormalDelegate class,
3562+
* mapping protocol methods to MCHandlerRefs, with
3563+
* a context parameter. */
3564+
MC_DLLEXPORT bool MCObjcCreateDelegateWithContext(MCStringRef p_protocol_name, MCArrayRef p_handler_mapping, MCValueRef p_context, MCObjcObjectRef& r_object);
3565+
3566+
/* Create an ObjcObject containing an instance of the com_livecode_MCObjcFormalDelegate class,
3567+
* mapping protocol methods to MCHandlerRefs. */
3568+
MC_DLLEXPORT bool MCObjcCreateDelegate(MCStringRef p_protocol_name, MCArrayRef p_handler_mapping, MCObjcObjectRef& r_object);
3569+
3570+
/* Create an ObjcObject containing an instance of the com_livecode_MCObjcInformalDelegate class,
3571+
* mapping informal protocol methods specified as a list of foreign handler to MCHandlerRefs, with
3572+
* a context parameter. */
3573+
MC_DLLEXPORT bool MCObjcCreateInformalDelegateWithContext(MCProperListRef p_foreign_handlers, MCArrayRef p_handler_mapping, MCValueRef p_context, MCObjcObjectRef& r_object);
3574+
3575+
/* Create an ObjcObject containing an instance of the com_livecode_MCObjcInformalDelegate class,
3576+
* mapping informal protocol methods specified as a list of foreign handler to MCHandlerRefs. */
3577+
MC_DLLEXPORT bool MCObjcCreateInformalDelegate(MCProperListRef p_foreign_handlers, MCArrayRef p_handler_mapping, MCObjcObjectRef& r_object);
3578+
35543579
////////////////////////////////////////////////////////////////////////////////
35553580

35563581
enum MCPickleFieldType

libfoundation/src/foundation-objc-dummy.cpp

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,32 @@ MC_DLLEXPORT_DEF uintptr_t MCObjcObjectGetActionProxySelector(void)
276276

277277
////////////////////////////////////////////////////////////////////////////////
278278

279+
extern "C" MC_DLLEXPORT_DEF
280+
bool MCObjcCreateDelegateWithContext(MCStringRef p_protocol_name, MCArrayRef p_handler_mapping, MCValueRef p_context, MCObjcObjectRef& r_object)
281+
{
282+
return false;
283+
}
284+
285+
extern "C" MC_DLLEXPORT_DEF
286+
bool MCObjcCreateDelegate(MCStringRef p_protocol_name, MCArrayRef p_handler_mapping, MCObjcObjectRef& r_object)
287+
{
288+
return false;
289+
}
290+
291+
extern "C" MC_DLLEXPORT_DEF
292+
bool MCObjcCreateInformalDelegateWithContext(MCProperListRef p_foreign_handlers, MCArrayRef p_handler_mapping, MCValueRef p_context, MCObjcObjectRef& r_object)
293+
{
294+
return false;
295+
}
296+
297+
extern "C" MC_DLLEXPORT_DEF
298+
bool MCObjcCreateInformalDelegate(MCProperListRef p_foreign_handlers, MCArrayRef p_handler_mapping, MCObjcObjectRef& r_object)
299+
{
300+
return false;
301+
}
302+
303+
////////////////////////////////////////////////////////////////////////////////
304+
279305
bool __MCObjcInitialize(void)
280306
{
281307
if (!MCNamedCustomTypeInfoCreate(MCNAME("com.livecode.objc.ObjcObject"),

0 commit comments

Comments
 (0)