nciagra (owner)

Forks

Revisions

gist: 158757 Download_button fork
public
Description:
CPNavigationController & CPViewController
Public Clone URL: git://gist.github.com/158757.git
Embed All Files: show embed
CPNavigationBar.j #
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
/*
* CPNavigationBar.j
* AppKit
*
* Created by Nicholas Small.
* Copyright 2009, 280 North, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
 
@import <AppKit/CPView.j>
 
 
@implementation CPNavigationBar : CPView
{
    id _delegate;
    
    CPArray _items;
    
    CPTextField _titleView;
    CPView _backView;
    CPView _actionView;
    
    CPView _leftView;
    CPView _rightView;
}
 
- (id)init
{
    self = [super initWithFrame:CGRectMakeZero()];
    
    if (self)
    {
        [self setAutoresizingMask:CPViewWidthSizable];
        [self setBackgroundColor:[CPColor colorWithPatternImage:[[CPImage alloc] initWithContentsOfFile:@"Resources/BrowserBar.png" size:CGSizeMake(1.0, 36.0)]]];
    }
    
    return self;
}
 
- (void)viewWillMoveToSuperview:(CPView)aView
{
    [self setFrame:CGRectMake(0.0, 0.0, CGRectGetWidth([aView bounds]), 36.0)];
}
 
- (void)pushNavigationItem:(CPNavigationItem)anItem animated:(BOOL)animated
{
    animated = !!animated;
    
    if ([_items containsObject:anItem])
        return;
    
    if (!_items)
        _items = [CPArray array];
    
    if ([_delegate respondsToSelector:@selector(navigationBar:shouldPushItem:)])
        if (![_delegate navigationBar:self shouldPushItem:anItem])
            return;
    
    ([_items lastObject] || {})._navigationBar = nil;
    
    anItem._navigationBar = self;
    [_items addObject:anItem];
    
    [self _layoutSubviewsAnimated:animated];
    
    if ([_delegate respondsToSelector:@selector(navigationBar:didPushItem:)])
        [_delegate navigationBar:self didPushItem:anItem];
}
 
- (void)popToNavigationItem:(CPNavigationItem)anItem animated:(BOOL)animated
{
    animated = !!animated;
    
    if (!_items || [_items count] <= 1)
        return;
    
    if ([_delegate respondsToSelector:@selector(navigationBar:shouldPopItem:)])
        if (![_delegate navigationBar:self shouldPopItem:anItem])
            return;
    
    var item = [_items lastObject];
    if (item)
        item._navigationBar = nil;
    
    var count = [_items count] - 1,
        index = [_items indexOfObject:anItem];
    
    for (; count > index; count--)
        [_items removeLastObject];
    
    var item = [_items lastObject];
    if (item)
        item._navigationBar = self;
    
    [self _layoutSubviewsAnimated:animated];
    
    if ([_delegate respondsToSelector:@selector(navigationBar:didPopItem:)])
        [_delegate navigationBar:self didPopItem:anItem];
}
 
- (void)popNavigationItemAnimated:(BOOL)animated
{
    [self popToNavigationItem:[self backItem] animated:animated];
}
 
- (void)setItems:(CPArray)anArray animated:(BOOL)animated
{
    _items = [CPArray array];
    
    var count = [anArray count];
    for (var i = 0; i < count; i++)
        [self pushNavigationItem:anArray[i] animated:(animated && i == count - 1) ? animated : NO];
}
 
- (CPArray)items
{
    return _items;
}
 
- (CPNavigationItem)topItem
{
    return [_items lastObject];
}
 
- (CPNavigationItem)backItem
{
    return [_items objectAtIndex:[_items count] - 2];
}
 
- (void)setDelegate:(id)aDelegate
{
    _delegate = aDelegate;
}
 
- (id)delegate
{
    return _delegate;
}
 
// Views
 
- (void)_setTitleView:(CPView)aView animated:(BOOL)animated
{
    if (_titleView)
    {
        if (animated)
        {
            var anim = [[CPPropertyAnimation alloc] initWithView:_titleView];
            [anim addProperty:@"alphaValue" start:1.0 end:0.0];
            [anim setDuration:0.3];
            [anim removeFromSuperviewOnEnd:YES];
            [anim startAnimation];
        }
        else
            [_titleView removeFromSuperview];
    }
    
    _titleView = aView;
    
    if (_titleView)
    {
        [_titleView setFrameOrigin:CGPointMake(CGRectGetMidX([self bounds]) - CGRectGetMidX([_titleView bounds]), 3.0)];
        [_titleView setAutoresizingMask:CPViewMinXMargin | CPViewMaxXMargin];
        
        if (animated)
        {
            var anim = [[CPPropertyAnimation alloc] initWithView:_titleView];
            [anim addProperty:@"alphaValue" start:0.0 end:1.0];
            [anim setDuration:0.3];
            [anim addToViewOnStart:self];
            [anim startAnimation];
        }
        else
            [self addSubview:_titleView];
    }
}
 
- (CPView)titleView
{
    return _titleView;
}
 
- (void)_setBackView:(CPView)aView animated:(BOOL)animated
{
    if (_backView)
    {
        if (animated)
        {
            var anim = [[CPPropertyAnimation alloc] initWithView:_backView];
            [anim addProperty:@"alphaValue" start:1.0 end:0.0];
            [anim setDuration:0.3];
            [anim removeFromSuperviewOnEnd:YES];
            [anim startAnimation];
        }
        else
            [_backView removeFromSuperview];
    }
    
    _backView = aView;
    
    if (_backView)
    {
        [_backView setFrameOrigin:CGPointMake(CGRectGetMinX([_titleView frame]) - CGRectGetWidth([_backView bounds]) - 20.0, 6.0)];
        [_backView setAutoresizingMask:CPViewMinXMargin | CPViewMaxXMargin];
        
        if (animated)
        {
            var anim = [[CPPropertyAnimation alloc] initWithView:_backView];
            [anim addProperty:@"alphaValue" start:0.0 end:1.0];
            [anim setDuration:0.3];
            [anim addToViewOnStart:self];
            [anim startAnimation];
        }
        else
            [self addSubview:_backView];
    }
}
 
- (CPView)backView
{
    return _backView;
}
 
- (void)_setActionView:(CPView)aView animated:(BOOL)animated
{
    if (_actionView)
    {
        if (animated)
        {
            var anim = [[CPPropertyAnimation alloc] initWithView:_actionView];
            [anim addProperty:@"alphaValue" start:1.0 end:0.0];
            [anim setDuration:0.3];
            [anim removeFromSuperviewOnEnd:YES];
            [anim startAnimation];
        }
        else
            [_actionView removeFromSuperview];
    }
    
    _actionView = aView;
    
    if (_actionView)
    {
        [_actionView setFrameOrigin:CGPointMake(CGRectGetMaxX([_titleView frame]) + 20.0, 6.0)];
        [_actionView setAutoresizingMask:CPViewMinXMargin | CPViewMaxXMargin];
        
        if (animated)
        {
            var anim = [[CPPropertyAnimation alloc] initWithView:_actionView];
            [anim addProperty:@"alphaValue" start:0.0 end:1.0];
            [anim setDuration:0.3];
            [anim addToViewOnStart:self];
            [anim startAnimation];
        }
        else
            [self addSubview:_actionView];
    }
}
 
- (CPView)actionView
{
    return _actionView;
}
 
- (void)_setLeftView:(CPView)aView animated:(BOOL)animated
{
    if (_leftView)
    {
        if (animated)
        {
            var anim = [[CPPropertyAnimation alloc] initWithView:_leftView];
            [anim addProperty:@"alphaValue" start:1.0 end:0.0];
            [anim setDuration:0.3];
            [anim removeFromSuperviewOnEnd:YES];
            [anim startAnimation];
        }
        else
            [_leftView removeFromSuperview];
    }
    
    _leftView = aView;
    
    if (_leftView)
    {
        [_leftView setFrameOrigin:CGPointMake(6.0, 6.0)];
        
        if (animated)
        {
            var anim = [[CPPropertyAnimation alloc] initWithView:_leftView];
            [anim addProperty:@"alphaValue" start:0.0 end:1.0];
            [anim setDuration:0.3];
            [anim addToViewOnStart:self];
            [anim startAnimation];
        }
        else
            [self addSubview:_leftView];
    }
}
 
- (void)_setRightView:(CPView)aView animated:(BOOL)animated
{
    if (_rightView)
    {
        if (animated)
        {
            var anim = [[CPPropertyAnimation alloc] initWithView:_rightView];
            [anim addProperty:@"alphaValue" start:1.0 end:0.0];
            [anim setDuration:0.3];
            [anim removeFromSuperviewOnEnd:YES];
            [anim startAnimation];
        }
        else
            [_rightView removeFromSuperview];
    }
    
    _rightView = aView;
    
    if (_rightView)
    {
        [_rightView setFrameOrigin:CGPointMake(CGRectGetWidth([self bounds]) - CGRectGetWidth([_rightView bounds]) - 6.0, 6.0)];
        [_rightView setAutoresizingMask:CPViewMinXMargin];
        
        if (animated)
        {
            var anim = [[CPPropertyAnimation alloc] initWithView:_rightView];
            [anim addProperty:@"alphaValue" start:0.0 end:1.0];
            [anim setDuration:0.3];
            [anim addToViewOnStart:self];
            [anim startAnimation];
        }
        else
            [self addSubview:_rightView];
    }
}
 
- (void)_layoutSubviewsAnimated:(BOOL)animated
{
    var anItem = [self topItem];
    
    // Title
    
    var titleView = [anItem titleView];
    if (!titleView)
    {
        titleView = [[CPTextField alloc] initWithFrame:CGRectMakeZero()];
        [titleView setFont:[CPFont boldSystemFontOfSize:22.0]];
        [titleView setTextShadowColor:[CPColor whiteColor]];
        [titleView setTextShadowOffset:CGSizeMake(0.0, 1.0)];
        [titleView setStringValue:[anItem title]];
        [titleView sizeToFit];
        
        [anItem setTitleView:titleView];
    }
    
    [self _setTitleView:titleView animated:animated];
    
    var backItem = [self backItem];
    
    if (backItem && ![anItem hidesBackButton])
    {
        var backButton = [backItem backButton];
        
        if (!backButton)
        {
            backButton = [[CPButton alloc] initWithFrame:CGRectMake(0.0, 0.0, 90.0, 24.0)];
            [backButton setAutoresizingMask:CPViewMinXMargin | CPViewMaxXMargin];
            [backButton setTitle:[backItem title]];
            [backButton setTarget:self];
            [backButton setAction:@selector(popNavigationItemAnimated:)];
            
            var currentColor = [[backButton currentValueForThemeAttribute:@"bezel-color"] patternImage];
            var tpi = [[CPThreePartImage alloc] initWithImageSlices:[
                [[CPImage alloc] initWithContentsOfFile:@"Resources/button-bezel-left-point.png" size:CGSizeMake(12.0, 24.0)],
                [currentColor imageSlices][1],
                [currentColor imageSlices][2]
            ] isVertical:NO];
            [backButton setValue:[CPColor colorWithPatternImage:tpi] forThemeAttribute:@"bezel-color"];
            var inset = CGInsetMakeCopy([backButton currentValueForThemeAttribute:@"content-inset"]);
            inset.left += 11.0;
            inset.right += 4.0;
            [backButton setValue:inset forThemeAttribute:@"content-inset"];
            [backButton sizeToFit];
            
            [backItem setBackButton:backButton];
        }
    }
    
    [self _setBackView:[anItem hidesBackButton] ? nil : [backItem backButton] animated:animated];
    [self _setActionView:[anItem actionButton] animated:animated];
    
    [self _setLeftView:[anItem leftView] animated:animated];
    [self _setRightView:[anItem rightView] animated:animated];
}
 
@end
 
CPNavigationController.j #
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
/*
* CPNavigationController.j
* AppKit
*
* Created by Nicholas Small.
* Copyright 2009, 280 North, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
 
@import "CPViewController.j"
@import "CPNavigationItem.j"
@import "CPNavigationBar.j"
@import "CPPropertyAnimation.j"
 
 
@implementation CPNavigationController : CPViewController
{
    CPMutableArray _viewControllers;
    CPViewController _visibleViewController;
    CPViewController _disappearingViewController;
    
    // Navigation Bar
    CPNavigationBar _navigationBar;
    Class _navigationBarClass;
    BOOL _navigationBarHidden;
    
    id _delegate;
    
    BOOL _isPopping;
    BOOL _receivedPopFromNavigationBar;
}
 
- (id)initWithRootViewController:(CPViewController)aController
{
    if (!aController)
        return nil;
    
    self = [super init];
    
    if (self)
    {
        [self _addViewController:aController];
        [self setTitle:[aController title]];
        _radioItem = [aController radioItem];
        
        _visibleViewController = aController;
    }
    
    return self;
}
 
- (void)pushViewController:(CPViewController)aController animated:(BOOL)animated
{
    _isPopping = NO;
    
    _disappearingViewController = [self topViewController];
    
    [self _addViewController:aController];
    
    _visibleViewController = [self topViewController];
    
    [_navigationBar pushNavigationItem:[_visibleViewController navigationItem] animated:animated];
    
    [self _displayViewAnimated:animated];
}
 
- (void)popToViewController:(CPViewController)aController animated:(BOOL)animated
{
    _isPopping = YES;
    
    if ([self topViewController] == [self bottomViewController])
        return NO;
    
    _disappearingViewController = [self topViewController];
    
    var index = [_viewControllers indexOfObject:aController],
        count = [_viewControllers count] - 1;
    
    for (; count > index; count--)
        [_viewControllers removeLastObject];
    
    _visibleViewController = [self topViewController];
    
    if (_receivedPopFromNavigationBar)
        _receivedPopFromNavigationBar = NO;
    else
        [_navigationBar popToNavigationItem:[aController navigationItem] animated:animated];
    
    [self _displayViewAnimated:animated];
}
 
- (void)popToRootViewControllerAnimated:(BOOL)animated
{
    [self popToViewController:[self bottomViewController] animated:animated];
}
 
- (void)popViewControllerAnimated:(BOOL)animated
{
    [self popToViewController:[self previousViewController] animated:animated];
}
 
- (CPViewController)topViewController
{
    return [_viewControllers lastObject];
}
 
- (CPViewController)bottomViewController
{
    return [_viewControllers objectAtIndex:0];
}
 
- (CPViewController)visibleViewController
{
    return _visibleViewController;
}
 
- (CPViewController)previousViewController
{
    var count = [_viewControllers count];
    
    if (count <= 1)
        return nil;
    
    return [_viewControllers objectAtIndex:count - 2];
}
 
// View Manager
 
- (void)loadView
{
    var view = [[_CPLayoutView alloc] init];
    view._viewController = self;
    
    _navigationBar = [[(_navigationBarClass || CPNavigationBar) alloc] init];
    [_navigationBar setDelegate:self];
    [_navigationBar setItems:[self _navigationItems] animated:NO];
    [view addSubview:_navigationBar];
    
    [self setView:view];
}
 
- (void)viewWillAppear:(BOOL)animated
{
    [self _displayViewAnimated:NO];
}
 
// Private API
 
- (CPArray)_navigationItems
{
    var array = [CPMutableArray array];
    
    var count = [_viewControllers count];
    for (var i = 0; i < count; i++)
        [array addObject:[_viewControllers[i] navigationItem]];
    
    return array;
}
 
- (BOOL)_addViewController:(CPViewController)aController
{
    if ([_viewControllers containsObject:aController])
        return NO;
    
    if (!_viewControllers)
        _viewControllers = [CPMutableArray array];
    
    aController._parentViewController = self;
    
    [_viewControllers addObject:aController];
    
    return YES;
}
 
- (void)_displayViewAnimated:(BOOL)animated
{
    if (_disappearingViewController)
    {
        [_disappearingViewController viewWillDisappear:animated];
        
        if (animated)
            [self _startTransition];
        else
            [[_disappearingViewController view] removeFromSuperview];
        
        [_disappearingViewController viewDidDisappear:animated];
    }
    
    if (_visibleViewController)
    {
        var view = [_visibleViewController view];
        
        [_visibleViewController viewWillAppear:animated];
        
        [view setFrameSize:CGSizeMake(CGRectGetWidth([_view bounds]), CGRectGetHeight([_view bounds]) - CGRectGetMaxY([_navigationBar frame]))];
        [view setFrameOrigin:CGPointMake(animated ? (_isPopping ? -CGRectGetWidth([view bounds]) : CGRectGetWidth([view bounds])) : 0.0, CGRectGetMaxY([_navigationBar frame]))];
        [view setAutoresizingMask:CPViewWidthSizable | CPViewHeightSizable];
        [_view addSubview:view];
        
        if (animated)
            [self _endTransition];
        
        [_visibleViewController viewDidAppear:animated];
    }
}
 
- (void)_startTransition
{
    if (!_disappearingViewController)
        return;
    
    var view = [_disappearingViewController view],
        anim = [[CPPropertyAnimation alloc] initWithView:view];
    
    if (_isPopping)
        [anim addProperty:@"frame" start:CGRectMakeCopy([view frame]) end:CGRectMake([view frame].size.width, [view frame].origin.y, [view frame].size.width, [view frame].size.height)];
    else
        [anim addProperty:@"frame" start:CGRectMakeCopy([view frame]) end:CGRectMake(-[view frame].size.width, [view frame].origin.y, [view frame].size.width, [view frame].size.height)];
    
    [anim addProperty:@"alphaValue" start:1.0 end:0.3];
    
    [anim setDuration:0.3];
    [anim startAnimation];
    [anim setDelegate:self];
}
 
- (void)_endTransition
{
    var view = [_visibleViewController view],
        anim = [[CPPropertyAnimation alloc] initWithView:view];
    
    [anim addProperty:@"frame" start:CGRectMakeCopy([view frame]) end:CGRectMake(0.0, [view frame].origin.y, [view frame].size.width, [view frame].size.height)];
    [anim addProperty:@"alphaValue" start:0.3 end:1.0];
    
    [anim setDuration:0.3];
    [anim startAnimation];
}
 
- (void)animationDidEnd:(CPAnimation)anAnimation
{
    var view = [_disappearingViewController view];
    
    [view removeFromSuperview];
    [view setAlphaValue:1.0];
}
 
@end
 
@implementation CPNavigationController (CPNavigationBar)
 
- (void)navigationBar:(CPNavigationBar)aBar didPopItem:(CPNavigationItem)anItem
{
    _receivedPopFromNavigationBar = YES;
    [self popToViewController:anItem._viewController animated:YES];
}
 
@end
 
CPNavigationItem.j #
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
/*
* CPNavigationItem.j
* AppKit
*
* Created by Nicholas Small.
* Copyright 2009, 280 North, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
 
@import <Foundation/CPObject.j>
 
 
@implementation CPNavigationItem : CPObject
{
    CPNavigationBar _navigationBar;
    CPViewController _viewController;
    
    CPString _title;
    CPView _titleView;
    
    CPButton _backButton;
    BOOL _hidesBackButton;
    
    CPButton _actionButton;
    BOOL _hidesActionButton;
    
    CPView _leftView;
    CPView _rightView;
}
 
- (id)initWithTitle:(CPString)aTitle
{
    if (!aTitle)
        return nil;
    
    self = [super init];
    
    if (self)
    {
        [self setTitle:aTitle];
    }
    
    return self;
}
 
- (void)setTitle:(CPString)aTitle
{
    if (!aTitle || _title == aTitle)
        return;
    
    _title = [aTitle copy];
    
    if ([_titleView isKindOfClass:CPTextField])
    {
        [_titleView setStringValue:_title];
        [_titleView sizeToFit];
    }
    
    if ([_backButton isKindOfClass:CPButton])
    {
        [_backButton setTitle:_title];
        [_backButton sizeToFit];
    }
    
    [_navigationBar _layoutSubviewsAnimated:NO];
}
 
- (CPString)title
{
    return _title;
}
 
- (void)setTitleView:(CPView)aView
{
    if (_titleView == aView)
        return;
    
    _titleView = aView;
}
 
- (CPView)titleView
{
    return _titleView;
}
 
// Back Button
 
- (void)setBackButton:(CPButton)aButton
{
    if (_backButton == aButton)
        return;
    
    _backButton = aButton;
}
 
- (CPButton)backButton
{
    return _backButton;
}
 
- (void)setHidesBackButton:(BOOL)aFlag animated:(BOOL)animated
{
    if (_hidesBackButton == aFlag)
        return;
    
    _hidesBackButton = aFlag;
    
    [_navigationBar _setBackView:_hidesBackButton ? nil : [_navigationBar backView] animated:animated];
}
 
- (BOOL)hidesBackButton
{
    return _hidesBackButton;
}
 
// Action Button
 
- (void)setActionButton:(CPButton)aButton animated:(BOOL)animated
{
    if (_actionButton == aButton)
        return;
    
    _actionButton = aButton;
    
    [_navigationBar _setActionView:_actionButton animated:animated];
}
 
- (CPButton)actionButton
{
    return _actionButton;
}
 
- (void)setHidesActionButton:(BOOL)aFlag animated:(BOOL)animated
{
    if (_hidesActionButton == aFlag)
        return;
    
    _hidesActionButton = aFlag;
    
    [_navigationBar _setActionButton:_hidesActionButton ? nil : [_navigationBar actionView]];
}
 
- (BOOL)hidesActionButton
{
    return _hidesActionButton;
}
 
// Left/Right Views
 
- (void)setLeftView:(CPView)aView animated:(BOOL)animated
{
    _leftView = aView;
    
    [_navigationBar _setLeftView:_leftView animated:animated];
}
 
- (CPView)leftView
{
    return _leftView;
}
 
- (void)setRightView:(CPView)aView animated:(BOOL)animated
{
    _rightView = aView;
    
    [_navigationBar _setRightView:_rightView animated:animated];
}
 
- (CPView)rightView
{
    return _rightView;
}
 
- (CPButton)buttonForActionButton
{
    var button = [[CPButton alloc] initWithFrame:CGRectMake(0.0, 0.0, 90.0, 24.0)];
    [button setTitle:@"Action"];
    [button setDefaultButton:YES];
    
    return button;
}
 
@end
 
CPViewController.j #
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
/*
* CPViewController.j
* AppKit
*
* Created by Nicholas Small.
* Copyright 2009, 280 North, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
 
@import <AppKit/CPResponder.j>
 
 
/*! @class CPViewController
The CPViewController class provides the fundamental view-management controller for Cappuccino applications.
The basic view controller class supports the presentation of an associated view in addition to basic support
for managing modal views and, in the future, animations. Subclasses such as CPNavigationController and
CPTabBarController provide additional behavior for managing complex hierarchies of view controllers and views.
You use each instance of CPViewController to manage a single view (and hierarchy). For a simple view controller,
this entails managing the view hierarchy responsible for presenting your application content.
A typical view hierarchy consists of a root view—a reference to which is available in the view property of this class—
and one or more subviews presenting the actual content. In the case of navigation and tab bar controllers, the view
controller manages not only the high-level view hierarchy (which provides the navigation controls) but also one
or more additional view controllers that handle the presentation of the application content.
Unlike UIViewController in Cocoa Touch, a CPViewController does not represent an entire screen of content. You
will add your root view to an existing view or window's content view. You can manage many view controllers
on screen at once. CPViewController is also the preferred way of working with Cibs.
Subclasses can override -loadView to create their custom view hierarchy, or specify a cib name to be loaded automatically.
It has methods that are called when a view appears or disappears.
This class is also a good place for delegate & datasource methods, and other controller stuff.
*/
@implementation CPViewController : CPResponder
{
    CPView _view;
    
    CPRadioItem _radioItem;
    CPNavigationItem _navigationItem;
    
    CPString _title;
    
    CPString _cibName;
    CPBundle _cibBundle;
    
    CPViewController _parentViewController;
    CPDictionary _childViewControllers;
    
    BOOL _isViewLoaded;
}
 
