github project is at https://github.com/patrickdlogan/cappex
~/dev$ capp gen cappex Creating Frameworks directory in /home/patrick/dev/cappex/Frameworks… Copying /home/patrick/software/narwhal/packages/objective-j/Frameworks/Objective-J ==> /home/patrick/dev/cappex/Frameworks/Objective-J Copying /home/patrick/software/narwhal/packages/objective-j/Frameworks/Debug/Objective-J ==> /home/patrick/dev/cappex/Frameworks/Debug/Objective-J Copying /home/patrick/software/narwhal/packages/cappuccino/Frameworks/Foundation ==> /home/patrick/dev/cappex/Frameworks/Foundation Copying /home/patrick/software/narwhal/packages/cappuccino/Frameworks/Debug/Foundation ==> /home/patrick/dev/cappex/Frameworks/Debug/Foundation Copying /home/patrick/software/narwhal/packages/cappuccino/Frameworks/AppKit ==> /home/patrick/dev/cappex/Frameworks/AppKit Copying /home/patrick/software/narwhal/packages/cappuccino/Frameworks/Debug/AppKit ==> /home/patrick/dev/cappex/Frameworks/Debug/AppKit
~/dev/cappex$ git flow init Initialized empty Git repository in home/patrick/dev/cappex.git/ No branches exist yet. Base branches must be created now. Branch name for production releases: [master] Branch name for “next release” development: [develop]
How to name your supporting branch prefixes? Feature branches? [feature/] Release branches? [release/] Hotfix branches? [hotfix/] Support branches? [support/] Version tag prefix? [] v
file:///home/patrick/dev/cappex/index.html
If you see Hello World! then you are in business.
var label = [[CPTextField alloc] initWithFrame:CGRectMakeZero()];
[label setStringValue:@”Hello World!”]; [label setFont:[CPFont boldSystemFontOfSize:24.0]];
[label sizeToFit];
[label setAutoresizingMask:CPViewMinXMargin | CPViewMaxXMargin | CPViewMinYMargin | CPViewMaxYMargin]; [label setCenter:[contentView center]];
[contentView addSubview:label];
This could be done with a splitter, using CPSplitView. This example though will have two subviews with auto-constrained proportions.
In place of the removed code, above, add the following lines.
var bounds = [contentView bounds]; var width = CGRectGetWidth(bounds); var height = CGRectGetHeight(bounds);
var leftView = [[CPView alloc] initWithFrame: CGRectMake(0, 0, width - 300, height)]; [leftView setAutoresizingMask: CPViewHeightSizable | CPViewWidthSizable]; [leftView setBackgroundColor: [CPColor blueColor]];
[contentView addSubview: leftView];
Reload the application in the browser. The hello world label should be gone. In its place should be a large blue rectangle to the left of a more narrow white rectangle.
This code is working with views, and uses two terms for dimensions: bounds and frames. Every view has two coordinate systems: the rectangle of the view within its parent is the frame. The view then provides its own relative coordinate system, its bounds, for subviews.
So every view is framed within a parent and provides bounds for its own children. The message:
[[CPView alloc] initWithFrame: CGRectMake(0, 0, width - 300, height)
allocates a new instance of CPView and frames it within its parent at the parent’s location (0, 0). The new view has the same height as the parent, but the width is more narrow.
The next message:
[leftView setAutoresizingMask: CPViewHeightSizable | CPViewWidthSizable];
tells the view how to reposition itself when its parent is resized. In this example the view will retain its same location, (0, 0), and it will change its height and width proportionally. That is it will continue to use all of the parent height, and all of the available width less 300 points.
Resizing the browser window should illustrate how the left, blue view resizes according to the above constraints. The right, white rectangle remains 300 points wide, while the left, blue rectangle uses the remaining available width.
Add the following code to position another subview on the right side.
var rightView = [[CPView alloc] initWithFrame: CGRectMake(width - 300, 0, 300, height)]; [rightView setAutoresizingMask: CPViewHeightSizable | CPViewMinXMargin]; [rightView setBackgroundColor: [CPColor yellowColor]];
[contentView addSubview: rightView];
This positions the right subview at the location (width - 300, 0) which is the top, right location of the left, blue rectangle. As with the left view, the right view will auto-resize to use the available height.
The width though will remain fixed at 300 points as the parent is resized. The constraint CPViewMinXMargin tells the resizing algorithm to adjust the margin to the left of the view. In this example the left margin of the right-side view is exactly the width given to the left-side view.
As the browser window is resized, the blue view should fluctuate in width, which the yellow view width should remain at 300 points.
The next step will split the right view vertically into two deeper subviews. The top view view has a fixed height, and is green. The bottom view uses the available remaining height and is purple. They both use the full width of their parent.
To avoid a run-on method, these views are created in their own method of the AppController.
- (void)addSubviewsToView:(CPView)aView
{ // Code goes here.
This method takes any view as an argument and will add the two subviews to it. First the top view.
var bounds = [aView bounds]; var width = CGRectGetWidth(bounds); var height = CGRectGetHeight(bounds);
var topView = [[CPView alloc] initWithFrame: CGRectMake(0, 0, width, 200)]; [topView setAutoresizingMask: CPViewWidthSizable | CPViewMaxYMargin]; var topController = [[GreenViewController alloc] initWithView: topView];
[aView addSubview: topView];
Notice the top view is added a the location (0, 0) of the parent view. This will be the location of the frame for the top view within the bounds of the parent view. The parent view bounds are its own coordinate system, indendent of that parent’s parent view. And so (0, 0) is the top-most, left-most location of the immediate parent view.
The CPViewMaxYMargin resizing constraint fixes the height of the top view at 200. All the remaining height is given to the Y margin below the top view. (This same are will be allocated to the bottom view in the code below.)
After the top view is allocated and initialized, it is sent to an instance of GreenController, which sets the background color to green. A controller is usually intended to mediate between a data model and the view. Which may occur later in the life of this example project.
The bottom view is handled similarly.
var bottomView = [[CPView alloc] initWithFrame: CGRectMake(0, 200, width, height - 200)]; [bottomView setAutoresizingMask: CPViewWidthSizable | CPViewHeightSizable]; var bottomController = [[PurpleViewController alloc] initWithView: bottomView];
[aView addSubview: bottomView];
The bottom view is positioned at (0, 200), just below the top view. It has the full width of the parent, and all of the height not taken by the 200-point height of the top view.
The bottom view will use all the available width when resized. The height will use the available height less the 200-point fixed height of the top view. A PurpleController is used to set the background color.
When the app is (re-)loaded in the browser, the left, yellow view is now obscured by its two subviews. Instead of yellow, there should be a green top rectangle and a bottom purple one. Resizing the browser window should maintain the relative rectangles of the blue, green, and purple rectangles corresponding to the view frames established within the borders of their parents. Each view provides its own bounds for its own subviews, independent of the nesting of subviews within subviews within subviews.