]> git.saurik.com Git - wxWidgets.git/blob - docs/cocoa/coding_patterns.txt
Compilation fix for wxMSW with wxUSE_MSGBOX_HOOK==0.
[wxWidgets.git] / docs / cocoa / coding_patterns.txt
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