Merge remote-tracking branch 'Slevenbits/master' into slevenbits-master

This commit is contained in:
Stefan Eilers
2012-12-26 21:33:40 +01:00
14 changed files with 282 additions and 98 deletions
+8
View File
@@ -0,0 +1,8 @@
language: node_js
node_js:
- 0.8
before_install:
- jdk_switcher use openjdk6
install: ./bootstrap.sh --quiet --noprompt --directory ./narwhal
script: jake test
env: PATH=/home/travis/builds/cappuccino/cappuccino/narwhal/bin:$PATH CAPP_BUILD=/home/travis/builds/cappuccino/cappuccino/Build NARWHAL_ENGINE=rhino
+80 -60
View File
@@ -75,32 +75,33 @@ CPCriticalAlertStyle = 2;
representing the first button added to the alert which appears on the
right, 1 representing the next button to the left and so on)
*/
@implementation CPAlert : CPView
@implementation CPAlert : CPObject
{
BOOL _showHelp @accessors(property=showsHelp);
BOOL _showSuppressionButton @accessors(property=showsSuppressionButton);
BOOL _showHelp @accessors(property=showsHelp);
BOOL _showSuppressionButton @accessors(property=showsSuppressionButton);
CPAlertStyle _alertStyle @accessors(property=alertStyle);
CPString _title @accessors(property=title);
CPView _accessoryView @accessors(property=accessoryView);
CPImage _icon @accessors(property=icon);
CPAlertStyle _alertStyle @accessors(property=alertStyle);
CPString _title @accessors(property=title);
CPView _accessoryView @accessors(property=accessoryView);
CPImage _icon @accessors(property=icon);
CPArray _buttons @accessors(property=buttons,readonly);
CPCheckBox _suppressionButton @accessors(property=suppressionButton,readonly);
CPArray _buttons @accessors(property=buttons,readonly);
CPCheckBox _suppressionButton @accessors(property=suppressionButton,readonly);
id _delegate @accessors(property=delegate);
id _modalDelegate;
SEL _didEndSelector;
id _delegate @accessors(property=delegate);
id _modalDelegate;
SEL _didEndSelector;
CPWindow _window @accessors(property=window,readonly);
int _defaultWindowStyle;
_CPAlertThemeView _themeView @accessors(property=themeView, readonly);
CPWindow _window @accessors(property=window,readonly);
int _defaultWindowStyle;
CPImageView _alertImageView;
CPTextField _informativeLabel;
CPTextField _messageLabel;
CPButton _alertHelpButton;
CPImageView _alertImageView;
CPTextField _informativeLabel;
CPTextField _messageLabel;
CPButton _alertHelpButton;
BOOL _needsLayout;
BOOL _needsLayout;
}
#pragma mark Creating Alerts
@@ -164,6 +165,7 @@ CPCriticalAlertStyle = 2;
_showHelp = NO;
_needsLayout = YES;
_defaultWindowStyle = CPTitledWindowMask;
_themeView = [_CPAlertThemeView new];
_messageLabel = [CPTextField labelWithTitle:@"Alert"];
_alertImageView = [[CPImageView alloc] init];
@@ -180,6 +182,11 @@ CPCriticalAlertStyle = 2;
#pragma mark Accessors
- (CPTheme)theme
{
return [_themeView theme];
}
/*!
set the theme to use
@@ -197,9 +204,20 @@ CPCriticalAlertStyle = 2;
_window = nil; // will be regenerated at next layout
_needsLayout = YES;
[super setTheme:aTheme];
[_themeView setTheme:aTheme];
}
- (void)setValue:(id)aValue forThemeAttribute:(CPString)aName
{
[_themeView setValue:aValue forThemeAttribute:aName];
}
- (void)setValue:(id)aValue forThemeAttribute:(CPString)aName inState:(CPThemeState)aState
{
[_themeView setValue:aValue forThemeAttribute:aName inState:aState];
}
/*! @deprecated
*/
- (void)setWindowStyle:(int)aStyle
@@ -334,16 +352,16 @@ CPCriticalAlertStyle = 2;
*/
- (void)_layoutMessageView
{
var inset = [self currentValueForThemeAttribute:@"content-inset"],
var inset = [_themeView currentValueForThemeAttribute:@"content-inset"],
sizeWithFontCorrection = 6.0,
messageLabelWidth,
messageLabelTextSize;
[_messageLabel setTextColor:[self currentValueForThemeAttribute:@"message-text-color"]];
[_messageLabel setFont:[self currentValueForThemeAttribute:@"message-text-font"]];
[_messageLabel setTextShadowColor:[self currentValueForThemeAttribute:@"message-text-shadow-color"]];
[_messageLabel setTextShadowOffset:[self currentValueForThemeAttribute:@"message-text-shadow-offset"]];
[_messageLabel setAlignment:[self currentValueForThemeAttribute:@"message-text-alignment"]];
[_messageLabel setTextColor:[_themeView currentValueForThemeAttribute:@"message-text-color"]];
[_messageLabel setFont:[_themeView currentValueForThemeAttribute:@"message-text-font"]];
[_messageLabel setTextShadowColor:[_themeView currentValueForThemeAttribute:@"message-text-shadow-color"]];
[_messageLabel setTextShadowOffset:[_themeView currentValueForThemeAttribute:@"message-text-shadow-offset"]];
[_messageLabel setAlignment:[_themeView currentValueForThemeAttribute:@"message-text-alignment"]];
[_messageLabel setLineBreakMode:CPLineBreakByWordWrapping];
messageLabelWidth = CGRectGetWidth([[_window contentView] frame]) - inset.left - inset.right;
@@ -357,18 +375,18 @@ CPCriticalAlertStyle = 2;
*/
- (void)_layoutInformativeView
{
var inset = [self currentValueForThemeAttribute:@"content-inset"],
defaultElementsMargin = [self currentValueForThemeAttribute:@"default-elements-margin"],
var inset = [_themeView currentValueForThemeAttribute:@"content-inset"],
defaultElementsMargin = [_themeView currentValueForThemeAttribute:@"default-elements-margin"],
sizeWithFontCorrection = 6.0,
informativeLabelWidth,
informativeLabelOriginY,
informativeLabelTextSize;
[_informativeLabel setTextColor:[self currentValueForThemeAttribute:@"informative-text-color"]];
[_informativeLabel setFont:[self currentValueForThemeAttribute:@"informative-text-font"]];
[_informativeLabel setTextShadowColor:[self currentValueForThemeAttribute:@"informative-text-shadow-color"]];
[_informativeLabel setTextShadowOffset:[self currentValueForThemeAttribute:@"informative-text-shadow-offset"]];
[_informativeLabel setAlignment:[self currentValueForThemeAttribute:@"informative-text-alignment"]];
[_informativeLabel setTextColor:[_themeView currentValueForThemeAttribute:@"informative-text-color"]];
[_informativeLabel setFont:[_themeView currentValueForThemeAttribute:@"informative-text-font"]];
[_informativeLabel setTextShadowColor:[_themeView currentValueForThemeAttribute:@"informative-text-shadow-color"]];
[_informativeLabel setTextShadowOffset:[_themeView currentValueForThemeAttribute:@"informative-text-shadow-offset"]];
[_informativeLabel setAlignment:[_themeView currentValueForThemeAttribute:@"informative-text-alignment"]];
[_informativeLabel setLineBreakMode:CPLineBreakByWordWrapping];
informativeLabelWidth = CGRectGetWidth([[_window contentView] frame]) - inset.left - inset.right;
@@ -386,8 +404,8 @@ CPCriticalAlertStyle = 2;
if (!_accessoryView)
return;
var inset = [self currentValueForThemeAttribute:@"content-inset"],
defaultElementsMargin = [self currentValueForThemeAttribute:@"default-elements-margin"],
var inset = [_themeView currentValueForThemeAttribute:@"content-inset"],
defaultElementsMargin = [_themeView currentValueForThemeAttribute:@"default-elements-margin"],
accessoryViewWidth = CGRectGetWidth([[_window contentView] frame]) - inset.left - inset.right,
accessoryViewOriginY = CGRectGetMaxY([_informativeLabel frame]) + defaultElementsMargin;
@@ -403,16 +421,16 @@ CPCriticalAlertStyle = 2;
if (!_showSuppressionButton)
return;
var inset = [self currentValueForThemeAttribute:@"content-inset"],
suppressionViewXOffset = [self currentValueForThemeAttribute:@"suppression-button-x-offset"],
suppressionViewYOffset = [self currentValueForThemeAttribute:@"suppression-button-y-offset"],
defaultElementsMargin = [self currentValueForThemeAttribute:@"default-elements-margin"],
var inset = [_themeView currentValueForThemeAttribute:@"content-inset"],
suppressionViewXOffset = [_themeView currentValueForThemeAttribute:@"suppression-button-x-offset"],
suppressionViewYOffset = [_themeView currentValueForThemeAttribute:@"suppression-button-y-offset"],
defaultElementsMargin = [_themeView currentValueForThemeAttribute:@"default-elements-margin"],
suppressionButtonViewOriginY = CGRectGetMaxY([(_accessoryView || _informativeLabel) frame]) + defaultElementsMargin + suppressionViewYOffset;
[_suppressionButton setTextColor:[self currentValueForThemeAttribute:@"suppression-button-text-color"]];
[_suppressionButton setFont:[self currentValueForThemeAttribute:@"suppression-button-text-font"]];
[_suppressionButton setTextShadowColor:[self currentValueForThemeAttribute:@"suppression-button-text-shadow-color"]];
[_suppressionButton setTextShadowOffset:[self currentValueForThemeAttribute:@"suppression-button-text-shadow-offset"]];
[_suppressionButton setTextColor:[_themeView currentValueForThemeAttribute:@"suppression-button-text-color"]];
[_suppressionButton setFont:[_themeView currentValueForThemeAttribute:@"suppression-button-text-font"]];
[_suppressionButton setTextShadowColor:[_themeView currentValueForThemeAttribute:@"suppression-button-text-shadow-color"]];
[_suppressionButton setTextShadowOffset:[_themeView currentValueForThemeAttribute:@"suppression-button-text-shadow-offset"]];
[_suppressionButton sizeToFit];
[_suppressionButton setFrameOrigin:CGPointMake(inset.left + suppressionViewXOffset, suppressionButtonViewOriginY)];
@@ -424,12 +442,12 @@ CPCriticalAlertStyle = 2;
*/
- (CGSize)_layoutButtonsFromView:(CPView)lastView
{
var inset = [self currentValueForThemeAttribute:@"content-inset"],
minimumSize = [self currentValueForThemeAttribute:@"size"],
buttonOffset = [self currentValueForThemeAttribute:@"button-offset"],
helpLeftOffset = [self currentValueForThemeAttribute:@"help-image-left-offset"],
var inset = [_themeView currentValueForThemeAttribute:@"content-inset"],
minimumSize = [_themeView currentValueForThemeAttribute:@"size"],
buttonOffset = [_themeView currentValueForThemeAttribute:@"button-offset"],
helpLeftOffset = [_themeView currentValueForThemeAttribute:@"help-image-left-offset"],
aRepresentativeButton = [_buttons objectAtIndex:0],
defaultElementsMargin = [self currentValueForThemeAttribute:@"default-elements-margin"],
defaultElementsMargin = [_themeView currentValueForThemeAttribute:@"default-elements-margin"],
panelSize = [[_window contentView] frame].size,
buttonsOriginY,
offsetX;
@@ -461,8 +479,8 @@ CPCriticalAlertStyle = 2;
if (_showHelp)
{
var helpImage = [self currentValueForThemeAttribute:@"help-image"],
helpImagePressed = [self currentValueForThemeAttribute:@"help-image-pressed"],
var helpImage = [_themeView currentValueForThemeAttribute:@"help-image"],
helpImagePressed = [_themeView currentValueForThemeAttribute:@"help-image-pressed"],
helpImageSize = helpImage ? [helpImage size] : CGSizeMakeZero(),
helpFrame = CGRectMake(helpLeftOffset, buttonsOriginY, helpImageSize.width, helpImageSize.height);
@@ -487,7 +505,7 @@ CPCriticalAlertStyle = 2;
if (!_window)
[self _createWindowWithStyle:nil];
var iconOffset = [self currentValueForThemeAttribute:@"image-offset"],
var iconOffset = [_themeView currentValueForThemeAttribute:@"image-offset"],
theImage = _icon,
finalSize;
@@ -495,13 +513,13 @@ CPCriticalAlertStyle = 2;
switch (_alertStyle)
{
case CPWarningAlertStyle:
theImage = [self currentValueForThemeAttribute:@"warning-image"];
theImage = [_themeView currentValueForThemeAttribute:@"warning-image"];
break;
case CPInformationalAlertStyle:
theImage = [self currentValueForThemeAttribute:@"information-image"];
theImage = [_themeView currentValueForThemeAttribute:@"information-image"];
break;
case CPCriticalAlertStyle:
theImage = [self currentValueForThemeAttribute:@"error-image"];
theImage = [_themeView currentValueForThemeAttribute:@"error-image"];
break;
}
@@ -592,7 +610,7 @@ CPCriticalAlertStyle = 2;
- (void)_createWindowWithStyle:(int)forceStyle
{
var frame = CGRectMakeZero();
frame.size = [self currentValueForThemeAttribute:@"size"];
frame.size = [_themeView currentValueForThemeAttribute:@"size"];
_window = [[CPWindow alloc] initWithContentRect:frame styleMask:forceStyle || _defaultWindowStyle];
[_window setLevel:CPStatusWindowLevel];
@@ -633,8 +651,8 @@ CPCriticalAlertStyle = 2;
{
if ([_window isSheet])
{
[CPApp endSheet:_window returnCode:[aSender tag]];
[_window orderOut:nil];
[CPApp endSheet:_window returnCode:[aSender tag]];
}
else
{
@@ -650,17 +668,19 @@ CPCriticalAlertStyle = 2;
*/
- (void)_alertDidEnd:(CPWindow)aWindow returnCode:(int)returnCode contextInfo:(id)contextInfo
{
if ([_delegate respondsToSelector:@selector(alertDidEnd:returnCode:)])
[_delegate alertDidEnd:self returnCode:returnCode];
if (_didEndSelector)
objj_msgSend(_modalDelegate, _didEndSelector, self, returnCode, contextInfo);
_modalDelegate = nil;
_didEndSelector = nil;
if ([_delegate respondsToSelector:@selector(alertDidEnd:returnCode:)])
[_delegate alertDidEnd:self returnCode:returnCode];
}
#pragma mark Theme Attributes
@end
@implementation _CPAlertThemeView : CPView
+ (CPString)defaultThemeClass
{
+15 -10
View File
@@ -20,6 +20,8 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#import "../Foundation/CPRange.h"
@import <Foundation/CPBundle.j>
@import "CPCompatibility.j"
@@ -139,9 +141,7 @@ CPRunContinuesResponse = -1002;
{
_eventListeners = [];
_windows = [];
[_windows addObject:nil];
_windows = [[CPNull null]];
}
return self;
@@ -655,6 +655,10 @@ CPRunContinuesResponse = -1002;
*/
- (CPWindow)windowWithWindowNumber:(int)aWindowNumber
{
// Never allow _windows[0] to be returned - it's an internal CPNull placeholder.
if (!aWindowNumber)
return nil;
return _windows[aWindowNumber];
}
@@ -663,7 +667,8 @@ CPRunContinuesResponse = -1002;
*/
- (CPArray)windows
{
return _windows;
// Return all windows, but not the CPNull placeholder in _windows[0].
return [_windows subarrayWithRange:_CPMakeRange(1, [_windows count] - 1)];
}
/*!
@@ -1042,9 +1047,9 @@ CPRunContinuesResponse = -1002;
if (!args || args.length == 0)
{
_args = [];
#if PLATFORM(DOM)
window.location.hash = @"#";
#endif
// Don't use if PLATFORM(DOM) here - the unit test fakes window.location so we should play along.
if (window && window.location)
window.location.hash = @"#";
return;
}
@@ -1059,9 +1064,9 @@ CPRunContinuesResponse = -1002;
var hash = [toEncode componentsJoinedByString:@"/"];
#if PLATFORM(DOM)
window.location.hash = @"#" + hash;
#endif
// Don't use if PLATFORM(DOM) here - the unit test fakes window.location so we should play along.
if (window && window.location)
window.location.hash = @"#" + hash;
}
- (void)_reloadArguments
+4
View File
@@ -706,7 +706,11 @@ var CPScrollDestinationNone = 0,
{
if (![self hasThemeState:CPThemeStateEditing])
return @"";
#if PLATFORM(DOM)
return [self _inputElement].value;
#else
return @"";
#endif
}
- (void)moveUp:(id)sender
+5
View File
@@ -651,6 +651,9 @@ var CPViewFlags = { },
if (_window === aWindow)
return;
// Unlike in Cocoa you can observe the window of a view.
[self willChangeValueForKey:@"window"];
[[self window] _dirtyKeyViewLoop];
// Clear out first responder if we're the first responder and leaving.
@@ -678,6 +681,8 @@ var CPViewFlags = { },
[self viewDidMoveToWindow];
[[self window] _dirtyKeyViewLoop];
[self didChangeValueForKey:@"window"];
}
/*!
+4
View File
@@ -901,6 +901,8 @@ CPTexturedBackgroundWindowMask
[_platformWindow orderFront:self];
[_platformWindow order:CPWindowAbove window:self relativeTo:nil];
#else
_isVisible = YES;
#endif
if (!CPApp._keyWindow)
@@ -946,6 +948,8 @@ CPTexturedBackgroundWindowMask
#if PLATFORM(DOM)
[_platformWindow order:CPWindowOut window:self relativeTo:nil];
#else
_isVisible = NO;
#endif
[self _updateMainAndKeyWindows];
+2 -2
View File
@@ -1832,7 +1832,7 @@ var themedButtonValues = nil,
[self registerThemeValues:themedAlertValues forView:alert];
return alert;
return [alert themeView];
}
+ (CPStepper)themedStepper
@@ -2095,7 +2095,7 @@ var themedButtonValues = nil,
[self registerThemeValues:hudSpecificValues forView:alert inherit:themedAlertValues];
return alert;
return [alert themeView];
}
@end
+23 -2
View File
@@ -38,6 +38,7 @@ var _CPAutocompleteMenuMaximumHeight = 307;
float widestItemWidth;
CPWindow _menuWindow;
CPWindow _parentWindow @accessors(property=parentWindow);
CPScrollView scrollView;
CPTableView tableView;
@@ -48,7 +49,8 @@ var _CPAutocompleteMenuMaximumHeight = 307;
{
if (self = [super init])
{
textField = aTextField;
[self bind:@"parentWindow" toObject:self withKeyPath:@"textField.window" options:nil];
[self setTextField:aTextField];
_menuWindow = [[CPWindow alloc] initWithContentRect:CGRectMake(0, 0, 100, 100) styleMask:CPBorderlessWindowMask];
@@ -185,7 +187,7 @@ var _CPAutocompleteMenuMaximumHeight = 307;
{
var indexOfSelectedItem = [self indexOfSelectedItem];
[self setContentArray:[textField _completionsForSubstring:[textField _inputElement].value indexOfToken:0 indexOfSelectedItem:indexOfSelectedItem]];
[self setContentArray:[textField _completionsForSubstring:[textField _editorValue] indexOfToken:0 indexOfSelectedItem:indexOfSelectedItem]];
if (![contentArray count])
return;
@@ -258,6 +260,25 @@ var _CPAutocompleteMenuMaximumHeight = 307;
[textField _complete:self];
}
- (void)setParentWindow:(CPWindow)aWindow
{
if (aWindow === _parentWindow)
return;
if (_parentWindow)
[[CPNotificationCenter defaultCenter] removeObserver:self];
_parentWindow = aWindow;
if (_parentWindow)
[[CPNotificationCenter defaultCenter] addObserver:self selector:@selector(parentWindowWillClose:) name:CPWindowWillCloseNotification object:_parentWindow];
}
- (void)parentWindowWillClose:(CPNotification)aNotification
{
[self _hideCompletions];
}
@end
+42
View File
@@ -29,6 +29,48 @@ Now you can go to GitHub and [make a pull request](https://help.github.com/artic
Some things are difficult to unit test in a way that's worthwhile. For instance a change to the mouse interaction of a `CPButton`. In this case a manual test is fine instead. Find the closest related manual test in `Tests/Manual` and add whatever it takes to easily reproduce the patched behavior, ideally with some labels describing how someone running the manual test should exercise it.
## Commit Messages
The style and format of your commit messages are very important to the health of the project. A good commit message helps not only users reading the release notes, but also your fellow Cappuccino developers as they review `git log` or `git blame` to figure out what you were doing.
This is the preferred format of commit messages for complex changes:
[New:] <what's new.>
[OPTIONAL DESCRIPTION ABOUT WHAT THIS THING DOES.]
Or for fixes:
Fixed: <what was fixed.>
BEFORE
AFTER
**Example:**
nib2cib support for table view background colour.
**Example:**
Fixed: CPAlert was a CPView.
Before this fix, CPAlert subclassed CPView, causing an API mismatch with Cocoa and exposing unexpected methods in the public API which made no sense for a CPAlert such as autoresizing masks. It also lead to CPAlert mistakingly shadowing _window from its CPView superclass.
This fix makes CPAlert inherit from CPObject as it should. The theming, which needs to be applied to a view, was moved to an internal view.
The summary line concisely says what was fixed or improved by the commit. If it's an improvement or something new it optionally starts with "New:". If it's a bug fix it starts with "Fixed:".
For a 'new' change, the rest of the summary line answers the question *"What is new and where?"*. Optionally the summary can be followed with more detail about what's new and why it's great.
If the summary starts with "Fixed:" the rest of the summary line answers the question *"What was fixed?"*. E.g. "Fixed: the blargh had too many blurghs."
(Note it does NOT answer the question *"what was done to fix it?"* E.g. **WRONG:** "Fixed: removed 2 blurghs from the blargh." That would imply that your very change was what was fixed by itself, causing an infinite loop in the universe and ending the world as we know it.)
The rest of the fix message is in a before / after form. The first part describes what the situation was before the fix and says why the fix was needed. The after part describes what change was made to rectify the situation and any implementation notes that might be useful to know for people reviewing it in the future.
Naturally small or unimportant commits have less rigid requirements. If you can comfortably and easily describe the whole change in the summary only, go ahead. Maybe you fixed a tiny typo or did a minor refactoring.
## Cappuccino Coding Style Guidelines
### Trailing whitespace
+5 -5
View File
@@ -263,8 +263,8 @@ task ("demos", function()
rm_rf(demosDir);
FILE.mkdirs(demosDir);
OS.system("curl -L http://github.com/280north/cappuccino-demos/zipball/master > "+zipQuoted);
OS.system("(cd "+demosQuoted+" && unzip "+zipQuoted+" -d demos)");
OS.system("curl -L http://github.com/280north/cappuccino-demos/zipball/master > " + zipQuoted);
OS.system("(cd " + demosQuoted + " && unzip " + zipQuoted + " -d demos)");
require("objective-j");
@@ -311,8 +311,8 @@ task ("demos", function()
cp_r(FILE.join($STARTER_DOWNLOAD_APPLICATION, "Frameworks"), FILE.join(demo.path(), "Frameworks"));
rm_rf(FILE.join(demo.path(), "Frameworks", "Debug"));
var outputPath = demo.name().replace(/\s/g, "-")+".zip";
OS.system("cd "+OS.enquote(FILE.dirname(demo.path()))+" && zip -ry -8 "+OS.enquote(outputPath)+" "+OS.enquote(FILE.basename(demo.path())));
var outputPath = demo.name().replace(/\s/g, "-") + ".zip";
OS.system("cd " + OS.enquote(FILE.dirname(demo.path()))+" && zip -ry -8 " + OS.enquote(outputPath) + " " + OS.enquote(FILE.basename(demo.path())));
// remove the frameworks
rm_rf(FILE.join(demo.path(), "Frameworks"));
@@ -321,7 +321,7 @@ task ("demos", function()
// Testing
task("test", ["CommonJS", "test-only", "check-missing-imports"]);
task("test", ["CommonJS", "test-only"]);
task("test-only", function()
{
+9 -3
View File
@@ -1,7 +1,7 @@
@import <AppKit/CPApplication.j>
@import <AppKit/CPWindow.j>
globalResults = [];
var globalResults = [];
@implementation MyAppDelegate : CPObject
{
@@ -77,6 +77,12 @@ globalResults = [];
globalResults = []
}
- (void)tearDown
{
// This is the only way to clear the global window list between tests. You'd normally never do this.
CPApp = nil;
}
- (void)receiveNotification:(CPNotification)aNote
{
globalResults.push(aNote);
@@ -182,12 +188,12 @@ globalResults = [];
- (void)testWindows
{
[self assert:@"My Great Window" equals:[[[app windows] objectAtIndex:1] title]];
[self assert:[[aWindow title]] equals:[[app windows] valueForKey:@"title"]];
}
- (void)testWindowWithWindowNumber
{
[self assert:@"My Great Window" equals:[[app windowWithWindowNumber:1] title]];
[self assert:@"My Great Window" equals:[[app windowWithWindowNumber:[aWindow windowNumber]] title]];
}
- (void)testReplyToApplicationShouldTerminate
+18 -12
View File
@@ -28,15 +28,15 @@
- (void)setUp
{
_tabView = [[CPTabView alloc] initWithFrame:CGRectMake(0, 0, 800, 600)];
_tabItem1 = [[CPTabViewItem alloc] initWithIdentifier:@"id1"];
_tabItem1 = [[CPTabViewItem alloc] initWithIdentifier:@"id1"];
[_tabItem1 setLabel:@"Item A"];
[_tabItem1 setView:[[CPView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)]]
_tabItem2 = [[CPTabViewItem alloc] initWithIdentifier:@"id2"];
[_tabItem2 setLabel:@"Item B"];
[_tabItem2 setView:[[CPView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)]]
[_tabView addTabViewItem:_tabItem1];
[_tabView addTabViewItem:_tabItem2];
}
@@ -44,35 +44,41 @@
- (void)testCreate
{
[self assertNotNull:_tabView];
}
- (void)testMiddle
{
var tabs = [_tabView tabs];
[self assert:([_tabView frameSize].width / 2) equals:CPRectGetMidX([tabs frame])];
[self assert:([_tabView frameSize].width / 2) equals:CGRectGetMidX([tabs frame])];
}
- (void)testMiddleAfterMoveFrame
{
var tabs = [_tabView tabs];
[_tabView setFrame:CPRectMake(10, 100, 1000, 200)];
[self assert:([_tabView frameSize].width / 2) equals:CPRectGetMidX([tabs frame])];
[_tabView setFrame:CGRectMake(10, 100, 1000, 200)];
// Perform this manually for the sake of the unit test.
[_tabView layoutIfNeeded];
[self assert:([_tabView frameSize].width / 2) equals:CGRectGetMidX([tabs frame])];
}
- (void)testMiddleAfterMoveBound
{
var tabs = [_tabView tabs];
[_tabView setBounds:CPRectMake(100, 100, 20, 300)];
[self assert:([tabs boundsSize].width / 2) equals:CPRectGetMidX([tabs bounds])];
[_tabView setBounds:CGRectMake(100, 100, 20, 300)];
// Perform this manually for the sake of the unit test.
[_tabView layoutIfNeeded];
[self assert:([tabs boundsSize].width / 2) equals:CGRectGetMidX([tabs bounds])];
}
- (void)testBoxHeight
{
var box = [_tabView box],
tabs = [_tabView tabs];
[_tabView setFrame:CPRectMake(0, 0, 800, 800)];
[_tabView setFrame:CGRectMake(0, 0, 800, 800)];
// Perform this manually for the sake of the unit test.
[_tabView layoutIfNeeded];
[self assert:[box frameSize].height equals:800 - [tabs frameSize].height / 2];
}
@@ -87,4 +93,4 @@
[self assertNull:[_tabItem1 tabView]];
}
@end
@end
+49
View File
@@ -28,4 +28,53 @@
[[aWindow contentView] addSubview:tokenField];
}
- (void)testCloseParentWindow
{
var aWindow = [[CPWindow alloc] initWithContentRect:CGRectMake(0.0, 0.0, 1024.0, 768.0) styleMask:CPTitledWindowMask],
tokenField = [[CPTokenField alloc] initWithFrame:CGRectMake(10, 10, 100, 28)];
[[aWindow contentView] addSubview:tokenField];
[tokenField setEnabled:YES];
[tokenField setEditable:YES];
[aWindow makeKeyAndOrderFront:nil];
// Start autocomplete.
var tokenDelegate = [TokenFieldDelegate new];
[tokenDelegate setCompletions:[@"Tokyo", @"Toronto", @"Gothenburg", @"London"]];
[tokenField setDelegate:tokenDelegate];
[tokenField setStringValue:@"To"];
// Start autocomplete programmatically.
[aWindow makeFirstResponder:tokenField];
[[tokenField _autocompleteMenu] _showCompletions:nil];
// Verify we're now autocompleting.
[self assertTrue:[[tokenField _autocompleteMenu]._menuWindow isVisible] message:@"autocomplete visible"];
[aWindow close];
[self assertFalse:[[tokenField _autocompleteMenu]._menuWindow isVisible] message:@"autocomplete not visible when token field window closes"];
}
@end
@implementation TokenFieldDelegate : CPObject
{
CPArray completions @accessors;
}
- (CPArray)tokenField:(CPTokenField)aTokenField completionsForSubstring:(CPString)substring indexOfToken:(int)tokenIndex indexOfSelectedItem:(int)selectedIndex
{
var r = [];
if (!substring)
return completions;
for (var i = 0; i < completions.length; i++)
if (completions[i].toLowerCase().indexOf(completions.toLowerCase()) == 0)
r.push(completions[i]);
return r;
}
@end
+18 -4
View File
@@ -144,6 +144,7 @@ github_ref="v0.9.6-RC2"
noprompt=""
install_capp=""
install_method="zip"
verbosity=1
while [ $# -gt 0 ]; do
case "$1" in
@@ -153,6 +154,8 @@ while [ $# -gt 0 ]; do
--clone-http) install_method="clone --http";;
--github-user) github_user="$2"; shift;;
--github-ref) github_ref="$2"; shift;;
-q|--quiet) verbosity=$[verbosity - 1];;
-v|--verbose) verbosity=$[verbosity + 1];;
*) cat >&2 <<-EOT
usage: ./bootstrap.sh [OPTIONS]
@@ -162,6 +165,8 @@ usage: ./bootstrap.sh [OPTIONS]
--clone-http: Do "git clone http://" instead of downloading zips.
--github-user [USER]: Github user (default: $github_user).
--github-ref [REF]: Use another git ref (default: $github_ref).
-q | --quiet: Output less logging.
-v | --verbose: Output more logging.
EOT
exit 1;;
esac
@@ -174,7 +179,8 @@ github_path="$github_user/cappuccino-base"
# The purpose of bootstrap is to install Cappuccino.
install_cappuccino="yes"
sed "s/\[\[ CAPPUCCINO_VERSION \]\]/$github_ref/" <<EOT
if (( $verbosity > 0 )); then
sed "s/\[\[ CAPPUCCINO_VERSION \]\]/$github_ref/" <<EOT
_______ ____ ___ __ __________(_)__ ___
/ __/ _ \`/ _ \/ _ \/ // / __/ __/ / _ \/ _ \\
@@ -192,8 +198,10 @@ sed "s/\[\[ CAPPUCCINO_VERSION \]\]/$github_ref/" <<EOT
http://github.com/cappuccino/cappuccino
irc://irc.freenode.org#cappuccino
This script will install the Cappuccino environment for you. Continue?
EOT
fi
echo "This script will install the Cappuccino environment for you. Continue?"
if ! prompt "yes"; then
install_cappuccino="no"
@@ -274,11 +282,17 @@ if [ "$install_cappuccino" ]; then
zip_ball="http://github.com/$github_path/zipball/$github_ref"
echo "Downloading Cappuccino base from \"$zip_ball\"..."
$(which curl &> /dev/null && echo curl -L -o || echo wget --no-check-certificate -O) "$tmp_zip" "$zip_ball"
curl_quiet_arg=""
wget_quiet_arg=""
if (( $verbosity < 1 )); then curl_quiet_arg="--silent"; wget_quiet_arg="--no-verbose"; fi
$(which curl &> /dev/null && echo curl $curl_quiet_arg -L -o || echo $wget_quiet_arg --no-check-certificate -O) "$tmp_zip" "$zip_ball"
check_and_exit
echo "Installing Cappuccino base..."
unzip "$tmp_zip" -d "$install_directory"
quiet_arg=""
if (( $verbosity < 2 )); then quiet_arg="-q"; fi
unzip $quiet_arg "$tmp_zip" -d "$install_directory"
check_and_exit
rm "$tmp_zip"
check_and_exit