~brian-sidebotham/wxwidgets-cmake/wxpython-2.9.4

« back to all changes in this revision

Viewing changes to docs/cocoa/coding_patterns.txt

  • Committer: Brian Sidebotham
  • Date: 2013-08-03 14:30:08 UTC
  • Revision ID: brian.sidebotham@gmail.com-20130803143008-c7806tkych1tp6fc
Initial import into Bazaar

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
=== wxCocoa coding patterns ===
 
2
 
 
3
Any language or library tends to have a particular set of coding patterns that serve to make the code easier to read
 
4
by making it look consistent across the project.  Objective-C makes particularly heavy use of patterns as does wxWidgets.
 
5
It is not the intention of this document to repeat Cocoa or wxWidgets documentation except for clarity.
 
6
 
 
7
--- Class design ---
 
8
 
 
9
wxCocoa takes a rather unique approach by decoupling interaction between C++ and Objective-C from the wxWidgets classes.
 
10
For any given Objective-C class you wish to override messages from or receive action messages from (e.g. as a delegate
 
11
or notification observer) you should implement a C++ wxCocoa##ObjcClass class and one or more Objective-C classes.
 
12
 
 
13
The C++ class goes in a file include/wx/cocoa/ObjcClass.h (where ObjcClass is the Objective-C class name) and the
 
14
Objective-C classes can either be declared in the implementation file (src/cocoa/ObjcClass.h) or separated into an
 
15
include/wx/cocoa/objc/ObjcClass.h file.
 
16
 
 
17
Take NSButton as an example.  The include/wx/cocoa/NSButton.h declares a wxCocoaNSButton class.  Classes such as
 
18
wxButton, wxCheckBox, and wxRadioButton all multiply inherit from this (protected).  These classes can almost
 
19
be thought of as an interface whereby the inheriting class is essentially declaring that it is able to respond
 
20
to the various Cocoa_ methods that will be called.  It is not quite a pure interface as it actually contains the
 
21
logic for this as well, but it can be thought of from a design perspective as such.
 
22
 
 
23
Because we do not wish to subclass Objective-C classes except when absolutely necessary we use a hash map so
 
24
that the wxCocoaObjcClass instance can be retrieved knowing only the ObjcClass instance.  This is acheived by
 
25
the sm_cocoaHash static member and the GetFromCocoa method.  These are provided by the HASHMAP series of macros
 
26
in the include/wx/cocoa/ObjcAssociate.h header.
 
27
 
 
28
In addition to the GetFromCocoa method, the pattern also provides for a pair of Associate##ObjcClass and
 
29
Disassociate##ObjcClass methods.  These non-virtual methods if implemented by the macro merely insert and
 
30
remove the Objective-C/C++ pair from the hash map.  More often than not they require more than just associating
 
31
using the hash map but also require setTarget: and setAction: to be called.  This is a leftover of the original
 
32
design where it was expected that the classes would be subclasses already containing the code to call the
 
33
C++ virtual methods.  Later design decisions changed this to use target/action and delegates whenever possible
 
34
which is more often the case than not.
 
35
 
 
36
To implement a response to an action message, one should simply create a singleton instance of a controller class
 
37
that can be used for all instances of the given Objective-C class.  For NSButton there is the wxNSButtonTarget
 
38
class which implements the (arbitrarily named) wxNSButtonAction: method.  The wxCocoaNSButton::AssociateNSButton
 
39
method is implemented to setTarget:sm_cocoaTarget (the singleton wxNSButtonTarget) and
 
40
setAction:@selector(wxNSButtonAction:).  When the button is clicked, the NSButton will send a wxNSButtonAction:
 
41
message to its target (the singleton wxNSButtonTarget) with itself as the sender.  The implementation of
 
42
that message simply looks up the wxCocoaNSButton in the hash map and calls the Cocoa_wxNSButtonAction method.
 
43
 
 
44
The wxWidgets class (e.g. wxButton or wxCheckBox) implements that method as it sees fit.  For example, to
 
45
simply send the corresponding wxWidgets wxEvent.
 
46
 
 
47
It should be noted that a better design might have used a generic target/action handler since target/action isn't
 
48
actually specific to buttons.  This might be a future design change.
 
