From 3d5b30ab9ef8be1cc704b4f09e3346d4c4923201 Mon Sep 17 00:00:00 2001 From: Antoine Mercadal Date: Fri, 8 Jul 2011 15:50:29 +0200 Subject: [PATCH 01/11] add CPPopover (Cocoa API compliant) --- AppKit/AppKit.j | 1 + AppKit/CPPopover.j | 239 +++++++++ AppKit/CPWindow/CPWindow.j | 1 + AppKit/CPWindow/_CPAttachedWindowView.j | 317 ++++++++++++ AppKit/_CPAttachedWindow.j | 501 +++++++++++++++++++ Tests/Manual/CPPopover/AppController.j | 168 +++++++ Tests/Manual/CPPopover/Info.plist | 12 + Tests/Manual/CPPopover/Jakefile | 93 ++++ Tests/Manual/CPPopover/Resources/spinner.gif | Bin 0 -> 1849 bytes Tests/Manual/CPPopover/index-debug.html | 103 ++++ Tests/Manual/CPPopover/index.html | 78 +++ Tests/Manual/CPPopover/main.j | 18 + 12 files changed, 1531 insertions(+) create mode 100644 AppKit/CPPopover.j create mode 100644 AppKit/CPWindow/_CPAttachedWindowView.j create mode 100644 AppKit/_CPAttachedWindow.j create mode 100644 Tests/Manual/CPPopover/AppController.j create mode 100644 Tests/Manual/CPPopover/Info.plist create mode 100644 Tests/Manual/CPPopover/Jakefile create mode 100644 Tests/Manual/CPPopover/Resources/spinner.gif create mode 100644 Tests/Manual/CPPopover/index-debug.html create mode 100644 Tests/Manual/CPPopover/index.html create mode 100644 Tests/Manual/CPPopover/main.j diff --git a/AppKit/AppKit.j b/AppKit/AppKit.j index 0a98e86d1..6dd81c956 100644 --- a/AppKit/AppKit.j +++ b/AppKit/AppKit.j @@ -66,6 +66,7 @@ @import "CPOutlineView.j" @import "CPPanel.j" @import "CPPasteboard.j" +@import "CPPopover.j" @import "CPPopUpButton.j" @import "CPPredicateEditor.j" @import "CPPredicateEditorRowTemplate.j" diff --git a/AppKit/CPPopover.j b/AppKit/CPPopover.j new file mode 100644 index 000000000..ad3a83205 --- /dev/null +++ b/AppKit/CPPopover.j @@ -0,0 +1,239 @@ +/* + * CPPopover.j + * AppKit + * + * Created by Antoine Mercadal. + * Copyright 2011 Antoine Mercadal. + * + * 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 + +@import "CPButton.j" +@import "CPColor.j" +@import "CPImage.j" +@import "CPImageView.j" +@import "CPResponder.j" +@import "CPView.j" +@import "_CPAttachedWindow.j" + + +CPPopoverBehaviorApplicationDefined = 0; +CPPopoverBehaviorTransient = 1; +CPPopoverBehaviorSemitransient = 2; + + +/*! @ingroup appkit + @class CPPopover + + This class represent a widget that displays a attached + view relative to another one. + + Delegate can implement: + – popoverShouldClose:(CPPopover)aPopOver + – popoverWillShow:(CPPopover)aPopOver + – popoverDidShow:(CPPopover)aPopOver + – popoverWillClose:(CPPopover)aPopOver + – popoverDidClose:(CPPopover)aPopOver +*/ +@implementation CPPopover : CPResponder +{ + @outlet CPViewController _contentViewController @accessors(property=contentViewController); + @outlet id _delegate @accessors(property=delegate); + + BOOL _animates @accessors(property=animates); + BOOL _shown @accessors(getter=shown); + int _appearance @accessors(property=appearance); + int _behavior @accessors(getter=behavior); + + BOOL _needsCompute; + _CPAttachedWindow _attachedWindow; +} + + +#pragma mark - +#pragma mark INitialization + +/*! + Initialize the CPPopover witn default values + + @returns anInitialized CPPopover +*/ +- (CPPopover)init +{ + if (self = [super init]) + { + _animates = YES; + _appearance = CPPopoverAppearanceMinimal; + _behavior = CPPopoverBehaviorApplicationDefined; + _needsCompute = YES; + _shown = NO; + } + + return self; +} + + +#pragma mark - +#pragma mark Getters / Setters + +/*! + Returns the current rect of the popover + + @return CPRect represeting the frame of the popover +*/ +- (CPRect)positionningRect +{ + if (!_attachedWindow || ![_attachedWindow isVisible]) + return nil; + return [_attachedWindow frame]; +} + +/*! Sets the frame of the popover + @param aRect the desired frame +*/ +- (void)setPositionningRect:(CPRect)aRect +{ + if (!_attachedWindow || ![_attachedWindow isVisible]) + return; + [_attachedWindow setFrame:aRect]; +} + +/*! + Returns the size of the popover's view + + @return CPSize represeting the size of the popover's view +*/ +- (CPRect)contentSize +{ + if (!_attachedWindow || ![_attachedWindow isVisible]) + return nil; + return [[_contentViewController view] frameSize]; +} + +/*! + Sets the size of of the popover's view + + @param aSize the desired size +*/ +- (void)setContentSize:(CPSize)aSize +{ + [[_contentViewController view] setFrameSize:aSize]; +} + +/*! + Indicates if CPPopover is visible + + @returns YES if visible +*/ +- (BOOL)shown +{ + if (!_attachedWindow) + return NO; + return [_attachedWindow isVisible]; +} + +/*! + Set the behaviour of the CPPopover. It can be + - CPPopoverBehaviorTransient: the popover will be close if another control outside the popover become the responder + - CPPopoverBehaviorApplicationDefined: (DEFAULT) the application is responsible for closing the popover + + @param aBehaviour the desired behaviour +*/ +- (void)setBehaviour:(int)aBehaviour +{ + if (_behavior == aBehaviour) + return; + + _behavior = aBehaviour; + _needsCompute = YES; +} + + +#pragma mark - +#pragma mark Positionning + +/*! + Show the popover + + @param positioningRect if set, the popover will be positionned to a random rect relative to the window + @param positioningView if set, the popover will be positioned relative to this view + @param preferredEdge: CPRectEdge representing the prefered positionning. +*/ +- (void)showRelativeToRect:(CPRect)positioningRect ofView:(CPView)positioningView preferredEdge:(CPRectEdge)preferredEdge +{ + if (_delegate && [_delegate respondsToSelector:@selector(popoverWillShow:)]) + [_delegate popoverWillShow:self]; + + if (!_contentViewController) + [CPException raise:CPInternalInconsistencyException reason:@"contentViewController must not be nil"]; + + if (_needsCompute) + { + var styleMask = (_behavior == CPPopoverBehaviorTransient) ? CPClosableOnBlurWindowMask : nil; + _attachedWindow = [[_CPAttachedWindow alloc] initWithContentRect:CPRectMakeZero() styleMask:styleMask]; + } + + [_attachedWindow setAppearance:_appearance]; + [_attachedWindow setAnimates:_animates]; + [_attachedWindow setMovableByWindowBackground:NO]; + [_attachedWindow setFrame:[_attachedWindow frameRectForContentRect:[[_contentViewController view] frame]]]; + [_attachedWindow setContentView:[_contentViewController view]]; + + if (positioningRect) + [_attachedWindow positionRelativeToRect:positioningRect preferredEdge:preferredEdge]; + else if (positioningView) + [_attachedWindow positionRelativeToView:positioningView preferredEdge:preferredEdge]; + else + [CPException raise:CPInvalidArgumentException reason:@"you must set positioningRect or positioningRect"]; + + if (_delegate && [_delegate respondsToSelector:@selector(popoverDidShow:)]) + [_delegate popoverDidShow:self]; +} + +/*! + Closes the popover +*/ +- (void)close +{ + if (_delegate && [_delegate respondsToSelector:@selector(popoverShouldClose:)]); + if (![_delegate popoverShouldClose:self]) + return; + + if (_delegate && [_delegate respondsToSelector:@selector(popoverWillClose:)]) + [_delegate popoverWillClose:self]; + + [_attachedWindow close]; + + if (_delegate && [_delegate respondsToSelector:@selector(popoverDidClose:)]) + [_delegate popoverDidClose:self]; +} + + +#pragma mark - +#pragma mark Action + +/*! + Close the popover + + @param aSender the sender of the action +*/ +- (IBAction)performClose:(id)aSender +{ + [self close]; +} + +@end \ No newline at end of file diff --git a/AppKit/CPWindow/CPWindow.j b/AppKit/CPWindow/CPWindow.j index b87bde7c4..1238017a5 100644 --- a/AppKit/CPWindow/CPWindow.j +++ b/AppKit/CPWindow/CPWindow.j @@ -2928,5 +2928,6 @@ CPCustomWindowShadowStyle = 3; @import "_CPHUDWindowView.j" @import "_CPBorderlessWindowView.j" @import "_CPBorderlessBridgeWindowView.j" +@import "_CPAttachedWindowView.j" @import "CPDragServer.j" @import "CPView.j" diff --git a/AppKit/CPWindow/_CPAttachedWindowView.j b/AppKit/CPWindow/_CPAttachedWindowView.j new file mode 100644 index 000000000..dd7b6e925 --- /dev/null +++ b/AppKit/CPWindow/_CPAttachedWindowView.j @@ -0,0 +1,317 @@ +/* + * _CPAttachedWindowView.j + * AppKit + * + * Created by Antoine Mercadal + * Copyright 2011 + * + * 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 "_CPWindowView.j" + + +/*! + @ignore + + A custom CPWindowView that manage border and cursor +*/ +@implementation _CPAttachedWindowView : _CPWindowView +{ + BOOL _mouseDownPressed @accessors(getter=isMouseDownPressed, setter=setMouseDownPressed:); + float _arrowOffsetX @accessors(property=arrowOffsetX); + float _arrowOffsetY @accessors(property=arrowOffsetY); + int _appearance @accessors(property=appearance); + unsigned _preferredEdge @accessors(property=preferredEdge); + + BOOL _useGlowingEffect; + CPColor _backgroundColor; + CPColor _strokeColor; + CPImage _cursorBackgroundBottom; + CPImage _cursorBackgroundLeft; + CPImage _cursorBackgroundRight; + CPImage _cursorBackgroundTop; + CPSize _cursorSize; +} + +/*! + Compute the contentView frame from a given window frame + + @param aFrameRect the window frame +*/ +- (CGRect)contentRectForFrameRect:(CGRect)aFrameRect +{ + var contentRect = CGRectMakeCopy(aFrameRect); + + // @todo change border art and remove this pixel perfect adaptation + // return CGRectInset(contentRect, 20, 20); + + contentRect.origin.x += 18; + contentRect.origin.y += 17; + contentRect.size.width -= 35; + contentRect.size.height -= 37; + + return contentRect; +} + +/*! + Compute the window frame from a given contentView frame + + @param aContentRect the contentView frame +*/ ++ (CGRect)frameRectForContentRect:(CGRect)aContentRect +{ + var frameRect = CGRectMakeCopy(aContentRect); + + // @todo change border art and remove this pixel perfect adaptation + //return CGRectOffset(frameRect, 20, 20); + + frameRect.origin.x -= 18; + frameRect.origin.y -= 17; + frameRect.size.width += 35; + frameRect.size.height += 37; + return frameRect; +} + +/*! + Initialize the _CPWindowView +*/ +- (id)initWithFrame:(CPRect)aFrame styleMask:(unsigned)aStyleMask +{ + if (self = [super initWithFrame:aFrame styleMask:aStyleMask]) + { + var bundle = [CPBundle bundleForClass:[self class]]; + _arrowOffsetX = 0.0; + _arrowOffsetY = 0.0; + + // @TODO: make this themable + _useGlowingEffect = YES; + _appearance = CPPopoverAppearanceMinimal; + _cursorSize = CPSizeMake(15, 10); + } + + return self; +} + +/*! + Hide the cursor +*/ +- (void)hideCursor +{ + _cursorSize = CPSizeMakeZero(); + [self setNeedsDisplay:YES]; +} + +/*! + Show the cursor +*/ +- (void)showCursor +{ + _cursorSize = CPSizeMake(15, 10); + [self setNeedsDisplay:YES]; + _mouseDownPressed = NO; +} + +/*! + Draw the view +*/ +- (void)drawRect:(CGRect)aRect +{ + [super drawRect:aRect]; + + if (_appearance == CPPopoverAppearanceMinimal) + { + _strokeColor = [CPColor colorWithCalibratedRed:(172.0 / 255) green:(172.0 / 255) blue:(172.0 / 255) alpha:1.0]; + _backgroundColor = [CPColor colorWithCalibratedRed:(241.0 / 255) green:(241.0 / 255) blue:(241.0 / 255) alpha:0.93]; + } + else + { + _strokeColor = [CPColor colorWithCalibratedRed:(172.0 / 255) green:(172.0 / 255) blue:(172.0 / 255) alpha:1.0]; + _backgroundColor = [CPColor colorWithCalibratedRed:(50.0 / 255) green:(50.0 / 255) blue:(50.0 / 255) alpha:0.93]; + } + + + var context = [[CPGraphicsContext currentContext] graphicsPort], + radius = 5, + arrowWidth = _cursorSize.width, + arrowHeight = _cursorSize.height, + strokeWidth = 2; + + CGContextSetStrokeColor(context, _strokeColor); + CGContextSetLineWidth(context, strokeWidth); + CGContextBeginPath(context); + + aRect.origin.x += strokeWidth; + aRect.origin.y += strokeWidth; + aRect.size.width -= strokeWidth * 2; + aRect.size.height -= strokeWidth * 2; + + if (_useGlowingEffect) + { + var shadowColor = [[CPColor blackColor] colorWithAlphaComponent:.2], + shadowSize = CGSizeMake(0, 0), + shadowBlur = 15; + + //compensate for the shadow blur + aRect.origin.x += shadowBlur; + aRect.origin.y += shadowBlur; + aRect.size.width -= shadowBlur * 2; + aRect.size.height -= shadowBlur * 2; + + //set the shadow + CGContextSetShadow(context, CGSizeMake(0,0), 20); + CGContextSetShadowWithColor(context, shadowSize, shadowBlur, shadowColor); + } + + CGContextSetFillColor(context, _backgroundColor); + + var xMin = _CGRectGetMinX(aRect), + xMax = _CGRectGetMaxX(aRect), + yMin = _CGRectGetMinY(aRect), + yMax = _CGRectGetMaxY(aRect); + + // draw! + switch (_preferredEdge) + { + case CPMinXEdge: + // origin ne + CGContextMoveToPoint(context, xMin + radius, yMin); + + // ne + CGContextAddLineToPoint(context, xMax - radius, yMin); + CGContextAddCurveToPoint(context, xMax - radius, yMin, xMax, yMin, xMax, yMin + radius); + + // arrow CPMinXEdge + CGContextAddLineToPoint(context, xMax, (aRect.size.height / 2) + aRect.origin.y + _arrowOffsetY - (arrowHeight - 2)); + CGContextAddLineToPoint(context, aRect.size.width + arrowHeight + aRect.origin.x + _arrowOffsetX, (aRect.size.height / 2) + aRect.origin.y + _arrowOffsetY); + CGContextAddLineToPoint(context, aRect.size.width + aRect.origin.x + _arrowOffsetX, (aRect.size.height / 2 + (arrowWidth / 2)) + aRect.origin.y + _arrowOffsetY); + + // se + CGContextAddLineToPoint(context, xMax, yMax - radius); + CGContextAddCurveToPoint(context, xMax, yMax - radius, xMax, yMax, xMax - radius, yMax); + + // sw + CGContextAddLineToPoint(context, xMin + radius, yMax); + CGContextAddCurveToPoint(context, xMin + radius, yMax, xMin, yMax, xMin, yMax - radius); + + // nw + CGContextAddLineToPoint(context, xMin, yMin + radius); + CGContextAddCurveToPoint(context, xMin, yMin + radius, xMin, yMin, xMin + radius, yMin); + break; + + case CPMaxXEdge: + // origin ne + CGContextMoveToPoint(context, xMin + radius, yMin); + + // ne + CGContextAddLineToPoint(context, xMax - radius, yMin); + CGContextAddCurveToPoint(context, xMax - radius, yMin, xMax, yMin, xMax, yMin + radius); + + // se + CGContextAddLineToPoint(context, xMax, yMax - radius); + CGContextAddCurveToPoint(context, xMax, yMax - radius, xMax, yMax, xMax - radius, yMax); + + // sw + CGContextAddLineToPoint(context, xMin + radius, yMax); + CGContextAddCurveToPoint(context, xMin + radius, yMax, xMin, yMax, xMin, yMax - radius); + + // arrow CPMaxXEdge + CGContextAddLineToPoint(context, xMin, (aRect.size.height / 2 + (arrowWidth / 2) + aRect.origin.y + _arrowOffsetY)); + CGContextAddLineToPoint(context, aRect.origin.x - arrowHeight + _arrowOffsetX, (aRect.size.height / 2) + aRect.origin.y + _arrowOffsetY); + CGContextAddLineToPoint(context, aRect.origin.x + _arrowOffsetX, (aRect.size.height / 2 - (arrowWidth / 2) + aRect.origin.y + _arrowOffsetY)); + + // nw + CGContextAddLineToPoint(context, xMin, yMin + radius); + CGContextAddCurveToPoint(context, xMin, yMin + radius, xMin, yMin, xMin + radius, yMin); + + break; + + case CPMaxYEdge: + // origin nw + CGContextMoveToPoint(context, xMin, yMin + yMin); + + // nw + CGContextAddLineToPoint(context, xMin, yMin + radius); + CGContextAddCurveToPoint(context, xMin, yMin + radius, xMin, yMin, xMin + radius, yMin); + + // arrow CPMaxYEdge + CGContextAddLineToPoint(context, (aRect.size.width / 2) + aRect.origin.x + _arrowOffsetX - (arrowWidth / 2), yMin); + CGContextAddLineToPoint(context, (aRect.size.width / 2) + aRect.origin.x + _arrowOffsetX, aRect.origin.y - arrowHeight + _arrowOffsetY); + CGContextAddLineToPoint(context, (aRect.size.width / 2) + (arrowWidth / 2) + aRect.origin.x + _arrowOffsetX, aRect.origin.y + _arrowOffsetY); + + // ne + CGContextAddLineToPoint(context, xMax - radius, yMin); + CGContextAddCurveToPoint(context, xMax - radius, yMin, xMax, yMin, xMax, yMin + radius); + + // se + CGContextAddLineToPoint(context, xMax, yMax - radius); + CGContextAddCurveToPoint(context, xMax, yMax - radius, xMax, yMax, xMax - radius, yMax); + + // sw + CGContextAddLineToPoint(context, xMin + radius, yMax); + CGContextAddCurveToPoint(context, xMin + radius, yMax, xMin, yMax, xMin, yMax - radius); + break; + + case CPMinYEdge: + // origin nw + CGContextMoveToPoint(context, xMin, yMin + yMin); + + // nw + CGContextAddLineToPoint(context, xMin, yMin + radius); + CGContextAddCurveToPoint(context, xMin, yMin + radius, xMin, yMin, xMin + radius, yMin); + + // ne + CGContextAddLineToPoint(context, xMax - radius, yMin); + CGContextAddCurveToPoint(context, xMax - radius, yMin, xMax, yMin, xMax, yMin + radius); + + // se + CGContextAddLineToPoint(context, xMax, yMax - radius); + CGContextAddCurveToPoint(context, xMax, yMax - radius, xMax, yMax, xMax - radius, yMax); + + // arrow CPMinYEdge + CGContextAddLineToPoint(context, (aRect.size.width / 2) + (arrowWidth / 2) + aRect.origin.x + _arrowOffsetX , yMax); + CGContextAddLineToPoint(context, (aRect.size.width / 2) + aRect.origin.x + _arrowOffsetX, aRect.size.height + aRect.origin.y + arrowHeight + _arrowOffsetY); + CGContextAddLineToPoint(context, (aRect.size.width / 2) - (arrowWidth / 2) + aRect.origin.x + _arrowOffsetX, aRect.size.height + aRect.origin.y + _arrowOffsetY); + + // sw + CGContextAddLineToPoint(context, xMin + radius, yMax); + CGContextAddCurveToPoint(context, xMin + radius, yMax, xMin, yMax, xMin, yMax - radius); + break; + + default: + // no computed edge means standard rounded rect + CGContextAddPath(context, CGPathWithRoundedRectangleInRect(aRect, radius, radius, YES, YES, YES, YES)); + } + + CGContextClosePath(context); + + //Draw it + CGContextStrokePath(context); + CGContextFillPath(context); +} + +- (void)mouseDown:(CPEvent)anEvent +{ + _mouseDownPressed = YES; + [super mouseDown:anEvent]; +} + +- (void)mouseUp:(CPEvent)anEvent +{ + _mouseDownPressed = NO; + [super mouseUp:anEvent]; +} + +@end diff --git a/AppKit/_CPAttachedWindow.j b/AppKit/_CPAttachedWindow.j new file mode 100644 index 000000000..28f94ddf3 --- /dev/null +++ b/AppKit/_CPAttachedWindow.j @@ -0,0 +1,501 @@ +/* + * _CPToolTipWindowView.j + * AppKit + * + * Created by Antoine Mercadal + * Copyright 2011 + * + * 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 + +@import "CPButton.j" +@import "CPWindow.j" + + +CPClosableOnBlurWindowMask = 1 << 4; +CPPopoverAppearanceMinimal = 0; +CPPopoverAppearanceHUD = 1; + + +/*! + @ignore + + This is a simple attached window like the one that pops up + when you double click on a meeting in iCal +*/ +@implementation _CPAttachedWindow : CPWindow +{ + BOOL _animates @accessors(property=animates); + id _targetView @accessors(property=targetView); + int _appearance @accessors(getter=appearance); + + BOOL _closeOnBlur; + BOOL _isClosed; + BOOL _shouldPerformAnimation; + CPButton _closeButton; + float _animationDuration; +} + +/*! + override default windowView class loader + + @param aStyleMask the window mask + @return the windowView class +*/ + +#pragma mark - +#pragma mark Class methods + ++ (Class)_windowViewClassForStyleMask:(unsigned)aStyleMask +{ + return _CPAttachedWindowView; +} + + +#pragma mark - +#pragma mark Initialization + +/*! + Create and init a _CPAttachedWindow with given size of and view + + @param aSize the size of the attached window + @param aView the target view + @return ready to use _CPAttachedWindow +*/ ++ (id)attachedWindowWithSize:(CGSize)aSize forView:(CPView)aView +{ + return [_CPAttachedWindow attachedWindowWithSize:aSize forView:aView styleMask:nil]; +} + +/*! + Create and init a _CPAttachedWindow with given size of and view + + @param aSize the size of the attached window + @param aView the target view + @return ready to use _CPAttachedWindow + @param styleMask the window style mask (combine CPClosableWindowMask and CPClosableOnBlurWindowMask) +*/ ++ (id)attachedWindowWithSize:(CGSize)aSize forView:(CPView)aView styleMask:(int)aMask +{ + var attachedWindow = [[_CPAttachedWindow alloc] initWithContentRect:CPRectMake(0.0, 0.0, aSize.width, aSize.height) styleMask:aMask]; + + [attachedWindow attachToView:aView]; + + return attachedWindow; +} + +/*! + Create and init a _CPAttachedWindow with given frame + + @param aFrame the frame of the attached window + @return ready to use _CPAttachedWindow +*/ +- (id)initWithContentRect:(CGRect)aFrame +{ + self = [self initWithContentRect:aFrame styleMask:nil] + return self; +} + +/*! + Create and init a _CPAttachedWindow with given frame + + @param aFrame the frame of the attached window + @param styleMask the window style mask (combine CPClosableWindowMask and CPClosableOnBlurWindowMask) + @return ready to use _CPAttachedWindow +*/ +- (id)initWithContentRect:(CGRect)aFrame styleMask:(unsigned)aStyleMask +{ + if (self = [super initWithContentRect:aFrame styleMask:aStyleMask]) + { + _animates = YES; + _animates = YES; + _animationDuration = 150; + _closeOnBlur = (aStyleMask & CPClosableOnBlurWindowMask); + _isClosed = NO; + _shouldPerformAnimation = _animates; + + [self setLevel:CPStatusWindowLevel]; + [self setMovableByWindowBackground:YES]; + [self setHasShadow:NO]; + + _DOMElement.style.WebkitBackfaceVisibility = "hidden"; + _DOMElement.style.WebkitTransitionProperty = "-webkit-transform, opacity"; + _DOMElement.style.WebkitTransitionDuration = _animationDuration + "ms"; + + [_windowView setNeedsDisplay:YES]; + } + + return self; +} + +#pragma mark - +#pragma mark Getters / Setters + +- (void)setAppearance:(int)anAppearance +{ + if (_appearance == anAppearance) + return; + + [_windowView setAppearance:anAppearance]; +} + +#pragma mark - +#pragma mark Observer + +/*! + Update the _CPAttachedWindow frame if a resize event is observed + +*/ +- (void)observeValueForKeyPath:(CPString)aPath ofObject:(id)anObject change:(CPDictionary)theChange context:(void)aContext +{ + if ([aPath isEqual:@"frame"]) + { + // @TODO: not recompute everything, just compute the move offset + var g = [_windowView preferredEdge]; + + [self positionRelativeToView:_targetView preferredEdge:g]; + } +} + + +#pragma mark - +#pragma mark Notification handlers + +- (void)_attachedWindowDidMove:(CPNotification)aNotification +{ + if ([_windowView isMouseDownPressed]) + { + [_targetView removeObserver:self forKeyPath:@"frame"]; + [_windowView hideCursor]; + [self setLevel:CPNormalWindowLevel]; + [_closeButton setFrameOrigin:CPPointMake(1.0, 1.0)]; + [[CPNotificationCenter defaultCenter] removeObserver:self name:CPWindowDidMoveNotification object:self]; + } +} + + +#pragma mark - +#pragma mark Utilities + +- (CPPoint)computeOrigin:(CPView)aView preferredEdge:(int)anEdge +{ + var frameView = [aView frame], + currentView = aView, + origin = [aView frameOrigin], + lastView; + + // if somebody succeed to use the conversion function of CPView + // to get this working, please do. + while (currentView = [currentView superview]) + { + origin.x += [currentView frameOrigin].x; + origin.y += [currentView frameOrigin].y; + lastView = currentView; + } + + origin.x += [[lastView window] frame].origin.x; + origin.y += [[lastView window] frame].origin.y; + + // take care of the scrolling point + if ([aView enclosingScrollView]) + { + var offsetPoint = [[[aView enclosingScrollView] contentView] boundsOrigin]; + origin.x -= offsetPoint.x; + origin.y -= offsetPoint.y; + } + + return [self computeOriginFromRect:CPRectMake(origin.x, origin.y, CPRectGetWidth(frameView), CPRectGetHeight(frameView)) preferredEdge:anEdge]; +} + +- (CPPoint)computeOriginFromRect:(CPRect)aRect preferredEdge:(int)anEdge +{ + var nativeRect = [[[CPApp mainWindow] platformWindow] nativeContentRect], + originLeft = CPPointCreateCopy(aRect.origin), + originRight = CPPointCreateCopy(aRect.origin), + originTop = CPPointCreateCopy(aRect.origin), + originBottom = CPPointCreateCopy(aRect.origin); + + // CPMaxXEdge + originRight.x += aRect.size.width; + originRight.y += (aRect.size.height / 2.0) - (CPRectGetHeight([self frame]) / 2.0) + + // CPMinXEdge + originLeft.x -= CPRectGetWidth([self frame]); + originLeft.y += (aRect.size.height / 2.0) - (CPRectGetHeight([self frame]) / 2.0) + + // CPMaxYEdge + originBottom.x += aRect.size.width / 2.0 - CPRectGetWidth([self frame]) / 2.0; + originBottom.y += aRect.size.height; + + // CPMinYEdge + originTop.x += aRect.size.width / 2.0 - CPRectGetWidth([self frame]) / 2.0; + originTop.y -= CPRectGetHeight([self frame]); + + var requestedEdge = (anEdge !== nil) ? anEdge : CPMaxXEdge, + requestedOrigin; + + switch (requestedEdge) + { + case CPMaxXEdge: + requestedOrigin = originRight; + break; + case CPMinXEdge: + requestedOrigin = originLeft; + break; + case CPMinYEdge: + requestedOrigin = originTop; + break; + case CPMaxYEdge: + requestedOrigin = originBottom; + break; + } + + var origins = [requestedOrigin, originRight, originLeft, originTop, originBottom], + edges = [requestedEdge, CPMaxXEdge, CPMinXEdge, CPMinYEdge, CPMaxYEdge]; + + for (var i = 0; i < origins.length; i++) + { + var o = origins[i], + g = edges[i]; + + [_windowView setArrowOffsetX:0]; + [_windowView setArrowOffsetY:0]; + [_windowView setPreferredEdge:g]; + + if (o.x < 0) + { + [_windowView setArrowOffsetX:o.x]; + o.x = 0; + } + if (o.x + CPRectGetWidth([self frame]) > nativeRect.size.width) + { + [_windowView setArrowOffsetX:(o.x + CPRectGetWidth([self frame]) - nativeRect.size.width)]; + o.x = nativeRect.size.width - CPRectGetWidth([self frame]); + } + if (o.y < 0) + { + [_windowView setArrowOffsetY:o.y]; + o.y = 0; + } + if (o.y + CPRectGetHeight([self frame]) > nativeRect.size.height) + { + [_windowView setArrowOffsetY:(CPRectGetHeight([self frame]) + o.y - nativeRect.size.height)]; + o.y = nativeRect.size.height - CPRectGetHeight([self frame]); + } + + switch (g) + { + case CPMaxXEdge: + if (o.x >= (aRect.origin.x + aRect.size.width)) + return o; + break; + case CPMinXEdge: + if ((o.x + _frame.size.width) <= aRect.origin.x) + return o; + break; + case CPMaxYEdge: + if (o.y >= (aRect.origin.y + aRect.size.height)) + return o; + break; + case CPMinYEdge: + if ((o.y + _frame.size.height) <= aRect.origin.y) + return o; + break; + } + } + + [_windowView setPreferredEdge:nil]; + return requestedOrigin; +} + +/*! + Compute the frame needed to be placed to the given view + and position the attached window according to this view (edge will be automatic) + + @param aView the view where _CPAttachedWindow must be attached +*/ +- (void)positionRelativeToView:(CPView)aView +{ + [self positionRelativeToView:aView preferredEdge:nil]; +} + +/*! + Compute the frame needed to be placed to the given view + and position the attached window according to this view + + @param aView the view where _CPAttachedWindow must be attached + @param anEdge the preferd edge to use +*/ +- (void)positionRelativeToView:(CPView)aView preferredEdge:(int)anEdge +{ + var point = [self computeOrigin:aView preferredEdge:anEdge]; + + [self setFrameOrigin:point]; + [_windowView showCursor]; + [self setLevel:CPStatusWindowLevel]; + [_closeButton setFrameOrigin:CPPointMake(1.0, 1.0)]; + [_windowView setNeedsDisplay:YES]; + [self makeKeyAndOrderFront:nil]; + + _targetView = aView; + [_targetView addObserver:self forKeyPath:@"frame" options:nil context:nil]; +} + +/*! + Position the _CPAttachedWindow to a random point + + @param aPoint the point where the _CPAttachedWindow will be attached +*/ +- (void)positionRelativeToRect:(CPRect)aRect +{ + [self positionRelativeToRect:aRect preferredEdge:nil] +} + +/*! + Position the _CPAttachedWindow to a random point + + @param aPoint the point where the _CPAttachedWindow will be attached + @param anEdge the prefered edge +*/ +- (void)positionRelativeToRect:(CPRect)aRect preferredEdge:(int)anEdge +{ + var point = [self computeOriginFromRect:aRect preferredEdge:anEdge]; + + [self setFrameOrigin:point]; + [_windowView showCursor]; + [self setLevel:CPStatusWindowLevel]; + [_closeButton setFrameOrigin:CPPointMake(1.0, 1.0)]; + [_windowView setNeedsDisplay:YES]; + [self makeKeyAndOrderFront:nil]; +} + + +#pragma mark - +#pragma mark Actions + +/*! + Closes the _CPAttachedWindow + + @param sender the sender of the action +*/ +- (IBAction)close:(id)aSender +{ + [self close]; +} + + +#pragma mark - +#pragma mark Overrides + +/*! + Called when the window is loowing focus and close the window if CPClosableOnBlurWindowMask is setted +*/ +- (void)resignMainWindow +{ + if (_closeOnBlur && !_isClosed) + { + // set a close flag to avoid infinite loop + _isClosed = YES; + [self close]; + + if (_delegate && [_delegate respondsToSelector:@selector(didAttachedWindowClose:)]) + [_delegate didAttachedWindowClose:self]; + } +} + +/*! + Order front the window as usual and add listener for CPWindowDidMoveNotification + + @param sender the sender of the action +*/ +- (IBAction)orderFront:(is)aSender +{ + [super orderFront:aSender]; + + var tranformOrigin = "50% 100%"; + + switch ([_windowView preferredEdge]) + { + case CPMaxYEdge: + var posX = 50 + (([_windowView arrowOffsetX] * 100) / _frame.size.width); + tranformOrigin = posX + "% 0%"; // 50 0 + break; + case CPMinYEdge: + var posX = 50 + (([_windowView arrowOffsetX] * 100) / _frame.size.width); + tranformOrigin = posX + "% 100%"; // 50 100 + break; + case CPMinXEdge: + var posY = 50 + (([_windowView arrowOffsetY] * 100) / _frame.size.height); + tranformOrigin = "100% " + posY + "%"; // 100 50 + break; + case CPMaxXEdge: + var posY = 50 + (([_windowView arrowOffsetY] * 100) / _frame.size.height); + tranformOrigin = "0% "+ posY + "%"; // 0 50 + break; + } + + // @TODO: implement for FF + if (_animates && _shouldPerformAnimation && typeof(_DOMElement.style.WebkitTransform) != "undefined") + { + _DOMElement.style.opacity = 0; + _DOMElement.style.WebkitTransform = "scale(0)"; + _DOMElement.style.WebkitTransformOrigin = tranformOrigin; + window.setTimeout(function(){ + _DOMElement.style.height = _frame.size.height + @"px"; + _DOMElement.style.width = _frame.size.width + @"px"; + _DOMElement.style.opacity = 1; + _DOMElement.style.WebkitTransform = "scale(1.1)"; + var transitionEndFunction = function(){ + _DOMElement.style.WebkitTransform = "scale(1)"; + _DOMElement.removeEventListener("webkitTransitionEnd", transitionEndFunction, YES); + }; + _DOMElement.addEventListener("webkitTransitionEnd", transitionEndFunction, YES) + },0); + } + + [[CPNotificationCenter defaultCenter] addObserver:self selector:@selector(_attachedWindowDidMove:) name:CPWindowDidMoveNotification object:self]; + + _shouldPerformAnimation = NO; + _isClosed = NO; +} + +/*! + Close the windo with animation +*/ +- (void)close +{ + if (_animates) + { + _DOMElement.style.opacity = 0; + var transitionEndFunction = function(){ + [super close]; + _DOMElement.removeEventListener("webkitTransitionEnd", transitionEndFunction, YES); + }; + _DOMElement.addEventListener("webkitTransitionEnd", transitionEndFunction, YES); + } + else + [super close]; + + [_targetView removeObserver:self forKeyPath:@"frame"]; + + _shouldPerformAnimation = _animates; + + if (_delegate && [_delegate respondsToSelector:@selector(didAttachedWindowClose:)]) + [_delegate didAttachedWindowClose:self]; +} + +@end diff --git a/Tests/Manual/CPPopover/AppController.j b/Tests/Manual/CPPopover/AppController.j new file mode 100644 index 000000000..3891d89f2 --- /dev/null +++ b/Tests/Manual/CPPopover/AppController.j @@ -0,0 +1,168 @@ +/* + * AppController.j + * test + * + * Created by You on July 6, 2011. + * Copyright 2011, Your Company All rights reserved. + */ + +@import + +@import + +@implementation AppController : CPObject +{ + CPPopUpButton buttonGravity; + CPPopUpButton buttonStyle; + CPPopUpButton buttonAnimation; + CPPopUpButton buttonBehaviour; +} + +- (void)applicationDidFinishLaunching:(CPNotification)aNotification +{ + var theWindow = [[CPWindow alloc] initWithContentRect:CGRectMakeZero() styleMask:CPBorderlessBridgeWindowMask], + contentView = [theWindow contentView]; + + button = [CPButton buttonWithTitle:@"click"]; + [button setTarget:self]; + [button setAction:@selector(open:)]; + [button setFrameOrigin:CPPointMake(10, 60)]; + [contentView addSubview:button]; + + button = [CPButton buttonWithTitle:@"click"]; + [button setTarget:self]; + [button setAction:@selector(open:)]; + [button setFrameOrigin:CPPointMake( [contentView frameSize].width - 50, 60)]; + [contentView addSubview:button]; + + + button = [CPButton buttonWithTitle:@"click"]; + [button setTarget:self]; + [button setAction:@selector(open:)]; + [button setFrameOrigin:CPPointMake( [contentView frameSize].width - 50, [contentView frameSize].height - 50)]; + [contentView addSubview:button]; + + button = [CPButton buttonWithTitle:@"click"]; + [button setTarget:self]; + [button setAction:@selector(open:)]; + [button setFrameOrigin:CPPointMake( 10, [contentView frameSize].height - 50)]; + [contentView addSubview:button]; + + button = [CPButton buttonWithTitle:@"click"]; + [button setTarget:self]; + [button setAction:@selector(open:)]; + [button setAutoresizingMask:CPViewMinXMargin | CPViewMaxXMargin | CPViewMinYMargin | CPViewMaxYMargin] + [button setCenter:[contentView center]]; + [contentView addSubview:button]; + + buttonGravity = [[CPPopUpButton alloc] initWithFrame:CPRectMake(10, 10, 130, 24)]; + [buttonGravity addItemWithTitle:"Automatic"]; + [buttonGravity addItemWithTitle:"Bottom"]; + [buttonGravity addItemWithTitle:"Top"]; + [buttonGravity addItemWithTitle:"Right"]; + [buttonGravity addItemWithTitle:"Left"]; + [contentView addSubview:buttonGravity]; + + buttonStyle = [[CPPopUpButton alloc] initWithFrame:CPRectMake(150, 10, 130, 24)]; + [buttonStyle addItemWithTitle:"Minimal"]; + [buttonStyle addItemWithTitle:"HUD"]; + [contentView addSubview:buttonStyle]; + + buttonAnimation = [[CPPopUpButton alloc] initWithFrame:CPRectMake(290, 10, 130, 24)]; + [buttonAnimation addItemWithTitle:"With animation"]; + [buttonAnimation addItemWithTitle:"No animation"]; + [contentView addSubview:buttonAnimation]; + + buttonBehaviour = [[CPPopUpButton alloc] initWithFrame:CPRectMake(430, 10, 130, 24)]; + [buttonBehaviour addItemWithTitle:"Transient"]; + [buttonBehaviour addItemWithTitle:"Not managed"]; + [contentView addSubview:buttonBehaviour]; + + + [theWindow orderFront:self]; +} + +- (IBAction)open:(id)sender +{ + var g; + switch([buttonGravity title]) + { + case "Automatic": + g = nil; + break; + case "Bottom": + g = CPMaxYEdge; + break; + case "Top": + g = CPMinYEdge; + break; + case "Left": + g = CPMinXEdge; + break; + case "Right": + g = CPMaxXEdge; + break; + } + + var a; + switch([buttonStyle title]) + { + case "Minimal": + a = CPPopoverAppearanceMinimal; + break; + case "HUD": + a = CPPopoverAppearanceHUD; + break; + } + + var p = [[CPPopover alloc] init], + viewC = [[CPViewController alloc] init]; + view = [[CPView alloc] initWithFrame:CPRectMake(0.0, 0.0, 320, 300)], + label = [CPTextField labelWithTitle:[buttonGravity title]]; + + [label setFont:[CPFont boldSystemFontOfSize:30.0]]; + [label setFrameOrigin:CPPointMake(0, 70)]; + [label setValue:[CPColor colorWithHexString:@"fff"] forThemeAttribute:@"text-shadow-color"]; + [label setValue:CGSizeMake(0.0, 1.0) forThemeAttribute:@"text-shadow-offset"]; + [label setTextColor:[CPColor colorWithHexString:@"444"]]; + [label setFrameSize:CPSizeMake([view frame].size.width, 50)]; + [label setAlignment:CPCenterTextAlignment]; + [view addSubview:label]; + + [viewC setView:view]; + [p setContentViewController:viewC]; + [p setAnimates:([buttonAnimation title] == @"With animation")]; + [p setBehaviour:([buttonBehaviour title] == @"Transient") ? CPPopoverBehaviorTransient : CPPopoverBehaviorApplicationDefined]; + [p setAppearance:a]; + [p setDelegate:self]; + [p showRelativeToRect:nil ofView:sender preferredEdge:g]; + CPLog.info("content size - w:" + [p contentSize].width + " h:" + [p contentSize].width); + CPLog.info("positionning rect - x: " + [p positionningRect].origin.x + " y: " + [p positionningRect].origin.x + + " w:" + [p positionningRect].size.width + " h:" + [p positionningRect].size.width); +} + + +#pragma mark - +#pragma mark CPPopover Delegate + +- (void)popoverWillShow:(CPPopover)aPopover +{ + CPLog.info("popover " + aPopover + " will show"); +} + +- (void)popoverDidShow:(CPPopover)aPopover +{ + CPLog.info("popover " + aPopover + " did show"); +} + +- (void)popoverWillClose:(CPPopover)aPopover +{ + CPLog.info("popover " + aPopover + " will close"); +} + +- (void)popoverDidClose:(CPPopover)aPopover +{ + CPLog.info("popover " + aPopover + " did close"); +} + +@end diff --git a/Tests/Manual/CPPopover/Info.plist b/Tests/Manual/CPPopover/Info.plist new file mode 100644 index 000000000..3eb7a23af --- /dev/null +++ b/Tests/Manual/CPPopover/Info.plist @@ -0,0 +1,12 @@ + + + + + CPApplicationDelegateClass + AppController + CPBundleName + test + CPPrincipalClass + CPApplication + + diff --git a/Tests/Manual/CPPopover/Jakefile b/Tests/Manual/CPPopover/Jakefile new file mode 100644 index 000000000..07be3fd32 --- /dev/null +++ b/Tests/Manual/CPPopover/Jakefile @@ -0,0 +1,93 @@ +/* + * Jakefile + * test + * + * Created by You on July 6, 2011. + * Copyright 2011, Your Company All rights reserved. + */ + +var ENV = require("system").env, + FILE = require("file"), + JAKE = require("jake"), + task = JAKE.task, + FileList = JAKE.FileList, + app = require("cappuccino/jake").app, + configuration = ENV["CONFIG"] || ENV["CONFIGURATION"] || ENV["c"] || "Debug", + OS = require("os"); + +app ("test", function(task) +{ + task.setBuildIntermediatesPath(FILE.join("Build", "test.build", configuration)); + task.setBuildPath(FILE.join("Build", configuration)); + + task.setProductName("test"); + task.setIdentifier("com.yourcompany.test"); + task.setVersion("1.0"); + task.setAuthor("Your Company"); + task.setEmail("feedback @nospam@ yourcompany.com"); + task.setSummary("test"); + task.setSources((new FileList("**/*.j")).exclude(FILE.join("Build", "**"))); + task.setResources(new FileList("Resources/**")); + task.setIndexFilePath("index.html"); + task.setInfoPlistPath("Info.plist"); + + if (configuration === "Debug") + task.setCompilerFlags("-DDEBUG -g"); + else + task.setCompilerFlags("-O"); +}); + +task ("default", ["test"], function() +{ + printResults(configuration); +}); + +task ("build", ["default"]); + +task ("debug", function() +{ + ENV["CONFIGURATION"] = "Debug"; + JAKE.subjake(["."], "build", ENV); +}); + +task ("release", function() +{ + ENV["CONFIGURATION"] = "Release"; + JAKE.subjake(["."], "build", ENV); +}); + +task ("run", ["debug"], function() +{ + OS.system(["open", FILE.join("Build", "Debug", "test", "index.html")]); +}); + +task ("run-release", ["release"], function() +{ + OS.system(["open", FILE.join("Build", "Release", "test", "index.html")]); +}); + +task ("deploy", ["release"], function() +{ + FILE.mkdirs(FILE.join("Build", "Deployment", "test")); + OS.system(["press", "-f", FILE.join("Build", "Release", "test"), FILE.join("Build", "Deployment", "test")]); + printResults("Deployment") +}); + +task ("desktop", ["release"], function() +{ + FILE.mkdirs(FILE.join("Build", "Desktop", "test")); + require("cappuccino/nativehost").buildNativeHost(FILE.join("Build", "Release", "test"), FILE.join("Build", "Desktop", "test", "test.app")); + printResults("Desktop") +}); + +task ("run-desktop", ["desktop"], function() +{ + OS.system([FILE.join("Build", "Desktop", "test", "test.app", "Contents", "MacOS", "NativeHost"), "-i"]); +}); + +function printResults(configuration) +{ + print("----------------------------"); + print(configuration+" app built at path: "+FILE.join("Build", configuration, "test")); + print("----------------------------"); +} diff --git a/Tests/Manual/CPPopover/Resources/spinner.gif b/Tests/Manual/CPPopover/Resources/spinner.gif new file mode 100644 index 0000000000000000000000000000000000000000..06dbc2bc21dddcf0e09b566d5b211aee89570f52 GIT binary patch literal 1849 zcma*odr(tX9tZHtz31lM+(&Y`A`Ou`NeG&R#DrIfV%?hng21vs6v>ooRQQFbk7E_K!V( z{?5!fpZWgIZ%*d6t%i*j24bL}AZUJm9)h6R*;%L4IWse3G#VWaN1#$zSSXcBhlYlH zJ|D}n{r&w2f+Qp)SS*&n?*G4}{}!h?td_mjU6Kad-rW*QwWYCUk7d^e+sjpZAn6kT z5rM;`{~_}-wm+L@%+E;JphLm}C3WzQAQD0wBoY|rOTQX{(@pp-WAA8Iw0HKl|15j) zhGh(SFWu#5zUCw^T!}C~eC}~?{xG-r7ugQC>taC+|L^xr&a%{#cgUOZXIM&v?)k^2 z1i5%0!U00wq)r4vlAwXBA_m5J5WNeuBm-XJQ5pG4GD%vc|K){+TJ{AkHqN@SJKczX zU}QRd9n*_bf^h94)6Ctv{CUfz_;qs#RDuQ$DR%y1&5G-a6rhf*4Y zl+UOD1*GE{GQ7vv;c-tTIMkz0ovO>9j%b5hpa@a*CBJ#W!0@ndSwF|%hMkdFtXiU) z`BEp+yhkt8ZY>tg&uyH@%^%K4jDrbA;et&vnnzZ3z22@e6<27z2Vk13kb7OtISyxv z(XoO-LNQCZe3lfzc%*pNqU<9T+p~teThOEB+~e&U0mC|zS{Q)dJZ)!^1f7L(zg*L> z*vSW%?dR~@@0(}x6-V^A`~ImD;)NaBUnP8;RH=bS2@pk`wp>>8r&jGjmGPy%1BW}v zoPo}ca$~bze@efc3kapuEVW1!%teOZT3j2Tw5=hjiAf02d}7dL0oFC%@=RXp5Ow#% z@a>+AM|YWt$n&e`>sB-32gBcuTi>R>*|8@lp`y6thc7ydyqDsuUn~YzZf|{-R@-3y zL#wx{Ii}xxL_csiW*LBn0-A$>zp4WOmkf6=ilrjlW?-l9+bCd7|i?*b5NTmy<<6NZ0T8$SN@mwdC2f z6jqK=N@bS@!=YSj%>h0}*4p+%0HbTIrE$w7UMT6+AZZ&DAo*qZAAi(OtNbIfl#Dw^ zJWeiCp~zi#&t6x}m9)O^eR4HiLV3QA<<0>HZ8%$^lrSE94Wb}=+MM^!b>n#5&-JQR zkr-CEu9C;_F*7DqDisulV6Pmg$nFL0TPn%~*m^-`Z3^BgU(sNpnx%nW(!eV9A&FvI zHL3X3lw8Wji^6=8KbQDE-e%b?svdFxxdh$8r97@7$ojeI`6AL8Ob6QC$qh&>ZWVDX1F{0vJnT%%mE;GveKWR%C} zM&4d;Jf3s9|HAA)yVUPo`Aq;0do#)uHSXi5*QF*)x@MVVHr+cN)uMZ__F|&Ta#p8d z53TOKtce!PJUuie8UWol-S(`c2nH?UGqJP{!4RR4u$LCfn)q-hj0^f=h(VYy)T6eN zhRO!ja-aDBTcgeyP(8Ua4IdiOog^*CQa?R(cP#9AgL9`j>EX-6Yf1lzX(!~``M1XC zNmM<4<6d~wWZ$Xrk0K}UteTrq@LBBk#MsjkK;pbuVhe)NI7(7Pf(l?lxC7=1Z7Pzl zMbS;nV4NI5_N{1$P)&XC)huOGQ+h^zpYUZfb*1n6>!`#*b3y52LGmi+<4sY5jyD#- zw&$d}DTguLAfnRtjrM*JfqtHqUu6rQoU=g%1E9xk%;)TDm^2<8n-0IhTsQFaek)MY}>PK4;nBk=ow4pF>vzkO& gna%Org-kgQCf=+BeMi^RbuY;YE~rTjend;_cdi8t>i_@% literal 0 HcmV?d00001 diff --git a/Tests/Manual/CPPopover/index-debug.html b/Tests/Manual/CPPopover/index-debug.html new file mode 100644 index 000000000..631a52ca1 --- /dev/null +++ b/Tests/Manual/CPPopover/index-debug.html @@ -0,0 +1,103 @@ + + + + + + + + + + + + + + + CPPopover Test + + + + + + + + + + + + + + +
+
+ + + +
+
+ + + diff --git a/Tests/Manual/CPPopover/index.html b/Tests/Manual/CPPopover/index.html new file mode 100644 index 000000000..20629ad2d --- /dev/null +++ b/Tests/Manual/CPPopover/index.html @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + + CPPopover Test + + + + + + + + + + + + +
+
+ + + +
+
+ + + + diff --git a/Tests/Manual/CPPopover/main.j b/Tests/Manual/CPPopover/main.j new file mode 100644 index 000000000..ff9216569 --- /dev/null +++ b/Tests/Manual/CPPopover/main.j @@ -0,0 +1,18 @@ +/* + * AppController.j + * test + * + * Created by You on July 6, 2011. + * Copyright 2011, Your Company All rights reserved. + */ + +@import +@import + +@import "AppController.j" + + +function main(args, namedArgs) +{ + CPApplicationMain(args, namedArgs); +} From 0246d501e298f05f32374647c89bed9a6e4cb4e7 Mon Sep 17 00:00:00 2001 From: Antoine Mercadal Date: Fri, 8 Jul 2011 17:41:23 +0200 Subject: [PATCH 02/11] full support for CPPopover and nib2cib + enchanced delegates --- AppKit/CPPopover.j | 77 +++++++++++++++++++++++-- AppKit/CPWindow/_CPAttachedWindowView.j | 11 ++-- Tools/nib2cib/NSAppKit.j | 1 + Tools/nib2cib/NSPopover.j | 58 +++++++++++++++++++ 4 files changed, 135 insertions(+), 12 deletions(-) create mode 100644 Tools/nib2cib/NSPopover.j diff --git a/AppKit/CPPopover.j b/AppKit/CPPopover.j index ad3a83205..a99a16543 100644 --- a/AppKit/CPPopover.j +++ b/AppKit/CPPopover.j @@ -36,6 +36,13 @@ CPPopoverBehaviorTransient = 1; CPPopoverBehaviorSemitransient = 2; +var CPPopoverDelegate_popover_willShow_ = 1 << 0, + CPPopoverDelegate_popover_didShow_ = 1 << 1, + CPPopoverDelegate_popover_shouldClose_ = 1 << 2, + CPPopoverDelegate_popover_willClose_ = 1 << 3, + CPPopoverDelegate_popover_didClose_ = 1 << 4; + + /*! @ingroup appkit @class CPPopover @@ -52,7 +59,7 @@ CPPopoverBehaviorSemitransient = 2; @implementation CPPopover : CPResponder { @outlet CPViewController _contentViewController @accessors(property=contentViewController); - @outlet id _delegate @accessors(property=delegate); + @outlet id _delegate @accessors(getter=delegate); BOOL _animates @accessors(property=animates); BOOL _shown @accessors(getter=shown); @@ -61,6 +68,7 @@ CPPopoverBehaviorSemitransient = 2; BOOL _needsCompute; _CPAttachedWindow _attachedWindow; + int _implementedDelegateMethods; } @@ -163,6 +171,30 @@ CPPopoverBehaviorSemitransient = 2; } +- (void)setDelegate:(id)aDelegate +{ + if (_delegate === aDelegate) + return; + + _delegate = aDelegate; + _implementedDelegateMethods = 0; + + if ([_delegate respondsToSelector:@selector(popoverWillShow:)]) + _implementedDelegateMethods |= CPPopoverDelegate_popover_willShow_; + + if ([_delegate respondsToSelector:@selector(popoverDidShow:)]) + _implementedDelegateMethods |= CPPopoverDelegate_popover_didShow_; + + if ([_delegate respondsToSelector:@selector(popoverShouldClose:)]) + _implementedDelegateMethods |= CPPopoverDelegate_popover_shouldClose_; + + if ([_delegate respondsToSelector:@selector(popoverWillClose:)]) + _implementedDelegateMethods |= CPPopoverDelegate_popover_willClose_; + + if ([_delegate respondsToSelector:@selector(popoverDidClose:)]) + _implementedDelegateMethods |= CPPopoverDelegate_popover_didClose_; +} + #pragma mark - #pragma mark Positionning @@ -175,7 +207,7 @@ CPPopoverBehaviorSemitransient = 2; */ - (void)showRelativeToRect:(CPRect)positioningRect ofView:(CPView)positioningView preferredEdge:(CPRectEdge)preferredEdge { - if (_delegate && [_delegate respondsToSelector:@selector(popoverWillShow:)]) + if (_implementedDelegateMethods & CPPopoverDelegate_popover_willShow_) [_delegate popoverWillShow:self]; if (!_contentViewController) @@ -200,7 +232,7 @@ CPPopoverBehaviorSemitransient = 2; else [CPException raise:CPInvalidArgumentException reason:@"you must set positioningRect or positioningRect"]; - if (_delegate && [_delegate respondsToSelector:@selector(popoverDidShow:)]) + if (_implementedDelegateMethods & CPPopoverDelegate_popover_didShow_) [_delegate popoverDidShow:self]; } @@ -209,16 +241,16 @@ CPPopoverBehaviorSemitransient = 2; */ - (void)close { - if (_delegate && [_delegate respondsToSelector:@selector(popoverShouldClose:)]); + if (_implementedDelegateMethods & CPPopoverDelegate_popover_shouldClose_) if (![_delegate popoverShouldClose:self]) return; - if (_delegate && [_delegate respondsToSelector:@selector(popoverWillClose:)]) + if (_implementedDelegateMethods & CPPopoverDelegate_popover_willClose_) [_delegate popoverWillClose:self]; [_attachedWindow close]; - if (_delegate && [_delegate respondsToSelector:@selector(popoverDidClose:)]) + if (_implementedDelegateMethods & CPPopoverDelegate_popover_didClose_) [_delegate popoverDidClose:self]; } @@ -236,4 +268,37 @@ CPPopoverBehaviorSemitransient = 2; [self close]; } +@end + + +@implementation CPPopover (CPCoding) + +- (id)initWithCoder:(CPCoder)aCoder +{ + self = [super initWithCoder:aCoder]; + + if (self) + { + _needsCompute = [aCoder decodeIntForKey:@"_needsCompute"]; + _appearance = [aCoder decodeIntForKey:@"_appearance"]; + _animates = [aCoder decodeBoolForKey:@"_animates"]; + _contentViewController = [aCoder decodeObjectForKey:@"_contentViewController"]; + [self setDelegate:[aCoder decodeObjectForKey:@"_delegate"]]; + [self setBehaviour:[aCoder decodeIntForKey:@"_behavior"]]; + } + return self; +} + +- (void)encodeWithCoder:(CPCoder)aCoder +{ + [super encodeWithCoder:aCoder]; + + [aCoder encodeInt:_behavior forKey:@"_behavior"]; + [aCoder encodeInt:_appearance forKey:@"_appearance"]; + [aCoder encodeBool:_needsCompute forKey:@"_needsCompute"]; + [aCoder encodeObject:_contentViewController forKey:@"_contentViewController"]; + [aCoder encodeObject:_delegate forKey:@"_delegate"]; + [aCoder encodeObject:_animates forKey:@"_animates"]; +} + @end \ No newline at end of file diff --git a/AppKit/CPWindow/_CPAttachedWindowView.j b/AppKit/CPWindow/_CPAttachedWindowView.j index dd7b6e925..23bc8b247 100644 --- a/AppKit/CPWindow/_CPAttachedWindowView.j +++ b/AppKit/CPWindow/_CPAttachedWindowView.j @@ -133,12 +133,12 @@ if (_appearance == CPPopoverAppearanceMinimal) { - _strokeColor = [CPColor colorWithCalibratedRed:(172.0 / 255) green:(172.0 / 255) blue:(172.0 / 255) alpha:1.0]; + _strokeColor = [CPColor colorWithCalibratedRed:(187.0 / 255) green:(187.0 / 255) blue:(187.0 / 255) alpha:0.7]; _backgroundColor = [CPColor colorWithCalibratedRed:(241.0 / 255) green:(241.0 / 255) blue:(241.0 / 255) alpha:0.93]; } else { - _strokeColor = [CPColor colorWithCalibratedRed:(172.0 / 255) green:(172.0 / 255) blue:(172.0 / 255) alpha:1.0]; + _strokeColor = [CPColor colorWithCalibratedRed:(187.0 / 255) green:(187.0 / 255) blue:(187.0 / 255) alpha:0.7]; _backgroundColor = [CPColor colorWithCalibratedRed:(50.0 / 255) green:(50.0 / 255) blue:(50.0 / 255) alpha:0.93]; } @@ -161,17 +161,16 @@ if (_useGlowingEffect) { var shadowColor = [[CPColor blackColor] colorWithAlphaComponent:.2], - shadowSize = CGSizeMake(0, 0), + shadowSize = CGSizeMake(0, 7), shadowBlur = 15; //compensate for the shadow blur aRect.origin.x += shadowBlur; - aRect.origin.y += shadowBlur; + aRect.origin.y += shadowBlur + shadowSize.height / 2; aRect.size.width -= shadowBlur * 2; - aRect.size.height -= shadowBlur * 2; + aRect.size.height -= shadowBlur * 2 + shadowSize.height; //set the shadow - CGContextSetShadow(context, CGSizeMake(0,0), 20); CGContextSetShadowWithColor(context, shadowSize, shadowBlur, shadowColor); } diff --git a/Tools/nib2cib/NSAppKit.j b/Tools/nib2cib/NSAppKit.j index 7d265da88..db5f995be 100644 --- a/Tools/nib2cib/NSAppKit.j +++ b/Tools/nib2cib/NSAppKit.j @@ -79,6 +79,7 @@ @import "NSWindowTemplate.j" @import "WebView.j" @import "NSSortDescriptor.j" +@import "NSPopover.j" function CP_NSMapClassName(aClassName) diff --git a/Tools/nib2cib/NSPopover.j b/Tools/nib2cib/NSPopover.j new file mode 100644 index 000000000..3a965da27 --- /dev/null +++ b/Tools/nib2cib/NSPopover.j @@ -0,0 +1,58 @@ +/* + * NSPopover.j + * nib2cib + * + * Created by Francisco Tolmasky. + * Copyright 2008, 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 + + +@implementation CPPopover (NSCoding) + +- (id)NS_initWithCoder:(CPCoder)aCoder +{ + self = [super NS_initWithCoder:aCoder]; + + if (self) + { + _needsCompute = YES; + _shown = NO; + _behavior = [aCoder decodeIntForKey:@"NSBehavior"]; + _appearance = [aCoder decodeIntForKey:@"NSAppearance"]; + _animates = [aCoder decodeBoolForKey:@"NSAnimates"]; + } + + return self; +} + +@end + +@implementation NSPopover : CPPopover + +- (id)initWithCoder:(CPCoder)aCoder +{ + return [self NS_initWithCoder:aCoder]; +} + +- (Class)classForKeyedArchiver +{ + return [CPPopover class]; +} + +@end From 3d62791477cd2dc8ec5698ce847f7898f9a5bafe Mon Sep 17 00:00:00 2001 From: Antoine Mercadal Date: Sat, 9 Jul 2011 11:50:52 +0200 Subject: [PATCH 03/11] fix animation lack in FF prevents Popover to close --- AppKit/_CPAttachedWindow.j | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AppKit/_CPAttachedWindow.j b/AppKit/_CPAttachedWindow.j index 28f94ddf3..70126b632 100644 --- a/AppKit/_CPAttachedWindow.j +++ b/AppKit/_CPAttachedWindow.j @@ -478,7 +478,7 @@ CPPopoverAppearanceHUD = 1; */ - (void)close { - if (_animates) + if (_animates && typeof(_DOMElement.style.WebkitTransform) != "undefined") { _DOMElement.style.opacity = 0; var transitionEndFunction = function(){ From 916b9a399b3d1bf5a66acb54d8c2d96250dec533 Mon Sep 17 00:00:00 2001 From: Antoine Mercadal Date: Sat, 9 Jul 2011 15:29:13 +0200 Subject: [PATCH 04/11] make color matching Aristo 2.0 ones --- AppKit/CPWindow/_CPAttachedWindowView.j | 72 ++++++++++--------------- 1 file changed, 28 insertions(+), 44 deletions(-) diff --git a/AppKit/CPWindow/_CPAttachedWindowView.j b/AppKit/CPWindow/_CPAttachedWindowView.j index 23bc8b247..7ab660aab 100644 --- a/AppKit/CPWindow/_CPAttachedWindowView.j +++ b/AppKit/CPWindow/_CPAttachedWindowView.j @@ -36,13 +36,6 @@ int _appearance @accessors(property=appearance); unsigned _preferredEdge @accessors(property=preferredEdge); - BOOL _useGlowingEffect; - CPColor _backgroundColor; - CPColor _strokeColor; - CPImage _cursorBackgroundBottom; - CPImage _cursorBackgroundLeft; - CPImage _cursorBackgroundRight; - CPImage _cursorBackgroundTop; CPSize _cursorSize; } @@ -131,50 +124,42 @@ { [super drawRect:aRect]; - if (_appearance == CPPopoverAppearanceMinimal) - { - _strokeColor = [CPColor colorWithCalibratedRed:(187.0 / 255) green:(187.0 / 255) blue:(187.0 / 255) alpha:0.7]; - _backgroundColor = [CPColor colorWithCalibratedRed:(241.0 / 255) green:(241.0 / 255) blue:(241.0 / 255) alpha:0.93]; - } - else - { - _strokeColor = [CPColor colorWithCalibratedRed:(187.0 / 255) green:(187.0 / 255) blue:(187.0 / 255) alpha:0.7]; - _backgroundColor = [CPColor colorWithCalibratedRed:(50.0 / 255) green:(50.0 / 255) blue:(50.0 / 255) alpha:0.93]; - } - - var context = [[CPGraphicsContext currentContext] graphicsPort], radius = 5, arrowWidth = _cursorSize.width, arrowHeight = _cursorSize.height, - strokeWidth = 2; + strokeWidth = 1, + strokeColor, + shadowColor = [[CPColor blackColor] colorWithAlphaComponent:.2], + shadowSize = CGSizeMake(0, 7), + shadowBlur = 15, + gradient; - CGContextSetStrokeColor(context, _strokeColor); - CGContextSetLineWidth(context, strokeWidth); - CGContextBeginPath(context); - - aRect.origin.x += strokeWidth; - aRect.origin.y += strokeWidth; - aRect.size.width -= strokeWidth * 2; - aRect.size.height -= strokeWidth * 2; - - if (_useGlowingEffect) + if (_appearance == CPPopoverAppearanceMinimal) { - var shadowColor = [[CPColor blackColor] colorWithAlphaComponent:.2], - shadowSize = CGSizeMake(0, 7), - shadowBlur = 15; - - //compensate for the shadow blur - aRect.origin.x += shadowBlur; - aRect.origin.y += shadowBlur + shadowSize.height / 2; - aRect.size.width -= shadowBlur * 2; - aRect.size.height -= shadowBlur * 2 + shadowSize.height; - - //set the shadow - CGContextSetShadowWithColor(context, shadowSize, shadowBlur, shadowColor); + gradient = CGGradientCreateWithColorComponents(CGColorSpaceCreateDeviceRGB(), [(254.0 / 255), (254.0 / 255), (254.0 / 255), 0.93, + (231.0 / 255), (231.0 / 255), (231.0 / 255), 0.93], [0,1], 2); + strokeColor = [CPColor colorWithHexString:@"B8B8B8"]; + } + else + { + gradient = CGGradientCreateWithColorComponents(CGColorSpaceCreateDeviceRGB(), [(38.0 / 255), (38.0 / 255), (38.0 / 255), 0.93, + (18.0 / 255), (18.0 / 255), (18.0 / 255), 0.93], [0,1], 2); + strokeColor = [CPColor colorWithHexString:@"222222"]; } - CGContextSetFillColor(context, _backgroundColor); + // fix rect to take care of stroke and shadow + aRect.origin.x += strokeWidth + shadowBlur; + aRect.origin.y += strokeWidth + (shadowBlur + shadowSize.height / 2); + aRect.size.width -= (strokeWidth * 2) + (shadowBlur * 2); + aRect.size.height -= (strokeWidth * 2) + (shadowBlur * 2 + shadowSize.height); + + CGContextSetStrokeColor(context, strokeColor); + CGContextSetLineWidth(context, strokeWidth); + CGContextBeginPath(context); + CGContextSetShadowWithColor(context, shadowSize, shadowBlur, shadowColor); + CGContextDrawLinearGradient(context, gradient, CGPointMake(CPRectGetMidX(aRect), 0.0), CGPointMake(CPRectGetMidX(aRect), aRect.size.height), 0); + var xMin = _CGRectGetMinX(aRect), xMax = _CGRectGetMaxX(aRect), @@ -234,7 +219,6 @@ // nw CGContextAddLineToPoint(context, xMin, yMin + radius); CGContextAddCurveToPoint(context, xMin, yMin + radius, xMin, yMin, xMin + radius, yMin); - break; case CPMaxYEdge: From 28733ec592537dc16e163d726f039acc7ccf11ea Mon Sep 17 00:00:00 2001 From: Antoine Mercadal Date: Sat, 9 Jul 2011 15:29:37 +0200 Subject: [PATCH 05/11] label are white in CPPopoverTest if popover is HUD --- Tests/Manual/CPPopover/AppController.j | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/Tests/Manual/CPPopover/AppController.j b/Tests/Manual/CPPopover/AppController.j index 3891d89f2..722aa6489 100644 --- a/Tests/Manual/CPPopover/AppController.j +++ b/Tests/Manual/CPPopover/AppController.j @@ -23,7 +23,7 @@ var theWindow = [[CPWindow alloc] initWithContentRect:CGRectMakeZero() styleMask:CPBorderlessBridgeWindowMask], contentView = [theWindow contentView]; - button = [CPButton buttonWithTitle:@"click"]; + var button = [CPButton buttonWithTitle:@"click"]; [button setTarget:self]; [button setAction:@selector(open:)]; [button setFrameOrigin:CPPointMake(10, 60)]; @@ -35,7 +35,6 @@ [button setFrameOrigin:CPPointMake( [contentView frameSize].width - 50, 60)]; [contentView addSubview:button]; - button = [CPButton buttonWithTitle:@"click"]; [button setTarget:self]; [button setAction:@selector(open:)]; @@ -55,6 +54,7 @@ [button setCenter:[contentView center]]; [contentView addSubview:button]; + buttonGravity = [[CPPopUpButton alloc] initWithFrame:CPRectMake(10, 10, 130, 24)]; [buttonGravity addItemWithTitle:"Automatic"]; [buttonGravity addItemWithTitle:"Bottom"]; @@ -78,7 +78,6 @@ [buttonBehaviour addItemWithTitle:"Not managed"]; [contentView addSubview:buttonBehaviour]; - [theWindow orderFront:self]; } @@ -122,9 +121,9 @@ [label setFont:[CPFont boldSystemFontOfSize:30.0]]; [label setFrameOrigin:CPPointMake(0, 70)]; - [label setValue:[CPColor colorWithHexString:@"fff"] forThemeAttribute:@"text-shadow-color"]; + [label setValue:(a == CPPopoverAppearanceHUD) ? [CPColor colorWithHexString:@"333"] : [CPColor colorWithHexString:@"fff"] forThemeAttribute:@"text-shadow-color"]; [label setValue:CGSizeMake(0.0, 1.0) forThemeAttribute:@"text-shadow-offset"]; - [label setTextColor:[CPColor colorWithHexString:@"444"]]; + [label setTextColor:(a == CPPopoverAppearanceHUD) ? [CPColor whiteColor] : [CPColor colorWithHexString:@"444"]]; [label setFrameSize:CPSizeMake([view frame].size.width, 50)]; [label setAlignment:CPCenterTextAlignment]; [view addSubview:label]; From 156610ee078d537e76dd7e922522ae71fefc3d82 Mon Sep 17 00:00:00 2001 From: Brian Donovan Date: Fri, 5 Aug 2011 08:32:44 -0700 Subject: [PATCH 06/11] Allow CPAlert subclasses to use +alertWithMessageText:... et al. --- AppKit/CPAlert.j | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/AppKit/CPAlert.j b/AppKit/CPAlert.j index b9c2fc7df..5eb8a8245 100644 --- a/AppKit/CPAlert.j +++ b/AppKit/CPAlert.j @@ -117,7 +117,7 @@ CPCriticalAlertStyle = 2; */ + (CPAlert)alertWithMessageText:(CPString)aMessage defaultButton:(CPString)defaultButtonTitle alternateButton:(CPString)alternateButtonTitle otherButton:(CPString)otherButtonTitle informativeTextWithFormat:(CPString)informativeText { - var alert = [[CPAlert alloc] init]; + var alert = [[self alloc] init]; [alert setMessageText:aMessage]; [alert addButtonWithTitle:defaultButtonTitle]; @@ -142,7 +142,7 @@ CPCriticalAlertStyle = 2; */ + (CPAlert)alertWithError:(CPString)anErrorMessage { - var alert = [[CPAlert alloc] init]; + var alert = [[self alloc] init]; [alert setMessageText:anErrorMessage]; [alert setAlertStyle:CPCriticalAlertStyle]; From f8404c7a97114ab40d67fe30e525d4cd5aeb6bd2 Mon Sep 17 00:00:00 2001 From: Aparajita Fishman Date: Fri, 5 Aug 2011 14:36:24 -0700 Subject: [PATCH 07/11] Added some missing range checking. --- Foundation/CPArray/_CPJavaScriptArray.j | 27 ++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/Foundation/CPArray/_CPJavaScriptArray.j b/Foundation/CPArray/_CPJavaScriptArray.j index bc05d40e7..9913fd890 100644 --- a/Foundation/CPArray/_CPJavaScriptArray.j +++ b/Foundation/CPArray/_CPJavaScriptArray.j @@ -156,8 +156,7 @@ var concat = Array.prototype.concat, - (void)makeObjectsPerformSelector:(SEL)aSelector withObjects:(CPArray)objects { if (!aSelector) - [CPException raise:CPInvalidArgumentException - reason:"makeObjectsPerformSelector:withObjects: 'aSelector' can't be nil"]; + _CPRaiseInvalidArgumentException(self, _cmd, 'attempt to pass a nil selector'); var index = 0, count = self.length; @@ -198,7 +197,7 @@ var concat = Array.prototype.concat, - (CPArray)subarrayWithRange:(CPRange)aRange { if (aRange.location < 0 || CPMaxRange(aRange) > self.length) - [CPException raise:CPRangeException reason:"subarrayWithRange: aRange out of bounds"]; + [CPException raise:CPRangeException reason:_cmd + " aRange out of bounds"]; return slice.call(self, aRange.location, CPMaxRange(aRange)); } @@ -208,13 +207,19 @@ var concat = Array.prototype.concat, return join.call(self, aString); } -- (void)insertObject:(id)anObject atIndex:(CPUInteger)anIndex +- (void)insertObject:(id)anObject atIndex:(int)anIndex { + if (anIndex >= self.length || anIndex < 0) + _CPRaiseRangeException(self, _cmd, anIndex, self.length); + splice.call(self, anIndex, 0, anObject); } -- (void)removeObjectAtIndex:(CPUInteger)anIndex +- (void)removeObjectAtIndex:(int)anIndex { + if (anIndex >= self.length || anIndex < 0) + _CPRaiseRangeException(self, _cmd, anIndex, self.length); + splice.call(self, anIndex, 1); } @@ -235,16 +240,28 @@ var concat = Array.prototype.concat, - (void)removeObjectsInRange:(CPRange)aRange { + if (aRange.location < 0 || CPMaxRange(aRange) > self.length) + [CPException raise:CPRangeException reason:_cmd + " aRange out of bounds"]; + splice.call(self, aRange.location, aRange.length); } - (void)replaceObjectAtIndex:(int)anIndex withObject:(id)anObject { + if (anIndex >= self.length || anIndex < 0) + _CPRaiseRangeException(self, _cmd, anIndex, self.length); + self[anIndex] = anObject; } - (void)replaceObjectsInRange:(CPRange)aRange withObjectsFromArray:(CPArray)anArray range:(CPRange)otherRange { + if (aRange.location < 0 || CPMaxRange(aRange) > self.length) + [CPException raise:CPRangeException reason:_cmd + " aRange out of bounds"]; + + if (otherRange.location < 0 || CPMaxRange(otherRange) > anArray.length) + [CPException raise:CPRangeException reason:_cmd + " otherRange out of bounds"]; + if (otherRange && (otherRange.location !== 0 || otherRange.length !== [anArray count])) anArray = [anArray subarrayWithRange:otherRange]; From e32515e5cca55c6a7f6af1c69992911c22bc63d5 Mon Sep 17 00:00:00 2001 From: alexmipego Date: Sun, 7 Aug 2011 20:22:14 +0100 Subject: [PATCH 08/11] Make boxEnclosingView: copy the auto resizing settings of the view --- AppKit/CPBox.j | 1 + 1 file changed, 1 insertion(+) diff --git a/AppKit/CPBox.j b/AppKit/CPBox.j index 1e5de960f..90b4bb87d 100644 --- a/AppKit/CPBox.j +++ b/AppKit/CPBox.j @@ -61,6 +61,7 @@ CPGrooveBorder = 3; var box = [[self alloc] initWithFrame:CGRectMakeZero()], enclosingView = [aView superview]; + [box setAutoresizingMask:[aView autoresizingMask]]; [box setFrameFromContentFrame:[aView frame]]; [enclosingView replaceSubview:aView with:box]; From 8e203ec78ed897c13ff43531be421faadc989090 Mon Sep 17 00:00:00 2001 From: Aparajita Fishman Date: Mon, 8 Aug 2011 15:13:37 -0700 Subject: [PATCH 09/11] Fixed range checking for insertObject:atIndex: --- Foundation/CPArray/_CPJavaScriptArray.j | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Foundation/CPArray/_CPJavaScriptArray.j b/Foundation/CPArray/_CPJavaScriptArray.j index 9913fd890..7a84057bb 100644 --- a/Foundation/CPArray/_CPJavaScriptArray.j +++ b/Foundation/CPArray/_CPJavaScriptArray.j @@ -209,7 +209,7 @@ var concat = Array.prototype.concat, - (void)insertObject:(id)anObject atIndex:(int)anIndex { - if (anIndex >= self.length || anIndex < 0) + if (anIndex > self.length || anIndex < 0) _CPRaiseRangeException(self, _cmd, anIndex, self.length); splice.call(self, anIndex, 0, anObject); From c45039fd70ea914470cf00dd2fca95df8f2039d2 Mon Sep 17 00:00:00 2001 From: Jonathan R H Evans Date: Tue, 9 Aug 2011 10:15:23 +0100 Subject: [PATCH 10/11] Added zsh to the list of shell configs that xcodecapp-cocoa checks. --- Tools/xcodecapp-cocoa/AppController.m | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Tools/xcodecapp-cocoa/AppController.m b/Tools/xcodecapp-cocoa/AppController.m index 27882e343..61b67baaf 100644 --- a/Tools/xcodecapp-cocoa/AppController.m +++ b/Tools/xcodecapp-cocoa/AppController.m @@ -62,13 +62,15 @@ void fsevents_callback(ConstFSEventStreamRef streamRef, void *userData, size_t n _profilePath = [@"source ~/.profile" stringByExpandingTildeInPath]; else if([fm fileExistsAtPath:[@"~/.bashrc" stringByExpandingTildeInPath]]) _profilePath = [@"source ~/.bashrc" stringByExpandingTildeInPath]; + else if([fm fileExistsAtPath:[@"~/.zshrc" stringByExpandingTildeInPath]]) + _profilePath = [@"source ~/.zshrc" stringByExpandingTildeInPath]; else { NSAlert *alert = [NSAlert alertWithMessageText:@"Cannot find any valid profile file" defaultButton:@"Ok" alternateButton:nil otherButton:nil - informativeTextWithFormat:@"We have checked for ~/.bash_profile, ~/.profile and ~/.bashrc without luck.\n\nYou need to have on of this file to tell XCodeCapp-cocoa where is located nib2cib. Now we gonna try to without sourcing one this file and it may fail.\n\nIf you notice any error or weird behaviour, please look at Mac OS' Console.app for log message and open a ticket."]; + informativeTextWithFormat:@"We have checked for ~/.bash_profile, ~/.profile, ~/.bashrc and ~/.zshrc without luck.\n\nYou need to have on of this file to tell XCodeCapp-cocoa where is located nib2cib. Now we gonna try to without sourcing one this file and it may fail.\n\nIf you notice any error or weird behaviour, please look at Mac OS' Console.app for log message and open a ticket."]; [alert runModal]; _profilePath = @""; } From 9f06ed725e3113b60186fdc9f56be66debf3971b Mon Sep 17 00:00:00 2001 From: cacaodev Date: Thu, 11 Aug 2011 21:06:19 +0200 Subject: [PATCH 11/11] CPTableView: -drawBackroundInClipRect: Fixes a bug where we were drawing rows from 0 instead of the first exposed when usesAlternatingBackgroundColors:YES --- AppKit/CPTableView.j | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/AppKit/CPTableView.j b/AppKit/CPTableView.j index 2853079a4..5b20634ce 100644 --- a/AppKit/CPTableView.j +++ b/AppKit/CPTableView.j @@ -3544,16 +3544,16 @@ Your delegate can implement this method to avoid subclassing the tableview to ad } var exposedRows = [self _unboundedRowsInRect:aRect], + firstRow = FLOOR(exposedRows.location / colorCount) * colorCount, lastRow = CPMaxRange(exposedRows), colorIndex = 0, - groupRowRects = [], - row = exposedRows.location; + groupRowRects = []; //loop through each color so we only draw once for each color while (colorIndex < colorCount) { CGContextBeginPath(context); - for (var row = colorIndex; row <= lastRow; row += colorCount) + for (var row = firstRow + colorIndex; row <= lastRow; row += colorCount) { // if it's not a group row draw it otherwise we draw it later if (![_groupRows containsIndex:row])