/*!
Convenience initializer calls -initWithCibName:bundle: with nil for both parameters.
*/
- (id)init
{
    return [self initWithCibName:nil bundle:nil];
}
 
/*!
The designated initializer. If you subclass CPViewController, you must call the super implementation of this method, even if you aren't using a Cib.
In the specified Cib, the File's Owner proxy should have its class set to your view controller subclass, with the view outlet connected to the main view.
If you pass in a nil Cib name, then you must either call -setView: before -view is invoked, or override -loadView to set up your views.
@param cibNameOrNil The path to the cib to load for the root view or nil to programmatically create views.
@param cibBundleOrNil The bundle that the cib is located in or nil for the main bundle.
*/
- (id)initWithCibName:(CPString)cibNameOrNil bundle:(CPBundle)cibBundleOrNil
{
    self = [super init];
    
    if (self)
    {
        // We're lazy. Don't load the cib until someone actually requests the view.
        _cibName = cibNameOrNil;
        _cibBundle = cibBundleOrNil || [CPBundle mainBundle];
    }
    
    return self;
}
 
/*!
Programmatically creates the view that the controller manages.
You should never call this method directly. The view controller calls this method when the view property is requested but is nil.
If you create your views manually, you must override this method and use it to create your view and assign it to the view property.
The default implementation for programmatic views is to create a plain view. You can invoke super to utilize this view.
If you use Interface Builder to create your views and initialize the view controller—that is, you initialize the view using the
initWithCibName:bundle: method—then you must not override this method. The consequences risk shattering the space-time continuum.
Note: The cib loading system is currently asynchronous.
*/
- (void)loadView
{
    if ([self isViewLoaded])
        return;
    
    if (_cibName)
    {
        var cib = [[CPCib alloc] initWithContentsOfURL:[_cibBundle pathForResource:_cibName + @".cib"]];
        [cib instantiateCibWithExternalNameTable:[CPDictionary dictionaryWithObject:self forKey:CPCibOwner]];
    }
    else
    {
        _view = [[CPView alloc] initWithFrame:CGRectMakeZero()];
        [_view setBackgroundColor:[CPColor blueColor]];
    }
}
 