49
 
 
50
Of note, wxCocoaNSButton does not inherit from anything, particularly from wxCocoaNSControl.  This is because
 
51
of the C++ non-virtual base class problem.  Instead, wxControl inherits from wxControlBase and wxCocoaNSControl.
 
52
wxButtonBase in turn inherits from wxControl and wxButton in turn inherits from wxButtonBase and wxCocoaNSButton.
 
53
 
 
54
One may be wondering how NSControl events (if any) make their way to the wxControl.  The answer is in the way
 
55
the Associate* methods are called.  This is where the Set* methods come in.
 
56
 
 
57
Within the wxWidgets class (e.g. wxButton) there is a SetNSButton(NSButton*) method.  This method calls
 
58
AssociateNSButton and DisassociateNSButton appropriately and also calls the base class SetNSControl implemented
 
59
by the wxControl class (note: not the wxCocoaNSControl class).  SetNSControl does a similar thing but then
 
60
calls its base class SetNSView method.  All of these are implemented using the same macro except for SetNSView
 
61
which is implemented to do proper retain/release and set the m_cocoaNSView instance variable in wxWindow.
 
62
 
 
63
In addition to the Set* set of methods, there is also a Get* set.  These are implemented (inline) to cast
 
64
the root class pointer type to the desired type.   For instance, GetNSButton merely returns
 
65
(NSButton*)m_cocoaNSView.  These are a convenience for coding the library itself and are also public such that
 
66
users of wxCocoa wishing to make Cocoa-specific calls can easily get at a properly-typed instance.
 
67
 
 
68
This works well for the common case like a button or checkbox where one Cocoa class clearly represents one
 
69
wxWidgets class.  For more complex cases involving a Cocoa view hierarchy one may need to implement these
 
70
methods in a different manner.
 
71
 
 
72
 
 
73
--- The view hierarchy ---
 
74
 
 
75
Because the Cocoa view hierarchy isn't a perfect match with the wxWidgets hierarchy, there are some conventions
 
76
used to resolve this conflict.  The first is that m_cocoaNSView is defined to be the view which most-closely
 
77
represents the wxWidgets view.  For instance, a wxButton has an NSButton instance and a wxStaticBox has an NSBox
 
78
instance.  Unfortunately, wxWidgets defines some behaviour that Cocoa cannot directly implement.  This is primarily
 
79
window scrolling (e.g. without using a wxScrolledWindow) and window hiding.
 
80
 
 
81
Scrolling is implemented in a separate class known as wxWindowCocoaScrollView.  This class does not fit into
 
82
the wxWidgets class hierarchy but instead implements the wxCocoaNSView interface itself, including listening for
 
83
the Cocoa_FrameChanged notification.  This is a good example of why the Objective-C to C++ shim code is
 
84
unrelated to the wxWidgets class hierarchy.  As you can clearly see, it allows the shim code to be used for
 
85
classes that aren't part of the wxWidgets hierarchy.
 
86
 
 
87
Hiding is implemented in another class known as wxWindowCocoaHider in a similar manner to wxWindowCocoaScrollView.
 
88
This is an artifact of the pre-Panther days of Cocoa where there was no method for hiding a view.
 
89
 
 
90
What these classes do is provide a Cocoa view that sits between the wxWidget's parent window's view and the
 
91
m_cocoaNSView provided by the window.  The wxWindow class has a GetNSViewForSuperview() method that returns either
 
92
the m_cocoaNSView (if the window does not need scrolling behaviour and is not hidden) or returns the scroll view
 
93
for the case of scrolling or the dummy view in the case of hiding.  As the name suggests, the method is used
 
94
from the parent wxWindow (the superview) when it sends something like an addSubview: message.  The method is under
 
95
no circumstances intended to be used as the receiver of an addSubview message.  In fact, not even the GetNSView()
 
96
method should be used for this as in [m_parent->GetNSView() addSubview:GetNSViewForSuperView()] because this
 
97
functionality is provided by the CocoaAddChild method.
 
98
 
 
99
Note that there is a small hole in the API here because classes other than wxWindow wishing to implement a view
 
100
hierarchy will not be able to correctly do this since CocoaAddChild is not virtual and there is no virtual
 
101
GetNSViewForSubviews() method.
 
102