/*!
Called after the view has been loaded.
For view controllers created in code, this is after -loadView. For view controllers unarchived from a cib, this is after the view is set.
This method is most commonly used to perform additional initialization steps on views that are loaded from cib files.
The default implementation does nothing, but it is recommended your invoke super in case of future changes.
*/
- (void)viewDidLoad
{
}
 
/*!
Called after the view controller's view is set to nil. Not invoked as a result of -dealloc.
The default implementation does nothing, but it is recommended your invoke super in case of future changes.
*/
- (void)viewDidUnload
{
}
 
/*!
Returns the view that the controller manages.
If this property is nil, the controller sends loadView to itself to create the view that it manages.
Subclasses should override the loadView method to create any custom views. The default value is nil.
Note: An error will not be thrown if after -loadView, the view property is still nil. -view will simply return nil, but will continue to call -loadView on subsequent calls.
*/
- (CPView)view
{
    if (!_view)
    {
        [self loadView];
        
        if (_view)
            [self viewDidLoad];
    }
    
    return _view;
}
 
 
/*!
Manually sets the view that the controller manages.
Setting to nil will cause -loadView to be called on all subsequent calls of -view.
@param aView The view this controller should represent.
*/
- (void)setView:(CPView)aView
{
    if (_view && _view == aView)
        return;
    
    if (_view)
        _view._viewController = nil;
    
    if (_view)
        [_view removeFromSuperview];
    
    _view = aView;
    
    if (_view)
        _view._viewController = self;
    
    _isViewLoaded = !!_view;
}
 
// Auxiliary View Items
 
/*!
Returns the navigation item for this view controller or creates it if it doesn't exist. You MUST use this
method to create a navigation item for a view controller; there is no setter.
Note: The title of the view controller must already in order for a navigation item to be created.
*/
- (CPNavigationItem)navigationItem
{
    if (!_navigationItem)
    {
        _navigationItem = [[CPNavigationItem alloc] initWithTitle:[self title]];
        _navigationItem._viewController = self;
    }
    
    return _navigationItem;
}
 
/*!
Returns the radio item for this view controller or creates it if it doesn't exist. You MUST use this
method to create a radio item for a view controller; there is no setter.
Note: Unlike a navigation item, a radio item MAY be created without a title.
*/
- (CPRadioItem)radioItem
{
    if (!_radioItem)
    {
        _radioItem = [[CPRadioItem alloc] init];
        [_radioItem setTitle:[self title]];
    }
    
    return _radioItem;
}
 
/*!
Returns the parent controller (navigation or tab bar) of the current view controller or nil.
Parent view controllers are relevant in navigation, tab bar, and modal view controller hierarchies.
In each of these hierarchies, the parent is the object responsible for displaying the current view controller.
If you are using a view controller as a standalone object—that is, not as part of a view controller hierarchy—the value in this property is nil.
*/
- (CPViewController)parentViewController
{
    return _parentViewController;
}
 
// View Appearance Hooks
 
/*!
Sent to the controller before the view appears on screen.
The default implementation of this method does nothing. Subclasses may override this method to take an appropriate action. When doing so, they must invoke super.
@param animated Will be YES if the view is animating. Animations are supported in the API, but don't actually do anything yet.
*/
- (void)viewWillAppear:(BOOL)animated
{
}
 
/*!
Sent to the controller after the view fully appears on screen.
The default implementation of this method does nothing. Subclasses may override this method to take an appropriate action. When doing so, they must invoke super.
@param animated Will be YES if the view is animating. Animations are supported in the API, but don't actually do anything yet.
*/
- (void)viewDidAppear:(BOOL)animated
{
}
 
/*!
Sent to the controller before the view is dismissed, covered, or otherwise hidden from view.
The default implementation of this method does nothing. Subclasses may override this method to take an appropriate action. When doing so, they must invoke super.
@param animated Will be YES if the view is animating. Animations are supported in the API, but don't actually do anything yet.
*/
- (void)viewWillDisappear:(BOOL)animated
{
}
 
/*!
Sent to the controller after the view is dismissed, covered, or otherwise hidden from view.
The default implementation of this method does nothing. Subclasses may override this method to take an appropriate action. When doing so, they must invoke super.
@param animated Will be YES if the view is animating. Animations are supported in the API, but don't actually do anything yet.
*/
- (void)viewDidDisappear:(BOOL)animated
{
}
 
// Accessors
 
/*!
Returns the localized title that represents the view this controller manages.
*/
- (CPString)title
{
    return _title;
}
 
/*!
Sets the localized title that represents the view this controller manages.
Subclasses should set the title to a human-readable string that represents the view to the user.
If the receiver has a navigation or tab bar item, its title will change to reflect the new title.
If the receiver is a navigation controller, the default value is the top view controller’s title.
@param aTitle A string for the new title. This string will use -copy.
*/
- (void)setTitle:(CPString)aTitle
{
    if (_title == aTitle)
        return;
    
    _title = [aTitle copy];
    
    if (_navigationItem) // Don't create them if they don't already exist. Aka don't call [self navigationItem].
        [_navigationItem setTitle:_title];
    
    if (_radioItem)
        [_radioItem setTitle:_title];
}
 
/*!
Returns a BOOL that indicated whether the view property has successfully been loaded (not nil).
Calling this method will not cause the view to load if it is currently nil.
*/
- (BOOL)isViewLoaded
{
    return _isViewLoaded;
}
 
/*!
Returns the path of the cib file this view controller represents.
This attribute is read-only. You can only set it through -initWithCibName:bundle:
*/
- (CPString)cibName
{
    return _cibName;
}
 
/*!
Returns the bundle that the cib file this view controller represents resides in.
This attribute is read-only. You can only set it through -initWithCibName:bundle:
*/
- (CPBundle)cibBundle
{
    return _cibBundle;
}
 
@end
 
/*!
@ignore
*/
@implementation _CPLayoutView : CPView
{
    CPViewController _viewController;
}
 
- (id)init
{
    self = [super initWithFrame:CGRectMakeZero()];
    
    if (self)
    {
        [self setAutoresizingMask:CPViewWidthSizable | CPViewHeightSizable];
    }
    
    return self;
}
 
- (void)viewWillMoveToSuperview:(CPView)aView
{
    [self setFrame:CGRectMakeCopy([aView bounds])];
    
    [_viewController viewWillAppear:NO];
}
 
- (void)viewDidMoveToSuperview
{
    [_viewController viewDidAppear:NO];
}
 
- (void)removeFromSuperview
{
    [_viewController viewWillDisappear:NO];
    
    [super removeFromSuperview];
    
    [_viewController viewDidDisappear:NO];
}
 
@end