mirror of
https://github.com/cappuccino/cappuccino.git
synced 2026-10-19 23:05:01 +00:00
Merge remote-tracking branch 'cappuccino/master' into CPAnimationContext
This commit is contained in:
commit
6fd2d12d3f
508 files changed
+24974
-15395
No files matched your search
+5
-1
@@ -20,6 +20,7 @@
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
@import "_CPObject+Theme.j"
|
||||
@import "_CPToolTip.j"
|
||||
@import "CALayer.j"
|
||||
@import "CGGeometry.j"
|
||||
@@ -27,6 +28,7 @@
|
||||
@import "CPAlert.j"
|
||||
@import "CPAnimation.j"
|
||||
@import "CPAnimationContext.j"
|
||||
@import "CPAppearance.j"
|
||||
@import "CPApplication.j"
|
||||
@import "CPArrayController.j"
|
||||
@import "CPBezierPath.j"
|
||||
@@ -102,13 +104,15 @@
|
||||
@import "CPTokenField.j"
|
||||
@import "CPToolbar.j"
|
||||
@import "CPToolbarItem.j"
|
||||
@import "CPTrackingArea.j"
|
||||
@import "CPTreeNode.j"
|
||||
@import "CPUserDefaultsController.j"
|
||||
@import "CPView.j"
|
||||
@import "CPViewAnimator.j"
|
||||
@import "CPViewAnimation.j"
|
||||
@import "CPViewController.j"
|
||||
@import "CPVisualEffectView.j"
|
||||
@import "CPWebView.j"
|
||||
@import "CPWindow.j"
|
||||
@import "CPWindowController.j"
|
||||
@import "CPWorkspace.j"
|
||||
@import "CPWorkspace.j"
|
||||
+2
-2
@@ -821,12 +821,12 @@ var bottomHeight = 71;
|
||||
else if (_modalDelegate)
|
||||
{
|
||||
if (_didEndSelector)
|
||||
objj_msgSend(_modalDelegate, _didEndSelector, self, returnCode, contextInfo);
|
||||
_modalDelegate.isa.objj_msgSend3(_modalDelegate, _didEndSelector, self, returnCode, contextInfo);
|
||||
}
|
||||
else if (_delegate)
|
||||
{
|
||||
if (_didEndSelector)
|
||||
objj_msgSend(_delegate, _didEndSelector, self, returnCode);
|
||||
_delegate.isa.objj_msgSend2(_delegate, _didEndSelector, self, returnCode);
|
||||
else
|
||||
[self _sendDelegateAlertDidEndReturnCode:returnCode];
|
||||
}
|
||||
|
||||
@@ -0,0 +1,162 @@
|
||||
/*
|
||||
* CPAppearance.j
|
||||
* AppKit
|
||||
*
|
||||
* Created by Antoine Mercadal.
|
||||
* Copyright 2015, Cappuccino Project.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
@import <Foundation/Foundation.j>
|
||||
@import "CPTheme.j"
|
||||
|
||||
CPAppearanceNameAqua = @"CPAppearanceNameAqua";
|
||||
CPAppearanceNameLightContent = @"CPAppearanceNameLightContent";
|
||||
CPAppearanceNameVibrantDark = @"CPAppearanceNameVibrantDark";
|
||||
CPAppearanceNameVibrantLight = @"CPAppearanceNameVibrantLight";
|
||||
|
||||
var _CPAppearanceCurrent = nil,
|
||||
_CPAppearancesRegistry = @{};
|
||||
|
||||
|
||||
@protocol CPAppearanceCustomization <CPObject>
|
||||
|
||||
@required
|
||||
- (CPAppearance)appearance;
|
||||
- (void)setAppearance:(CPAppearance)appearance;
|
||||
- (CPAppearance)effectiveAppearance;
|
||||
- (void)setEffectiveAppearance:(CPAppearance)appearance;
|
||||
|
||||
@end
|
||||
|
||||
CPThemeStateAppearanceAqua = CPThemeState("appearance-aqua");
|
||||
CPThemeStateAppearanceLightContent = CPThemeState("appearance-light-content");
|
||||
CPThemeStateAppearanceVibrantLight = CPThemeState("appearance-vibrant-light");
|
||||
CPThemeStateAppearanceVibrantDark = CPThemeState("appearance-vibrant-dark");
|
||||
|
||||
|
||||
/*!
|
||||
@ingroup appkit
|
||||
|
||||
A CPAppareance represents the appearance of an to a subset of UI elements.
|
||||
This is a very lightweight implementation of the NSAppearance system, but
|
||||
We are using it for compliance, and especially for the CPVisualEffectView
|
||||
*/
|
||||
@implementation CPAppearance : CPObject
|
||||
{
|
||||
BOOL _allowsVibrancy @accessors(property=allowsVibrancy);
|
||||
|
||||
CPString _name;
|
||||
}
|
||||
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Class Methods
|
||||
|
||||
/*! Returns the current default CPAppearance
|
||||
*/
|
||||
+ (CPAppearance)currentAppearance
|
||||
{
|
||||
if (!_CPAppearanceCurrent)
|
||||
_CPAppearanceCurrent = [CPAppearance appearanceNamed:CPAppearanceNameAqua];
|
||||
|
||||
return _CPAppearanceCurrent;
|
||||
}
|
||||
|
||||
/*! Sets the current default CPAppearance
|
||||
@param appearance the new current appearance
|
||||
*/
|
||||
+ (void)setCurrentAppearance:(CPAppearance)anAppearance
|
||||
{
|
||||
_CPAppearanceCurrent = anAppearance;
|
||||
}
|
||||
|
||||
/*! Returns the CPAppearance object with the given name
|
||||
@param name the name of the appearance
|
||||
*/
|
||||
+ (CPAppearance)appearanceNamed:(CPString)aName
|
||||
{
|
||||
if (![_CPAppearancesRegistry containsKey:aName])
|
||||
{
|
||||
[_CPAppearancesRegistry setObject:[[CPAppearance alloc] initWithAppearanceNamed:aName bundle:nil]
|
||||
forKey:aName];
|
||||
}
|
||||
|
||||
return [_CPAppearancesRegistry objectForKey:aName];
|
||||
}
|
||||
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Initialization
|
||||
|
||||
/*! Creates a CPAppearance object initialized to the specified appearance file in the specified bundle
|
||||
This method does actually nothing special. It just creates a default appearance object
|
||||
*/
|
||||
- (id)initWithAppearanceNamed:(CPString)aName bundle:(CPBundle)bundle
|
||||
{
|
||||
if (self = [super init])
|
||||
{
|
||||
_name = aName;
|
||||
_allowsVibrancy = YES;
|
||||
|
||||
if ([_CPAppearancesRegistry containsKey:aName])
|
||||
[CPException raise:CPInternalInconsistencyException reason:"Appearance with name '" + aName + "' is already declared."];
|
||||
|
||||
[_CPAppearancesRegistry setObject:self forKey:aName];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Implementation
|
||||
|
||||
- (BOOL)isEqual:(id)anObject
|
||||
{
|
||||
if (![anObject isKindOfClass:CPAppearance])
|
||||
return NO;
|
||||
|
||||
return self._name == anObject._name;
|
||||
}
|
||||
|
||||
- (CPString)description
|
||||
{
|
||||
return @"<CPAppearance @" + [self UID] + @" name: " + _name + ">";
|
||||
}
|
||||
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark CPCoding
|
||||
|
||||
- (id)initWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
if (self = [super init])
|
||||
{
|
||||
_name = [aCoder decodeObjectForKey:@"_name"];
|
||||
_allowsVibrancy = [aCoder decodeBoolForKey:@"_allowsVibrancy"];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)encodeWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
[aCoder encodeObject:_name forKey:@"_name"];
|
||||
[aCoder encodeBool:_allowsVibrancy forKey:@"_allowsVibrancy"];
|
||||
}
|
||||
|
||||
@end
|
||||
+34
-8
@@ -47,6 +47,8 @@ var CPMainCibFile = @"CPMainCibFile",
|
||||
@protocol CPApplicationDelegate <CPObject>
|
||||
|
||||
@optional
|
||||
- (CPApplicationTerminateReply)applicationShouldTerminate:(CPApplication)sender;
|
||||
- (CPString)applicationShouldTerminateMessage:(CPApplication)sender;
|
||||
- (void)applicationDidBecomeActive:(CPNotification)aNotification;
|
||||
- (void)applicationDidChangeScreenParameters:(CPNotification)aNotification;
|
||||
- (void)applicationDidFinishLaunching:(CPNotification)aNotification;
|
||||
@@ -58,6 +60,9 @@ var CPMainCibFile = @"CPMainCibFile",
|
||||
|
||||
@end
|
||||
|
||||
var CPApplicationDelegate_applicationShouldTerminate_ = 1 << 0,
|
||||
CPApplicationDelegate_applicationShouldTerminateMessage_ = 1 << 1;
|
||||
|
||||
/*!
|
||||
@ingroup appkit
|
||||
@class CPApplication
|
||||
@@ -103,6 +108,8 @@ var CPMainCibFile = @"CPMainCibFile",
|
||||
|
||||
//
|
||||
id <CPApplicationDelegate> _delegate;
|
||||
CPInteger _implementedDelegateMethods;
|
||||
|
||||
BOOL _finishedLaunching;
|
||||
BOOL _isActive;
|
||||
|
||||
@@ -165,6 +172,8 @@ var CPMainCibFile = @"CPMainCibFile",
|
||||
if (_delegate == aDelegate)
|
||||
return;
|
||||
|
||||
_implementedDelegateMethods = 0;
|
||||
|
||||
var defaultCenter = [CPNotificationCenter defaultCenter],
|
||||
delegateNotifications =
|
||||
[
|
||||
@@ -205,6 +214,12 @@ var CPMainCibFile = @"CPMainCibFile",
|
||||
if ([_delegate respondsToSelector:selector])
|
||||
[defaultCenter addObserver:_delegate selector:selector name:notificationName object:self];
|
||||
}
|
||||
|
||||
if ([_delegate respondsToSelector:@selector(applicationShouldTerminate:)])
|
||||
_implementedDelegateMethods |= CPApplicationDelegate_applicationShouldTerminate_
|
||||
|
||||
if ([_delegate respondsToSelector:@selector(applicationShouldTerminateMessage:)])
|
||||
_implementedDelegateMethods |= CPApplicationDelegate_applicationShouldTerminateMessage_
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -426,12 +441,7 @@ var CPMainCibFile = @"CPMainCibFile",
|
||||
{
|
||||
// callback method for terminate:
|
||||
if (didCloseAll)
|
||||
{
|
||||
if ([_delegate respondsToSelector:@selector(applicationShouldTerminate:)])
|
||||
[self replyToApplicationShouldTerminate:[_delegate applicationShouldTerminate:self]];
|
||||
else
|
||||
[self replyToApplicationShouldTerminate:YES];
|
||||
}
|
||||
[self replyToApplicationShouldTerminate:[self _sendDelegateApplicationShouldTerminate]];
|
||||
}
|
||||
|
||||
- (void)replyToApplicationShouldTerminate:(BOOL)terminate
|
||||
@@ -974,7 +984,7 @@ var CPMainCibFile = @"CPMainCibFile",
|
||||
*/
|
||||
- (void)setTarget:(id)aTarget selector:(SEL)aSelector forNextEventMatchingMask:(unsigned int)aMask untilDate:(CPDate)anExpiration inMode:(CPString)aMode dequeue:(BOOL)shouldDequeue
|
||||
{
|
||||
_eventListeners.splice(_eventListenerInsertionIndex++, 0, _CPEventListenerMake(aMask, function (anEvent) { objj_msgSend(aTarget, aSelector, anEvent); }, shouldDequeue));
|
||||
_eventListeners.splice(_eventListenerInsertionIndex++, 0, _CPEventListenerMake(aMask, function (anEvent) { if (aTarget != null) aTarget.isa.objj_msgSend1(aTarget, aSelector, anEvent); }, shouldDequeue));
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -1210,6 +1220,22 @@ var CPMainCibFile = @"CPMainCibFile",
|
||||
userInfo:nil];
|
||||
}
|
||||
|
||||
- (BOOL)_sendDelegateApplicationShouldTerminate
|
||||
{
|
||||
if (!(_implementedDelegateMethods & CPApplicationDelegate_applicationShouldTerminate_))
|
||||
return YES;
|
||||
|
||||
return [_delegate applicationShouldTerminate:self];
|
||||
}
|
||||
|
||||
- (CPString)_sendDelegateApplicationShouldTerminateMessage
|
||||
{
|
||||
if (!(_implementedDelegateMethods & CPApplicationDelegate_applicationShouldTerminateMessage_))
|
||||
return @"You have attempted to leave this page. Are you sure you want to exit this page?";
|
||||
|
||||
return [_delegate applicationShouldTerminateMessage:self];
|
||||
}
|
||||
|
||||
- (void)_didResignActive
|
||||
{
|
||||
if (self._activeMenu)
|
||||
@@ -1332,7 +1358,7 @@ var _CPAppBootstrapperActions = nil;
|
||||
{
|
||||
var action = _CPAppBootstrapperActions.shift();
|
||||
|
||||
if (objj_msgSend(self, action))
|
||||
if (self.isa.objj_msgSend0(self, action))
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -31,6 +31,7 @@ CPApplicationWillResignActiveNotification = @"CPApplicationWillResignA
|
||||
CPApplicationDidResignActiveNotification = @"CPApplicationDidResignActiveNotification";
|
||||
CPApplicationDidChangeScreenParametersNotification = @"CPApplicationDidChangeScreenParametersNotification";
|
||||
|
||||
@typedef CPApplicationTerminateReply
|
||||
CPTerminateNow = YES;
|
||||
CPTerminateCancel = NO;
|
||||
CPTerminateLater = -1; // not currently supported
|
||||
|
||||
+17
-2
@@ -598,7 +598,8 @@ var CPBoxTypeKey = @"CPBoxTypeKey",
|
||||
CPBoxBorderTypeKey = @"CPBoxBorderTypeKey",
|
||||
CPBoxTitle = @"CPBoxTitle",
|
||||
CPBoxTitlePosition = @"CPBoxTitlePosition",
|
||||
CPBoxTitleView = @"CPBoxTitleView";
|
||||
CPBoxTitleView = @"CPBoxTitleView",
|
||||
CPBoxContentView = @"CPBoxContentView";
|
||||
|
||||
@implementation CPBox (CPCoding)
|
||||
|
||||
@@ -615,7 +616,20 @@ var CPBoxTypeKey = @"CPBoxTypeKey",
|
||||
_titlePosition = [aCoder decodeIntForKey:CPBoxTitlePosition];
|
||||
_titleView = [aCoder decodeObjectForKey:CPBoxTitleView] || [CPTextField labelWithTitle:_title];
|
||||
|
||||
_contentView = [self subviews][0];
|
||||
if (_boxType != CPBoxSeparator)
|
||||
{
|
||||
// FIXME: we have a problem with CIB decoding here.
|
||||
// We should be able to simply add : _contentView = [self subviews][0]
|
||||
// but first box subview seems to be malformed (badly decoded).
|
||||
// For example, when deployed, this view doesn't have its _trackingAreas array initialized.
|
||||
// As a (temporary) workaround, we encode/decode the _contentView property. We then transfer the subview hierarchy
|
||||
// and replace the first (and only) box subview with this _contentView
|
||||
|
||||
_contentView = [aCoder decodeObjectForKey:CPBoxContentView] || [[CPView alloc] initWithFrame:[self bounds]];
|
||||
var malformedContentView = [self subviews][0];
|
||||
[_contentView setSubviews:[malformedContentView subviews]];
|
||||
[self replaceSubview:malformedContentView with:_contentView];
|
||||
}
|
||||
|
||||
[self setAutoresizesSubviews:YES];
|
||||
[_contentView setAutoresizingMask:CPViewWidthSizable | CPViewHeightSizable];
|
||||
@@ -635,6 +649,7 @@ var CPBoxTypeKey = @"CPBoxTypeKey",
|
||||
[aCoder encodeObject:_title forKey:CPBoxTitle];
|
||||
[aCoder encodeInt:_titlePosition forKey:CPBoxTitlePosition];
|
||||
[aCoder encodeObject:_titleView forKey:CPBoxTitleView];
|
||||
[aCoder encodeObject:_contentView forKey:CPBoxContentView];
|
||||
}
|
||||
|
||||
@end
|
||||
+3
-3
@@ -280,7 +280,7 @@ CPButtonImageOffset = 3.0;
|
||||
break;
|
||||
|
||||
case CPOffState:
|
||||
[self unsetThemeState:[CPThemeStateSelected, CPButtonStateMixed, CPThemeStateHighlighted]];
|
||||
[self unsetThemeStates:[CPThemeStateSelected, CPButtonStateMixed, CPThemeStateHighlighted]];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -665,10 +665,10 @@ CPButtonImageOffset = 3.0;
|
||||
*/
|
||||
- (void)sizeToFit
|
||||
{
|
||||
[self setFrameSize:[self _minimumFrameSize]];
|
||||
|
||||
[self layoutSubviews];
|
||||
|
||||
[self setFrameSize:[self _minimumFrameSize]];
|
||||
|
||||
if ([self ephemeralSubviewNamed:@"content-view"])
|
||||
[self layoutSubviews];
|
||||
}
|
||||
|
||||
@@ -112,6 +112,8 @@
|
||||
|
||||
- (void)awakeFromCib
|
||||
{
|
||||
[super awakeFromCib];
|
||||
|
||||
var view = [self superview],
|
||||
subview = self;
|
||||
|
||||
@@ -270,15 +272,15 @@
|
||||
currentButtonOffset += width - 1;
|
||||
}
|
||||
|
||||
[button setValue:normalColor forThemeAttribute:@"bezel-color" inState:[CPThemeStateNormal, CPThemeStateBordered]];
|
||||
[button setValue:highlightedColor forThemeAttribute:@"bezel-color" inState:[CPThemeStateHighlighted, CPThemeStateBordered, ]];
|
||||
[button setValue:disabledColor forThemeAttribute:@"bezel-color" inState:[CPThemeStateDisabled, CPThemeStateBordered]];
|
||||
[button setValue:normalColor forThemeAttribute:@"bezel-color" inStates:[CPThemeStateNormal, CPThemeStateBordered]];
|
||||
[button setValue:highlightedColor forThemeAttribute:@"bezel-color" inStates:[CPThemeStateHighlighted, CPThemeStateBordered, ]];
|
||||
[button setValue:disabledColor forThemeAttribute:@"bezel-color" inStates:[CPThemeStateDisabled, CPThemeStateBordered]];
|
||||
[button setValue:textColor forThemeAttribute:@"text-color" inState:CPThemeStateBordered];
|
||||
|
||||
// FIXME shouldn't need this
|
||||
[button setValue:normalColor forThemeAttribute:@"bezel-color" inState:[CPThemeStateNormal, CPThemeStateBordered, CPPopUpButtonStatePullsDown]];
|
||||
[button setValue:highlightedColor forThemeAttribute:@"bezel-color" inState:[CPThemeStateHighlighted, CPThemeStateBordered, CPPopUpButtonStatePullsDown]];
|
||||
[button setValue:disabledColor forThemeAttribute:@"bezel-color" inState:[CPThemeStateDisabled, CPThemeStateBordered, CPPopUpButtonStatePullsDown]];
|
||||
[button setValue:normalColor forThemeAttribute:@"bezel-color" inStates:[CPThemeStateNormal, CPThemeStateBordered, CPPopUpButtonStatePullsDown]];
|
||||
[button setValue:highlightedColor forThemeAttribute:@"bezel-color" inStates:[CPThemeStateHighlighted, CPThemeStateBordered, CPPopUpButtonStatePullsDown]];
|
||||
[button setValue:disabledColor forThemeAttribute:@"bezel-color" inStates:[CPThemeStateDisabled, CPThemeStateBordered, CPPopUpButtonStatePullsDown]];
|
||||
|
||||
[self addSubview:button];
|
||||
}
|
||||
|
||||
+1
-1
@@ -195,7 +195,7 @@
|
||||
|
||||
- (CGRect)documentVisibleRect
|
||||
{
|
||||
return [self convertRect:[self bounds] fromView:_documentView];
|
||||
return [_documentView visibleRect];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -60,6 +60,8 @@ var CPCollectionViewDelegate_collectionView_acceptDrop_index_dropOperation_
|
||||
|
||||
@end
|
||||
|
||||
var HORIZONTAL_MARGIN = 2;
|
||||
|
||||
/*!
|
||||
@ingroup appkit
|
||||
@class CPCollectionView
|
||||
@@ -88,9 +90,6 @@ var CPCollectionViewDelegate_collectionView_acceptDrop_index_dropOperation_
|
||||
@param indices the indices to obtain drag types
|
||||
@return an array of drag types (CPString)
|
||||
*/
|
||||
|
||||
var HORIZONTAL_MARGIN = 2;
|
||||
|
||||
@implementation CPCollectionView : CPView
|
||||
{
|
||||
CPArray _content;
|
||||
|
||||
+55
-6
@@ -25,6 +25,7 @@
|
||||
|
||||
@import "CGColor.j"
|
||||
|
||||
@import "_CPObject+Theme.j"
|
||||
@import "CPCompatibility.j"
|
||||
@import "CPImage.j"
|
||||
|
||||
@@ -65,7 +66,8 @@ var cachedBlackColor,
|
||||
cachedOrangeColor,
|
||||
cachedPurpleColor,
|
||||
cachedShadowColor,
|
||||
cachedClearColor;
|
||||
cachedClearColor,
|
||||
cachedThemeColor;
|
||||
|
||||
/// @endcond
|
||||
|
||||
@@ -78,7 +80,7 @@ var cachedBlackColor,
|
||||
<p>It also provides some class helper methods that
|
||||
returns instances of commonly used colors.</p>
|
||||
*/
|
||||
@implementation CPColor : CPObject
|
||||
@implementation CPColor : CPObject <CPTheme>
|
||||
{
|
||||
CPArray _components;
|
||||
|
||||
@@ -86,6 +88,27 @@ var cachedBlackColor,
|
||||
CPString _cssString;
|
||||
}
|
||||
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Theming
|
||||
|
||||
+ (CPString)defaultThemeClass
|
||||
{
|
||||
return "color";
|
||||
}
|
||||
|
||||
+ (CPDictionary)themeAttributes
|
||||
{
|
||||
return @{
|
||||
@"alternate-selected-control-color": [CPNull null],
|
||||
@"secondary-selected-control-color" : [CPNull null]
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Static methods
|
||||
|
||||
/*!
|
||||
Creates a color in the RGB colorspace, with an alpha value.
|
||||
Each component should be between the range of 0.0 to 1.0. For
|
||||
@@ -436,14 +459,22 @@ var cachedBlackColor,
|
||||
return cachedClearColor;
|
||||
}
|
||||
|
||||
+ (CPColor)_cachedThemeColor
|
||||
{
|
||||
if (!cachedThemeColor)
|
||||
cachedThemeColor = [self colorWithCalibratedWhite:0.0 alpha:0.0];
|
||||
|
||||
return cachedThemeColor;
|
||||
}
|
||||
|
||||
+ (CPColor)alternateSelectedControlColor
|
||||
{
|
||||
return [[CPColor alloc] _initWithRGBA:[0.22, 0.46, 0.84, 1.0]];
|
||||
return [[self _cachedThemeColor] valueForThemeAttribute:@"alternate-selected-control-color"];
|
||||
}
|
||||
|
||||
+ (CPColor)secondarySelectedControlColor
|
||||
{
|
||||
return [[CPColor alloc] _initWithRGBA:[0.83, 0.83, 0.83, 1.0]];
|
||||
return [[self _cachedThemeColor] valueForThemeAttribute:@"secondary-selected-control-color"];
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -489,6 +520,10 @@ var cachedBlackColor,
|
||||
// use it (issue #1413.)
|
||||
[self _initCSSStringFromComponents];
|
||||
|
||||
_theme = [CPTheme defaultTheme];
|
||||
_themeState = CPThemeStateNormal;
|
||||
[self _loadThemeAttributes];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@@ -502,6 +537,10 @@ var cachedBlackColor,
|
||||
_components = components;
|
||||
|
||||
[self _initCSSStringFromComponents];
|
||||
|
||||
_theme = [CPTheme defaultTheme];
|
||||
_themeState = CPThemeStateNormal;
|
||||
[self _loadThemeAttributes];
|
||||
}
|
||||
|
||||
return self;
|
||||
@@ -528,6 +567,10 @@ var cachedBlackColor,
|
||||
_patternImage = anImage;
|
||||
_cssString = "url(\"" + [_patternImage filename] + "\")";
|
||||
_components = [0.0, 0.0, 0.0, 1.0];
|
||||
|
||||
_theme = [CPTheme defaultTheme];
|
||||
_themeState = CPThemeStateNormal;
|
||||
[self _loadThemeAttributes];
|
||||
}
|
||||
|
||||
return self;
|
||||
@@ -835,9 +878,13 @@ var CPColorComponentsKey = @"CPColorComponentsKey",
|
||||
- (id)initWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
if ([aCoder containsValueForKey:CPColorPatternImageKey])
|
||||
return [self _initWithPatternImage:[aCoder decodeObjectForKey:CPColorPatternImageKey]];
|
||||
self = [self _initWithPatternImage:[aCoder decodeObjectForKey:CPColorPatternImageKey]];
|
||||
else
|
||||
self = [self _initWithRGBA:[aCoder decodeObjectForKey:CPColorComponentsKey]];
|
||||
|
||||
return [self _initWithRGBA:[aCoder decodeObjectForKey:CPColorComponentsKey]];
|
||||
[self _decodeThemeObjectsWithCoder:aCoder];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -850,6 +897,8 @@ var CPColorComponentsKey = @"CPColorComponentsKey",
|
||||
[aCoder encodeObject:_patternImage forKey:CPColorPatternImageKey];
|
||||
else
|
||||
[aCoder encodeObject:_components forKey:CPColorComponentsKey];
|
||||
|
||||
[self _encodeThemeObjectsWithCoder:aCoder];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -483,13 +483,12 @@ var CPColorPanelSwatchesCookie = "CPColorPanelSwatchesCookie";
|
||||
];
|
||||
}
|
||||
|
||||
var cookieValue = eval(cookieValue),
|
||||
result = [];
|
||||
var cookieValue = eval(cookieValue);
|
||||
|
||||
for (var i = 0; i < cookieValue.length; i++)
|
||||
result.push([CPColor colorWithHexString:cookieValue[i]]);
|
||||
|
||||
return result;
|
||||
return [cookieValue arrayByApplyingBlock:function(value)
|
||||
{
|
||||
return [CPColor colorWithHexString:value];
|
||||
}];
|
||||
}
|
||||
|
||||
- (CPArray)saveColorList
|
||||
|
||||
+5
-6
@@ -843,8 +843,9 @@ var CPComboBoxTextSubview = @"text",
|
||||
// In FireFox this needs to be done in setTimeout, otherwise there is no caret
|
||||
// We have to save the input element now, when we lose focus it will change.
|
||||
var element = [self _inputElement];
|
||||
window.setTimeout(function() {
|
||||
|
||||
[[CPRunLoop mainRunLoop] performBlock:function()
|
||||
{
|
||||
// This will prevent to jump to the focused element
|
||||
var previousScrollingOrigin = [self _scrollToVisibleRectAndReturnPreviousOrigin];
|
||||
|
||||
@@ -852,7 +853,7 @@ var CPComboBoxTextSubview = @"text",
|
||||
|
||||
[self _restorePreviousScrollingOrigin:previousScrollingOrigin];
|
||||
|
||||
}, 0);
|
||||
} argument:nil order:0 modes:[CPDefaultRunLoopMode]];
|
||||
#endif
|
||||
|
||||
return NO;
|
||||
@@ -1141,11 +1142,9 @@ var CPComboBoxTextSubview = @"text",
|
||||
// Directly nuke _items, [_items removeAll] will trigger an extra call to setContent
|
||||
_items = [];
|
||||
|
||||
var values = [];
|
||||
|
||||
[anArray enumerateObjectsUsingBlock:function(object)
|
||||
var values = [anArray arrayByApplyingBlock:function(object)
|
||||
{
|
||||
values.push([object description]);
|
||||
return [object description];
|
||||
}];
|
||||
|
||||
[self addItemsWithObjectValues:values];
|
||||
|
||||
+65
-8
@@ -27,6 +27,7 @@
|
||||
@import "CPShadow.j"
|
||||
@import "CPView.j"
|
||||
@import "CPKeyValueBinding.j"
|
||||
@import "CPTrackingArea.j"
|
||||
|
||||
@global CPApp
|
||||
|
||||
@@ -42,13 +43,6 @@
|
||||
|
||||
@end
|
||||
|
||||
@typedef CPTextAlignment
|
||||
CPLeftTextAlignment = 0;
|
||||
CPRightTextAlignment = 1;
|
||||
CPCenterTextAlignment = 2;
|
||||
CPJustifiedTextAlignment = 3;
|
||||
CPNaturalTextAlignment = 4;
|
||||
|
||||
@typedef CPControlSize
|
||||
CPRegularControlSize = 0;
|
||||
CPSmallControlSize = 1;
|
||||
@@ -126,6 +120,8 @@ var CPControlBlackColor = [CPColor blackColor];
|
||||
CGPoint _previousTrackingLocation;
|
||||
|
||||
CPControlSize _controlSize;
|
||||
|
||||
CPWritingDirection _baseWritingDirection @accessors(property=baseWritingDirection);
|
||||
}
|
||||
|
||||
+ (CPDictionary)themeAttributes
|
||||
@@ -205,7 +201,6 @@ var CPControlBlackColor = [CPColor blackColor];
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Control Size
|
||||
|
||||
@@ -1016,6 +1011,62 @@ var CPControlBlackColor = [CPColor blackColor];
|
||||
return [self hasThemeState:CPThemeStateHighlighted];
|
||||
}
|
||||
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Base writing direction
|
||||
|
||||
/*!
|
||||
Sets the initial writing direction of the receiver
|
||||
@param writingDirection - It could be CPWritingDirectionNatural, CPWritingDirectionLeftToRight, CPWritingDirectionRightToLeft
|
||||
*/
|
||||
- (void)setBaseWritingDirection:(CPWritingDirection)writingDirection
|
||||
{
|
||||
if (writingDirection == _baseWritingDirection)
|
||||
return;
|
||||
|
||||
[self willChangeValueForKey:@"baseWritingDirection"];
|
||||
_baseWritingDirection = writingDirection;
|
||||
[self didChangeValueForKey:@"baseWritingDirection"];
|
||||
|
||||
#if PLATFORM(DOM)
|
||||
|
||||
var style;
|
||||
|
||||
switch (_baseWritingDirection)
|
||||
{
|
||||
case CPWritingDirectionNatural:
|
||||
style = "initial";
|
||||
break;
|
||||
|
||||
case CPWritingDirectionLeftToRight:
|
||||
style = "ltr";
|
||||
break;
|
||||
|
||||
case CPWritingDirectionRightToLeft:
|
||||
style = "rtl";
|
||||
break;
|
||||
|
||||
default:
|
||||
style = "initial";
|
||||
}
|
||||
|
||||
_DOMElement.style.direction = style;
|
||||
#endif
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation CPControl (CPTrackingArea)
|
||||
|
||||
- (void)updateTrackingAreas
|
||||
{
|
||||
[self removeAllTrackingAreas];
|
||||
[self addTrackingArea:[[CPTrackingArea alloc] initWithRect:CGRectMakeZero()
|
||||
options:CPTrackingMouseEnteredAndExited | CPTrackingActiveInKeyWindow | CPTrackingInVisibleRect
|
||||
owner:self
|
||||
userInfo:nil]];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
var CPControlActionKey = @"CPControlActionKey",
|
||||
@@ -1027,6 +1078,7 @@ var CPControlActionKey = @"CPControlActionKey",
|
||||
CPControlSendsActionOnEndEditingKey = @"CPControlSendsActionOnEndEditingKey",
|
||||
CPControlTargetKey = @"CPControlTargetKey",
|
||||
CPControlValueKey = @"CPControlValueKey",
|
||||
CPControlBaseWrittingDirectionKey = @"CPControlBaseWrittingDirectionKey";
|
||||
|
||||
__Deprecated__CPImageViewImageKey = @"CPImageViewImageKey";
|
||||
|
||||
@@ -1055,6 +1107,8 @@ var CPControlActionKey = @"CPControlActionKey",
|
||||
[self setFormatter:[aCoder decodeObjectForKey:CPControlFormatterKey]];
|
||||
|
||||
[self setControlSize:[aCoder decodeIntForKey:CPControlControlSizeKey]];
|
||||
|
||||
[self setBaseWritingDirection:[aCoder decodeIntForKey:CPControlBaseWrittingDirectionKey]];
|
||||
}
|
||||
|
||||
return self;
|
||||
@@ -1089,6 +1143,9 @@ var CPControlActionKey = @"CPControlActionKey",
|
||||
[aCoder encodeObject:_formatter forKey:CPControlFormatterKey];
|
||||
|
||||
[aCoder encodeInt:_controlSize forKey:CPControlControlSizeKey];
|
||||
|
||||
[aCoder encodeInt:_baseWritingDirection forKey:CPControlBaseWrittingDirectionKey];
|
||||
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -123,6 +123,9 @@ var currentCursor = nil,
|
||||
|
||||
- (void)set
|
||||
{
|
||||
if (currentCursor === self)
|
||||
return;
|
||||
|
||||
currentCursor = self;
|
||||
|
||||
#if PLATFORM(DOM)
|
||||
|
||||
@@ -630,14 +630,6 @@ CPEraDatePickerElementFlag = 0x0100;
|
||||
return [[_locale objectForKey:CPLocaleCountryCode] isEqualToString:@"US"];
|
||||
}
|
||||
|
||||
/*! Check if we are in the english format or not. Depending on the locale
|
||||
*/
|
||||
- (BOOL)_isEnglishFormat
|
||||
{
|
||||
return [[_locale objectForKey:CPLocaleLanguageCode] isEqualToString:@"en"];
|
||||
}
|
||||
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Key event
|
||||
|
||||
|
||||
@@ -1114,30 +1114,30 @@ var CPShortWeekDayNameArrayEn = [@"Mo", @"Tu", @"We", @"Th", @"Fr", @"Sa", @"Su"
|
||||
[_textField setValue:[_datePicker valueForThemeAttribute:@"tile-text-shadow-color" inState:CPThemeStateDisabled] forThemeAttribute:@"text-shadow-color" inState:CPThemeStateDisabled];
|
||||
[_textField setValue:[_datePicker valueForThemeAttribute:@"tile-text-shadow-offset" inState:CPThemeStateDisabled] forThemeAttribute:@"text-shadow-offset" inState:CPThemeStateDisabled];
|
||||
|
||||
[_textField setValue:[_datePicker valueForThemeAttribute:@"tile-font" inState:[CPThemeStateDisabled, CPThemeStateSelected]] forThemeAttribute:@"font" inState:[CPThemeStateDisabled, CPThemeStateSelected]];
|
||||
[_textField setValue:[_datePicker valueForThemeAttribute:@"tile-text-color" inState:[CPThemeStateDisabled, CPThemeStateSelected]]forThemeAttribute:@"text-color" inState:[CPThemeStateDisabled, CPThemeStateSelected]];
|
||||
[_textField setValue:[_datePicker valueForThemeAttribute:@"tile-text-shadow-color" inState:[CPThemeStateDisabled, CPThemeStateSelected]] forThemeAttribute:@"text-shadow-color" inState:[CPThemeStateDisabled, CPThemeStateSelected]];
|
||||
[_textField setValue:[_datePicker valueForThemeAttribute:@"tile-text-shadow-offset" inState:[CPThemeStateDisabled, CPThemeStateSelected]] forThemeAttribute:@"text-shadow-offset" inState:[CPThemeStateDisabled, CPThemeStateSelected]];
|
||||
[_textField setValue:[_datePicker valueForThemeAttribute:@"tile-font" inStates:[CPThemeStateDisabled, CPThemeStateSelected]] forThemeAttribute:@"font" inStates:[CPThemeStateDisabled, CPThemeStateSelected]];
|
||||
[_textField setValue:[_datePicker valueForThemeAttribute:@"tile-text-color" inStates:[CPThemeStateDisabled, CPThemeStateSelected]]forThemeAttribute:@"text-color" inStates:[CPThemeStateDisabled, CPThemeStateSelected]];
|
||||
[_textField setValue:[_datePicker valueForThemeAttribute:@"tile-text-shadow-color" inStates:[CPThemeStateDisabled, CPThemeStateSelected]] forThemeAttribute:@"text-shadow-color" inStates:[CPThemeStateDisabled, CPThemeStateSelected]];
|
||||
[_textField setValue:[_datePicker valueForThemeAttribute:@"tile-text-shadow-offset" inStates:[CPThemeStateDisabled, CPThemeStateSelected]] forThemeAttribute:@"text-shadow-offset" inStates:[CPThemeStateDisabled, CPThemeStateSelected]];
|
||||
|
||||
[_textField setValue:[_datePicker valueForThemeAttribute:@"tile-font" inState:CPThemeStateHighlighted] forThemeAttribute:@"font" inState:CPThemeStateHighlighted];
|
||||
[_textField setValue:[_datePicker valueForThemeAttribute:@"tile-text-color" inState:CPThemeStateHighlighted] forThemeAttribute:@"text-color" inState:CPThemeStateHighlighted];
|
||||
[_textField setValue:[_datePicker valueForThemeAttribute:@"tile-text-shadow-color" inState:CPThemeStateHighlighted] forThemeAttribute:@"text-shadow-color" inState:CPThemeStateHighlighted];
|
||||
[_textField setValue:[_datePicker valueForThemeAttribute:@"tile-text-shadow-offset" inState:CPThemeStateHighlighted] forThemeAttribute:@"text-shadow-offset" inState:CPThemeStateHighlighted];
|
||||
|
||||
[_textField setValue:[_datePicker valueForThemeAttribute:@"tile-font" inState:[CPThemeStateHighlighted, CPThemeStateSelected]] forThemeAttribute:@"font" inState:[CPThemeStateHighlighted, CPThemeStateSelected]];
|
||||
[_textField setValue:[_datePicker valueForThemeAttribute:@"tile-text-color" inState:[CPThemeStateHighlighted, CPThemeStateSelected]] forThemeAttribute:@"text-color" inState:[CPThemeStateHighlighted, CPThemeStateSelected]];
|
||||
[_textField setValue:[_datePicker valueForThemeAttribute:@"tile-text-shadow-color" inState:[CPThemeStateHighlighted, CPThemeStateSelected]] forThemeAttribute:@"text-shadow-color" inState:[CPThemeStateHighlighted, CPThemeStateSelected]];
|
||||
[_textField setValue:[_datePicker valueForThemeAttribute:@"tile-text-shadow-offset" inState:[CPThemeStateHighlighted, CPThemeStateSelected]] forThemeAttribute:@"text-shadow-offset" inState:[CPThemeStateHighlighted, CPThemeStateSelected]];
|
||||
[_textField setValue:[_datePicker valueForThemeAttribute:@"tile-font" inStates:[CPThemeStateHighlighted, CPThemeStateSelected]] forThemeAttribute:@"font" inStates:[CPThemeStateHighlighted, CPThemeStateSelected]];
|
||||
[_textField setValue:[_datePicker valueForThemeAttribute:@"tile-text-color" inStates:[CPThemeStateHighlighted, CPThemeStateSelected]] forThemeAttribute:@"text-color" inStates:[CPThemeStateHighlighted, CPThemeStateSelected]];
|
||||
[_textField setValue:[_datePicker valueForThemeAttribute:@"tile-text-shadow-color" inStates:[CPThemeStateHighlighted, CPThemeStateSelected]] forThemeAttribute:@"text-shadow-color" inStates:[CPThemeStateHighlighted, CPThemeStateSelected]];
|
||||
[_textField setValue:[_datePicker valueForThemeAttribute:@"tile-text-shadow-offset" inStates:[CPThemeStateHighlighted, CPThemeStateSelected]] forThemeAttribute:@"text-shadow-offset" inStates:[CPThemeStateHighlighted, CPThemeStateSelected]];
|
||||
|
||||
[_textField setValue:[_datePicker valueForThemeAttribute:@"tile-font" inState:[CPThemeStateDisabled, CPThemeStateHighlighted, CPThemeStateSelected]] forThemeAttribute:@"font" inState:[CPThemeStateDisabled, CPThemeStateHighlighted, CPThemeStateSelected]];
|
||||
[_textField setValue:[_datePicker valueForThemeAttribute:@"tile-text-color" inState:[CPThemeStateDisabled, CPThemeStateHighlighted, CPThemeStateSelected]] forThemeAttribute:@"text-color" inState:[CPThemeStateDisabled, CPThemeStateHighlighted, CPThemeStateSelected]];
|
||||
[_textField setValue:[_datePicker valueForThemeAttribute:@"tile-text-shadow-color" inState:[CPThemeStateDisabled, CPThemeStateHighlighted, CPThemeStateSelected]] forThemeAttribute:@"text-shadow-color" inState:[CPThemeStateDisabled, CPThemeStateHighlighted, CPThemeStateSelected]];
|
||||
[_textField setValue:[_datePicker valueForThemeAttribute:@"tile-text-shadow-offset" inState:[CPThemeStateDisabled, CPThemeStateHighlighted, CPThemeStateSelected]] forThemeAttribute:@"text-shadow-offset" inState:[CPThemeStateDisabled, CPThemeStateHighlighted, CPThemeStateSelected]];
|
||||
[_textField setValue:[_datePicker valueForThemeAttribute:@"tile-font" inStates:[CPThemeStateDisabled, CPThemeStateHighlighted, CPThemeStateSelected]] forThemeAttribute:@"font" inStates:[CPThemeStateDisabled, CPThemeStateHighlighted, CPThemeStateSelected]];
|
||||
[_textField setValue:[_datePicker valueForThemeAttribute:@"tile-text-color" inStates:[CPThemeStateDisabled, CPThemeStateHighlighted, CPThemeStateSelected]] forThemeAttribute:@"text-color" inStates:[CPThemeStateDisabled, CPThemeStateHighlighted, CPThemeStateSelected]];
|
||||
[_textField setValue:[_datePicker valueForThemeAttribute:@"tile-text-shadow-color" inStates:[CPThemeStateDisabled, CPThemeStateHighlighted, CPThemeStateSelected]] forThemeAttribute:@"text-shadow-color" inStates:[CPThemeStateDisabled, CPThemeStateHighlighted, CPThemeStateSelected]];
|
||||
[_textField setValue:[_datePicker valueForThemeAttribute:@"tile-text-shadow-offset" inStates:[CPThemeStateDisabled, CPThemeStateHighlighted, CPThemeStateSelected]] forThemeAttribute:@"text-shadow-offset" inStates:[CPThemeStateDisabled, CPThemeStateHighlighted, CPThemeStateSelected]];
|
||||
|
||||
[_textField setValue:[_datePicker valueForThemeAttribute:@"tile-font" inState:[CPThemeStateDisabled, CPThemeStateHighlighted]] forThemeAttribute:@"font" inState:[CPThemeStateDisabled, CPThemeStateHighlighted]];
|
||||
[_textField setValue:[_datePicker valueForThemeAttribute:@"tile-text-color" inState:[CPThemeStateDisabled, CPThemeStateHighlighted]] forThemeAttribute:@"text-color" inState:[CPThemeStateDisabled, CPThemeStateHighlighted]];
|
||||
[_textField setValue:[_datePicker valueForThemeAttribute:@"tile-text-shadow-color" inState:[CPThemeStateDisabled, CPThemeStateHighlighted]] forThemeAttribute:@"text-shadow-color" inState:[CPThemeStateDisabled, CPThemeStateHighlighted]];
|
||||
[_textField setValue:[_datePicker valueForThemeAttribute:@"tile-text-shadow-offset" inState:[CPThemeStateDisabled, CPThemeStateHighlighted]] forThemeAttribute:@"text-shadow-offset" inState:[CPThemeStateDisabled, CPThemeStateHighlighted]];
|
||||
[_textField setValue:[_datePicker valueForThemeAttribute:@"tile-font" inStates:[CPThemeStateDisabled, CPThemeStateHighlighted]] forThemeAttribute:@"font" inStates:[CPThemeStateDisabled, CPThemeStateHighlighted]];
|
||||
[_textField setValue:[_datePicker valueForThemeAttribute:@"tile-text-color" inStates:[CPThemeStateDisabled, CPThemeStateHighlighted]] forThemeAttribute:@"text-color" inStates:[CPThemeStateDisabled, CPThemeStateHighlighted]];
|
||||
[_textField setValue:[_datePicker valueForThemeAttribute:@"tile-text-shadow-color" inStates:[CPThemeStateDisabled, CPThemeStateHighlighted]] forThemeAttribute:@"text-shadow-color" inStates:[CPThemeStateDisabled, CPThemeStateHighlighted]];
|
||||
[_textField setValue:[_datePicker valueForThemeAttribute:@"tile-text-shadow-offset" inStates:[CPThemeStateDisabled, CPThemeStateHighlighted]] forThemeAttribute:@"text-shadow-offset" inStates:[CPThemeStateDisabled, CPThemeStateHighlighted]];
|
||||
|
||||
[self addSubview:_textField];
|
||||
|
||||
|
||||
@@ -139,7 +139,7 @@ var RADIANS = Math.PI / 180;
|
||||
[_minuteHandLayer setImage:[_datePicker currentValueForThemeAttribute:@"minute-hand-image"]];
|
||||
[_secondHandLayer setImage:[_datePicker currentValueForThemeAttribute:@"second-hand-image"]];
|
||||
|
||||
if ([_datePicker _isEnglishFormat])
|
||||
if ([_datePicker _isAmericanFormat])
|
||||
{
|
||||
if (dateValue.getHours() > 11)
|
||||
[_PMAMTextField setStringValue:@"PM"]
|
||||
|
||||
@@ -198,7 +198,6 @@ var CPZeroKeyCode = 48,
|
||||
else
|
||||
[self _selectTextField:_firstTextField];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*! Select a textField
|
||||
@@ -277,74 +276,99 @@ var CPZeroKeyCode = 48,
|
||||
}
|
||||
}
|
||||
|
||||
/*! performKeyEquivalent event
|
||||
Used for moving in the textField
|
||||
/*!
|
||||
PerformKeyEquivalent event
|
||||
We need to override that to handle the tab key
|
||||
*/
|
||||
- (BOOL)performKeyEquivalent:(CPEvent)anEvent
|
||||
{
|
||||
if (![self isEnabled] || !_currentTextField || [[self window] firstResponder] != _datePicker)
|
||||
return NO;
|
||||
|
||||
var key = [anEvent charactersIgnoringModifiers];
|
||||
|
||||
if (key == CPUpArrowFunctionKey)
|
||||
if ([anEvent charactersIgnoringModifiers] === CPTabCharacter)
|
||||
{
|
||||
[_currentTextField _invalidTimer];
|
||||
[_stepper setDoubleValue:[_currentTextField intValue]];
|
||||
[_stepper performClickUp:self];
|
||||
if ([anEvent modifierFlags] & CPShiftKeyMask)
|
||||
[self insertBacktab:self];
|
||||
else
|
||||
[self insertTab:self];
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
if (key == CPDownArrowFunctionKey)
|
||||
else if ([anEvent charactersIgnoringModifiers] === CPBackTabCharacter)
|
||||
{
|
||||
[_currentTextField _invalidTimer];
|
||||
[_stepper setDoubleValue:[_currentTextField intValue]];
|
||||
[_stepper performClickDown:self];
|
||||
[self insertBacktab:self];
|
||||
return YES;
|
||||
}
|
||||
|
||||
if (key == CPLeftArrowFunctionKey || [anEvent keyCode] == CPTabKeyCode && [anEvent modifierFlags] & CPShiftKeyMask)
|
||||
{
|
||||
if (_currentTextField == _firstTextField && [anEvent keyCode] == CPTabKeyCode)
|
||||
{
|
||||
var previousValidKeyView = [_datePicker previousValidKeyView];
|
||||
|
||||
if (previousValidKeyView)
|
||||
[[self window] makeFirstResponder:previousValidKeyView];
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
[self _selectTextField:[_currentTextField previousTextField]];
|
||||
return YES;
|
||||
}
|
||||
|
||||
if (key == CPRightArrowFunctionKey || [anEvent keyCode] == CPTabKeyCode)
|
||||
{
|
||||
if (_currentTextField == _lastTextField && [anEvent keyCode] == CPTabKeyCode)
|
||||
{
|
||||
var nextValidKeyView = [_datePicker nextValidKeyView];
|
||||
|
||||
if (nextValidKeyView)
|
||||
[[self window] makeFirstResponder:nextValidKeyView];
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
[self _selectTextField:[_currentTextField nextTextField]];
|
||||
return YES;
|
||||
}
|
||||
|
||||
if ([anEvent keyCode] == CPReturnKeyCode)
|
||||
{
|
||||
[_currentTextField _endEditing];
|
||||
|
||||
return [super performKeyEquivalent:anEvent];
|
||||
}
|
||||
|
||||
return [super performKeyEquivalent:anEvent];
|
||||
}
|
||||
|
||||
- (void)insertTab:(id)sender
|
||||
{
|
||||
if (!_currentTextField)
|
||||
return;
|
||||
|
||||
if (_currentTextField == _lastTextField)
|
||||
[[self window] selectNextKeyView:self];
|
||||
else
|
||||
[self moveRight:sender];
|
||||
}
|
||||
|
||||
- (void)moveRight:(id)sender
|
||||
{
|
||||
if (!_currentTextField)
|
||||
return;
|
||||
|
||||
[self _selectTextField:[_currentTextField nextTextField]];
|
||||
}
|
||||
|
||||
- (void)insertBacktab:(id)sender
|
||||
{
|
||||
if (!_currentTextField)
|
||||
return;
|
||||
|
||||
if (_currentTextField == _firstTextField)
|
||||
[[self window] selectPreviousKeyView:self];
|
||||
else
|
||||
[self moveLeft:sender];
|
||||
}
|
||||
|
||||
- (void)moveLeft:(id)sender
|
||||
{
|
||||
if (!_currentTextField)
|
||||
return;
|
||||
|
||||
[self _selectTextField:[_currentTextField previousTextField]];
|
||||
}
|
||||
|
||||
- (void)moveDown:(id)sender
|
||||
{
|
||||
if (!_currentTextField)
|
||||
return;
|
||||
|
||||
[_currentTextField _invalidTimer];
|
||||
[_stepper setDoubleValue:[_currentTextField intValue]];
|
||||
[_stepper performClickDown:self];
|
||||
}
|
||||
|
||||
- (void)moveUp:(id)sender
|
||||
{
|
||||
if (!_currentTextField)
|
||||
return;
|
||||
|
||||
[_currentTextField _invalidTimer];
|
||||
[_stepper setDoubleValue:[_currentTextField intValue]];
|
||||
[_stepper performClickUp:self];
|
||||
}
|
||||
|
||||
- (void)insertNewline:(id)sender
|
||||
{
|
||||
if (!_currentTextField)
|
||||
return;
|
||||
|
||||
[_currentTextField _endEditing];
|
||||
}
|
||||
|
||||
/*! KeyDown event
|
||||
We just care care about the event A/P and every numbers
|
||||
*/
|
||||
@@ -353,7 +377,9 @@ var CPZeroKeyCode = 48,
|
||||
if (![self isEnabled])
|
||||
return;
|
||||
|
||||
if ([_datePicker _isEnglishFormat] && [_currentTextField dateType] == CPAMPMDateType && ([anEvent keyCode] == CPAKeyCode || [anEvent keyCode] == CPPKeyCode || [anEvent keyCode] == CPMajAKeyCode || [anEvent keyCode] == CPMajPKeyCode))
|
||||
[self interpretKeyEvents:[anEvent]];
|
||||
|
||||
if ([_datePicker _isAmericanFormat] && [_currentTextField dateType] == CPAMPMDateType && ([anEvent keyCode] == CPAKeyCode || [anEvent keyCode] == CPPKeyCode || [anEvent keyCode] == CPMajAKeyCode || [anEvent keyCode] == CPMajPKeyCode))
|
||||
{
|
||||
if ([anEvent keyCode] == CPAKeyCode || [anEvent keyCode] == CPMajAKeyCode)
|
||||
[_currentTextField setStringValue:@"AM"];
|
||||
@@ -368,6 +394,7 @@ var CPZeroKeyCode = 48,
|
||||
[_currentTextField setValueForKeyEvent:anEvent];
|
||||
}
|
||||
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Layout methods
|
||||
|
||||
@@ -725,7 +752,7 @@ var CPZeroKeyCode = 48,
|
||||
|
||||
if (hour != currentHour)
|
||||
{
|
||||
if (([_datePicker _isEnglishFormat] || [_datePicker _isAmericanFormat]))
|
||||
if ([_datePicker _isAmericanFormat])
|
||||
{
|
||||
if (![self _isAMHour])
|
||||
{
|
||||
@@ -1050,7 +1077,7 @@ var CPZeroKeyCode = 48,
|
||||
- (void)_updateHiddenTextFields
|
||||
{
|
||||
var datePickerElements = [_datePicker datePickerElements],
|
||||
isEnglishFormat = [_datePicker _isEnglishFormat];
|
||||
isAmericanFormat = [_datePicker _isAmericanFormat];
|
||||
|
||||
if (datePickerElements & CPYearMonthDatePickerElementFlag)
|
||||
{
|
||||
@@ -1083,7 +1110,7 @@ var CPZeroKeyCode = 48,
|
||||
[_textFieldSeparatorThree setHidden:NO];
|
||||
[_textFieldSeparatorFour setHidden:YES];
|
||||
|
||||
if (isEnglishFormat)
|
||||
if (isAmericanFormat)
|
||||
[_textFieldPMAM setHidden:NO];
|
||||
else
|
||||
[_textFieldPMAM setHidden:YES];
|
||||
@@ -1115,9 +1142,9 @@ var CPZeroKeyCode = 48,
|
||||
verticalInset = contentInset.top - contentInset.bottom,
|
||||
firstTexField = _textFieldMonth,
|
||||
secondTextField = _textFieldDay,
|
||||
isEnglishFormat = [_datePicker _isEnglishFormat];
|
||||
isAmericanFormat = [_datePicker _isAmericanFormat];
|
||||
|
||||
if (!isEnglishFormat)
|
||||
if (!isAmericanFormat)
|
||||
{
|
||||
firstTexField = _textFieldDay;
|
||||
secondTextField = _textFieldMonth;
|
||||
@@ -1131,7 +1158,7 @@ var CPZeroKeyCode = 48,
|
||||
else
|
||||
[secondTextField setFrameOrigin:CGPointMake(CGRectGetMaxX([_textFieldSeparatorOne frame]) + separatorContentInset.right, verticalInset)];
|
||||
|
||||
if (isEnglishFormat && [secondTextField isHidden])
|
||||
if (isAmericanFormat && [secondTextField isHidden])
|
||||
[_textFieldSeparatorTwo setFrameOrigin:CGPointMake(CGRectGetMaxX([firstTexField frame]) + separatorContentInset.left, verticalInset)];
|
||||
else
|
||||
[_textFieldSeparatorTwo setFrameOrigin:CGPointMake(CGRectGetMaxX([secondTextField frame]) + separatorContentInset.left, verticalInset)];
|
||||
@@ -1206,7 +1233,7 @@ var CPZeroKeyCode = 48,
|
||||
{
|
||||
var datePickerElements = [_datePicker datePickerElements];
|
||||
|
||||
if ([_datePicker _isEnglishFormat])
|
||||
if ([_datePicker _isAmericanFormat])
|
||||
{
|
||||
if (datePickerElements & CPYearMonthDayDatePickerElementFlag || datePickerElements & CPYearMonthDatePickerElementFlag)
|
||||
[[self superview] setFirstTextField:_textFieldMonth];
|
||||
@@ -1249,9 +1276,9 @@ var CPZeroKeyCode = 48,
|
||||
var datePickerElements = [_datePicker datePickerElements],
|
||||
firstTexField = _textFieldMonth,
|
||||
secondTextField = _textFieldDay,
|
||||
isEnglishFormat = [_datePicker _isEnglishFormat];
|
||||
isAmericanFormat = [_datePicker _isAmericanFormat];
|
||||
|
||||
if (!isEnglishFormat)
|
||||
if (!isAmericanFormat)
|
||||
{
|
||||
firstTexField = _textFieldDay;
|
||||
secondTextField = _textFieldMonth;
|
||||
@@ -1266,7 +1293,7 @@ var CPZeroKeyCode = 48,
|
||||
|
||||
if (datePickerElements & CPHourMinuteSecondDatePickerElementFlag || datePickerElements & CPHourMinuteDatePickerElementFlag)
|
||||
[_textFieldYear setNextTextField:_textFieldHour];
|
||||
else if (isEnglishFormat || (datePickerElements & CPYearMonthDayDatePickerElementFlag) == CPYearMonthDayDatePickerElementFlag)
|
||||
else if (isAmericanFormat || (datePickerElements & CPYearMonthDayDatePickerElementFlag) == CPYearMonthDayDatePickerElementFlag)
|
||||
[_textFieldYear setNextTextField:firstTexField];
|
||||
else
|
||||
[_textFieldYear setNextTextField:secondTextField];
|
||||
@@ -1275,7 +1302,7 @@ var CPZeroKeyCode = 48,
|
||||
|
||||
if ((datePickerElements & CPHourMinuteSecondDatePickerElementFlag) == CPHourMinuteSecondDatePickerElementFlag)
|
||||
[_textFieldMinute setNextTextField:_textFieldSecond];
|
||||
else if (isEnglishFormat)
|
||||
else if (isAmericanFormat)
|
||||
[_textFieldMinute setNextTextField:_textFieldPMAM];
|
||||
else if ((datePickerElements & CPYearMonthDayDatePickerElementFlag) == CPYearMonthDayDatePickerElementFlag)
|
||||
[_textFieldMinute setNextTextField:firstTexField];
|
||||
@@ -1284,7 +1311,7 @@ var CPZeroKeyCode = 48,
|
||||
else
|
||||
[_textFieldMinute setNextTextField:_textFieldHour];
|
||||
|
||||
if (isEnglishFormat)
|
||||
if (isAmericanFormat)
|
||||
[_textFieldSecond setNextTextField:_textFieldPMAM];
|
||||
else if ((datePickerElements & CPYearMonthDayDatePickerElementFlag) == CPYearMonthDayDatePickerElementFlag)
|
||||
[_textFieldSecond setNextTextField:firstTexField];
|
||||
@@ -1304,9 +1331,9 @@ var CPZeroKeyCode = 48,
|
||||
var datePickerElements = [_datePicker datePickerElements],
|
||||
firstTexField = _textFieldMonth,
|
||||
secondTextField = _textFieldDay,
|
||||
isEnglishFormat = [_datePicker _isEnglishFormat];
|
||||
isAmericanFormat = [_datePicker _isAmericanFormat];
|
||||
|
||||
if (!isEnglishFormat)
|
||||
if (!isAmericanFormat)
|
||||
{
|
||||
firstTexField = _textFieldDay;
|
||||
secondTextField = _textFieldMonth;
|
||||
@@ -1322,14 +1349,14 @@ var CPZeroKeyCode = 48,
|
||||
|
||||
if (datePickerElements & CPYearMonthDatePickerElementFlag)
|
||||
[_textFieldHour setPreviousTextField:_textFieldYear];
|
||||
else if (isEnglishFormat)
|
||||
else if (isAmericanFormat)
|
||||
[_textFieldHour setPreviousTextField:_textFieldPMAM];
|
||||
else if ((datePickerElements & CPHourMinuteSecondDatePickerElementFlag) == CPHourMinuteSecondDatePickerElementFlag)
|
||||
[_textFieldHour setPreviousTextField:_textFieldSecond];
|
||||
else
|
||||
[_textFieldHour setPreviousTextField:_textFieldMinute];
|
||||
|
||||
if (!isEnglishFormat)
|
||||
if (!isAmericanFormat)
|
||||
[_textFieldYear setPreviousTextField:_textFieldMonth];
|
||||
else if ((datePickerElements & CPYearMonthDayDatePickerElementFlag) == CPYearMonthDayDatePickerElementFlag)
|
||||
[_textFieldYear setPreviousTextField:_textFieldDay];
|
||||
@@ -1338,7 +1365,7 @@ var CPZeroKeyCode = 48,
|
||||
|
||||
[secondTextField setPreviousTextField:firstTexField];
|
||||
|
||||
if (isEnglishFormat && datePickerElements & CPHourMinuteDatePickerElementFlag)
|
||||
if (isAmericanFormat && datePickerElements & CPHourMinuteDatePickerElementFlag)
|
||||
[firstTexField setPreviousTextField:_textFieldPMAM];
|
||||
else if ((datePickerElements & CPHourMinuteSecondDatePickerElementFlag) == CPHourMinuteSecondDatePickerElementFlag)
|
||||
[firstTexField setPreviousTextField:_textFieldSecond];
|
||||
@@ -1530,7 +1557,7 @@ var CPMonthDateType = 0,
|
||||
}
|
||||
}
|
||||
|
||||
if (parseInt(newValue) > [self _maxNumberWithMaxDate] || ([_datePicker _isEnglishFormat] && _dateType == CPHourDateType && parseInt(newValue) > 12))
|
||||
if (parseInt(newValue) > [self _maxNumberWithMaxDate] || ([_datePicker _isAmericanFormat] && _dateType == CPHourDateType && parseInt(newValue) > 12))
|
||||
return;
|
||||
|
||||
_firstEvent = NO;
|
||||
@@ -1549,7 +1576,7 @@ var CPMonthDateType = 0,
|
||||
|
||||
if ([stringValue length])
|
||||
{
|
||||
if ([_datePicker _isEnglishFormat] && [self dateType] == CPHourDateType)
|
||||
if ([_datePicker _isAmericanFormat] && [self dateType] == CPHourDateType)
|
||||
{
|
||||
var isAMHour = [[self superview] _isAMHour];
|
||||
|
||||
@@ -1593,7 +1620,7 @@ var CPMonthDateType = 0,
|
||||
if (![objectValue length])
|
||||
objectValue = [self objectValue];
|
||||
|
||||
if ([_datePicker _isEnglishFormat] && [self dateType] == CPHourDateType)
|
||||
if ([_datePicker _isAmericanFormat] && [self dateType] == CPHourDateType)
|
||||
{
|
||||
var isAMHour = [[self superview] _isAMHour];
|
||||
|
||||
@@ -1621,7 +1648,7 @@ var CPMonthDateType = 0,
|
||||
}
|
||||
else if (_dateType != CPAMPMDateType)
|
||||
{
|
||||
if (_dateType == CPHourDateType && [_datePicker _isEnglishFormat])
|
||||
if (_dateType == CPHourDateType && [_datePicker _isAmericanFormat])
|
||||
{
|
||||
var value = parseInt(aStringValue);
|
||||
|
||||
|
||||
+22
-13
@@ -515,7 +515,9 @@ var CPDocumentUntitledCount = 0;
|
||||
|
||||
alert("There was an error retrieving the document.");
|
||||
|
||||
objj_msgSend(session.delegate, session.didReadSelector, self, NO, session.contextInfo);
|
||||
var theDelegate = session.delegate;
|
||||
|
||||
theDelegate.isa.objj_msgSend3(theDelegate, session.didReadSelector, self, NO, session.contextInfo);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -540,7 +542,9 @@ var CPDocumentUntitledCount = 0;
|
||||
|
||||
_writeRequest = nil;
|
||||
|
||||
objj_msgSend(session.delegate, session.didSaveSelector, self, NO, session.contextInfo);
|
||||
var theDelegate = session.delegate;
|
||||
|
||||
theDelegate.isa.objj_msgSend3(theDelegate, session.didSaveSelector, self, NO, session.contextInfo);
|
||||
[self _sendDocumentSavedNotification:NO];
|
||||
}
|
||||
}
|
||||
@@ -553,14 +557,15 @@ var CPDocumentUntitledCount = 0;
|
||||
*/
|
||||
- (void)connection:(CPURLConnection)aConnection didReceiveData:(CPString)aData
|
||||
{
|
||||
var session = aConnection.session;
|
||||
var session = aConnection.session,
|
||||
theDelegate = session.delegate;
|
||||
|
||||
// READ
|
||||
if (aConnection == _readConnection)
|
||||
{
|
||||
[self readFromData:[CPData dataWithRawString:aData] ofType:session.fileType error:nil];
|
||||
|
||||
objj_msgSend(session.delegate, session.didReadSelector, self, YES, session.contextInfo);
|
||||
theDelegate.isa.objj_msgSend3(theDelegate, session.didReadSelector, self, YES, session.contextInfo);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -569,7 +574,7 @@ var CPDocumentUntitledCount = 0;
|
||||
|
||||
_writeRequest = nil;
|
||||
|
||||
objj_msgSend(session.delegate, session.didSaveSelector, self, YES, session.contextInfo);
|
||||
theDelegate.isa.objj_msgSend3(theDelegate, session.didSaveSelector, self, YES, session.contextInfo);
|
||||
[self _sendDocumentSavedNotification:YES];
|
||||
}
|
||||
}
|
||||
@@ -580,10 +585,11 @@ var CPDocumentUntitledCount = 0;
|
||||
*/
|
||||
- (void)connection:(CPURLConnection)aConnection didFailWithError:(CPError)anError
|
||||
{
|
||||
var session = aConnection.session;
|
||||
var session = aConnection.session,
|
||||
theDelegate = session.delegate;
|
||||
|
||||
if (_readConnection == aConnection)
|
||||
objj_msgSend(session.delegate, session.didReadSelector, self, NO, session.contextInfo);
|
||||
theDelegate.isa.objj_msgSend3(theDelegate, session.didReadSelector, self, NO, session.contextInfo);
|
||||
|
||||
else
|
||||
{
|
||||
@@ -597,7 +603,7 @@ var CPDocumentUntitledCount = 0;
|
||||
|
||||
alert("There was an error saving the document.");
|
||||
|
||||
objj_msgSend(session.delegate, session.didSaveSelector, self, NO, session.contextInfo);
|
||||
theDelegate.isa.objj_msgSend3(theDelegate, session.didSaveSelector, self, NO, session.contextInfo);
|
||||
[self _sendDocumentSavedNotification:NO];
|
||||
}
|
||||
}
|
||||
@@ -858,21 +864,24 @@ var CPDocumentUntitledCount = 0;
|
||||
[self canCloseDocumentWithDelegate:self shouldCloseSelector:@selector(_document:shouldClose:context:) contextInfo:{delegate:delegate, selector:selector, context:info}];
|
||||
|
||||
else if ([delegate respondsToSelector:selector])
|
||||
objj_msgSend(delegate, selector, self, YES, info);
|
||||
delegate.isa.objj_msgSend3(delegate, selector, self, YES, info);
|
||||
}
|
||||
|
||||
- (void)_document:(CPDocument)aDocument shouldClose:(BOOL)shouldClose context:(Object)context
|
||||
{
|
||||
var theDelegate = context.delegate;
|
||||
|
||||
if (aDocument === self && shouldClose)
|
||||
[self close];
|
||||
|
||||
objj_msgSend(context.delegate, context.selector, aDocument, shouldClose, context.context);
|
||||
if (theDelegate != null)
|
||||
theDelegate.isa.objj_msgSend3(theDelegate, context.selector, aDocument, shouldClose, context.context);
|
||||
}
|
||||
|
||||
- (void)canCloseDocumentWithDelegate:(id)aDelegate shouldCloseSelector:(SEL)aSelector contextInfo:(Object)context
|
||||
{
|
||||
if (![self isDocumentEdited])
|
||||
return [aDelegate respondsToSelector:aSelector] && objj_msgSend(aDelegate, aSelector, self, YES, context);
|
||||
return [aDelegate respondsToSelector:aSelector] && aDelegate.isa.objj_msgSend3(aDelegate, aSelector, self, YES, context);
|
||||
|
||||
_canCloseAlert = [[CPAlert alloc] init];
|
||||
|
||||
@@ -901,8 +910,8 @@ var CPDocumentUntitledCount = 0;
|
||||
|
||||
if (returnCode === 0)
|
||||
[self saveDocumentWithDelegate:delegate didSaveSelector:selector contextInfo:context];
|
||||
else
|
||||
objj_msgSend(delegate, selector, self, returnCode === 2, context);
|
||||
else if (delegate != null)
|
||||
delegate.isa.objj_msgSend3(delegate, selector, self, returnCode === 2, context);
|
||||
|
||||
_canCloseAlert = nil;
|
||||
}
|
||||
|
||||
@@ -402,8 +402,10 @@ var CPSharedDocumentController = nil;
|
||||
}
|
||||
}
|
||||
|
||||
if ([context.delegate respondsToSelector:context.selector])
|
||||
objj_msgSend(context.delegate, context.selector, self, [[self documents] count] === 0, context.context);
|
||||
var theDelegate = context.delegate;
|
||||
|
||||
if ([theDelegate respondsToSelector:context.selector])
|
||||
theDelegate.isa.objj_msgSend3(theDelegate, context.selector, self, [[self documents] count] === 0, context.context);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
@import "CPCompatibility.j"
|
||||
@import "CGGeometry.j"
|
||||
@import "CPText.j"
|
||||
@import "CPTrackingArea.j"
|
||||
|
||||
@class CPTextField
|
||||
@class CPWindow
|
||||
@@ -82,6 +83,8 @@ var _CPEventPeriodicEventPeriod = 0,
|
||||
BOOL _suppressCappuccinoCut;
|
||||
BOOL _suppressCappuccinoPaste;
|
||||
#endif
|
||||
|
||||
CPTrackingArea _trackingArea;
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -141,6 +144,27 @@ var _CPEventPeriodicEventPeriod = 0,
|
||||
timestamp:aTimestamp windowNumber:aWindowNumber context:aGraphicsContext eventNumber:anEventNumber clickCount:aClickCount pressure:aPressure];
|
||||
}
|
||||
|
||||
/*!
|
||||
Creates a new mouse tracking event.
|
||||
|
||||
@param anEventType the event type
|
||||
@param aPoint the location of the cursor in the window specified by \c aWindowNumber
|
||||
@param modifierFlags a bitwise combination of the modifiers specified in the CPEvent globals
|
||||
@param aTimestamp the time the event occurred
|
||||
@param aWindowNumber the number of the CPWindow where the event occurred
|
||||
@param aGraphicsContext the graphics context where the event occurred
|
||||
@param anEventNumber a number for this event
|
||||
@param aTrackingArea the tracking area that triggered the event
|
||||
@throws CPInternalInconsistencyException if an invalid event type is provided
|
||||
@return the new mouse event
|
||||
*/
|
||||
+ (id)enterExitEventWithType:(CPEventType)anEventType location:(CGPoint)aPoint modifierFlags:(unsigned)modifierFlags
|
||||
timestamp:(CPTimeInterval)aTimestamp windowNumber:(int)aWindowNumber context:(CPGraphicsContext)aGraphicsContext
|
||||
eventNumber:(int)anEventNumber trackingArea:(CPTrackingArea)aTrackingArea
|
||||
{
|
||||
return [[self alloc] _initEnterExitEventWithType:anEventType location:aPoint modifierFlags:modifierFlags timestamp:aTimestamp windowNumber:aWindowNumber context:aGraphicsContext eventNumber:anEventNumber trackingArea:aTrackingArea];
|
||||
}
|
||||
|
||||
/*!
|
||||
Creates a new custom event.
|
||||
|
||||
@@ -201,6 +225,28 @@ var _CPEventPeriodicEventPeriod = 0,
|
||||
return self;
|
||||
}
|
||||
|
||||
/* @ignore */
|
||||
- (id)_initEnterExitEventWithType:(CPEventType)anEventType location:(CGPoint)aPoint modifierFlags:(unsigned)modifierFlags
|
||||
timestamp:(CPTimeInterval)aTimestamp windowNumber:(int)aWindowNumber context:(CPGraphicsContext)aGraphicsContext
|
||||
eventNumber:(int)anEventNumber trackingArea:(CPTrackingArea)aTrackingArea
|
||||
{
|
||||
if ((anEventType != CPMouseEntered) && (anEventType != CPMouseExited) && (anEventType != CPCursorUpdate))
|
||||
[CPException raise:CPInternalInconsistencyException reason:"Invalid event type"];
|
||||
|
||||
if (self = [self _initWithType:anEventType])
|
||||
{
|
||||
_location = CGPointCreateCopy(aPoint);
|
||||
_modifierFlags = modifierFlags;
|
||||
_timestamp = aTimestamp;
|
||||
_context = aGraphicsContext;
|
||||
_eventNumber = anEventNumber;
|
||||
_trackingArea = aTrackingArea;
|
||||
_window = [CPApp windowWithWindowNumber:aWindowNumber];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
/* @ignore */
|
||||
- (id)_initKeyEventWithType:(CPEventType)anEventType location:(CGPoint)aPoint modifierFlags:(unsigned int)modifierFlags
|
||||
timestamp:(CPTimeInterval)aTimestamp windowNumber:(int)aWindowNumber context:(CPGraphicsContext)aGraphicsContext
|
||||
@@ -583,6 +629,14 @@ var _CPEventPeriodicEventPeriod = 0,
|
||||
}
|
||||
}
|
||||
|
||||
- (CPTrackingArea)trackingArea
|
||||
{
|
||||
if ((_type !== CPMouseEntered) && (_type !== CPMouseExited) && (_type !== CPCursorUpdate))
|
||||
[CPException raise:CPInternalInconsistencyException format:@"You can't call trackingArea for events of type %#x", _type]
|
||||
|
||||
return _trackingArea;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
function _CPEventFirePeriodEvent()
|
||||
|
||||
+6
-5
@@ -70,11 +70,12 @@ CPGradientDrawsAfterEndingLocation = kCGGradientDrawsAfterEndLocation;
|
||||
{
|
||||
if (self = [super init])
|
||||
{
|
||||
var cgColors = [],
|
||||
count = [someColors count],
|
||||
colorSpace = [aColorSpace CGColorSpace] || CGColorSpaceCreateDeviceRGB;
|
||||
for (var i = 0; i < count; i++)
|
||||
cgColors.push(CGColorCreate(colorSpace, [someColors[i] components]));
|
||||
var colorSpace = [aColorSpace CGColorSpace] || CGColorSpaceCreateDeviceRGB,
|
||||
cgColors = [someColors arrayByApplyingBlock:function(color)
|
||||
{
|
||||
return CGColorCreate(colorSpace, [color components])
|
||||
}];
|
||||
|
||||
_gradient = CGGradientCreateWithColors(colorSpace, cgColors, someLocations);
|
||||
}
|
||||
|
||||
|
||||
+4
-4
@@ -43,10 +43,10 @@ function CPDrawTiledRects(
|
||||
if (sides.length != grays.length)
|
||||
[CPException raise:CPInvalidArgumentException reason:@"sides (length: " + sides.length + ") and grays (length: " + grays.length + ") must have the same length."];
|
||||
|
||||
var colors = [];
|
||||
|
||||
for (var i = 0; i < grays.length; ++i)
|
||||
colors.push([CPColor colorWithCalibratedWhite:grays[i] alpha:1.0]);
|
||||
var colors = [grays arrayByApplyingBlock:function(gray)
|
||||
{
|
||||
return [CPColor colorWithCalibratedWhite:gray alpha:1.0];
|
||||
}];
|
||||
|
||||
return CPDrawColorTiledRects(boundsRect, clipRect, sides, colors);
|
||||
}
|
||||
|
||||
+1
-1
@@ -275,7 +275,7 @@ function CPAppKitImage(aFilename, aSize)
|
||||
*/
|
||||
- (CGSize)size
|
||||
{
|
||||
return _size;
|
||||
return CGSizeMakeCopy(_size);
|
||||
}
|
||||
|
||||
+ (id)imageNamed:(CPString)aName
|
||||
|
||||
@@ -27,6 +27,9 @@
|
||||
@import "CPController.j"
|
||||
@import "CPKeyValueBinding.j"
|
||||
|
||||
@class _CPManagedProxy
|
||||
@class CPPredicate;
|
||||
|
||||
/*!
|
||||
@class
|
||||
|
||||
@@ -47,6 +50,9 @@
|
||||
|
||||
BOOL _isEditable;
|
||||
BOOL _automaticallyPreparesContent;
|
||||
BOOL _usesLazyFetching @accessors(getter=usesLazyFetching, setter=setUsesLazyFetching:);
|
||||
BOOL _isUsingManagedProxy;
|
||||
_CPManagedProxy _managedProxy;
|
||||
|
||||
CPCountedSet _observedKeys;
|
||||
}
|
||||
@@ -179,6 +185,46 @@
|
||||
return _automaticallyPreparesContent;
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the entity name the controller handles.
|
||||
|
||||
@param CPString newEntityName - The new entity name.
|
||||
*/
|
||||
- (void)setEntityName:(CPString)newEntityName
|
||||
{
|
||||
[_managedProxy setEntityName:newEntityName];
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the entity name.
|
||||
|
||||
@return CPString - The name of the entity.
|
||||
*/
|
||||
- (CPString)entityName
|
||||
{
|
||||
return [_managedProxy entityName];
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the predicate used to fetch content.
|
||||
|
||||
@param CPPredicate newPredicate - The fetch predicate.
|
||||
*/
|
||||
- (void)setFetchPredicate:(CPPredicate)newPredicate
|
||||
{
|
||||
[_managedProxy setFetchPredicate:newPredicate];
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the fetch predicate.
|
||||
|
||||
@return CPPredicate - The predicate used to fetch content.
|
||||
*/
|
||||
- (CPPredicate)fetchPredicate
|
||||
{
|
||||
return [_managedProxy fetchPredicate];
|
||||
}
|
||||
|
||||
/*!
|
||||
Overridden by a subclass that require control over the creation of new objects.
|
||||
*/
|
||||
@@ -189,6 +235,7 @@
|
||||
|
||||
/*!
|
||||
Sets the object class when creating new objects.
|
||||
|
||||
@param Class - the class of new objects that will be created.
|
||||
*/
|
||||
- (void)setObjectClass:(Class)aClass
|
||||
@@ -363,7 +410,10 @@
|
||||
var CPObjectControllerContentKey = @"CPObjectControllerContentKey",
|
||||
CPObjectControllerObjectClassNameKey = @"CPObjectControllerObjectClassNameKey",
|
||||
CPObjectControllerIsEditableKey = @"CPObjectControllerIsEditableKey",
|
||||
CPObjectControllerAutomaticallyPreparesContentKey = @"CPObjectControllerAutomaticallyPreparesContentKey";
|
||||
CPObjectControllerAutomaticallyPreparesContentKey = @"CPObjectControllerAutomaticallyPreparesContentKey",
|
||||
CPObjectControllerUsesLazyFetchingKey = @"CPObjectControllerUsesLazyFetchingKey",
|
||||
CPObjectControllerIsUsingManagedProxyKey = @"CPObjectControllerIsUsingManagedProxyKey",
|
||||
CPObjectControllerManagedProxyKey = @"CPObjectControllerManagedProxyKey";
|
||||
|
||||
@implementation CPObjectController (CPCoding)
|
||||
|
||||
@@ -374,12 +424,18 @@ var CPObjectControllerContentKey = @"CPObjectControllerCo
|
||||
if (self)
|
||||
{
|
||||
var objectClassName = [aCoder decodeObjectForKey:CPObjectControllerObjectClassNameKey],
|
||||
objectClass = CPClassFromString(objectClassName);
|
||||
objectClass = CPClassFromString(objectClassName),
|
||||
content = [aCoder decodeObjectForKey:CPObjectControllerContentKey];
|
||||
|
||||
[self setObjectClass:objectClass || [CPMutableDictionary class]];
|
||||
[self setEditable:[aCoder decodeBoolForKey:CPObjectControllerIsEditableKey]];
|
||||
[self setAutomaticallyPreparesContent:[aCoder decodeBoolForKey:CPObjectControllerAutomaticallyPreparesContentKey]];
|
||||
[self setContent:[aCoder decodeObjectForKey:CPObjectControllerContentKey]];
|
||||
[self setUsesLazyFetching:[aCoder decodeBoolForKey:CPObjectControllerUsesLazyFetchingKey]];
|
||||
_isUsingManagedProxy = [aCoder decodeBoolForKey:CPObjectControllerIsUsingManagedProxyKey];
|
||||
_managedProxy = [aCoder decodeObjectForKey:CPObjectControllerManagedProxyKey];
|
||||
|
||||
if (content != nil)
|
||||
[self setContent:content];
|
||||
|
||||
_observedKeys = [[CPCountedSet alloc] init];
|
||||
}
|
||||
@@ -398,6 +454,11 @@ var CPObjectControllerContentKey = @"CPObjectControllerCo
|
||||
|
||||
[aCoder encodeBool:[self isEditable] forKey:CPObjectControllerIsEditableKey];
|
||||
[aCoder encodeBool:[self automaticallyPreparesContent] forKey:CPObjectControllerAutomaticallyPreparesContentKey];
|
||||
[aCoder encodeBool:[self usesLazyFetching] forKey:CPObjectControllerUsesLazyFetchingKey];
|
||||
[aCoder encodeBool:_isUsingManagedProxy forKey:CPObjectControllerIsUsingManagedProxyKey];
|
||||
|
||||
if (_managedProxy)
|
||||
[aCoder encodeObject:_managedProxy forKey:CPObjectControllerManagedProxyKey];
|
||||
}
|
||||
|
||||
- (void)awakeFromCib
|
||||
@@ -825,3 +886,38 @@ var CPObjectControllerContentKey = @"CPObjectControllerCo
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@implementation _CPManagedProxy : CPObject
|
||||
{
|
||||
CPString _entityName @accessors(getter=entityName, setter=setEntityName:);
|
||||
CPPredicate _fetchPredicate @accessors(getter=fetchPredicate, setter=setFetchPredicate:);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
var CPManagedProxyEntityNameKey = @"CPManagedProxyEntityNameKey",
|
||||
CPManagedProxyFetchPredicateKey = @"CPManagedProxyFetchPredicateKey";
|
||||
|
||||
@implementation _CPManagedProxy (CPCoding)
|
||||
|
||||
- (id)initWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
if (self)
|
||||
{
|
||||
[self setEntityName:[aCoder decodeObjectForKey:CPManagedProxyEntityNameKey]];
|
||||
[self setFetchPredicate:[aCoder decodeObjectForKey:CPManagedProxyFetchPredicateKey]];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)encodeWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
[aCoder encodeObject:[self entityName] forKey:CPManagedProxyEntityNameKey];
|
||||
[aCoder encodeObject:[self fetchPredicate] forKey:CPManagedProxyFetchPredicateKey];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -1417,7 +1417,7 @@ var CPOutlineViewCoalesceSelectionNotificationStateOff = 0,
|
||||
if (_dropItem)
|
||||
{
|
||||
[_dropOperationFeedbackView blink];
|
||||
[CPTimer scheduledTimerWithTimeInterval:.3 callback:objj_msgSend(self, "expandItem:", _dropItem) repeats:NO];
|
||||
[CPTimer scheduledTimerWithTimeInterval:.3 callback:[self expandItem:_dropItem] repeats:NO];
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
+3
-2
@@ -22,10 +22,13 @@
|
||||
|
||||
@import "CPWindow.j"
|
||||
|
||||
@global CPApp
|
||||
|
||||
CPOKButton = 1;
|
||||
CPCancelButton = 0;
|
||||
|
||||
CPDocModalWindowMask = 1 << 6;
|
||||
|
||||
/*!
|
||||
@ingroup appkit
|
||||
@class CPPanel
|
||||
@@ -51,8 +54,6 @@ CPCancelButton = 0;
|
||||
@global
|
||||
@class CPWindow
|
||||
*/
|
||||
CPDocModalWindowMask = 1 << 6;
|
||||
|
||||
@implementation CPPanel : CPWindow
|
||||
{
|
||||
BOOL _becomesKeyOnlyIfNeeded;
|
||||
|
||||
@@ -361,15 +361,10 @@ CPPopUpButtonStatePullsDown = CPThemeState("pulls-down");
|
||||
*/
|
||||
- (CPArray)itemTitles
|
||||
{
|
||||
var titles = [],
|
||||
items = [self itemArray],
|
||||
index = 0,
|
||||
count = [items count];
|
||||
|
||||
for (; index < count; ++index)
|
||||
titles.push([items[index] title]);
|
||||
|
||||
return titles;
|
||||
return [[self itemArray] arrayByApplyingBlock:function(item)
|
||||
{
|
||||
return [item title];
|
||||
}];
|
||||
}
|
||||
|
||||
/*!
|
||||
|
||||
@@ -86,6 +86,9 @@ var CPProgressIndicatorSpinningStyleColors = [];
|
||||
@"spinning-mini-gif": [CPNull null],
|
||||
@"spinning-small-gif": [CPNull null],
|
||||
@"spinning-regular-gif": [CPNull null],
|
||||
@"circular-border-color": [CPNull null],
|
||||
@"circular-border-size": 1,
|
||||
@"circular-color": [CPNull null]
|
||||
};
|
||||
}
|
||||
|
||||
@@ -368,6 +371,7 @@ var CPProgressIndicatorSpinningStyleColors = [];
|
||||
- (void)drawBar
|
||||
{
|
||||
[self setNeedsLayout];
|
||||
[self setNeedsDisplay:YES];
|
||||
}
|
||||
|
||||
- (CPView)createEphemeralSubviewNamed:(CPString)aName
|
||||
@@ -413,6 +417,9 @@ var CPProgressIndicatorSpinningStyleColors = [];
|
||||
{
|
||||
if (_style == CPProgressIndicatorSpinningStyle)
|
||||
{
|
||||
if (!_indeterminate)
|
||||
return;
|
||||
|
||||
// This will cause the bar view to go away due to having a nil rect when _style == CPProgressIndicatorSpinningStyle.
|
||||
[self layoutEphemeralSubviewNamed:"bar-view"
|
||||
positioned:CPWindowBelow
|
||||
@@ -438,6 +445,54 @@ var CPProgressIndicatorSpinningStyleColors = [];
|
||||
[self setBackgroundColor:nil];
|
||||
}
|
||||
|
||||
- (void)drawRect:(CGRect)aRect
|
||||
{
|
||||
if (_style == CPProgressIndicatorSpinningStyle && !_indeterminate)
|
||||
{
|
||||
var context = [[CPGraphicsContext currentContext] graphicsPort],
|
||||
rect = CGRectMakeCopy(aRect),
|
||||
borderSize = [self currentValueForThemeAttribute:@"circular-border-size"];
|
||||
|
||||
rect.origin.x += borderSize;
|
||||
rect.origin.y += borderSize;
|
||||
rect.size.width = rect.size.width - borderSize * 2;
|
||||
rect.size.height = rect.size.height - borderSize * 2;
|
||||
|
||||
if ([self doubleValue] > [self minValue] && [self doubleValue] < [self maxValue])
|
||||
{
|
||||
var midX = CGRectGetMidX(rect),
|
||||
midY = CGRectGetMidY(rect),
|
||||
endAngle = Math.PI * 2 * (([self doubleValue] - [self minValue]) / ([self maxValue] - [self minValue])) - Math.PI / 2,
|
||||
radius = MIN(rect.size.width / 2, rect.size.height / 2)
|
||||
|
||||
CGContextBeginPath(context);
|
||||
CGContextSetLineWidth(context, borderSize);
|
||||
CGContextSetFillColor(context, [self currentValueForThemeAttribute:@"circular-color"])
|
||||
CGContextMoveToPoint(context, midX, midY);
|
||||
CGContextAddArc(context, midX, midY, radius, 3 * Math.PI / 2, endAngle, YES)
|
||||
CGContextAddLineToPoint(context, midX, midY);
|
||||
CGContextClosePath(context);
|
||||
CGContextFillPath(context);
|
||||
CGContextStrokePath(context);
|
||||
}
|
||||
else if ([self doubleValue] == [self maxValue])
|
||||
{
|
||||
CGContextBeginPath(context);
|
||||
CGContextSetFillColor(context, [self currentValueForThemeAttribute:@"circular-color"])
|
||||
CGContextAddEllipseInRect(context, rect);
|
||||
CGContextClosePath(context);
|
||||
CGContextFillPath(context);
|
||||
}
|
||||
|
||||
CGContextBeginPath(context);
|
||||
CGContextSetStrokeColor(context , [self currentValueForThemeAttribute:@"circular-border-color"]);
|
||||
CGContextSetLineWidth(context, borderSize);
|
||||
CGContextAddEllipseInRect(context, rect);
|
||||
CGContextClosePath(context);
|
||||
CGContextStrokePath(context);
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
|
||||
+1
-3
@@ -29,6 +29,7 @@
|
||||
|
||||
@global CPApp
|
||||
|
||||
CPRadioImageOffset = 4.0;
|
||||
|
||||
/*!
|
||||
@ingroup appkit
|
||||
@@ -67,9 +68,6 @@
|
||||
option.
|
||||
|
||||
*/
|
||||
|
||||
CPRadioImageOffset = 4.0;
|
||||
|
||||
@implementation CPRadio : CPButton
|
||||
{
|
||||
CPRadioGroup _radioGroup;
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
@import <Foundation/CPObjJRuntime.j>
|
||||
|
||||
@import "CPEvent.j"
|
||||
@import "CPCursor.j"
|
||||
|
||||
@class CPKeyBinding
|
||||
@class CPMenu
|
||||
@@ -200,6 +201,18 @@ CPDeleteForwardKeyCode = 46;
|
||||
[_nextResponder performSelector:_cmd withObject:anEvent];
|
||||
}
|
||||
|
||||
/*!
|
||||
Notifies the receiver that the mouse entered the receiver's area and that it can adapt the cursor.
|
||||
@param anEvent contains information about the exit
|
||||
*/
|
||||
- (void)cursorUpdate:(CPEvent)anEvent
|
||||
{
|
||||
if (_nextResponder)
|
||||
[_nextResponder performSelector:_cmd withObject:anEvent];
|
||||
else
|
||||
[[CPCursor arrowCursor] set];
|
||||
}
|
||||
|
||||
/*!
|
||||
Notifies the receiver that the mouse scroll wheel has moved.
|
||||
@param anEvent information about the scroll
|
||||
|
||||
@@ -2121,7 +2121,7 @@ TODO: implement
|
||||
return;
|
||||
|
||||
var point = [self convertPoint:[event locationInWindow] fromView:nil],
|
||||
view = [_slices objectAtIndex:FLOOR(point.y / _sliceHeight)];
|
||||
view = [_slices objectAtIndex:FLOOR(MAX(0, point.y) / _sliceHeight)];
|
||||
|
||||
if ([self _dragShouldBeginFromMouseDown:view])
|
||||
[self _performDragForSlice:view withEvent:event];
|
||||
|
||||
@@ -86,15 +86,6 @@ var _isBrowserUsingOverlayScrollers = function()
|
||||
#endif
|
||||
};
|
||||
|
||||
/*!
|
||||
@ingroup appkit
|
||||
@class CPScrollView
|
||||
|
||||
Used to display views that are too large for the viewing area. the CPScrollView
|
||||
places scroll bars on the side of the view to allow the user to scroll and see the entire
|
||||
contents of the view.
|
||||
*/
|
||||
|
||||
var TIMER_INTERVAL = 0.2,
|
||||
CPScrollViewDelegate_scrollViewWillScroll_ = 1 << 0,
|
||||
CPScrollViewDelegate_scrollViewDidScroll_ = 1 << 1,
|
||||
@@ -104,7 +95,14 @@ var TIMER_INTERVAL = 0.2,
|
||||
var CPScrollerStyleGlobal = CPScrollerStyleOverlay,
|
||||
CPScrollerStyleGlobalChangeNotification = @"CPScrollerStyleGlobalChangeNotification";
|
||||
|
||||
/*!
|
||||
@ingroup appkit
|
||||
@class CPScrollView
|
||||
|
||||
Used to display views that are too large for the viewing area. the CPScrollView
|
||||
places scroll bars on the side of the view to allow the user to scroll and see the entire
|
||||
contents of the view.
|
||||
*/
|
||||
@implementation CPScrollView : CPView
|
||||
{
|
||||
CPClipView _contentView;
|
||||
@@ -1584,6 +1582,8 @@ var CPScrollViewContentViewKey = @"CPScrollViewContentView",
|
||||
*/
|
||||
- (void)awakeFromCib
|
||||
{
|
||||
[super awakeFromCib];
|
||||
|
||||
[self _updateScrollerStyle];
|
||||
[self _updateCornerAndHeaderView];
|
||||
}
|
||||
|
||||
+5
-5
@@ -50,11 +50,6 @@ CPNoScrollerParts = 0;
|
||||
CPOnlyScrollerArrows = 1;
|
||||
CPAllScrollerParts = 2;
|
||||
|
||||
/*!
|
||||
@ingroup appkit
|
||||
@class CPScroller
|
||||
*/
|
||||
|
||||
var PARTS_ARRANGEMENT = [CPScrollerKnobSlot, CPScrollerDecrementLine, CPScrollerIncrementLine, CPScrollerKnob],
|
||||
NAMES_FOR_PARTS = {},
|
||||
PARTS_FOR_NAMES = {};
|
||||
@@ -78,6 +73,11 @@ CPThemeStateScrollViewLegacy = CPThemeState("scroller-style-legacy");
|
||||
CPThemeStateScrollerKnobLight = CPThemeState("scroller-knob-light");
|
||||
CPThemeStateScrollerKnobDark = CPThemeState("scroller-knob-dark");
|
||||
|
||||
/*!
|
||||
@ingroup appkit
|
||||
@class CPScroller
|
||||
*/
|
||||
|
||||
@implementation CPScroller : CPControl
|
||||
{
|
||||
CPUsableScrollerParts _usableParts;
|
||||
|
||||
@@ -115,8 +115,7 @@ CPSegmentSwitchTrackingMomentary = 2;
|
||||
/*! @ignore */
|
||||
- (void)setSegments:(CPArray)segments
|
||||
{
|
||||
[_segments removeAllObjects];
|
||||
[_themeStates removeAllObjects];
|
||||
[self removeSegmentsAtIndexes:[CPIndexSet indexSetWithIndexesInRange:CPMakeRange(0, [self segmentCount])]];
|
||||
|
||||
[self insertSegments:segments atIndexes:[CPIndexSet indexSetWithIndexesInRange:CPMakeRange(0, [segments count])]];
|
||||
}
|
||||
@@ -135,6 +134,9 @@ CPSegmentSwitchTrackingMomentary = 2;
|
||||
|
||||
[_segments insertObjects:segments atIndexes:indices];
|
||||
[_themeStates insertObjects:newStates atIndexes:indices];
|
||||
|
||||
if (_selectedSegment >= [indices firstIndex])
|
||||
_selectedSegment += [indices count];
|
||||
}
|
||||
|
||||
/*! @ignore */
|
||||
@@ -143,6 +145,16 @@ CPSegmentSwitchTrackingMomentary = 2;
|
||||
if ([indices count] == 0)
|
||||
return;
|
||||
|
||||
[indices enumerateIndexesUsingBlock:function(idx, stop)
|
||||
{
|
||||
[[_segments objectAtIndex:idx] setSelected:NO];
|
||||
}];
|
||||
|
||||
if ([indices containsIndex:_selectedSegment])
|
||||
_selectedSegment = -1;
|
||||
else if ([indices lastIndex] < _selectedSegment)
|
||||
_selectedSegment -= [indices count];
|
||||
|
||||
[_segments removeObjectsAtIndexes:indices];
|
||||
[_themeStates removeObjectsAtIndexes:indices];
|
||||
}
|
||||
|
||||
+26
-20
@@ -26,6 +26,7 @@
|
||||
@import "CPImage.j"
|
||||
@import "CPView.j"
|
||||
@import "CPCursor.j"
|
||||
@import "CPTrackingArea.j"
|
||||
|
||||
@class CPUserDefaults
|
||||
@global CPApp
|
||||
@@ -562,26 +563,6 @@ var ShouldSuppressResizeNotifications = 1,
|
||||
//[[self window] setAcceptsMouseMovedEvents:YES];
|
||||
}
|
||||
|
||||
- (void)mouseEntered:(CPEvent)anEvent
|
||||
{
|
||||
// Tracking code handles cursor by itself.
|
||||
if (_currentDivider == CPNotFound)
|
||||
[self _updateResizeCursor:anEvent];
|
||||
}
|
||||
|
||||
- (void)mouseMoved:(CPEvent)anEvent
|
||||
{
|
||||
if (_currentDivider == CPNotFound)
|
||||
[self _updateResizeCursor:anEvent];
|
||||
}
|
||||
|
||||
- (void)mouseExited:(CPEvent)anEvent
|
||||
{
|
||||
if (_currentDivider == CPNotFound)
|
||||
// FIXME: we should use CPCursor push/pop (if previous currentCursor != arrow).
|
||||
[[CPCursor arrowCursor] set];
|
||||
}
|
||||
|
||||
- (void)_updateResizeCursor:(CPEvent)anEvent
|
||||
{
|
||||
var point = [self convertPoint:[anEvent locationInWindow] fromView:nil];
|
||||
@@ -1205,6 +1186,29 @@ The sum of the views and the sum of the dividers should be equal to the size of
|
||||
|
||||
@end
|
||||
|
||||
@implementation CPSplitView (CPTrackingArea)
|
||||
|
||||
- (void)updateTrackingAreas
|
||||
{
|
||||
[self removeAllTrackingAreas];
|
||||
|
||||
var options = CPTrackingCursorUpdate | CPTrackingActiveInKeyWindow;
|
||||
|
||||
for (var i = 0; i < _subviews.length - 1; i++)
|
||||
[self addTrackingArea:[[CPTrackingArea alloc] initWithRect:[self effectiveRectOfDividerAtIndex:i]
|
||||
options:options
|
||||
owner:self
|
||||
userInfo:nil]];
|
||||
}
|
||||
|
||||
- (void)cursorUpdate:(CPEvent)anEvent
|
||||
{
|
||||
if (_currentDivider === CPNotFound)
|
||||
[self _updateResizeCursor:anEvent];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@implementation CPSplitView (CPSplitViewDelegate)
|
||||
|
||||
@@ -1376,6 +1380,8 @@ The sum of the views and the sum of the dividers should be equal to the size of
|
||||
[_delegate splitViewDidResizeSubviews:[[CPNotification alloc] initWithName:CPSplitViewDidResizeSubviewsNotification object:self userInfo:userInfo]];
|
||||
|
||||
[[CPNotificationCenter defaultCenter] postNotificationName:CPSplitViewDidResizeSubviewsNotification object:self userInfo:userInfo];
|
||||
|
||||
[self updateTrackingAreas];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
+6
-6
@@ -188,12 +188,12 @@
|
||||
[_buttonUp setFrame:upFrame];
|
||||
[_buttonDown setFrame:downFrame];
|
||||
|
||||
[_buttonUp setValue:[self valueForThemeAttribute:@"bezel-color-up-button" inState:[controlSizeThemeState, CPThemeStateBordered]] forThemeAttribute:@"bezel-color" inState:CPThemeStateBordered];
|
||||
[_buttonUp setValue:[self valueForThemeAttribute:@"bezel-color-up-button" inState:[controlSizeThemeState, CPThemeStateBordered, CPThemeStateDisabled]] forThemeAttribute:@"bezel-color" inState:[CPThemeStateBordered, CPThemeStateDisabled]];
|
||||
[_buttonUp setValue:[self valueForThemeAttribute:@"bezel-color-up-button" inState:[controlSizeThemeState, CPThemeStateBordered, CPThemeStateHighlighted]] forThemeAttribute:@"bezel-color" inState:[CPThemeStateBordered, CPThemeStateHighlighted]];
|
||||
[_buttonDown setValue:[self valueForThemeAttribute:@"bezel-color-down-button" inState:[controlSizeThemeState, CPThemeStateBordered]] forThemeAttribute:@"bezel-color" inState:CPThemeStateBordered];
|
||||
[_buttonDown setValue:[self valueForThemeAttribute:@"bezel-color-down-button" inState:[controlSizeThemeState, CPThemeStateBordered, CPThemeStateDisabled]] forThemeAttribute:@"bezel-color" inState:[CPThemeStateBordered, CPThemeStateDisabled]];
|
||||
[_buttonDown setValue:[self valueForThemeAttribute:@"bezel-color-down-button" inState:[controlSizeThemeState, CPThemeStateBordered, CPThemeStateHighlighted]] forThemeAttribute:@"bezel-color" inState:[CPThemeStateBordered, CPThemeStateHighlighted]];
|
||||
[_buttonUp setValue:[self valueForThemeAttribute:@"bezel-color-up-button" inStates:[controlSizeThemeState, CPThemeStateBordered]] forThemeAttribute:@"bezel-color" inState:CPThemeStateBordered];
|
||||
[_buttonUp setValue:[self valueForThemeAttribute:@"bezel-color-up-button" inStates:[controlSizeThemeState, CPThemeStateBordered, CPThemeStateDisabled]] forThemeAttribute:@"bezel-color" inStates:[CPThemeStateBordered, CPThemeStateDisabled]];
|
||||
[_buttonUp setValue:[self valueForThemeAttribute:@"bezel-color-up-button" inStates:[controlSizeThemeState, CPThemeStateBordered, CPThemeStateHighlighted]] forThemeAttribute:@"bezel-color" inStates:[CPThemeStateBordered, CPThemeStateHighlighted]];
|
||||
[_buttonDown setValue:[self valueForThemeAttribute:@"bezel-color-down-button" inStates:[controlSizeThemeState, CPThemeStateBordered]] forThemeAttribute:@"bezel-color" inState:CPThemeStateBordered];
|
||||
[_buttonDown setValue:[self valueForThemeAttribute:@"bezel-color-down-button" inStates:[controlSizeThemeState, CPThemeStateBordered, CPThemeStateDisabled]] forThemeAttribute:@"bezel-color" inStates:[CPThemeStateBordered, CPThemeStateDisabled]];
|
||||
[_buttonDown setValue:[self valueForThemeAttribute:@"bezel-color-down-button" inStates:[controlSizeThemeState, CPThemeStateBordered, CPThemeStateHighlighted]] forThemeAttribute:@"bezel-color" inStates:[CPThemeStateBordered, CPThemeStateHighlighted]];
|
||||
}
|
||||
|
||||
- (void)_sizeToFit
|
||||
|
||||
+365
-85
@@ -34,6 +34,8 @@ CPNoTabsBezelBorder = 4; //Displays no tabs and has a bezeled border.
|
||||
CPNoTabsLineBorder = 5; //Has no tabs and displays a line border.
|
||||
CPNoTabsNoBorder = 6; //Displays no tabs and no border.
|
||||
|
||||
@class _CPTabViewBox
|
||||
|
||||
var CPTabViewDidSelectTabViewItemSelector = 1 << 1,
|
||||
CPTabViewShouldSelectTabViewItemSelector = 1 << 2,
|
||||
CPTabViewWillSelectTabViewItemSelector = 1 << 3,
|
||||
@@ -64,9 +66,10 @@ var CPTabViewDidSelectTabViewItemSelector = 1 << 1,
|
||||
CPArray _items;
|
||||
|
||||
CPSegmentedControl _tabs;
|
||||
CPBox _box;
|
||||
_CPTabViewBox _box;
|
||||
CPView _placeHolderView;
|
||||
|
||||
CPNumber _selectedIndex;
|
||||
CPTabViewItem _selectedTabViewItem;
|
||||
|
||||
CPTabViewType _type;
|
||||
CPFont _font;
|
||||
@@ -79,9 +82,8 @@ var CPTabViewDidSelectTabViewItemSelector = 1 << 1,
|
||||
{
|
||||
if (self = [super initWithFrame:aFrame])
|
||||
{
|
||||
_items = [CPArray array];
|
||||
|
||||
[self _init];
|
||||
_selectedTabViewItem = nil;
|
||||
[self setTabViewType:CPTopTabsBezelBorder];
|
||||
}
|
||||
|
||||
@@ -90,19 +92,26 @@ var CPTabViewDidSelectTabViewItemSelector = 1 << 1,
|
||||
|
||||
- (void)_init
|
||||
{
|
||||
_selectedIndex = CPNotFound;
|
||||
|
||||
_tabs = [[CPSegmentedControl alloc] initWithFrame:CGRectMake(0, 0, 0, 0)];
|
||||
_tabs = [[CPSegmentedControl alloc] initWithFrame:CGRectMakeZero()];
|
||||
[_tabs setHitTests:NO];
|
||||
[_tabs setSegments:[CPArray array]];
|
||||
|
||||
var height = [_tabs valueForThemeAttribute:@"min-size"].height;
|
||||
[_tabs setFrameSize:CGSizeMake(0, height)];
|
||||
|
||||
_box = [[CPBox alloc] initWithFrame:[self bounds]];
|
||||
_box = [[_CPTabViewBox alloc] initWithFrame:[self bounds]];
|
||||
[_box setTabView:self];
|
||||
[self setBackgroundColor:[CPColor colorWithCalibratedWhite:0.95 alpha:1.0]];
|
||||
|
||||
[self addSubview:_box];
|
||||
[self addSubview:_tabs];
|
||||
|
||||
_placeHolderView = nil;
|
||||
}
|
||||
|
||||
- (CPArray)items
|
||||
{
|
||||
return [_tabs segments];
|
||||
}
|
||||
|
||||
// Adding and Removing Tabs
|
||||
@@ -112,7 +121,7 @@ var CPTabViewDidSelectTabViewItemSelector = 1 << 1,
|
||||
*/
|
||||
- (void)addTabViewItem:(CPTabViewItem)aTabViewItem
|
||||
{
|
||||
[self insertTabViewItem:aTabViewItem atIndex:[_items count]];
|
||||
[self insertTabViewItem:aTabViewItem atIndex:[self numberOfTabViewItems]];
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -122,15 +131,18 @@ var CPTabViewDidSelectTabViewItemSelector = 1 << 1,
|
||||
*/
|
||||
- (void)insertTabViewItem:(CPTabViewItem)aTabViewItem atIndex:(CPUInteger)anIndex
|
||||
{
|
||||
[_items insertObject:aTabViewItem atIndex:anIndex];
|
||||
[self _insertTabViewItems:[aTabViewItem] atIndexes:[CPIndexSet indexSetWithIndex:anIndex]];
|
||||
}
|
||||
|
||||
[self _updateItems];
|
||||
[self _repositionTabs];
|
||||
- (void)_insertTabViewItems:(CPArray)tabViewItems atIndexes:(CPIndexSet)indexes
|
||||
{
|
||||
[_tabs insertSegments:tabViewItems atIndexes:indexes];
|
||||
[tabViewItems makeObjectsPerformSelector:@selector(_setTabView:) withObject:self];
|
||||
|
||||
[aTabViewItem _setTabView:self];
|
||||
[self tileWithChangedItem:[tabViewItems firstObject]];
|
||||
[self _reverseSetContent];
|
||||
|
||||
if (_delegateSelectors & CPTabViewDidChangeNumberOfTabViewItemsSelector)
|
||||
[_delegate tabViewDidChangeNumberOfTabViewItems:self];
|
||||
[self _sendDelegateTabViewDidChangeNumberOfTabViewItems];
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -139,23 +151,39 @@ var CPTabViewDidSelectTabViewItemSelector = 1 << 1,
|
||||
*/
|
||||
- (void)removeTabViewItem:(CPTabViewItem)aTabViewItem
|
||||
{
|
||||
var count = [_items count];
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
if ([_items objectAtIndex:i] === aTabViewItem)
|
||||
{
|
||||
[_items removeObjectAtIndex:i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
var idx = [[self items] indexOfObjectIdenticalTo:aTabViewItem];
|
||||
|
||||
[self _updateItems];
|
||||
[self _repositionTabs];
|
||||
if (idx == CPNotFound)
|
||||
return;
|
||||
|
||||
[_tabs removeSegmentsAtIndexes:[CPIndexSet indexSetWithIndex:idx]];
|
||||
[aTabViewItem _setTabView:nil];
|
||||
|
||||
if (_delegateSelectors & CPTabViewDidChangeNumberOfTabViewItemsSelector)
|
||||
[_delegate tabViewDidChangeNumberOfTabViewItems:self];
|
||||
[self tileWithChangedItem:nil];
|
||||
[self _didRemoveTabViewItem:aTabViewItem atIndex:idx];
|
||||
[self _reverseSetContent];
|
||||
|
||||
[self _sendDelegateTabViewDidChangeNumberOfTabViewItems];
|
||||
}
|
||||
|
||||
- (void)_didRemoveTabViewItem:(CPTabViewItem)aTabViewItem atIndex:(CPInteger)idx
|
||||
{
|
||||
// If the selection is managed by bindings, let the binder do that.
|
||||
if ([self binderForBinding:CPSelectionIndexesBinding] || [self binderForBinding:CPSelectedIndexBinding])
|
||||
return;
|
||||
|
||||
if (_selectedTabViewItem == aTabViewItem)
|
||||
{
|
||||
var didSelect = NO;
|
||||
|
||||
if (idx > 0)
|
||||
didSelect = [self selectTabViewItemAtIndex:idx - 1];
|
||||
else if ([self numberOfTabViewItems] > 0)
|
||||
didSelect = [self selectTabViewItemAtIndex:0];
|
||||
|
||||
if (didSelect == NO)
|
||||
_selectedTabViewItem == nil;
|
||||
}
|
||||
}
|
||||
|
||||
// Accessing Tabs
|
||||
@@ -166,7 +194,7 @@ var CPTabViewDidSelectTabViewItemSelector = 1 << 1,
|
||||
*/
|
||||
- (int)indexOfTabViewItem:(CPTabViewItem)aTabViewItem
|
||||
{
|
||||
return [_items indexOfObjectIdenticalTo:aTabViewItem];
|
||||
return [[self items] indexOfObjectIdenticalTo:aTabViewItem];
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -176,11 +204,10 @@ var CPTabViewDidSelectTabViewItemSelector = 1 << 1,
|
||||
*/
|
||||
- (int)indexOfTabViewItemWithIdentifier:(CPString)anIdentifier
|
||||
{
|
||||
for (var index = [_items count]; index >= 0; index--)
|
||||
if ([[_items[index] identifier] isEqual:anIdentifier])
|
||||
return index;
|
||||
|
||||
return CPNotFound;
|
||||
return [[self items] indexOfObjectPassingTest:function(item, idx, stop)
|
||||
{
|
||||
return [[item identifier] isEqual:anIdentifier];
|
||||
}];
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -189,7 +216,7 @@ var CPTabViewDidSelectTabViewItemSelector = 1 << 1,
|
||||
*/
|
||||
- (unsigned)numberOfTabViewItems
|
||||
{
|
||||
return [_items count];
|
||||
return [[self items] count];
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -198,7 +225,7 @@ var CPTabViewDidSelectTabViewItemSelector = 1 << 1,
|
||||
*/
|
||||
- (CPTabViewItem)tabViewItemAtIndex:(CPUInteger)anIndex
|
||||
{
|
||||
return [_items objectAtIndex:anIndex];
|
||||
return [[self items] objectAtIndex:anIndex];
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -207,7 +234,7 @@ var CPTabViewDidSelectTabViewItemSelector = 1 << 1,
|
||||
*/
|
||||
- (CPArray)tabViewItems
|
||||
{
|
||||
return [_items copy]; // Copy?
|
||||
return [[self items] copy]; // Copy?
|
||||
}
|
||||
|
||||
// Selecting a Tab
|
||||
@@ -217,7 +244,7 @@ var CPTabViewDidSelectTabViewItemSelector = 1 << 1,
|
||||
*/
|
||||
- (void)selectFirstTabViewItem:(id)aSender
|
||||
{
|
||||
if ([_items count] === 0)
|
||||
if ([self numberOfTabViewItems] === 0)
|
||||
return; // throw?
|
||||
|
||||
[self selectTabViewItemAtIndex:0];
|
||||
@@ -229,10 +256,10 @@ var CPTabViewDidSelectTabViewItemSelector = 1 << 1,
|
||||
*/
|
||||
- (void)selectLastTabViewItem:(id)aSender
|
||||
{
|
||||
if ([_items count] === 0)
|
||||
if ([self numberOfTabViewItems] === 0)
|
||||
return; // throw?
|
||||
|
||||
[self selectTabViewItemAtIndex:[_items count] - 1];
|
||||
[self selectTabViewItemAtIndex:[self numberOfTabViewItems] - 1];
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -241,12 +268,12 @@ var CPTabViewDidSelectTabViewItemSelector = 1 << 1,
|
||||
*/
|
||||
- (void)selectNextTabViewItem:(id)aSender
|
||||
{
|
||||
if (_selectedIndex === CPNotFound)
|
||||
if (_selectedTabViewItem === nil)
|
||||
return;
|
||||
|
||||
var nextIndex = _selectedIndex + 1;
|
||||
var nextIndex = [self indexOfTabViewItem:_selectedTabViewItem] + 1;
|
||||
|
||||
if (nextIndex === [_items count])
|
||||
if (nextIndex === [self numberOfTabViewItems])
|
||||
// does nothing. According to spec at (http://developer.apple.com/mac/library/DOCUMENTATION/Cocoa/Reference/ApplicationKit/Classes/NSTabView_Class/Reference/Reference.html#//apple_ref/occ/instm/NSTabView/selectNextTabViewItem:)
|
||||
return;
|
||||
|
||||
@@ -259,10 +286,10 @@ var CPTabViewDidSelectTabViewItemSelector = 1 << 1,
|
||||
*/
|
||||
- (void)selectPreviousTabViewItem:(id)aSender
|
||||
{
|
||||
if (_selectedIndex === CPNotFound)
|
||||
if (_selectedTabViewItem === nil)
|
||||
return;
|
||||
|
||||
var previousIndex = _selectedIndex - 1;
|
||||
var previousIndex = [self indexOfTabViewItem:_selectedTabViewItem] - 1;
|
||||
|
||||
if (previousIndex < 0)
|
||||
return; // does nothing. See above.
|
||||
@@ -285,22 +312,32 @@ var CPTabViewDidSelectTabViewItemSelector = 1 << 1,
|
||||
*/
|
||||
- (BOOL)selectTabViewItemAtIndex:(CPUInteger)anIndex
|
||||
{
|
||||
if (anIndex === _selectedIndex)
|
||||
return;
|
||||
|
||||
var aTabViewItem = [self tabViewItemAtIndex:anIndex];
|
||||
|
||||
if ((_delegateSelectors & CPTabViewShouldSelectTabViewItemSelector) && ![_delegate tabView:self shouldSelectTabViewItem:aTabViewItem])
|
||||
if (![self _selectTabViewItemAtIndex:anIndex])
|
||||
return NO;
|
||||
|
||||
if (_delegateSelectors & CPTabViewWillSelectTabViewItemSelector)
|
||||
[_delegate tabView:self willSelectTabViewItem:aTabViewItem];
|
||||
[self _reverseSetSelectedIndex];
|
||||
|
||||
[_tabs selectSegmentWithTag:anIndex];
|
||||
[self _setSelectedIndex:anIndex];
|
||||
return YES;
|
||||
}
|
||||
|
||||
if (_delegateSelectors & CPTabViewDidSelectTabViewItemSelector)
|
||||
[_delegate tabView:self didSelectTabViewItem:aTabViewItem];
|
||||
// Like selectTabViewItemAtIndex: but without bindings interaction
|
||||
- (BOOL)_selectTabViewItemAtIndex:(CPUInteger)anIndex
|
||||
{
|
||||
var aTabViewItem = [self tabViewItemAtIndex:anIndex];
|
||||
|
||||
if (aTabViewItem == _selectedTabViewItem)
|
||||
return NO;
|
||||
|
||||
if (![self _sendDelegateShouldSelectTabViewItem:aTabViewItem])
|
||||
return NO;
|
||||
|
||||
[self _sendDelegateWillSelectTabViewItem:aTabViewItem];
|
||||
|
||||
[_tabs setSelectedSegment:anIndex];
|
||||
_selectedTabViewItem = aTabViewItem;
|
||||
[self _displayItemView:[aTabViewItem view]];
|
||||
|
||||
[self _sendDelegateDidSelectTabViewItem:aTabViewItem];
|
||||
|
||||
return YES;
|
||||
}
|
||||
@@ -311,10 +348,7 @@ var CPTabViewDidSelectTabViewItemSelector = 1 << 1,
|
||||
*/
|
||||
- (CPTabViewItem)selectedTabViewItem
|
||||
{
|
||||
if (_selectedIndex != CPNotFound)
|
||||
return [_items objectAtIndex:_selectedIndex];
|
||||
|
||||
return nil;
|
||||
return _selectedTabViewItem;
|
||||
}
|
||||
|
||||
// Modifying the font
|
||||
@@ -375,6 +409,14 @@ var CPTabViewDidSelectTabViewItemSelector = 1 << 1,
|
||||
[self setNeedsLayout];
|
||||
}
|
||||
|
||||
- (void)tileWithChangedItem:(CPTabViewItem)aTabViewItem
|
||||
{
|
||||
var segment = aTabViewItem ? [self indexOfTabViewItem:aTabViewItem] : 0;
|
||||
[_tabs tileWithChangedSegment:segment];
|
||||
|
||||
[self setNeedsLayout];
|
||||
}
|
||||
|
||||
- (void)layoutSubviews
|
||||
{
|
||||
// Even if CPTabView's autoresizesSubviews is NO, _tabs and _box has to be laid out.
|
||||
@@ -392,7 +434,6 @@ var CPTabViewDidSelectTabViewItemSelector = 1 << 1,
|
||||
[_box setFrame:CGRectMake(0, origin, CGRectGetWidth(aFrame),
|
||||
CGRectGetHeight(aFrame) - segmentedHeight / 2)];
|
||||
|
||||
[self _updateItems];
|
||||
[self _repositionTabs];
|
||||
}
|
||||
}
|
||||
@@ -470,30 +511,238 @@ var CPTabViewDidSelectTabViewItemSelector = 1 << 1,
|
||||
[_tabs setCenter:CGPointMake(horizontalCenterOfSelf, verticalCenterOfTabs)];
|
||||
}
|
||||
|
||||
- (void)_setSelectedIndex:(CPNumber)index
|
||||
- (void)_displayItemView:(CPView)aView
|
||||
{
|
||||
_selectedIndex = index;
|
||||
[self _setContentViewFromItem:[_items objectAtIndex:_selectedIndex]];
|
||||
[_box setContentView:aView];
|
||||
}
|
||||
|
||||
- (void)_setContentViewFromItem:(CPTabViewItem)anItem
|
||||
// DELEGATE METHODS
|
||||
|
||||
- (BOOL)_sendDelegateShouldSelectTabViewItem:(CPTabViewItem)aTabViewItem
|
||||
{
|
||||
[_box setContentView:[anItem view]];
|
||||
if (_delegateSelectors & CPTabViewShouldSelectTabViewItemSelector)
|
||||
return [_delegate tabView:self shouldSelectTabViewItem:aTabViewItem];
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void)_updateItems
|
||||
- (void)_sendDelegateWillSelectTabViewItem:(CPTabViewItem)aTabViewItem
|
||||
{
|
||||
var count = [_items count];
|
||||
[_tabs setSegmentCount:count];
|
||||
if (_delegateSelectors & CPTabViewWillSelectTabViewItemSelector)
|
||||
[_delegate tabView:self willSelectTabViewItem:aTabViewItem];
|
||||
}
|
||||
|
||||
for (var i = 0; i < count; i++)
|
||||
- (void)_sendDelegateDidSelectTabViewItem:(CPTabViewItem)aTabViewItem
|
||||
{
|
||||
if (_delegateSelectors & CPTabViewDidSelectTabViewItemSelector)
|
||||
[_delegate tabView:self didSelectTabViewItem:aTabViewItem];
|
||||
}
|
||||
|
||||
- (void)_sendDelegateTabViewDidChangeNumberOfTabViewItems
|
||||
{
|
||||
if (_delegateSelectors & CPTabViewDidChangeNumberOfTabViewItemsSelector)
|
||||
[_delegate tabViewDidChangeNumberOfTabViewItems:self];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation CPTabView (BindingSupport)
|
||||
|
||||
+ (Class)_binderClassForBinding:(CPString)aBinding
|
||||
{
|
||||
if (aBinding == CPContentBinding)
|
||||
return [_CPTabViewContentBinder class];
|
||||
else if (aBinding == CPSelectionIndexesBinding || aBinding == CPSelectedIndexBinding)
|
||||
return [_CPTabViewSelectionBinder class];
|
||||
|
||||
return [super _binderClassForBinding:aBinding];
|
||||
}
|
||||
|
||||
+ (BOOL)isBindingExclusive:(CPString)aBinding
|
||||
{
|
||||
return (aBinding == CPSelectionIndexesBinding || aBinding == CPSelectedIndexBinding);
|
||||
}
|
||||
|
||||
- (void)_reverseSetContent
|
||||
{
|
||||
var theBinder = [self binderForBinding:CPContentBinding];
|
||||
[theBinder reverseSetValueFor:@"items"];
|
||||
}
|
||||
|
||||
- (void)_reverseSetSelectedIndex
|
||||
{
|
||||
var theBinder = [self binderForBinding:CPSelectionIndexesBinding];
|
||||
|
||||
if (theBinder !== nil)
|
||||
[theBinder reverseSetValueFor:@"selectionIndexes"];
|
||||
else
|
||||
{
|
||||
[_tabs setLabel:[[_items objectAtIndex:i] label] forSegment:i];
|
||||
[_tabs setTag:i forSegment:i];
|
||||
theBinder = [self binderForBinding:CPSelectedIndexBinding];
|
||||
[theBinder reverseSetValueFor:@"selectedIndex"];
|
||||
}
|
||||
}
|
||||
|
||||
- (CPBinder)binderForBinding:(CPString)aBinding
|
||||
{
|
||||
var cls = [[self class] _binderClassForBinding:aBinding]
|
||||
return [cls getBinding:aBinding forObject:self];
|
||||
}
|
||||
|
||||
- (void)setItems:(CPArray)tabViewItems
|
||||
{
|
||||
if ([tabViewItems isEqualToArray:[_tabs segments]])
|
||||
return;
|
||||
|
||||
[[self items] makeObjectsPerformSelector:@selector(_setTabView:) withObject:nil];
|
||||
[_tabs setSegments:tabViewItems];
|
||||
[tabViewItems makeObjectsPerformSelector:@selector(_setTabView:) withObject:self];
|
||||
|
||||
[self tileWithChangedItem:nil];
|
||||
|
||||
// Update the selection because setSegments: did remove all previous segments AND the selection.
|
||||
[_tabs setSelectedSegment:[self indexOfTabViewItem:_selectedTabViewItem]];
|
||||
|
||||
// should we send delegate methods in bindings mode ?
|
||||
//[self _delegateTabViewDidChangeNumberOfTabViewItems:self];
|
||||
}
|
||||
|
||||
- (void)_deselectAll
|
||||
{
|
||||
[_tabs setSelectedSegment:-1];
|
||||
_selectedTabViewItem = nil;
|
||||
}
|
||||
|
||||
- (void)_displayPlaceholder:(CPString)aPlaceholder
|
||||
{
|
||||
if (_placeHolderView == nil)
|
||||
{
|
||||
_placeHolderView = [[CPView alloc] initWithFrame:CGRectMakeZero()];
|
||||
var textField = [[CPTextField alloc] initWithFrame:CGRectMakeZero()];
|
||||
[textField setTag:1000];
|
||||
[textField setTextColor:[CPColor whiteColor]];
|
||||
[textField setFont:[CPFont boldFontWithName:@"Geneva" size:18 italic:YES]];
|
||||
[_placeHolderView addSubview:textField];
|
||||
}
|
||||
|
||||
if (_selectedIndex === CPNotFound)
|
||||
[self selectFirstTabViewItem:self];
|
||||
var textField = [_placeHolderView viewWithTag:1000];
|
||||
[textField setStringValue:aPlaceholder];
|
||||
[textField sizeToFit];
|
||||
|
||||
var boxBounds = [_box bounds],
|
||||
textFieldBounds = [textField bounds],
|
||||
origin = CGPointMake(CGRectGetWidth(boxBounds)/2 - CGRectGetWidth(textFieldBounds)/2, CGRectGetHeight(boxBounds)/2 - CGRectGetHeight(textFieldBounds));
|
||||
|
||||
[textField setFrameOrigin:origin];
|
||||
|
||||
[self _displayItemView:_placeHolderView];
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Override
|
||||
|
||||
/*!
|
||||
Enabled controls accept first mouse by default.
|
||||
*/
|
||||
- (BOOL)acceptsFirstMouse:(CPEvent)anEvent
|
||||
{
|
||||
return YES;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
var _CPTabViewContentBinderNull = @"NO CONTENT";
|
||||
|
||||
@implementation _CPTabViewContentBinder : CPBinder
|
||||
{
|
||||
}
|
||||
|
||||
- (void)_updatePlaceholdersWithOptions:(CPDictionary)options
|
||||
{
|
||||
[super _updatePlaceholdersWithOptions:options];
|
||||
[self _setPlaceholder:_CPTabViewContentBinderNull forMarker:CPNullMarker isDefault:YES];
|
||||
}
|
||||
|
||||
- (void)setPlaceholderValue:(id)aValue withMarker:(CPString)aMarker forBinding:(CPString)aBinding
|
||||
{
|
||||
[_source setItems:@[]];
|
||||
[_source _setPlaceholderView:aValue];
|
||||
}
|
||||
|
||||
- (void)setValue:(id)aValue forBinding:(CPString)aBinding
|
||||
{
|
||||
[_source setItems:aValue];
|
||||
}
|
||||
|
||||
- (id)valueForBinding:(CPString)aBinding
|
||||
{
|
||||
return [_source items];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
var _CPTabViewSelectionBinderMultipleValues = @"Multiple Selection",
|
||||
_CPTabViewSelectionBinderNoSelection = @"No Selection";
|
||||
|
||||
@implementation _CPTabViewSelectionBinder : CPBinder
|
||||
{
|
||||
}
|
||||
|
||||
- (void)_updatePlaceholdersWithOptions:(CPDictionary)options
|
||||
{
|
||||
[super _updatePlaceholdersWithOptions:options];
|
||||
|
||||
[self _setPlaceholder:_CPTabViewSelectionBinderMultipleValues forMarker:CPMultipleValuesMarker isDefault:YES];
|
||||
[self _setPlaceholder:_CPTabViewSelectionBinderNoSelection forMarker:CPNoSelectionMarker isDefault:YES];
|
||||
}
|
||||
|
||||
- (void)setPlaceholderValue:(id)aValue withMarker:(CPString)aMarker forBinding:(CPString)aBinding
|
||||
{
|
||||
if (aMarker == CPNoSelectionMarker || aMarker == CPNullMarker)
|
||||
[_source _deselectAll];
|
||||
|
||||
[_source _displayPlaceholder:aValue];
|
||||
}
|
||||
|
||||
- (void)setValue:(id)aValue forBinding:(CPString)aBinding
|
||||
{
|
||||
if (aBinding == CPSelectionIndexesBinding)
|
||||
{
|
||||
if (aValue == nil || [aValue count] == 0)
|
||||
{
|
||||
[_source _deselectAll];
|
||||
[_source _displayPlaceholder:_CPTabViewSelectionBinderNoSelection];
|
||||
}
|
||||
else if ([aValue count] > 1)
|
||||
[_source _displayPlaceholder:_CPTabViewSelectionBinderMultipleValues];
|
||||
else if ([aValue firstIndex] < [_source numberOfTabViewItems])
|
||||
[_source _selectTabViewItemAtIndex:[aValue firstIndex]];
|
||||
}
|
||||
else if (aBinding == CPSelectedIndexBinding)
|
||||
{
|
||||
if (aValue == CPNotFound)
|
||||
{
|
||||
[_source _deselectAll];
|
||||
[_source _displayPlaceholder:_CPTabViewSelectionBinderNoSelection];
|
||||
}
|
||||
else if (aValue < [_source numberOfTabViewItems])
|
||||
[_source _selectTabViewItemAtIndex:aValue];
|
||||
}
|
||||
}
|
||||
|
||||
- (id)valueForBinding:(CPString)aBinding
|
||||
{
|
||||
if (aBinding == CPSelectionIndexesBinding)
|
||||
{
|
||||
var result = [CPIndexSet indexSet],
|
||||
idx = [_source indexOfTabViewItem:[_source selectedTabViewItem]];
|
||||
|
||||
if (idx !== CPNotFound)
|
||||
[result addIndex:idx];
|
||||
|
||||
return result;
|
||||
}
|
||||
else if (aBinding == CPSelectedIndexBinding)
|
||||
return [_source indexOfTabViewItem:[_source selectedTabViewItem]];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -515,12 +764,13 @@ var CPTabViewItemsKey = "CPTabViewItemsKey",
|
||||
_font = [aCoder decodeObjectForKey:CPTabViewFontKey];
|
||||
[_tabs setFont:_font];
|
||||
|
||||
_items = [aCoder decodeObjectForKey:CPTabViewItemsKey];
|
||||
[_items makeObjectsPerformSelector:@selector(_setTabView:) withObject:self];
|
||||
var items = [aCoder decodeObjectForKey:CPTabViewItemsKey] || [CPArray array];
|
||||
[self _insertTabViewItems:items atIndexes:[CPIndexSet indexSetWithIndexesInRange:CPMakeRange(0, [items count])]];
|
||||
|
||||
[self setDelegate:[aCoder decodeObjectForKey:CPTabViewDelegateKey]];
|
||||
|
||||
self.selectOnAwake = [aCoder decodeObjectForKey:CPTabViewSelectedItemKey];
|
||||
_selectedTabViewItem = [aCoder decodeObjectForKey:CPTabViewSelectedItemKey];
|
||||
|
||||
_type = [aCoder decodeIntForKey:CPTabViewTypeKey];
|
||||
}
|
||||
|
||||
@@ -529,14 +779,23 @@ var CPTabViewItemsKey = "CPTabViewItemsKey",
|
||||
|
||||
- (void)awakeFromCib
|
||||
{
|
||||
[super awakeFromCib];
|
||||
|
||||
// This cannot be run in initWithCoder because it might call selectTabViewItem:, which is
|
||||
// not safe to call before the views of the tab views items are fully decoded.
|
||||
[self _updateItems];
|
||||
|
||||
if (self.selectOnAwake)
|
||||
if (_selectedTabViewItem)
|
||||
{
|
||||
[self selectTabViewItem:self.selectOnAwake];
|
||||
delete self.selectOnAwake;
|
||||
var idx = [self indexOfTabViewItem:_selectedTabViewItem];
|
||||
|
||||
if (idx !== CPNotFound)
|
||||
{
|
||||
// Temporarily set the selected item to not selected.
|
||||
// It allows the initial selection to be made correctly.
|
||||
_selectedTabViewItem = nil;
|
||||
|
||||
[self selectTabViewItemAtIndex:idx];
|
||||
}
|
||||
}
|
||||
|
||||
var type = _type;
|
||||
@@ -558,9 +817,7 @@ var CPTabViewItemsKey = "CPTabViewItemsKey",
|
||||
|
||||
[aCoder encodeObject:_items forKey:CPTabViewItemsKey];
|
||||
|
||||
var selected = [self selectedTabViewItem];
|
||||
if (selected)
|
||||
[aCoder encodeObject:selected forKey:CPTabViewSelectedItemKey];
|
||||
[aCoder encodeConditionalObject:_selectedTabViewItem forKey:CPTabViewSelectedItemKey];
|
||||
|
||||
[aCoder encodeInt:_type forKey:CPTabViewTypeKey];
|
||||
[aCoder encodeObject:_font forKey:CPTabViewFontKey];
|
||||
@@ -569,3 +826,26 @@ var CPTabViewItemsKey = "CPTabViewItemsKey",
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation _CPTabViewBox : CPBox
|
||||
{
|
||||
CPTabView _tabView @accessors(property=tabView);
|
||||
}
|
||||
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Override
|
||||
|
||||
- (CPView)hitTest:(CGPoint)aPoint
|
||||
{
|
||||
// Here we check if we have clicked on the segmentedControl of the tabView or not
|
||||
// If YES, the CPBox should not handle the click
|
||||
var segmentIndex = [_tabView._tabs testSegment:[_tabView._tabs convertPoint:aPoint fromView:[self superview]]];
|
||||
|
||||
if (segmentIndex != CPNotFound)
|
||||
return nil;
|
||||
|
||||
return [super hitTest:aPoint];
|
||||
}
|
||||
|
||||
@end
|
||||
+102
-10
@@ -24,6 +24,7 @@
|
||||
|
||||
@import "CPView.j"
|
||||
@class CPTabView
|
||||
@class CPViewController
|
||||
|
||||
/*
|
||||
The tab is currently selected.
|
||||
@@ -53,14 +54,34 @@ CPPressedTab = 2;
|
||||
*/
|
||||
@implementation CPTabViewItem : CPObject
|
||||
{
|
||||
id _identifier;
|
||||
CPString _label;
|
||||
id _identifier;
|
||||
CPString _label;
|
||||
CPInteger _tag @accessors(property=tag);
|
||||
|
||||
CPView _view;
|
||||
CPView _auxiliaryView;
|
||||
CPView _view;
|
||||
CPView _auxiliaryView;
|
||||
|
||||
CPTabView _tabView;
|
||||
unsigned _tabState; // Looks like it is not yet implemented
|
||||
CPTabView _tabView;
|
||||
unsigned _tabState; // Looks like it is not yet implemented
|
||||
|
||||
CPImage _image @accessors(property=image);
|
||||
CPViewController _viewController @accessors(getter=viewController);
|
||||
|
||||
BOOL _enabled @accessors(property=enabled);
|
||||
BOOL _selected @accessors(property=selected);
|
||||
CGRect _tabRect @accessors(property=frame);
|
||||
float _width @accessors(property=width);
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
*/
|
||||
+ (CPTabViewItem)tabViewItemWithViewController:(CPViewController)aViewController
|
||||
{
|
||||
var item = [[CPTabViewItem alloc] init];
|
||||
[item setViewController:aViewController];
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
- (id)init
|
||||
@@ -68,6 +89,19 @@ CPPressedTab = 2;
|
||||
return [self initWithIdentifier:@""];
|
||||
}
|
||||
|
||||
- (void)_init
|
||||
{
|
||||
_tag = 0;
|
||||
_viewController = nil;
|
||||
_image = nil;
|
||||
_tabState = 0;
|
||||
_tabView = nil;
|
||||
_enabled = YES;
|
||||
_selected = NO;
|
||||
_tabRect = CGRectMakeZero();
|
||||
_width = 0;
|
||||
}
|
||||
|
||||
/*!
|
||||
Initializes the tab view item with the specified identifier.
|
||||
@return the initialized CPTabViewItem
|
||||
@@ -76,8 +110,12 @@ CPPressedTab = 2;
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
if (self)
|
||||
_identifier = anIdentifier;
|
||||
[self _init];
|
||||
|
||||
_identifier = anIdentifier;
|
||||
_label = nil;
|
||||
_view = nil;
|
||||
//_auxiliaryView = nil;
|
||||
|
||||
return self;
|
||||
}
|
||||
@@ -89,8 +127,11 @@ CPPressedTab = 2;
|
||||
*/
|
||||
- (void)setLabel:(CPString)aLabel
|
||||
{
|
||||
if ([aLabel isEqualToString:_label])
|
||||
return;
|
||||
|
||||
_label = aLabel;
|
||||
[_tabView setNeedsLayout];
|
||||
[_tabView tileWithChangedItem:self];
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -101,6 +142,28 @@ CPPressedTab = 2;
|
||||
return _label;
|
||||
}
|
||||
|
||||
// Working With Images
|
||||
/*!
|
||||
Sets the CPTabViewItem's image.
|
||||
@param anImage the image for the item
|
||||
*/
|
||||
- (void)setImage:(CPImage)anImage
|
||||
{
|
||||
if ([anImage isEqual:_image])
|
||||
return;
|
||||
|
||||
_image = anImage;
|
||||
[_tabView tileWithChangedItem:self];
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the CPTabViewItem's image
|
||||
*/
|
||||
- (CPImage)image
|
||||
{
|
||||
return _image;
|
||||
}
|
||||
|
||||
// Checking the Tab Display State
|
||||
/*!
|
||||
Returns the tab's current state.
|
||||
@@ -140,7 +203,7 @@ CPPressedTab = 2;
|
||||
_view = aView;
|
||||
|
||||
if ([_tabView selectedTabViewItem] == self)
|
||||
[_tabView _setContentViewFromItem:self];
|
||||
[_tabView _displayItemView:_view];
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -148,6 +211,9 @@ CPPressedTab = 2;
|
||||
*/
|
||||
- (CPView)view
|
||||
{
|
||||
if (!_view && _viewController)
|
||||
return [_viewController view]; // The view controller loads here.
|
||||
|
||||
return _view;
|
||||
}
|
||||
|
||||
@@ -186,10 +252,32 @@ CPPressedTab = 2;
|
||||
_tabView = aView;
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the specified view controller for the tab view item.
|
||||
@param aViewController an instance of CPViewController.
|
||||
*/
|
||||
- (void)setViewController:(CPViewController)aViewController
|
||||
{
|
||||
_viewController = aViewController;
|
||||
|
||||
var identifier = [aViewController cibName],
|
||||
title = [_viewController title];
|
||||
|
||||
if (identifier)
|
||||
_identifier = identifier;
|
||||
|
||||
if (title)
|
||||
[self setLabel:title];
|
||||
|
||||
if ([_tabView selectedTabViewItem] == self)
|
||||
[_tabView _displayItemView:[_viewController view]];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
var CPTabViewItemIdentifierKey = "CPTabViewItemIdentifierKey",
|
||||
CPTabViewItemLabelKey = "CPTabViewItemLabelKey",
|
||||
CPTabViewItemImageKey = "CPTabViewItemImageKey",
|
||||
CPTabViewItemViewKey = "CPTabViewItemViewKey",
|
||||
CPTabViewItemAuxViewKey = "CPTabViewItemAuxViewKey";
|
||||
|
||||
@@ -202,8 +290,11 @@ var CPTabViewItemIdentifierKey = "CPTabViewItemIdentifierKey",
|
||||
|
||||
if (self)
|
||||
{
|
||||
[self _init];
|
||||
|
||||
_identifier = [aCoder decodeObjectForKey:CPTabViewItemIdentifierKey];
|
||||
_label = [aCoder decodeObjectForKey:CPTabViewItemLabelKey];
|
||||
_image = [aCoder decodeObjectForKey:CPTabViewItemImageKey];
|
||||
|
||||
_view = [aCoder decodeObjectForKey:CPTabViewItemViewKey];
|
||||
_auxiliaryView = [aCoder decodeObjectForKey:CPTabViewItemAuxViewKey];
|
||||
@@ -216,6 +307,7 @@ var CPTabViewItemIdentifierKey = "CPTabViewItemIdentifierKey",
|
||||
{
|
||||
[aCoder encodeObject:_identifier forKey:CPTabViewItemIdentifierKey];
|
||||
[aCoder encodeObject:_label forKey:CPTabViewItemLabelKey];
|
||||
[aCoder encodeObject:_image forKey:CPTabViewItemImageKey];
|
||||
|
||||
[aCoder encodeObject:_view forKey:CPTabViewItemViewKey];
|
||||
[aCoder encodeObject:_auxiliaryView forKey:CPTabViewItemAuxViewKey];
|
||||
|
||||
+24
-32
@@ -24,6 +24,7 @@
|
||||
@import "CPView.j"
|
||||
@import "CPCursor.j"
|
||||
@import "_CPImageAndTextView.j"
|
||||
@import "CPTrackingArea.j"
|
||||
|
||||
@class CPTableView
|
||||
|
||||
@@ -305,7 +306,9 @@ var CPTableHeaderViewResizeZone = 3.0,
|
||||
self = [super initWithFrame:aFrame];
|
||||
|
||||
if (self)
|
||||
{
|
||||
[self _init];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
@@ -456,34 +459,26 @@ var CPTableHeaderViewResizeZone = 3.0,
|
||||
_activeColumn = -1;
|
||||
}
|
||||
|
||||
- (void)mouseEntered:(CPEvent)theEvent
|
||||
@end
|
||||
|
||||
@implementation CPTableHeaderView (CPTrackingArea)
|
||||
|
||||
- (void)updateTrackingAreas
|
||||
{
|
||||
var location = [theEvent globalLocation];
|
||||
|
||||
if (CGPointEqualToPoint(location, _mouseEnterExitLocation))
|
||||
return;
|
||||
|
||||
_mouseEnterExitLocation = location;
|
||||
|
||||
[self _updateResizeCursor:theEvent];
|
||||
[self removeAllTrackingAreas];
|
||||
|
||||
var options = CPTrackingCursorUpdate | CPTrackingActiveInKeyWindow;
|
||||
|
||||
for (var i = 0; i < _tableView._tableColumns.length; i++)
|
||||
[self addTrackingArea:[[CPTrackingArea alloc] initWithRect:[self _cursorRectForColumn:i]
|
||||
options:options
|
||||
owner:self
|
||||
userInfo:nil]];
|
||||
}
|
||||
|
||||
- (void)mouseMoved:(CPEvent)theEvent
|
||||
- (void)cursorUpdate:(CPEvent)anEvent
|
||||
{
|
||||
[self _updateResizeCursor:theEvent];
|
||||
}
|
||||
|
||||
- (void)mouseExited:(CPEvent)theEvent
|
||||
{
|
||||
var location = [theEvent globalLocation];
|
||||
|
||||
if (CGPointEqualToPoint(location, _mouseEnterExitLocation))
|
||||
return;
|
||||
|
||||
_mouseEnterExitLocation = location;
|
||||
|
||||
// FIXME: we should use CPCursor push/pop (if previous currentCursor != arrow).
|
||||
[[CPCursor arrowCursor] set];
|
||||
[self _updateResizeCursor:anEvent];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -622,7 +617,7 @@ var CPTableHeaderViewResizeZone = 3.0,
|
||||
[[headerView subviews] makeObjectsPerformSelector:@selector(setHidden:) withObject:YES];
|
||||
|
||||
// The underlying column header shows normal state
|
||||
[headerView unsetThemeState:CPThemeStateHighlighted | CPThemeStateSelected];
|
||||
[headerView unsetThemeStates:[CPThemeStateHighlighted, CPThemeStateSelected]];
|
||||
|
||||
// Keep track of the location within the column header where the original mousedown occurred
|
||||
_columnDragHeaderView = [_columnDragView viewWithTag:CPTableHeaderViewDragColumnHeaderTag];
|
||||
@@ -697,6 +692,7 @@ var CPTableHeaderViewResizeZone = 3.0,
|
||||
[[_tableView headerView] setNeedsLayout];
|
||||
|
||||
[[CPCursor arrowCursor] set];
|
||||
[self updateTrackingAreas];
|
||||
}
|
||||
|
||||
- (BOOL)_shouldResizeTableColumn:(CPInteger)aColumnIndex at:(CGPoint)aPoint
|
||||
@@ -762,7 +758,10 @@ var CPTableHeaderViewResizeZone = 3.0,
|
||||
var tableColumn = [[_tableView tableColumns] objectAtIndex:aColumnIndex];
|
||||
|
||||
if ([tableColumn width] != _columnOldWidth)
|
||||
{
|
||||
[_tableView _didResizeTableColumn:tableColumn oldWidth:_columnOldWidth];
|
||||
[self updateTrackingAreas];
|
||||
}
|
||||
|
||||
[tableColumn setDisableResizingPosting:NO];
|
||||
[_tableView setDisableAutomaticResizing:NO];
|
||||
@@ -772,13 +771,6 @@ var CPTableHeaderViewResizeZone = 3.0,
|
||||
|
||||
- (void)_updateResizeCursor:(CPEvent)theEvent
|
||||
{
|
||||
// never get stuck in resize cursor mode (FIXME take out when we turn on tracking rects)
|
||||
if (![_tableView allowsColumnResizing] || ([theEvent type] === CPLeftMouseUp && ![[self window] acceptsMouseMovedEvents]))
|
||||
{
|
||||
[[CPCursor arrowCursor] set];
|
||||
return;
|
||||
}
|
||||
|
||||
var mouseLocation = [self convertPoint:[theEvent locationInWindow] fromView:nil],
|
||||
mouseOverLocation = CGPointMake(MAX(mouseLocation.x - CPTableHeaderViewResizeZone, 0.0), mouseLocation.y),
|
||||
overColumn = [self columnAtPoint:mouseOverLocation];
|
||||
|
||||
+28
-24
@@ -644,12 +644,8 @@ NOT YET IMPLEMENTED
|
||||
}];
|
||||
}
|
||||
|
||||
// Reloads the views AND the data
|
||||
- (void)_reloadDataViews
|
||||
- (void)_setupReload
|
||||
{
|
||||
//if (!_dataSource)
|
||||
// return;
|
||||
|
||||
_reloadAllRows = YES;
|
||||
_objectValues = { };
|
||||
_cachedRowHeights = [];
|
||||
@@ -661,11 +657,24 @@ NOT YET IMPLEMENTED
|
||||
|
||||
// This updates the size too.
|
||||
[self noteNumberOfRowsChanged];
|
||||
}
|
||||
|
||||
// Reloads the views AND the data
|
||||
- (void)_reloadDataViews
|
||||
{
|
||||
[self _setupReload];
|
||||
[self setNeedsLayout];
|
||||
[self setNeedsDisplay:YES];
|
||||
}
|
||||
|
||||
// Reloads the views AND the data
|
||||
- (void)_reloadDataViewsSynchronously
|
||||
{
|
||||
[self _setupReload];
|
||||
[self layout];
|
||||
[self display];
|
||||
}
|
||||
|
||||
//Target-action Behavior
|
||||
/*!
|
||||
Sets the message sent to the target when the user double-clicks an
|
||||
@@ -3593,6 +3602,9 @@ Your delegate can implement this method to avoid subclassing the tableview to ad
|
||||
[self setNeedsDisplay:YES];
|
||||
}
|
||||
|
||||
if (!_isViewBased)
|
||||
[self _setEditingState:NO forView:dataView];
|
||||
|
||||
[self _sendDelegateWillDisplayView:dataView forTableColumn:tableColumn row:row];
|
||||
|
||||
return dataView;
|
||||
@@ -3646,7 +3658,7 @@ Your delegate can implement this method to avoid subclassing the tableview to ad
|
||||
var view = nil;
|
||||
|
||||
if (_viewForTableColumnRowSelector)
|
||||
view = objj_msgSend(self, _viewForTableColumnRowSelector, aTableColumn, aRow);
|
||||
view = self.isa.objj_msgSend2(self, _viewForTableColumnRowSelector, aTableColumn, aRow);
|
||||
|
||||
if (!view)
|
||||
{
|
||||
@@ -4208,7 +4220,7 @@ Your delegate can implement this method to avoid subclassing the tableview to ad
|
||||
while (count--)
|
||||
{
|
||||
var currentIndex = indexes[count],
|
||||
rowRect = CGRectIntersection(objj_msgSend(self, rectSelector, currentIndex), aRect);
|
||||
rowRect = CGRectIntersection(self.isa.objj_msgSend1(self, rectSelector, currentIndex), aRect);
|
||||
|
||||
// group rows get the same highlight style as other rows if they're source list...
|
||||
if (!drawGradient)
|
||||
@@ -4282,7 +4294,7 @@ Your delegate can implement this method to avoid subclassing the tableview to ad
|
||||
|
||||
for (var i = 0; i < count2; i++)
|
||||
{
|
||||
var rect = objj_msgSend(self, rectSelector, indexes[i]),
|
||||
var rect = self.isa.objj_msgSend1(self, rectSelector, indexes[i]),
|
||||
minX = CGRectGetMinX(rect) - 0.5,
|
||||
maxX = CGRectGetMaxX(rect) - 0.5,
|
||||
minY = CGRectGetMinY(rect) - 0.5,
|
||||
@@ -4677,7 +4689,7 @@ Your delegate can implement this method to avoid subclassing the tableview to ad
|
||||
{
|
||||
if ([self _sendDelegateShouldEditTableColumn:column row:rowIndex])
|
||||
{
|
||||
[self editColumn:columnIndex row:rowIndex withEvent:nil select:YES];
|
||||
[self editColumn:columnIndex row:rowIndex withEvent:[CPApp currentEvent] select:YES];
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -5196,7 +5208,7 @@ Your delegate can implement this method to avoid subclassing the tableview to ad
|
||||
[self _notifyViewDidBecomeFirstResponder];
|
||||
|
||||
// This is for cell-based tables only. In view-based mode, we do not change the textfield apprearence during an edit.
|
||||
if (!_isViewBased && _editingRow !== CPNotFound && [responder isKindOfClass:[CPTextField class]] && [responder isEditable])
|
||||
if (!_isViewBased && _editingRow !== CPNotFound && [responder isKindOfClass:[CPTextField class]] && [responder isEditable] && [responder superview] == self)
|
||||
{
|
||||
[responder setBezeled:YES];
|
||||
[self _registerForEndEditingNote:responder];
|
||||
@@ -5300,6 +5312,7 @@ Your delegate can implement this method to avoid subclassing the tableview to ad
|
||||
if ([aView isKindOfClass:[CPTextField class]])
|
||||
[aView setBezeled:editingState];
|
||||
}
|
||||
|
||||
/*!
|
||||
Edits the dataview at a given row and column. This method is usually invoked automatically and should rarely be invoked directly
|
||||
The row at supplied rowIndex must be selected otherwise an exception is thrown.
|
||||
@@ -5314,11 +5327,7 @@ Your delegate can implement this method to avoid subclassing the tableview to ad
|
||||
if (![self isRowSelected:rowIndex])
|
||||
[[CPException exceptionWithName:@"Error" reason:@"Attempt to edit row " + rowIndex + " when not selected." userInfo:nil] raise];
|
||||
|
||||
[self reloadData];
|
||||
|
||||
// Process all events immediately to make sure table data views are reloaded.
|
||||
[[CPRunLoop currentRunLoop] limitDateForMode:CPDefaultRunLoopMode];
|
||||
|
||||
[self _reloadDataViewsSynchronously];
|
||||
[self scrollRowToVisible:rowIndex];
|
||||
[self scrollColumnToVisible:columnIndex];
|
||||
|
||||
@@ -6301,17 +6310,17 @@ var CPTableViewDataSourceKey = @"CPTableViewDataSourceKey",
|
||||
|
||||
var showCallback = function()
|
||||
{
|
||||
objj_msgSend(self, "setHidden:", NO)
|
||||
[self setHidden: NO];
|
||||
isBlinking = NO;
|
||||
};
|
||||
|
||||
var hideCallback = function()
|
||||
{
|
||||
objj_msgSend(self, "setHidden:", YES)
|
||||
[self setHidden: YES];
|
||||
isBlinking = YES;
|
||||
};
|
||||
|
||||
objj_msgSend(self, "setHidden:", YES);
|
||||
[self setHidden: YES];
|
||||
[CPTimer scheduledTimerWithTimeInterval:0.1 callback:showCallback repeats:NO];
|
||||
[CPTimer scheduledTimerWithTimeInterval:0.19 callback:hideCallback repeats:NO];
|
||||
[CPTimer scheduledTimerWithTimeInterval:0.27 callback:showCallback repeats:NO];
|
||||
@@ -6391,23 +6400,18 @@ var CPTableViewDataSourceKey = @"CPTableViewDataSourceKey",
|
||||
|
||||
- (void)awakeFromCib
|
||||
{
|
||||
[super awakeFromCib];
|
||||
[self setThemeState:CPThemeStateTableDataView];
|
||||
}
|
||||
|
||||
- (BOOL)setThemeState:(ThemeState)aState
|
||||
{
|
||||
if (aState.isa && [aState isKindOfClass:CPArray])
|
||||
aState = CPThemeState.apply(null, aState);
|
||||
|
||||
[super setThemeState:aState];
|
||||
[self recursivelyPerformSelector:@selector(setThemeState:) withObject:aState startingFrom:self];
|
||||
}
|
||||
|
||||
- (BOOL)unsetThemeState:(ThemeState)aState
|
||||
{
|
||||
if (aState.isa && [aState isKindOfClass:CPArray])
|
||||
aState = CPThemeState.apply(null, aState);
|
||||
|
||||
[super unsetThemeState:aState];
|
||||
[self recursivelyPerformSelector:@selector(unsetThemeState:) withObject:aState startingFrom:self];
|
||||
}
|
||||
|
||||
+13
-1
@@ -38,4 +38,16 @@ CPLeftTextMovement = 19;
|
||||
CPRightTextMovement = 20;
|
||||
CPUpTextMovement = 21;
|
||||
CPDownTextMovement = 22;
|
||||
CPCancelTextMovement = 23;
|
||||
CPCancelTextMovement = 23;
|
||||
|
||||
@typedef CPWritingDirection
|
||||
CPWritingDirectionNatural = -1;
|
||||
CPWritingDirectionLeftToRight = 0;
|
||||
CPWritingDirectionRightToLeft = 1;
|
||||
|
||||
@typedef CPTextAlignment
|
||||
CPLeftTextAlignment = 0;
|
||||
CPRightTextAlignment = 1;
|
||||
CPCenterTextAlignment = 2;
|
||||
CPJustifiedTextAlignment = 3;
|
||||
CPNaturalTextAlignment = 4;
|
||||
+48
-13
@@ -34,7 +34,6 @@
|
||||
@global CPStringPboardType
|
||||
@global CPCursor
|
||||
|
||||
|
||||
@protocol CPTextFieldDelegate <CPControlTextEditingDelegate>
|
||||
|
||||
@end
|
||||
@@ -66,11 +65,11 @@ var CPTextFieldDOMCurrentElement = nil,
|
||||
CPTextFieldCachedSelectStartFunction = nil,
|
||||
CPTextFieldCachedDragFunction = nil,
|
||||
CPTextFieldBlurHandler = nil,
|
||||
CPTextFieldInputFunction = nil;
|
||||
CPTextFieldInputFunction = nil,
|
||||
CPTexFieldCurrentCSSSelectableField = nil;
|
||||
|
||||
var CPSecureTextFieldCharacter = "\u2022";
|
||||
|
||||
|
||||
function CPTextFieldBlurFunction(anEvent, owner, domElement, inputElement, resigning, didBlurRef)
|
||||
{
|
||||
if (owner && domElement != inputElement.parentNode)
|
||||
@@ -87,7 +86,7 @@ function CPTextFieldBlurFunction(anEvent, owner, domElement, inputElement, resig
|
||||
*/
|
||||
if ([owner _isWithinUsablePlatformRect])
|
||||
{
|
||||
window.setTimeout(function()
|
||||
[[CPRunLoop mainRunLoop] performBlock:function()
|
||||
{
|
||||
// This will prevent to jump to the focused element
|
||||
var previousScrollingOrigin = [owner _scrollToVisibleRectAndReturnPreviousOrigin];
|
||||
@@ -95,7 +94,8 @@ function CPTextFieldBlurFunction(anEvent, owner, domElement, inputElement, resig
|
||||
inputElement.focus();
|
||||
|
||||
[owner _restorePreviousScrollingOrigin:previousScrollingOrigin];
|
||||
}, 0.0);
|
||||
}
|
||||
argument:nil order:0 modes:[CPDefaultRunLoopMode]];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -376,7 +376,7 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
|
||||
|
||||
_sendActionOn = CPKeyUpMask | CPKeyDownMask;
|
||||
|
||||
[self setValue:CPLeftTextAlignment forThemeAttribute:@"alignment"];
|
||||
[self setValue:CPNaturalTextAlignment forThemeAttribute:@"alignment"];
|
||||
}
|
||||
|
||||
return self;
|
||||
@@ -671,11 +671,8 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
|
||||
|
||||
CPTextFieldInputOwner = self;
|
||||
|
||||
window.setTimeout(function()
|
||||
[[CPRunLoop mainRunLoop] performBlock:function()
|
||||
{
|
||||
/*
|
||||
setTimeout handlers are not guaranteed to fire in the order they were initiated. This can cause a race condition when several windows with text fields are opened quickly, resulting in several instances of this timeout function being fired, perhaps out of order. So we have to check that by the time this function is fired, CPTextFieldInputOwner has not been changed to another text field in the meantime.
|
||||
*/
|
||||
if (CPTextFieldInputOwner !== self)
|
||||
return;
|
||||
|
||||
@@ -688,18 +685,42 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
|
||||
|
||||
// Select the text if the textfield became first responder through keyboard interaction
|
||||
if (!_willBecomeFirstResponderByClick)
|
||||
{
|
||||
[self _selectText:self immediately:YES];
|
||||
}
|
||||
else
|
||||
{
|
||||
var point = CGPointMake([self convertPointFromBase:[[CPApp currentEvent] locationInWindow]].x - [self currentValueForThemeAttribute:@"content-inset"].left, 0),
|
||||
position = [CPPlatformString charPositionOfString:[self stringValue] withFont:[self font] forPoint:point];
|
||||
|
||||
[self setSelectedRange:CPMakeRange(position, 0)];
|
||||
}
|
||||
|
||||
_willBecomeFirstResponderByClick = NO;
|
||||
|
||||
[self textDidFocus:[CPNotification notificationWithName:CPTextFieldDidFocusNotification object:self userInfo:nil]];
|
||||
}, 0.0);
|
||||
} argument:nil order:0 modes:[CPDefaultRunLoopMode]];
|
||||
|
||||
#endif
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
/*!
|
||||
Set the selection css style for the DOM element of the textField
|
||||
@ignore
|
||||
*/
|
||||
- (void)_setEnableCSSSelection:(BOOL)shouldEnable
|
||||
{
|
||||
#if PLATFORM (DOM)
|
||||
if (CPTexFieldCurrentCSSSelectableField)
|
||||
CPTexFieldCurrentCSSSelectableField._DOMElement.style[CPBrowserStyleProperty(@"user-select")] = @"none";
|
||||
|
||||
CPTexFieldCurrentCSSSelectableField = self;
|
||||
_DOMElement.style[CPBrowserStyleProperty(@"user-select")] = shouldEnable ? @"text" : @"none";
|
||||
#endif
|
||||
}
|
||||
|
||||
/*!
|
||||
Set the css style for the input element of the textField
|
||||
@ignore
|
||||
@@ -753,6 +774,10 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
|
||||
element.style.textAlign = "right";
|
||||
break;
|
||||
|
||||
case CPNaturalTextAlignment:
|
||||
element.style.textAlign = "";
|
||||
break;
|
||||
|
||||
default:
|
||||
element.style.textAlign = "left";
|
||||
}
|
||||
@@ -814,6 +839,7 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
|
||||
[self _resignFirstKeyResponder];
|
||||
|
||||
_isEditing = NO;
|
||||
|
||||
if ([self isEditable])
|
||||
{
|
||||
[self textDidEndEditing:[CPNotification notificationWithName:CPControlTextDidEndEditingNotification object:self userInfo:@{"CPTextMovement": [self _currentTextMovement]}]];
|
||||
@@ -989,6 +1015,7 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
|
||||
}
|
||||
else if ([self isSelectable])
|
||||
{
|
||||
[self _setEnableCSSSelection:YES];
|
||||
if (document.attachEvent)
|
||||
{
|
||||
CPTextFieldCachedSelectStartFunction = [[self window] platformWindow]._DOMBodyElement.onselectstart;
|
||||
@@ -1030,6 +1057,14 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
|
||||
}
|
||||
}
|
||||
|
||||
- (void)rightMouseDown:(CPEvent)anEvent
|
||||
{
|
||||
if ([self menuForEvent:anEvent] || [[self nextResponder] isKindOfClass:CPView])
|
||||
[super rightMouseDown:anEvent];
|
||||
else
|
||||
[[[anEvent window] platformWindow] _propagateContextMenuDOMEvent:YES];
|
||||
}
|
||||
|
||||
- (void)mouseDragged:(CPEvent)anEvent
|
||||
{
|
||||
if (![self isEnabled] || !([self isSelectable] || [self isEditable]))
|
||||
@@ -1180,7 +1215,7 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
|
||||
- (void)textDidFocus:(CPNotification)note
|
||||
{
|
||||
// this looks to prevent false propagation of notifications for other objects
|
||||
if ([note object] != self)
|
||||
if ([note object] !== self)
|
||||
return;
|
||||
|
||||
if (_implementedDelegateMethods & CPTextFieldDelegate_controlTextDidFocus_)
|
||||
@@ -1482,7 +1517,7 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
|
||||
if (immediately)
|
||||
element.select();
|
||||
else
|
||||
window.setTimeout(function() { element.select(); }, 0);
|
||||
[[CPRunLoop mainRunLoop] performBlock:function(){ element.select(); } argument:nil order:0 modes:[CPDefaultRunLoopMode]];
|
||||
}
|
||||
else if (wind !== nil && [wind makeFirstResponder:self])
|
||||
[self _selectText:sender immediately:immediately];
|
||||
|
||||
+135
-55
@@ -36,7 +36,6 @@ var CPThemesByName = { },
|
||||
/*!
|
||||
@ingroup appkit
|
||||
*/
|
||||
|
||||
@implementation CPTheme : CPObject
|
||||
{
|
||||
CPString _name;
|
||||
@@ -142,20 +141,19 @@ var CPThemesByName = { },
|
||||
|
||||
if (!className)
|
||||
{
|
||||
if ([aClass isKindOfClass:[CPView class]])
|
||||
if ([aClass respondsToSelector:@selector(defaultThemeClass)])
|
||||
{
|
||||
if ([aClass respondsToSelector:@selector(defaultThemeClass)])
|
||||
className = [aClass defaultThemeClass];
|
||||
else if ([aClass respondsToSelector:@selector(themeClass)])
|
||||
{
|
||||
CPLog.warn(@"%@ themeClass is deprecated in favor of defaultThemeClass", CPStringFromClass(aClass));
|
||||
className = [aClass themeClass];
|
||||
}
|
||||
else
|
||||
return nil;
|
||||
className = [aClass defaultThemeClass];
|
||||
}
|
||||
else if ([aClass respondsToSelector:@selector(themeClass)])
|
||||
{
|
||||
CPLog.warn(@"%@ themeClass is deprecated in favor of defaultThemeClass", CPStringFromClass(aClass));
|
||||
className = [aClass themeClass];
|
||||
}
|
||||
else
|
||||
[CPException raise:CPInvalidArgumentException reason:@"aClass must be a class object or a string."];
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
|
||||
return [_attributes objectForKey:className];
|
||||
@@ -328,6 +326,7 @@ function ThemeState(stateNames)
|
||||
{
|
||||
if (!stateNames.hasOwnProperty(key))
|
||||
continue;
|
||||
|
||||
if (key !== 'normal')
|
||||
{
|
||||
this._stateNames[key] = true;
|
||||
@@ -345,8 +344,10 @@ function ThemeState(stateNames)
|
||||
this._stateNameString = stateNameKeys[0];
|
||||
|
||||
var stateNameLength = stateNameKeys.length;
|
||||
|
||||
for (var stateIndex = 1; stateIndex < stateNameLength; stateIndex++)
|
||||
this._stateNameString = this._stateNameString + "+" + stateNameKeys[stateIndex];
|
||||
|
||||
this._stateNameCount = stateNameLength;
|
||||
}
|
||||
|
||||
@@ -393,7 +394,19 @@ ThemeState.prototype.without = function(aState)
|
||||
if (!aState || aState === [CPNull null])
|
||||
return this;
|
||||
|
||||
var firstTransform = CPThemeWithoutTransform[this._stateNameString],
|
||||
result;
|
||||
|
||||
if (firstTransform)
|
||||
{
|
||||
result = firstTransform[aState._stateNameString];
|
||||
|
||||
if (result)
|
||||
return result;
|
||||
}
|
||||
|
||||
var newStates = {};
|
||||
|
||||
for (var stateName in this._stateNames)
|
||||
{
|
||||
if (!this._stateNames.hasOwnProperty(stateName))
|
||||
@@ -403,25 +416,54 @@ ThemeState.prototype.without = function(aState)
|
||||
newStates[stateName] = true;
|
||||
}
|
||||
|
||||
return ThemeState._cacheThemeState(new ThemeState(newStates));
|
||||
result = ThemeState._cacheThemeState(new ThemeState(newStates));
|
||||
|
||||
if (!firstTransform)
|
||||
firstTransform = CPThemeWithoutTransform[this._stateNameString] = {};
|
||||
|
||||
firstTransform[aState._stateNameString] = result;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
ThemeState.prototype.and = function(aState)
|
||||
{
|
||||
return CPThemeState(this, aState);
|
||||
var firstTransform = CPThemeAndTransform[this._stateNameString],
|
||||
result;
|
||||
|
||||
if (firstTransform)
|
||||
{
|
||||
result = firstTransform[aState._stateNameString];
|
||||
|
||||
if (result)
|
||||
return result;
|
||||
}
|
||||
|
||||
result = CPThemeState(this, aState);
|
||||
|
||||
if (!firstTransform)
|
||||
firstTransform = CPThemeAndTransform[this._stateNameString] = {};
|
||||
|
||||
firstTransform[aState._stateNameString] = result;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
var CPThemeStates = {};
|
||||
var CPThemeStates = {},
|
||||
CPThemeWithoutTransform = {},
|
||||
CPThemeAndTransform = {};
|
||||
|
||||
ThemeState._cacheThemeState = function(aState)
|
||||
{
|
||||
// We do this caching so themeState equality works. Basically, doing CPThemeState('foo+bar') === CPThemeState('bar', 'foo') will return true.
|
||||
var themeState = CPThemeStates[String(aState)];
|
||||
|
||||
if (themeState === undefined)
|
||||
{
|
||||
themeState = aState;
|
||||
CPThemeStates[String(themeState)] = themeState;
|
||||
}
|
||||
|
||||
return themeState;
|
||||
}
|
||||
|
||||
@@ -439,14 +481,17 @@ function CPThemeState()
|
||||
throw "CPThemeState() must be called with at least one string argument";
|
||||
|
||||
var themeState;
|
||||
|
||||
if (arguments.length === 1 && typeof arguments[0] === 'string')
|
||||
{
|
||||
themeState = CPThemeStates[arguments[0]];
|
||||
|
||||
if (themeState !== undefined)
|
||||
return themeState;
|
||||
}
|
||||
|
||||
var stateNames = {};
|
||||
|
||||
for (var argIndex = 0; argIndex < arguments.length; argIndex++)
|
||||
{
|
||||
if (arguments[argIndex] === [CPNull null] || !arguments[argIndex])
|
||||
@@ -458,12 +503,14 @@ function CPThemeState()
|
||||
{
|
||||
if (!arguments[argIndex]._stateNames.hasOwnProperty(stateName))
|
||||
continue;
|
||||
|
||||
stateNames[stateName] = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var allNames = arguments[argIndex].split('+');
|
||||
|
||||
for (var nameIndex = 0; nameIndex < allNames.length; nameIndex++)
|
||||
stateNames[allNames[nameIndex]] = true;
|
||||
}
|
||||
@@ -523,6 +570,9 @@ CPThemeStateControlSizeRegular = CPThemeState("controlSizeRegular");
|
||||
CPThemeStateControlSizeSmall = CPThemeState("controlSizeSmall");
|
||||
CPThemeStateControlSizeMini = CPThemeState("controlSizeMini");
|
||||
|
||||
CPThemeStateNormalString = String(CPThemeStateNormal);
|
||||
|
||||
|
||||
@implementation _CPThemeAttribute : CPObject
|
||||
{
|
||||
CPString _name;
|
||||
@@ -533,7 +583,7 @@ CPThemeStateControlSizeMini = CPThemeState("controlSizeMini");
|
||||
_CPThemeAttribute _themeDefaultAttribute;
|
||||
}
|
||||
|
||||
- (id)initWithName:(CPString)aName defaultValue:(id)aDefaultValue
|
||||
- (id)initWithName:(CPString)aName defaultValue:(id)aDefaultValue defaultAttribute:(_CPThemeAttribute)aDefaultAttribute
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
@@ -542,7 +592,9 @@ CPThemeStateControlSizeMini = CPThemeState("controlSizeMini");
|
||||
_cache = { };
|
||||
_name = aName;
|
||||
_defaultValue = aDefaultValue;
|
||||
_values = @{};
|
||||
|
||||
if (aDefaultAttribute)
|
||||
_themeDefaultAttribute = aDefaultAttribute;
|
||||
}
|
||||
|
||||
return self;
|
||||
@@ -563,24 +615,41 @@ CPThemeStateControlSizeMini = CPThemeState("controlSizeMini");
|
||||
return [_values count] > 0;
|
||||
}
|
||||
|
||||
- (void)setValue:(id)aValue
|
||||
- (_CPThemeAttribute)attributeBySettingValue:(id)aValue
|
||||
{
|
||||
_cache = {};
|
||||
var attribute = [[_CPThemeAttribute alloc] initWithName:_name defaultValue:_defaultValue defaultAttribute:_themeDefaultAttribute];
|
||||
|
||||
if (aValue === undefined || aValue === nil)
|
||||
_values = @{};
|
||||
else
|
||||
_values = @{ String(CPThemeStateNormal): aValue };
|
||||
if (aValue !== undefined && aValue !== nil)
|
||||
attribute._values = @{ CPThemeStateNormalString: aValue };
|
||||
|
||||
return attribute;
|
||||
}
|
||||
|
||||
- (void)setValue:(id)aValue forState:(ThemeState)aState
|
||||
- (_CPThemeAttribute)attributeBySettingValue:(id)aValue forState:(ThemeState)aState
|
||||
{
|
||||
_cache = { };
|
||||
var shouldRemoveValue = aValue === undefined || aValue === nil,
|
||||
attribute = [[_CPThemeAttribute alloc] initWithName:_name defaultValue:_defaultValue defaultAttribute:_themeDefaultAttribute],
|
||||
values = _values;
|
||||
|
||||
if ((aValue === undefined) || (aValue === nil))
|
||||
[_values removeObjectForKey:String(aState)];
|
||||
else
|
||||
[_values setObject:aValue forKey:String(aState)];
|
||||
if (values != null)
|
||||
{
|
||||
values = [values copy];
|
||||
|
||||
if (shouldRemoveValue)
|
||||
[values removeObjectForKey:String(aState)];
|
||||
else
|
||||
[values setObject:aValue forKey:String(aState)];
|
||||
|
||||
attribute._values = values;
|
||||
}
|
||||
else if (!shouldRemoveValue)
|
||||
{
|
||||
values = [[CPDictionary alloc] init];
|
||||
[values setObject:aValue forKey:String(aState)];
|
||||
attribute._values = values;
|
||||
}
|
||||
|
||||
return attribute;
|
||||
}
|
||||
|
||||
- (id)value
|
||||
@@ -605,7 +674,7 @@ CPThemeStateControlSizeMini = CPThemeState("controlSizeMini");
|
||||
if (aState._stateNameCount > 1)
|
||||
{
|
||||
var states = [_values allKeys],
|
||||
count = states.length,
|
||||
count = states ? states.length : 0,
|
||||
largestThemeState = 0;
|
||||
|
||||
while (count--)
|
||||
@@ -643,27 +712,41 @@ CPThemeStateControlSizeMini = CPThemeState("controlSizeMini");
|
||||
return value;
|
||||
}
|
||||
|
||||
- (void)setParentAttribute:(_CPThemeAttribute)anAttribute
|
||||
- (_CPThemeAttribute)attributeBySettingParentAttribute:(_CPThemeAttribute)anAttribute
|
||||
{
|
||||
if (_themeDefaultAttribute === anAttribute)
|
||||
return;
|
||||
return self;
|
||||
|
||||
_cache = { };
|
||||
_themeDefaultAttribute = anAttribute;
|
||||
var attribute = [[_CPThemeAttribute alloc] initWithName:_name defaultValue:_defaultValue defaultAttribute:anAttribute];
|
||||
|
||||
attribute._values = [_values copy];
|
||||
|
||||
return attribute;
|
||||
}
|
||||
|
||||
- (_CPThemeAttribute)attributeMergedWithAttribute:(_CPThemeAttribute)anAttribute
|
||||
{
|
||||
var mergedAttribute = [[_CPThemeAttribute alloc] initWithName:_name defaultValue:_defaultValue];
|
||||
var mergedAttribute = [[_CPThemeAttribute alloc] initWithName:_name defaultValue:_defaultValue defaultAttribute:_themeDefaultAttribute];
|
||||
|
||||
mergedAttribute._values = [_values copy];
|
||||
[mergedAttribute._values addEntriesFromDictionary:anAttribute._values];
|
||||
|
||||
if (anAttribute._values)
|
||||
mergedAttribute._values ? [mergedAttribute._values addEntriesFromDictionary:anAttribute._values] : [anAttribute._values copy];
|
||||
|
||||
return mergedAttribute;
|
||||
}
|
||||
|
||||
- (CPString)description
|
||||
{
|
||||
return [super description] + @" Name: " + _name + @", defaultAttribute: " + _themeDefaultAttribute + @", defaultValue: " + _defaultValue + @", values: " + _values;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
// This is used to pass 'parrentAttribute' to the coder
|
||||
var ParentAttributeForCoder = nil;
|
||||
|
||||
@implementation _CPThemeAttribute (CPCoding)
|
||||
|
||||
- (id)initWithCoder:(CPCoder)aCoder
|
||||
@@ -677,13 +760,16 @@ CPThemeStateControlSizeMini = CPThemeState("controlSizeMini");
|
||||
_name = [aCoder decodeObjectForKey:@"name"];
|
||||
_defaultValue = [aCoder decodeObjectForKey:@"defaultValue"];
|
||||
_values = @{};
|
||||
_themeDefaultAttribute = ParentAttributeForCoder;
|
||||
|
||||
if ([aCoder containsValueForKey:@"value"])
|
||||
{
|
||||
var state = String(CPThemeStateNormal);
|
||||
var state;
|
||||
|
||||
if ([aCoder containsValueForKey:@"state"])
|
||||
state = [aCoder decodeObjectForKey:@"state"];
|
||||
else
|
||||
state = CPThemeStateNormalString
|
||||
|
||||
[_values setObject:[aCoder decodeObjectForKey:"value"] forKey:state];
|
||||
}
|
||||
@@ -711,7 +797,7 @@ CPThemeStateControlSizeMini = CPThemeState("controlSizeMini");
|
||||
[aCoder encodeObject:_defaultValue forKey:@"defaultValue"];
|
||||
|
||||
var keys = [_values allKeys],
|
||||
count = keys.length;
|
||||
count = keys ? keys.length : 0;
|
||||
|
||||
if (count === 1)
|
||||
{
|
||||
@@ -749,7 +835,7 @@ function CPThemeAttributeEncode(aCoder, aThemeAttribute)
|
||||
{
|
||||
var state = [values allKeys][0];
|
||||
|
||||
if (state === String(CPThemeStateNormal))
|
||||
if (state === CPThemeStateNormalString)
|
||||
{
|
||||
[aCoder encodeObject:[values objectForKey:state] forKey:key];
|
||||
|
||||
@@ -767,30 +853,24 @@ function CPThemeAttributeEncode(aCoder, aThemeAttribute)
|
||||
return NO;
|
||||
}
|
||||
|
||||
function CPThemeAttributeDecode(aCoder, anAttributeName, aDefaultValue, aTheme, aClass)
|
||||
function CPThemeAttributeDecode(aCoder, attribute)
|
||||
{
|
||||
var key = "$a" + anAttributeName;
|
||||
var key = "$a" + attribute._name;
|
||||
|
||||
if (![aCoder containsValueForKey:key])
|
||||
var attribute = [[_CPThemeAttribute alloc] initWithName:anAttributeName defaultValue:aDefaultValue];
|
||||
|
||||
else
|
||||
if ([aCoder containsValueForKey:key])
|
||||
{
|
||||
var attribute = [aCoder decodeObjectForKey:key];
|
||||
ParentAttributeForCoder = attribute._themeDefaultAttribute;
|
||||
|
||||
if (!attribute || !attribute.isa || ![attribute isKindOfClass:[_CPThemeAttribute class]])
|
||||
{
|
||||
var themeAttribute = [[_CPThemeAttribute alloc] initWithName:anAttributeName defaultValue:aDefaultValue];
|
||||
var decodedAttribute = [aCoder decodeObjectForKey:key];
|
||||
|
||||
[themeAttribute setValue:attribute];
|
||||
ParentAttributeForCoder = nil;
|
||||
|
||||
attribute = themeAttribute;
|
||||
}
|
||||
if (!decodedAttribute || !decodedAttribute.isa || ![decodedAttribute isKindOfClass:[_CPThemeAttribute class]])
|
||||
attribute = [attribute attributeBySettingValue:decodedAttribute];
|
||||
else
|
||||
attribute = decodedAttribute;
|
||||
}
|
||||
|
||||
if (aTheme && aClass)
|
||||
[attribute setParentAttribute:[aTheme attributeWithName:anAttributeName forClass:aClass]];
|
||||
|
||||
return attribute;
|
||||
}
|
||||
|
||||
|
||||
@@ -64,12 +64,10 @@
|
||||
*/
|
||||
- (CPArray)themeNames
|
||||
{
|
||||
var names = [];
|
||||
|
||||
for (var i = 0; i < _themes.length; ++i)
|
||||
names.push(_themes[i].substring(0, _themes[i].indexOf(".keyedtheme")));
|
||||
|
||||
return names;
|
||||
return [_themes arrayByApplyingBlock:function(theme)
|
||||
{
|
||||
return theme.substring(0, theme.indexOf(".keyedtheme"));
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)loadWithDelegate:(id)aDelegate
|
||||
|
||||
+4
-10
@@ -448,14 +448,14 @@ CPTokenFieldDeleteButtonType = 1;
|
||||
element.style.width = CGRectGetWidth(contentRect) + "px";
|
||||
element.style.height = [font defaultLineHeightForFont] + "px";
|
||||
|
||||
window.setTimeout(function()
|
||||
[[CPRunLoop mainRunLoop] performBlock:function()
|
||||
{
|
||||
[_tokenScrollView documentView]._DOMElement.appendChild(element);
|
||||
|
||||
//post CPControlTextDidBeginEditingNotification
|
||||
[self textDidBeginEditing:[CPNotification notificationWithName:CPControlTextDidBeginEditingNotification object:self userInfo:nil]];
|
||||
|
||||
window.setTimeout(function()
|
||||
[[CPRunLoop mainRunLoop] performBlock:function()
|
||||
{
|
||||
// This will prevent to jump to the focused element
|
||||
var previousScrollingOrigin = [self _scrollToVisibleRectAndReturnPreviousOrigin];
|
||||
@@ -465,10 +465,10 @@ CPTokenFieldDeleteButtonType = 1;
|
||||
[self _restorePreviousScrollingOrigin:previousScrollingOrigin];
|
||||
|
||||
CPTokenFieldInputOwner = self;
|
||||
}, 0.0);
|
||||
} argument:nil order:0 modes:[CPDefaultRunLoopMode]];
|
||||
|
||||
[self textDidFocus:[CPNotification notificationWithName:CPTextFieldDidFocusNotification object:self userInfo:nil]];
|
||||
}, 0.0);
|
||||
} argument:nil order:0 modes:[CPDefaultRunLoopMode]];
|
||||
|
||||
[[[self window] platformWindow] _propagateCurrentDOMEvent:YES];
|
||||
|
||||
@@ -1475,9 +1475,6 @@ CPTokenFieldDeleteButtonType = 1;
|
||||
|
||||
- (BOOL)setThemeState:(ThemeState)aState
|
||||
{
|
||||
if (aState.isa && [aState isKindOfClass:CPArray])
|
||||
aState = CPThemeState.apply(null, aState);
|
||||
|
||||
var r = [super setThemeState:aState];
|
||||
|
||||
// Share hover state with the disclosure and delete buttons.
|
||||
@@ -1492,9 +1489,6 @@ CPTokenFieldDeleteButtonType = 1;
|
||||
|
||||
- (BOOL)unsetThemeState:(ThemeState)aState
|
||||
{
|
||||
if (aState.isa && [aState isKindOfClass:CPArray])
|
||||
aState = CPThemeState.apply(null, aState);
|
||||
|
||||
var r = [super unsetThemeState:aState];
|
||||
|
||||
// Share hover state with the disclosure and delete button.
|
||||
|
||||
+4
-5
@@ -459,11 +459,10 @@ var CPToolbarsByIdentifier = nil,
|
||||
/* @ignore */
|
||||
- (id)_itemsWithIdentifiers:(CPArray)identifiers
|
||||
{
|
||||
var items = [];
|
||||
for (var i = 0; i < identifiers.length; i++)
|
||||
[items addObject:[self _itemForItemIdentifier:identifiers[i] willBeInsertedIntoToolbar:NO]];
|
||||
|
||||
return items;
|
||||
return [identifiers arrayByApplyingBlock:function(identifier)
|
||||
{
|
||||
return [self _itemForItemIdentifier:identifier willBeInsertedIntoToolbar:NO];
|
||||
}];
|
||||
}
|
||||
|
||||
/* @ignore */
|
||||
|
||||
@@ -0,0 +1,164 @@
|
||||
/*
|
||||
* CPTrackingArea.j
|
||||
* AppKit
|
||||
*
|
||||
* Created by Didier Korthoudt.
|
||||
* Copyright 2015, Cappuccino Project.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
@import <Foundation/Foundation.j>
|
||||
|
||||
@class CPView
|
||||
|
||||
/* @group CPTrackingAreaOptions */
|
||||
@typedef CPTrackingAreaOptions
|
||||
CPTrackingMouseEnteredAndExited = 1 << 1;
|
||||
CPTrackingMouseMoved = 1 << 2;
|
||||
CPTrackingCursorUpdate = 1 << 3;
|
||||
CPTrackingActiveWhenFirstResponder = 1 << 4;
|
||||
CPTrackingActiveInKeyWindow = 1 << 5;
|
||||
CPTrackingActiveInActiveApp = 1 << 6;
|
||||
CPTrackingActiveAlways = 1 << 7;
|
||||
CPTrackingAssumeInside = 1 << 8;
|
||||
CPTrackingInVisibleRect = 1 << 9;
|
||||
CPTrackingEnabledDuringMouseDrag = 1 << 10;
|
||||
|
||||
var CPTrackingAreaViewRectKey = @"CPTrackinkAreaViewRectKey",
|
||||
CPTrackingAreaOptionsKey = @"CPTrackingAreaOptionsKey",
|
||||
CPTrackingAreaOwnerKey = @"CPTrackingAreaOwnerKey",
|
||||
CPTrackingAreaUserInfoKey = @"CPTrackingAreaUserInfoKey",
|
||||
CPTrackingAreaReferencingViewKey = @"CPTrackingAreaReferencingViewKey",
|
||||
CPTrackingAreaWindowRect = @"CPTrackingAreaWindowRect";
|
||||
|
||||
CPTrackingOwnerImplementsMouseEntered = 1 << 1;
|
||||
CPTrackingOwnerImplementsMouseExited = 1 << 2;
|
||||
CPTrackingOwnerImplementsMouseMoved = 1 << 3;
|
||||
CPTrackingOwnerImplementsCursorUpdate = 1 << 4;
|
||||
|
||||
/*!
|
||||
@ingroup appkit
|
||||
|
||||
A CPTrackingArea defines a region of view that generates mouse-tracking and
|
||||
cursor-update events when the mouse is over that region.
|
||||
*/
|
||||
@implementation CPTrackingArea : CPObject
|
||||
{
|
||||
CGRect _viewRect @accessors(getter=rect);
|
||||
CPTrackingAreaOptions _options @accessors(getter=options);
|
||||
id _owner @accessors(getter=owner);
|
||||
CPDictionary _userInfo @accessors(getter=userInfo);
|
||||
|
||||
CPView _referencingView @accessors(property=view);
|
||||
CGRect _windowRect @accessors(getter=windowRect);
|
||||
|
||||
unsigned _implementedOwnerMethods @accessors(getter=implementedOwnerMethods);
|
||||
}
|
||||
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Initialization
|
||||
|
||||
/*!
|
||||
Initializes and returns an object defining a region of a view to receive mouse-tracking events, mouse-moved events, cursor-update events, or possibly
|
||||
all these events.
|
||||
*/
|
||||
- (CPTrackingArea)initWithRect:(CGRect)aRect options:(CPTrackingAreaOptions)options owner:(id)owner userInfo:(CPDictionary)userInfo
|
||||
{
|
||||
if (owner === nil)
|
||||
[CPException raise:CPInternalInconsistencyException reason:"No owner specified"];
|
||||
|
||||
if (options === 0)
|
||||
[CPException raise:CPInternalInconsistencyException reason:"Invalid CPTrackingArea options"];
|
||||
|
||||
// Check options:
|
||||
// - at least one of CPTrackingMouseEnteredAndExited, CPTrackingMouseMoved, CPTrackingCursorUpdate
|
||||
// - exactly one of CPTrackingActiveWhenFirstResponder, CPTrackingActiveInKeyWindow, CPTrackingActiveInActiveApp, CPTrackingActiveAlways
|
||||
// - no check on CPTrackingAssumeInside, CPTrackingInVisibleRect, CPTrackingEnableDuringMouseDrag
|
||||
|
||||
if (!((options & CPTrackingMouseEnteredAndExited) || (options & CPTrackingMouseMoved) || (options & CPTrackingCursorUpdate)))
|
||||
[CPException raise:CPInternalInconsistencyException reason:"Invalid CPTrackingAreaOptions: must use at least one of [CPTrackingMouseEnteredAndExited | CPTrackingMouseMoved | CPTrackingCursorUpdate]"];
|
||||
|
||||
if ((((options & CPTrackingActiveWhenFirstResponder) > 0) + ((options & CPTrackingActiveInKeyWindow) > 0) + ((options & CPTrackingActiveInActiveApp) > 0) + ((options & CPTrackingActiveAlways) > 0)) !== 1)
|
||||
[CPException raise:CPInternalInconsistencyException reason:"Tracking area options may only specify one of [CPTrackingActiveWhenFirstResponder | CPTrackingActiveInKeyWindow | CPTrackingActiveInActiveApp | CPTrackingActiveAlways]."];
|
||||
|
||||
if (self = [super init])
|
||||
{
|
||||
_viewRect = aRect;
|
||||
_options = options;
|
||||
_owner = owner;
|
||||
_userInfo = userInfo;
|
||||
|
||||
// Cache owner implemented methods
|
||||
|
||||
if ([_owner respondsToSelector:@selector(mouseEntered:)])
|
||||
_implementedOwnerMethods |= CPTrackingOwnerImplementsMouseEntered;
|
||||
|
||||
if ([_owner respondsToSelector:@selector(mouseExited:)])
|
||||
_implementedOwnerMethods |= CPTrackingOwnerImplementsMouseExited;
|
||||
|
||||
if ([_owner respondsToSelector:@selector(mouseMoved:)])
|
||||
_implementedOwnerMethods |= CPTrackingOwnerImplementsMouseMoved;
|
||||
|
||||
if ([_owner respondsToSelector:@selector(cursorUpdate:)])
|
||||
_implementedOwnerMethods |= CPTrackingOwnerImplementsCursorUpdate;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Implementation
|
||||
|
||||
- (void)_updateWindowRect
|
||||
{
|
||||
_windowRect = [_referencingView convertRect:((_options & CPTrackingInVisibleRect) ? [_referencingView visibleRect] : _viewRect) toView:[[_referencingView window] _windowView]];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark CPCoding
|
||||
|
||||
@implementation CPTrackingArea (CPCoding)
|
||||
|
||||
- (id)initWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
if (self = [super init])
|
||||
{
|
||||
_viewRect = [aCoder decodeObjectForKey:CPTrackingAreaViewRectKey];
|
||||
_options = [aCoder decodeObjectForKey:CPTrackingAreaOptionsKey];
|
||||
_owner = [aCoder decodeObjectForKey:CPTrackingAreaOwnerKey];
|
||||
_userInfo = [aCoder decodeObjectForKey:CPTrackingAreaUserInfoKey];
|
||||
_referencingView = [aCoder decodeObjectForKey:CPTrackingAreaReferencingViewKey];
|
||||
_windowRect = [aCoder decodeObjectForKey:CPTrackingAreaWindowRect];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)encodeWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
[aCoder encodeObject:_viewRect forKey:CPTrackingAreaViewRectKey];
|
||||
[aCoder encodeObject:_options forKey:CPTrackingAreaOptionsKey];
|
||||
[aCoder encodeObject:_owner forKey:CPTrackingAreaOwnerKey];
|
||||
[aCoder encodeObject:_userInfo forKey:CPTrackingAreaUserInfoKey];
|
||||
[aCoder encodeObject:_referencingView forKey:CPTrackingAreaReferencingViewKey];
|
||||
[aCoder encodeObject:_windowRect forKey:CPTrackingAreaWindowRect];
|
||||
}
|
||||
|
||||
@end
|
||||
+379
-315
@@ -24,12 +24,15 @@
|
||||
@import <Foundation/CPObjJRuntime.j>
|
||||
@import <Foundation/CPSet.j>
|
||||
|
||||
@import "_CPObject+Theme.j"
|
||||
@import "CGAffineTransform.j"
|
||||
@import "CGGeometry.j"
|
||||
@import "CPAppearance.j"
|
||||
@import "CPColor.j"
|
||||
@import "CPGraphicsContext.j"
|
||||
@import "CPResponder.j"
|
||||
@import "CPTheme.j"
|
||||
@import "CPTrackingArea.j"
|
||||
@import "CPWindow_Constants.j"
|
||||
@import "_CPDisplayServer.j"
|
||||
|
||||
@@ -112,8 +115,7 @@ CPViewMaxYMargin = 32;
|
||||
CPViewBoundsDidChangeNotification = @"CPViewBoundsDidChangeNotification";
|
||||
CPViewFrameDidChangeNotification = @"CPViewFrameDidChangeNotification";
|
||||
|
||||
var CachedNotificationCenter = nil,
|
||||
CachedThemeAttributes = nil;
|
||||
var CachedNotificationCenter = nil;
|
||||
|
||||
#if PLATFORM(DOM)
|
||||
var DOMElementPrototype = nil,
|
||||
@@ -127,11 +129,11 @@ var DOMElementPrototype = nil,
|
||||
|
||||
var CPViewFlags = { },
|
||||
CPViewHasCustomDrawRect = 1 << 0,
|
||||
CPViewHasCustomLayoutSubviews = 1 << 1;
|
||||
CPViewHasCustomLayoutSubviews = 1 << 1,
|
||||
CPViewHasCustomViewWillLayout = 1 << 2;
|
||||
|
||||
var CPViewHighDPIDrawingEnabled = YES;
|
||||
|
||||
|
||||
/*!
|
||||
@ingroup appkit
|
||||
@class CPView
|
||||
@@ -149,7 +151,7 @@ var CPViewHighDPIDrawingEnabled = YES;
|
||||
appearance. Other methods of CPView and CPResponder can
|
||||
also be overridden to handle user generated events.
|
||||
*/
|
||||
@implementation CPView : CPResponder
|
||||
@implementation CPView : CPResponder <CPTheme>
|
||||
{
|
||||
CPWindow _window;
|
||||
|
||||
@@ -218,12 +220,6 @@ var CPViewHighDPIDrawingEnabled = YES;
|
||||
BOOL _needsLayout;
|
||||
JSObject _ephemeralSubviews;
|
||||
|
||||
// Theming Support
|
||||
CPTheme _theme;
|
||||
CPString _themeClass;
|
||||
JSObject _themeAttributes;
|
||||
unsigned _themeState;
|
||||
|
||||
JSObject _ephemeralSubviewsForNames;
|
||||
CPSet _ephereralSubviews;
|
||||
|
||||
@@ -241,6 +237,13 @@ var CPViewHighDPIDrawingEnabled = YES;
|
||||
|
||||
BOOL _isObserving;
|
||||
|
||||
BOOL _allowsVibrancy @accessors(property=allowsVibrancy);
|
||||
CPAppearance _appearance @accessors(getter=appearance);
|
||||
CPAppearance _effectiveAppearance;
|
||||
|
||||
CPMutableArray _trackingAreas @accessors(getter=trackingAreas, copy);
|
||||
BOOL _inhibitUpdateTrackingAreas;
|
||||
|
||||
id _animator;
|
||||
CPDictionary _animationsDictionary;
|
||||
}
|
||||
@@ -294,27 +297,6 @@ var CPViewHighDPIDrawingEnabled = YES;
|
||||
return CPViewHighDPIDrawingEnabled;
|
||||
}
|
||||
|
||||
- (void)_setupViewFlags
|
||||
{
|
||||
var theClass = [self class],
|
||||
classUID = [theClass UID];
|
||||
|
||||
if (CPViewFlags[classUID] === undefined)
|
||||
{
|
||||
var flags = 0;
|
||||
|
||||
if ([theClass instanceMethodForSelector:@selector(drawRect:)] !== [CPView instanceMethodForSelector:@selector(drawRect:)])
|
||||
flags |= CPViewHasCustomDrawRect;
|
||||
|
||||
if ([theClass instanceMethodForSelector:@selector(layoutSubviews)] !== [CPView instanceMethodForSelector:@selector(layoutSubviews)])
|
||||
flags |= CPViewHasCustomLayoutSubviews;
|
||||
|
||||
CPViewFlags[classUID] = flags;
|
||||
}
|
||||
|
||||
_viewClassFlags = CPViewFlags[classUID];
|
||||
}
|
||||
|
||||
+ (CPSet)keyPathsForValuesAffectingFrame
|
||||
{
|
||||
return [CPSet setWithObjects:@"frameOrigin", @"frameSize"];
|
||||
@@ -330,6 +312,31 @@ var CPViewHighDPIDrawingEnabled = YES;
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (void)_setupViewFlags
|
||||
{
|
||||
var theClass = [self class],
|
||||
classUID = [theClass UID];
|
||||
|
||||
if (CPViewFlags[classUID] === undefined)
|
||||
{
|
||||
var flags = 0;
|
||||
|
||||
if ([theClass instanceMethodForSelector:@selector(drawRect:)] !== [CPView instanceMethodForSelector:@selector(drawRect:)]
|
||||
|| [theClass instanceMethodForSelector:@selector(viewWillDraw)] !== [CPView instanceMethodForSelector:@selector(viewWillDraw)])
|
||||
flags |= CPViewHasCustomDrawRect;
|
||||
|
||||
if ([theClass instanceMethodForSelector:@selector(viewWillLayout)] !== [CPView instanceMethodForSelector:@selector(viewWillLayout)])
|
||||
flags |= CPViewHasCustomViewWillLayout;
|
||||
|
||||
if ([theClass instanceMethodForSelector:@selector(layoutSubviews)] !== [CPView instanceMethodForSelector:@selector(layoutSubviews)])
|
||||
flags |= CPViewHasCustomLayoutSubviews;
|
||||
|
||||
CPViewFlags[classUID] = flags;
|
||||
}
|
||||
|
||||
_viewClassFlags = CPViewFlags[classUID];
|
||||
}
|
||||
|
||||
- (id)init
|
||||
{
|
||||
return [self initWithFrame:CGRectMakeZero()];
|
||||
@@ -352,6 +359,8 @@ var CPViewHighDPIDrawingEnabled = YES;
|
||||
_registeredDraggedTypes = [CPSet set];
|
||||
_registeredDraggedTypesArray = [];
|
||||
|
||||
_trackingAreas = [];
|
||||
|
||||
_tag = -1;
|
||||
|
||||
_frame = CGRectMakeCopy(aFrame);
|
||||
@@ -387,7 +396,6 @@ var CPViewHighDPIDrawingEnabled = YES;
|
||||
_animationsDictionary = @{};
|
||||
|
||||
[self _setupViewFlags];
|
||||
|
||||
[self _loadThemeAttributes];
|
||||
}
|
||||
|
||||
@@ -804,8 +812,30 @@ var CPViewHighDPIDrawingEnabled = YES;
|
||||
[aWindow _noteRegisteredDraggedTypes:_registeredDraggedTypes];
|
||||
}
|
||||
|
||||
// View must be removed from the current window viewsWithTrackingAreas
|
||||
if (_window && (_trackingAreas.length > 0))
|
||||
[_window _removeTrackingAreaView:self];
|
||||
|
||||
_window = aWindow;
|
||||
|
||||
if (_window)
|
||||
{
|
||||
var owners;
|
||||
|
||||
if (_trackingAreas.length > 0)
|
||||
{
|
||||
// View must be added to the new window viewsWithTrackingAreas
|
||||
[_window _addTrackingAreaView:self];
|
||||
owners = [self _calcTrackingAreaOwners];
|
||||
}
|
||||
else
|
||||
owners = [self];
|
||||
|
||||
// Notify that view tracking areas should be updated
|
||||
// Cocoa doesn't notify on leaving a window
|
||||
[self _updateTrackingAreasForOwners:owners];
|
||||
}
|
||||
|
||||
var count = [_subviews count];
|
||||
|
||||
while (count--)
|
||||
@@ -845,9 +875,8 @@ var CPViewHighDPIDrawingEnabled = YES;
|
||||
*/
|
||||
- (void)viewDidMoveToSuperview
|
||||
{
|
||||
// if (_graphicsContext)
|
||||
[self setNeedsDisplay:YES];
|
||||
|
||||
[self setNeedsLayout:YES];
|
||||
[self setNeedsDisplay:YES];
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -999,6 +1028,9 @@ var CPViewHighDPIDrawingEnabled = YES;
|
||||
|
||||
if (_isSuperviewAClipView)
|
||||
[[self superview] viewFrameChanged:[[CPNotification alloc] initWithName:CPViewFrameDidChangeNotification object:self userInfo:nil]];
|
||||
|
||||
if (!_inhibitUpdateTrackingAreas)
|
||||
[self _updateTrackingAreas];
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -1069,6 +1101,9 @@ var CPViewHighDPIDrawingEnabled = YES;
|
||||
|
||||
CPDOMDisplayServerSetStyleLeftTop(_DOMElement, transform, origin.x, origin.y);
|
||||
#endif
|
||||
|
||||
if (!_inhibitUpdateTrackingAreas && !_inhibitFrameAndBoundsChangedNotifications)
|
||||
[self _updateTrackingAreas];
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -1220,6 +1255,9 @@ var CPViewHighDPIDrawingEnabled = YES;
|
||||
|
||||
if (_isSuperviewAClipView && !_inhibitFrameAndBoundsChangedNotifications)
|
||||
[[self superview] viewFrameChanged:[[CPNotification alloc] initWithName:CPViewFrameDidChangeNotification object:self userInfo:nil]];
|
||||
|
||||
if (!_inhibitUpdateTrackingAreas && !_inhibitFrameAndBoundsChangedNotifications)
|
||||
[self _updateTrackingAreas];
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -1266,6 +1304,9 @@ var CPViewHighDPIDrawingEnabled = YES;
|
||||
|
||||
if (_isSuperviewAClipView)
|
||||
[[self superview] viewBoundsChanged:[[CPNotification alloc] initWithName:CPViewBoundsDidChangeNotification object:self userInfo:nil]];
|
||||
|
||||
if (!_inhibitUpdateTrackingAreas)
|
||||
[self _updateTrackingAreas];
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -1331,6 +1372,9 @@ var CPViewHighDPIDrawingEnabled = YES;
|
||||
|
||||
if (_isSuperviewAClipView && !_inhibitFrameAndBoundsChangedNotifications)
|
||||
[[self superview] viewBoundsChanged:[[CPNotification alloc] initWithName:CPViewBoundsDidChangeNotification object:self userInfo:nil]];
|
||||
|
||||
if (!_inhibitUpdateTrackingAreas && !_inhibitFrameAndBoundsChangedNotifications)
|
||||
[self _updateTrackingAreas];
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -1372,6 +1416,9 @@ var CPViewHighDPIDrawingEnabled = YES;
|
||||
|
||||
if (_isSuperviewAClipView && !_inhibitFrameAndBoundsChangedNotifications)
|
||||
[[self superview] viewBoundsChanged:[[CPNotification alloc] initWithName:CPViewBoundsDidChangeNotification object:self userInfo:nil]];
|
||||
|
||||
if (!_inhibitUpdateTrackingAreas && !_inhibitFrameAndBoundsChangedNotifications)
|
||||
[self _updateTrackingAreas];
|
||||
}
|
||||
|
||||
|
||||
@@ -1826,7 +1873,7 @@ var CPViewHighDPIDrawingEnabled = YES;
|
||||
else if ([[self nextResponder] isKindOfClass:CPView])
|
||||
[super rightMouseDown:anEvent];
|
||||
else
|
||||
[[[anEvent window] platformWindow] _propagateContextMenuDOMEvent:YES];
|
||||
[[[anEvent window] platformWindow] _propagateContextMenuDOMEvent:NO];
|
||||
}
|
||||
|
||||
- (CPMenu)menuForEvent:(CPEvent)anEvent
|
||||
@@ -2600,7 +2647,7 @@ setBoundsOrigin:
|
||||
}
|
||||
|
||||
#if PLATFORM(DOM)
|
||||
if (_needToSetTransformMatrix)
|
||||
if (_needToSetTransformMatrix && _highDPIRatio !== 1)
|
||||
[_graphicsContext graphicsPort].setTransform(_highDPIRatio, 0, 0 , _highDPIRatio, 0, 0);
|
||||
#endif
|
||||
|
||||
@@ -2627,7 +2674,7 @@ setBoundsOrigin:
|
||||
|
||||
- (void)setNeedsLayout:(BOOL)needLayout
|
||||
{
|
||||
if (!(_viewClassFlags & CPViewHasCustomLayoutSubviews) || !needLayout)
|
||||
if (!needLayout)
|
||||
{
|
||||
_needsLayout = NO;
|
||||
return;
|
||||
@@ -2643,18 +2690,44 @@ setBoundsOrigin:
|
||||
return _needsLayout;
|
||||
}
|
||||
|
||||
- (void)layout
|
||||
{
|
||||
_needsLayout = NO;
|
||||
|
||||
if (_viewClassFlags & CPViewHasCustomViewWillLayout)
|
||||
[self viewWillLayout];
|
||||
|
||||
if (_viewClassFlags & CPViewHasCustomLayoutSubviews)
|
||||
[self layoutSubviews];
|
||||
|
||||
[self viewDidLayout];
|
||||
}
|
||||
|
||||
- (void)layoutIfNeeded
|
||||
{
|
||||
if (_needsLayout)
|
||||
{
|
||||
_needsLayout = NO;
|
||||
[self layout];
|
||||
}
|
||||
|
||||
[self layoutSubviews];
|
||||
}
|
||||
/*!
|
||||
@ignore
|
||||
*/
|
||||
- (void)viewWillLayout
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/*!
|
||||
@ignore
|
||||
*/
|
||||
- (void)viewDidLayout
|
||||
{
|
||||
[self _recomputeAppearance];
|
||||
}
|
||||
|
||||
- (void)layoutSubviews
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -3096,33 +3169,19 @@ setBoundsOrigin:
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@implementation CPView (Theming)
|
||||
#pragma mark Theme States
|
||||
|
||||
- (unsigned)themeState
|
||||
{
|
||||
return _themeState;
|
||||
}
|
||||
|
||||
- (BOOL)hasThemeState:(ThemeState)aState
|
||||
{
|
||||
if (aState.isa && [aState isKindOfClass:CPArray])
|
||||
return _themeState.hasThemeState.apply(_themeState, aState);
|
||||
|
||||
return _themeState.hasThemeState(aState);
|
||||
}
|
||||
#pragma mark Override
|
||||
|
||||
- (BOOL)setThemeState:(ThemeState)aState
|
||||
{
|
||||
if (aState && aState.isa && [aState isKindOfClass:CPArray])
|
||||
aState = CPThemeState.apply(null, aState);
|
||||
var shouldLayout = [super setThemeState:aState];
|
||||
|
||||
if (_themeState.hasThemeState(aState))
|
||||
if (!shouldLayout)
|
||||
return NO;
|
||||
|
||||
_themeState = CPThemeState(_themeState, aState);
|
||||
|
||||
[self setNeedsLayout];
|
||||
[self setNeedsLayout:YES];
|
||||
[self setNeedsDisplay:YES];
|
||||
|
||||
return YES;
|
||||
@@ -3130,26 +3189,35 @@ setBoundsOrigin:
|
||||
|
||||
- (BOOL)unsetThemeState:(ThemeState)aState
|
||||
{
|
||||
if (aState && aState.isa && [aState isKindOfClass:CPArray])
|
||||
aState = CPThemeState.apply(null, aState);
|
||||
var shouldLayout = [super unsetThemeState:aState];
|
||||
|
||||
var oldThemeState = _themeState;
|
||||
_themeState = _themeState.without(aState);
|
||||
|
||||
if (oldThemeState === _themeState)
|
||||
if (!shouldLayout)
|
||||
return NO;
|
||||
|
||||
[self setNeedsLayout];
|
||||
[self setNeedsLayout:YES];
|
||||
[self setNeedsDisplay:YES];
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void)setThemeClass:(CPString)theClass
|
||||
{
|
||||
[super setThemeClass:theClass];
|
||||
|
||||
[self setNeedsLayout];
|
||||
[self setNeedsDisplay:YES];
|
||||
}
|
||||
|
||||
|
||||
#pragma mark First responder
|
||||
|
||||
- (BOOL)becomeFirstResponder
|
||||
{
|
||||
var r = [super becomeFirstResponder];
|
||||
|
||||
if (r)
|
||||
[self _notifyViewDidBecomeFirstResponder];
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
@@ -3158,6 +3226,7 @@ setBoundsOrigin:
|
||||
[self setThemeState:CPThemeStateFirstResponder];
|
||||
|
||||
var count = [_subviews count];
|
||||
|
||||
while (count--)
|
||||
[_subviews[count] _notifyViewDidBecomeFirstResponder];
|
||||
}
|
||||
@@ -3165,8 +3234,10 @@ setBoundsOrigin:
|
||||
- (BOOL)resignFirstResponder
|
||||
{
|
||||
var r = [super resignFirstResponder];
|
||||
|
||||
if (r)
|
||||
[self _notifyViewDidResignFirstResponder];
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
@@ -3175,6 +3246,7 @@ setBoundsOrigin:
|
||||
[self unsetThemeState:CPThemeStateFirstResponder];
|
||||
|
||||
var count = [_subviews count];
|
||||
|
||||
while (count--)
|
||||
[_subviews[count] _notifyViewDidResignFirstResponder];
|
||||
}
|
||||
@@ -3184,6 +3256,7 @@ setBoundsOrigin:
|
||||
[self setThemeState:CPThemeStateKeyWindow];
|
||||
|
||||
var count = [_subviews count];
|
||||
|
||||
while (count--)
|
||||
[_subviews[count] _notifyWindowDidBecomeKey];
|
||||
}
|
||||
@@ -3193,172 +3266,35 @@ setBoundsOrigin:
|
||||
[self unsetThemeState:CPThemeStateKeyWindow];
|
||||
|
||||
var count = [_subviews count];
|
||||
|
||||
while (count--)
|
||||
[_subviews[count] _notifyWindowDidResignKey];
|
||||
}
|
||||
|
||||
#pragma mark Theme Attributes
|
||||
|
||||
+ (CPString)defaultThemeClass
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (CPString)themeClass
|
||||
{
|
||||
if (_themeClass)
|
||||
return _themeClass;
|
||||
|
||||
return [[self class] defaultThemeClass];
|
||||
}
|
||||
|
||||
- (void)setThemeClass:(CPString)theClass
|
||||
{
|
||||
_themeClass = theClass;
|
||||
|
||||
[self _loadThemeAttributes];
|
||||
|
||||
[self setNeedsLayout];
|
||||
[self setNeedsDisplay:YES];
|
||||
}
|
||||
|
||||
+ (CPDictionary)themeAttributes
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
+ (CPArray)_themeAttributes
|
||||
{
|
||||
if (!CachedThemeAttributes)
|
||||
CachedThemeAttributes = {};
|
||||
|
||||
var theClass = [self class],
|
||||
CPViewClass = [CPView class],
|
||||
attributes = [],
|
||||
nullValue = [CPNull null];
|
||||
|
||||
for (; theClass && theClass !== CPViewClass; theClass = [theClass superclass])
|
||||
{
|
||||
var cachedAttributes = CachedThemeAttributes[class_getName(theClass)];
|
||||
|
||||
if (cachedAttributes)
|
||||
{
|
||||
attributes = attributes.length ? attributes.concat(cachedAttributes) : attributes;
|
||||
CachedThemeAttributes[[self className]] = attributes;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
var attributeDictionary = [theClass themeAttributes];
|
||||
|
||||
if (!attributeDictionary)
|
||||
continue;
|
||||
|
||||
var attributeKeys = [attributeDictionary allKeys],
|
||||
attributeCount = attributeKeys.length;
|
||||
|
||||
while (attributeCount--)
|
||||
{
|
||||
var attributeName = attributeKeys[attributeCount],
|
||||
attributeValue = [attributeDictionary objectForKey:attributeName];
|
||||
|
||||
attributes.push(attributeValue === nullValue ? nil : attributeValue);
|
||||
attributes.push(attributeName);
|
||||
}
|
||||
}
|
||||
|
||||
return attributes;
|
||||
}
|
||||
|
||||
- (void)_loadThemeAttributes
|
||||
{
|
||||
var theClass = [self class],
|
||||
attributes = [theClass _themeAttributes],
|
||||
count = attributes.length;
|
||||
|
||||
if (!count)
|
||||
return;
|
||||
|
||||
var theme = [self theme],
|
||||
themeClass = [self themeClass];
|
||||
|
||||
_themeAttributes = {};
|
||||
|
||||
while (count--)
|
||||
{
|
||||
var attributeName = attributes[count--],
|
||||
attribute = [[_CPThemeAttribute alloc] initWithName:attributeName defaultValue:attributes[count]];
|
||||
|
||||
[attribute setParentAttribute:[theme attributeWithName:attributeName forClass:themeClass]];
|
||||
|
||||
_themeAttributes[attributeName] = attribute;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setTheme:(CPTheme)aTheme
|
||||
{
|
||||
if (_theme === aTheme)
|
||||
return;
|
||||
|
||||
_theme = aTheme;
|
||||
|
||||
[self viewDidChangeTheme];
|
||||
}
|
||||
|
||||
- (void)_setThemeIncludingDescendants:(CPTheme)aTheme
|
||||
{
|
||||
[self setTheme:aTheme];
|
||||
[[self subviews] makeObjectsPerformSelector:@selector(_setThemeIncludingDescendants:) withObject:aTheme];
|
||||
}
|
||||
|
||||
- (CPTheme)theme
|
||||
{
|
||||
return _theme;
|
||||
}
|
||||
|
||||
- (void)viewDidChangeTheme
|
||||
- (void)objectDidChangeTheme
|
||||
{
|
||||
if (!_themeAttributes)
|
||||
return;
|
||||
|
||||
var theme = [self theme],
|
||||
themeClass = [self themeClass];
|
||||
|
||||
for (var attributeName in _themeAttributes)
|
||||
if (_themeAttributes.hasOwnProperty(attributeName))
|
||||
[_themeAttributes[attributeName] setParentAttribute:[theme attributeWithName:attributeName forClass:themeClass]];
|
||||
[super objectDidChangeTheme];
|
||||
|
||||
[self setNeedsLayout];
|
||||
[self setNeedsDisplay:YES];
|
||||
}
|
||||
|
||||
- (CPDictionary)_themeAttributeDictionary
|
||||
{
|
||||
var dictionary = @{};
|
||||
|
||||
if (_themeAttributes)
|
||||
{
|
||||
var theme = [self theme];
|
||||
|
||||
for (var attributeName in _themeAttributes)
|
||||
if (_themeAttributes.hasOwnProperty(attributeName))
|
||||
[dictionary setObject:_themeAttributes[attributeName] forKey:attributeName];
|
||||
}
|
||||
|
||||
return dictionary;
|
||||
}
|
||||
|
||||
- (void)setValue:(id)aValue forThemeAttribute:(CPString)aName inState:(ThemeState)aState
|
||||
{
|
||||
if (aState.isa && [aState isKindOfClass:CPArray])
|
||||
aState = CPThemeState.apply(null, aState);
|
||||
|
||||
if (!_themeAttributes || !_themeAttributes[aName])
|
||||
[CPException raise:CPInvalidArgumentException reason:[self className] + " does not contain theme attribute '" + aName + "'"];
|
||||
|
||||
var currentValue = [self currentValueForThemeAttribute:aName];
|
||||
|
||||
[_themeAttributes[aName] setValue:aValue forState:aState];
|
||||
[super setValue:aValue forThemeAttribute:aName inState:aState];
|
||||
|
||||
if ([self currentValueForThemeAttribute:aName] === currentValue)
|
||||
return;
|
||||
@@ -3369,12 +3305,9 @@ setBoundsOrigin:
|
||||
|
||||
- (void)setValue:(id)aValue forThemeAttribute:(CPString)aName
|
||||
{
|
||||
if (!_themeAttributes || !_themeAttributes[aName])
|
||||
[CPException raise:CPInvalidArgumentException reason:[self className] + " does not contain theme attribute '" + aName + "'"];
|
||||
|
||||
var currentValue = [self currentValueForThemeAttribute:aName];
|
||||
|
||||
[_themeAttributes[aName] setValue:aValue];
|
||||
[super setValue:aValue forThemeAttribute:aName ];
|
||||
|
||||
if ([self currentValueForThemeAttribute:aName] === currentValue)
|
||||
return;
|
||||
@@ -3383,82 +3316,6 @@ setBoundsOrigin:
|
||||
[self setNeedsLayout];
|
||||
}
|
||||
|
||||
- (id)valueForThemeAttribute:(CPString)aName inState:(ThemeState)aState
|
||||
{
|
||||
if (aState.isa && [aState isKindOfClass:CPArray])
|
||||
aState = CPThemeState.apply(null, aState);
|
||||
|
||||
if (!_themeAttributes || !_themeAttributes[aName])
|
||||
[CPException raise:CPInvalidArgumentException reason:[self className] + " does not contain theme attribute '" + aName + "'"];
|
||||
|
||||
return [_themeAttributes[aName] valueForState:aState];
|
||||
}
|
||||
|
||||
- (id)valueForThemeAttribute:(CPString)aName
|
||||
{
|
||||
if (!_themeAttributes || !_themeAttributes[aName])
|
||||
[CPException raise:CPInvalidArgumentException reason:[self className] + " does not contain theme attribute '" + aName + "'"];
|
||||
|
||||
return [_themeAttributes[aName] value];
|
||||
}
|
||||
|
||||
- (id)currentValueForThemeAttribute:(CPString)aName
|
||||
{
|
||||
if (!_themeAttributes || !_themeAttributes[aName])
|
||||
[CPException raise:CPInvalidArgumentException reason:[self className] + " does not contain theme attribute '" + aName + "'"];
|
||||
|
||||
return [_themeAttributes[aName] valueForState:_themeState];
|
||||
}
|
||||
|
||||
- (BOOL)hasThemeAttribute:(CPString)aName
|
||||
{
|
||||
return (_themeAttributes && _themeAttributes[aName] !== undefined);
|
||||
}
|
||||
|
||||
/*!
|
||||
Registers theme values encoded in an array at runtime. The format of the data in the array
|
||||
is the same as that used by ThemeDescriptors.j, with the exception that you need to use
|
||||
CPColorWithImages() in place of PatternColor(). For more information see the comments
|
||||
at the top of ThemeDescriptors.j.
|
||||
|
||||
@param themeValues array of theme values
|
||||
*/
|
||||
- (void)registerThemeValues:(CPArray)themeValues
|
||||
{
|
||||
for (var i = 0; i < themeValues.length; ++i)
|
||||
{
|
||||
var attributeValueState = themeValues[i],
|
||||
attribute = attributeValueState[0],
|
||||
value = attributeValueState[1],
|
||||
state = attributeValueState[2];
|
||||
|
||||
if (state)
|
||||
[self setValue:value forThemeAttribute:attribute inState:state];
|
||||
else
|
||||
[self setValue:value forThemeAttribute:attribute];
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
Registers theme values encoded in an array at runtime. The format of the data in the array
|
||||
is the same as that used by ThemeDescriptors.j, with the exception that you need to use
|
||||
CPColorWithImages() in place of PatternColor(). The values in \c inheritedValues are
|
||||
registered first, then those in \c themeValues override/augment the inherited values.
|
||||
For more information see the comments at the top of ThemeDescriptors.j.
|
||||
|
||||
@param themeValues array of base theme values
|
||||
@param inheritedValues array of overridden/additional theme values
|
||||
*/
|
||||
- (void)registerThemeValues:(CPArray)themeValues inherit:(CPArray)inheritedValues
|
||||
{
|
||||
// Register inherited values first, then override those with the subtheme values.
|
||||
if (inheritedValues)
|
||||
[self registerThemeValues:inheritedValues];
|
||||
|
||||
if (themeValues)
|
||||
[self registerThemeValues:themeValues];
|
||||
}
|
||||
|
||||
- (CPView)createEphemeralSubviewNamed:(CPString)aViewName
|
||||
{
|
||||
return nil;
|
||||
@@ -3517,6 +3374,225 @@ setBoundsOrigin:
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@implementation CPView (Appearance)
|
||||
|
||||
/*! Returns the receiver's appearance if any, or ask the superview and returns it.
|
||||
*/
|
||||
- (CPAppearance)effectiveAppearance
|
||||
{
|
||||
if (_appearance)
|
||||
return _appearance;
|
||||
|
||||
return [_superview effectiveAppearance];
|
||||
}
|
||||
|
||||
- (void)setAppearance:(CPAppearance)anAppearance
|
||||
{
|
||||
if ([_appearance isEqual:anAppearance])
|
||||
return;
|
||||
|
||||
[self willChangeValueForKey:@"appearance"];
|
||||
_appearance = anAppearance;
|
||||
[self didChangeValueForKey:@"appearance"];
|
||||
|
||||
[self setNeedsLayout:YES];
|
||||
}
|
||||
|
||||
/*! @ignore
|
||||
*/
|
||||
- (void)_recomputeAppearance
|
||||
{
|
||||
var effectiveAppearance = [self effectiveAppearance];
|
||||
|
||||
if ([effectiveAppearance isEqual:[CPAppearance appearanceNamed:CPAppearanceNameAqua]])
|
||||
{
|
||||
[self setThemeState:CPThemeStateAppearanceAqua];
|
||||
[self unsetThemeState:CPThemeStateAppearanceLightContent];
|
||||
[self unsetThemeState:CPThemeStateAppearanceVibrantLight];
|
||||
[self unsetThemeState:CPThemeStateAppearanceVibrantDark];
|
||||
}
|
||||
else if ([effectiveAppearance isEqual:[CPAppearance appearanceNamed:CPAppearanceNameLightContent]])
|
||||
{
|
||||
[self unsetThemeState:CPThemeStateAppearanceAqua];
|
||||
[self setThemeState:CPThemeStateAppearanceLightContent];
|
||||
[self unsetThemeState:CPThemeStateAppearanceVibrantLight];
|
||||
[self unsetThemeState:CPThemeStateAppearanceVibrantDark];
|
||||
}
|
||||
else if ([effectiveAppearance isEqual:[CPAppearance appearanceNamed:CPAppearanceNameVibrantLight]])
|
||||
{
|
||||
[self unsetThemeState:CPThemeStateAppearanceAqua];
|
||||
[self unsetThemeState:CPThemeStateAppearanceLightContent];
|
||||
[self setThemeState:CPThemeStateAppearanceVibrantLight];
|
||||
[self unsetThemeState:CPThemeStateAppearanceVibrantDark];
|
||||
}
|
||||
else if ([effectiveAppearance isEqual:[CPAppearance appearanceNamed:CPAppearanceNameVibrantDark]])
|
||||
{
|
||||
[self unsetThemeState:CPThemeStateAppearanceAqua];
|
||||
[self unsetThemeState:CPThemeStateAppearanceLightContent];
|
||||
[self unsetThemeState:CPThemeStateAppearanceVibrantLight];
|
||||
[self setThemeState:CPThemeStateAppearanceVibrantDark];
|
||||
}
|
||||
else
|
||||
{
|
||||
[self unsetThemeState:CPThemeStateAppearanceAqua];
|
||||
[self unsetThemeState:CPThemeStateAppearanceLightContent];
|
||||
[self unsetThemeState:CPThemeStateAppearanceVibrantLight];
|
||||
[self unsetThemeState:CPThemeStateAppearanceVibrantDark];
|
||||
}
|
||||
|
||||
// var start = [CPDate new];
|
||||
|
||||
for (var i = 0, size = [_subviews count]; i < size; i++)
|
||||
{
|
||||
[[_subviews objectAtIndex:i] _recomputeAppearance];
|
||||
}
|
||||
// [_subviews makeObjectsPerformSelector:@selector(_recomputeAppearance)];
|
||||
|
||||
/* var now = [CPDate new];
|
||||
var elapsedSeconds = [now timeIntervalSinceReferenceDate] - [start timeIntervalSinceReferenceDate];
|
||||
|
||||
CPLog.trace(@"_recomputeAppearance " + [_subviews count] + " subviews in " + elapsedSeconds + @" seconds");
|
||||
*/}
|
||||
|
||||
|
||||
@end
|
||||
|
||||
@implementation CPView (TrackingAreaAdditions)
|
||||
|
||||
- (void)addTrackingArea:(CPTrackingArea)trackingArea
|
||||
{
|
||||
// Consistency check
|
||||
if (!trackingArea || [_trackingAreas containsObjectIdenticalTo:trackingArea])
|
||||
return;
|
||||
|
||||
if ([trackingArea view])
|
||||
[CPException raise:CPInternalInconsistencyException reason:"Tracking area has already been added to another view."];
|
||||
|
||||
[_trackingAreas addObject:trackingArea];
|
||||
[trackingArea setView:self];
|
||||
|
||||
if (_window)
|
||||
[_window _addTrackingArea:trackingArea];
|
||||
|
||||
[trackingArea _updateWindowRect];
|
||||
}
|
||||
|
||||
- (void)removeTrackingArea:(CPTrackingArea)trackingArea
|
||||
{
|
||||
// Consistency check
|
||||
if (!trackingArea)
|
||||
return;
|
||||
|
||||
if (![_trackingAreas containsObjectIdenticalTo:trackingArea])
|
||||
[CPException raise:CPInternalInconsistencyException reason:"Trying to remove unreferenced trackingArea"];
|
||||
|
||||
[self _removeTrackingArea:trackingArea];
|
||||
}
|
||||
|
||||
/*!
|
||||
Invoked automatically when the view’s geometry changes such that its tracking areas need to be recalculated.
|
||||
|
||||
You should override this method to remove out of date tracking areas and add recomputed tracking areas;
|
||||
|
||||
Cocoa calls this on every view, whereas they have tracking area(s) or not.
|
||||
Cappuccino behaves differently :
|
||||
- updateTrackingAreas is called when placing a view in the view hierarchy (that is in a window)
|
||||
- if you have only CPTrackingInVisibleRect tracking areas attached to a view, it will not be called again (until you move the view in the hierarchy)
|
||||
- if you have at least one non-CPTrackingInVisibleRect tracking area attached, it will be called every time the view geometry could be modified
|
||||
You don't have to touch to CPTrackingInVisibleRect tracking areas, they will be automatically updated
|
||||
|
||||
Please note that it is the owner of a tracking area who is called for updateTrackingAreas.
|
||||
But, if a view without any tracking area is inserted in the view hierarchy (that is, in a window), the view is called for updateTrackingAreas.
|
||||
This enables you to use updateTrackingArea to initially attach your tracking areas to the view.
|
||||
*/
|
||||
- (void)updateTrackingAreas
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/*!
|
||||
This utility method is intended for CPView subclasses overriding updateTrackingAreas
|
||||
|
||||
Typical use would be :
|
||||
|
||||
- (void)updateTrackingAreas
|
||||
{
|
||||
[self removeAllTrackingAreas];
|
||||
|
||||
... add your specific updated tracking areas ...
|
||||
}
|
||||
|
||||
*/
|
||||
- (void)removeAllTrackingAreas
|
||||
{
|
||||
while (_trackingAreas.length > 0)
|
||||
[self _removeTrackingArea:_trackingAreas[0]];
|
||||
}
|
||||
|
||||
// Internal methods
|
||||
|
||||
- (void)_removeTrackingArea:(CPTrackingArea)trackingArea
|
||||
{
|
||||
if (_window)
|
||||
[_window _removeTrackingArea:trackingArea];
|
||||
|
||||
[trackingArea setView:nil];
|
||||
[_trackingAreas removeObjectIdenticalTo:trackingArea];
|
||||
}
|
||||
|
||||
- (void)_updateTrackingAreas
|
||||
{
|
||||
_inhibitUpdateTrackingAreas = YES;
|
||||
|
||||
[self _recursivelyUpdateTrackingAreas];
|
||||
|
||||
_inhibitUpdateTrackingAreas = NO;
|
||||
}
|
||||
|
||||
- (void)_recursivelyUpdateTrackingAreas
|
||||
{
|
||||
[self _updateTrackingAreasForOwners:[self _calcTrackingAreaOwners]];
|
||||
|
||||
for (var i = 0; i < _subviews.length; i++)
|
||||
[_subviews[i] _recursivelyUpdateTrackingAreas];
|
||||
}
|
||||
|
||||
- (CPArray)_calcTrackingAreaOwners
|
||||
{
|
||||
// First search all owners that must be notified
|
||||
// Remark: 99.99% of time, the only owner will be the view itself
|
||||
// In the same time, update the rects of InVisibleRect tracking areas
|
||||
|
||||
var owners = [];
|
||||
|
||||
for (var i = 0; i < _trackingAreas.length; i++)
|
||||
{
|
||||
var trackingArea = _trackingAreas[i];
|
||||
|
||||
if ([trackingArea options] & CPTrackingInVisibleRect)
|
||||
[trackingArea _updateWindowRect];
|
||||
|
||||
else
|
||||
{
|
||||
var owner = [trackingArea owner];
|
||||
|
||||
if (![owners containsObjectIdenticalTo:owner])
|
||||
[owners addObject:owner];
|
||||
}
|
||||
}
|
||||
|
||||
return owners;
|
||||
}
|
||||
|
||||
- (void)_updateTrackingAreasForOwners:(CPArray)owners
|
||||
{
|
||||
for (var i = 0; i < owners.length; i++)
|
||||
[owners[i] updateTrackingAreas];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
var CPViewAutoresizingMaskKey = @"CPViewAutoresizingMask",
|
||||
CPViewAutoresizesSubviewsKey = @"CPViewAutoresizesSubviews",
|
||||
CPViewBackgroundColorKey = @"CPViewBackgroundColor",
|
||||
@@ -3529,15 +3605,15 @@ var CPViewAutoresizingMaskKey = @"CPViewAutoresizingMask",
|
||||
CPViewSubviewsKey = @"CPViewSubviewsKey",
|
||||
CPViewSuperviewKey = @"CPViewSuperviewKey",
|
||||
CPViewTagKey = @"CPViewTagKey",
|
||||
CPViewThemeClassKey = @"CPViewThemeClassKey",
|
||||
CPViewThemeStateKey = @"CPViewThemeStateKey",
|
||||
CPViewWindowKey = @"CPViewWindowKey",
|
||||
CPViewNextKeyViewKey = @"CPViewNextKeyViewKey",
|
||||
CPViewPreviousKeyViewKey = @"CPViewPreviousKeyViewKey",
|
||||
CPReuseIdentifierKey = @"CPReuseIdentifierKey",
|
||||
CPViewScaleKey = @"CPViewScaleKey",
|
||||
CPViewSizeScaleKey = @"CPViewSizeScaleKey",
|
||||
CPViewIsScaledKey = @"CPViewIsScaledKey";
|
||||
CPViewIsScaledKey = @"CPViewIsScaledKey",
|
||||
CPViewAppearanceKey = @"CPViewAppearanceKey",
|
||||
CPViewTrackingAreasKey = @"CPViewTrackingAreasKey";
|
||||
|
||||
@implementation CPView (CPCoding)
|
||||
|
||||
@@ -3565,6 +3641,11 @@ var CPViewAutoresizingMaskKey = @"CPViewAutoresizingMask",
|
||||
|
||||
if (self)
|
||||
{
|
||||
_trackingAreas = [aCoder decodeObjectForKey:CPViewTrackingAreasKey];
|
||||
|
||||
if (!_trackingAreas)
|
||||
_trackingAreas = [];
|
||||
|
||||
// We have to manually check because it may be 0, so we can't use ||
|
||||
_tag = [aCoder containsValueForKey:CPViewTagKey] ? [aCoder decodeIntForKey:CPViewTagKey] : -1;
|
||||
_identifier = [aCoder decodeObjectForKey:CPReuseIdentifierKey];
|
||||
@@ -3634,23 +3715,9 @@ var CPViewAutoresizingMaskKey = @"CPViewAutoresizingMask",
|
||||
|
||||
[self setBackgroundColor:[aCoder decodeObjectForKey:CPViewBackgroundColorKey]];
|
||||
[self _setupViewFlags];
|
||||
[self _decodeThemeObjectsWithCoder:aCoder];
|
||||
|
||||
_theme = [CPTheme defaultTheme];
|
||||
_themeClass = [aCoder decodeObjectForKey:CPViewThemeClassKey];
|
||||
_themeState = CPThemeState([aCoder decodeObjectForKey:CPViewThemeStateKey]);
|
||||
_themeAttributes = {};
|
||||
|
||||
var theClass = [self class],
|
||||
themeClass = [self themeClass],
|
||||
attributes = [theClass _themeAttributes],
|
||||
count = attributes.length;
|
||||
|
||||
while (count--)
|
||||
{
|
||||
var attributeName = attributes[count--];
|
||||
|
||||
_themeAttributes[attributeName] = CPThemeAttributeDecode(aCoder, attributeName, attributes[count], _theme, themeClass);
|
||||
}
|
||||
[self setAppearance:[aCoder decodeObjectForKey:CPViewAppearanceKey]];
|
||||
|
||||
[self setNeedsDisplay:YES];
|
||||
[self setNeedsLayout];
|
||||
@@ -3727,12 +3794,7 @@ var CPViewAutoresizingMaskKey = @"CPViewAutoresizingMask",
|
||||
if (previousKeyView !== nil && ![previousKeyView isEqual:self])
|
||||
[aCoder encodeConditionalObject:previousKeyView forKey:CPViewPreviousKeyViewKey];
|
||||
|
||||
[aCoder encodeObject:[self themeClass] forKey:CPViewThemeClassKey];
|
||||
[aCoder encodeObject:String(_themeState) forKey:CPViewThemeStateKey];
|
||||
|
||||
for (var attributeName in _themeAttributes)
|
||||
if (_themeAttributes.hasOwnProperty(attributeName))
|
||||
CPThemeAttributeEncode(aCoder, _themeAttributes[attributeName]);
|
||||
[self _encodeThemeObjectsWithCoder:aCoder];
|
||||
|
||||
if (_identifier)
|
||||
[aCoder encodeObject:_identifier forKey:CPReuseIdentifierKey];
|
||||
@@ -3740,6 +3802,8 @@ var CPViewAutoresizingMaskKey = @"CPViewAutoresizingMask",
|
||||
[aCoder encodeSize:[self scaleSize] forKey:CPViewScaleKey];
|
||||
[aCoder encodeSize:[self _hierarchyScaleSize] forKey:CPViewSizeScaleKey];
|
||||
[aCoder encodeBool:_isScaled forKey:CPViewIsScaledKey];
|
||||
[aCoder encodeObject:_appearance forKey:CPViewAppearanceKey];
|
||||
[aCoder encodeObject:_trackingAreas forKey:CPViewTrackingAreasKey];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -0,0 +1,186 @@
|
||||
/*
|
||||
* CPVisualEffectView.j
|
||||
* AppKit
|
||||
*
|
||||
* Created by Antoine Mercadal.
|
||||
* Copyright 2015, 280 Cappuccino Project.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
@import <Foundation/Foundation.j>
|
||||
@import "CPAppearance.j"
|
||||
@import "CPView.j"
|
||||
|
||||
@typedef CPVisualEffectMaterial
|
||||
CPVisualEffectMaterialAppearanceBased = 0;
|
||||
CPVisualEffectMaterialLight = 1;
|
||||
CPVisualEffectMaterialDark = 2;
|
||||
CPVisualEffectMaterialTitlebar = 3;
|
||||
|
||||
@typedef CPVisualEffectBlendingMode
|
||||
CPVisualEffectBlendingModeBehindWindow = 0;
|
||||
CPVisualEffectBlendingModeWithinWindow = 1;
|
||||
|
||||
@typedef CPVisualEffectState
|
||||
CPVisualEffectStateFollowsWindowActiveState = 0;
|
||||
CPVisualEffectStateActive = 1;
|
||||
CPVisualEffectStateInactive = 2;
|
||||
|
||||
|
||||
/*! @ingroup appkit
|
||||
|
||||
Very naive implementation of CPVisualEffectView. This view allows
|
||||
to use vibrancy effect. This is only working with Safari 9+ and the
|
||||
support in Chrome/ium should come quite soon.
|
||||
Using this class with a browser that doesn't support backdrop-filter
|
||||
While still work, but you will not get the blurry effect.
|
||||
*/
|
||||
@implementation CPVisualEffectView : CPView
|
||||
{
|
||||
CPImage _maskImage @accessors(property=maskImage);
|
||||
CPVisualEffectBlendingMode _blendingMode @accessors(property=blendingMode);
|
||||
CPVisualEffectMaterial _material @accessors(property=material);
|
||||
CPVisualEffectState _state @accessors(property=state);
|
||||
}
|
||||
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Initialization
|
||||
|
||||
- (id)initWithFrame:(CGRect)aFrame
|
||||
{
|
||||
if (self = [super initWithFrame:aFrame])
|
||||
{
|
||||
_material = CPVisualEffectMaterialAppearanceBased;
|
||||
_blendingMode = CPVisualEffectBlendingModeWithinWindow;
|
||||
_state = CPVisualEffectStateFollowsWindowActiveState;
|
||||
_appearance = [CPAppearance appearanceNamed:CPAppearanceNameVibrantDark];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark CPVisualEffectView API
|
||||
|
||||
/*! Sets the appearance of the CPVisualEffectView.
|
||||
|
||||
Only CPAppearance named CPAppearanceNameVibrantDark or CPAppearanceNameVibrantLight are valid
|
||||
|
||||
@param anAppearance the CPAppearance.
|
||||
*/
|
||||
- (void)setAppearance:(CPAppearance)anAppearance
|
||||
{
|
||||
if (![self _validAppearance:anAppearance])
|
||||
[CPException raise:CPInvalidArgumentException reason:"Appearance can only be CPAppearanceNameVibrantDark or CPAppearanceNameVibrantLight in CPVisualEffectView, but is " + anAppearance];
|
||||
|
||||
[super setAppearance:anAppearance];
|
||||
|
||||
[self setNeedsLayout:YES];
|
||||
}
|
||||
|
||||
/*! Sets the received effect state.
|
||||
Possible values:
|
||||
<pre>
|
||||
CPVisualEffectStateFollowsWindowActiveState (default)
|
||||
CPVisualEffectStateActive
|
||||
CPVisualEffectStateInactive
|
||||
</pre>
|
||||
*/
|
||||
- (void)setState:(CPVisualEffectState)aState
|
||||
{
|
||||
if (_state == aState)
|
||||
return;
|
||||
|
||||
[self willChangeValueForKey:"state"];
|
||||
_state = aState;
|
||||
[self didChangeValueForKey:"state"];
|
||||
|
||||
[self setNeedsLayout:YES];
|
||||
}
|
||||
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Utilities
|
||||
|
||||
- (void)_setEffectEnabled:(BOOL)shouldEnable
|
||||
{
|
||||
var dark = [[self appearance] isEqual:[CPAppearance appearanceNamed:CPAppearanceNameVibrantDark]],
|
||||
prop = CPBrowserStyleProperty("backdrop-filter"),
|
||||
color = (dark ? [CPColor colorWithHexString:@"1e1e1e"] : [CPColor whiteColor]),
|
||||
finalColor = shouldEnable ? [color colorWithAlphaComponent:0.6] : color;
|
||||
|
||||
[self setBackgroundColor:finalColor];
|
||||
|
||||
#if PLATFORM(DOM)
|
||||
self._DOMElement.style[prop] = shouldEnable ? "blur(30px)" : nil;
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
- (void)layoutSubviews
|
||||
{
|
||||
switch (_state)
|
||||
{
|
||||
case CPVisualEffectStateFollowsWindowActiveState:
|
||||
[self _setEffectEnabled:[self hasThemeState:CPThemeStateKeyWindow]];
|
||||
break;
|
||||
|
||||
case CPVisualEffectStateActive:
|
||||
[self _setEffectEnabled:YES];
|
||||
break;
|
||||
|
||||
case CPVisualEffectStateInactive:
|
||||
[self _setEffectEnabled:NO];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)_validAppearance:(CPAppearance)anAppearance
|
||||
{
|
||||
return [anAppearance isEqual:[CPAppearance appearanceNamed:CPAppearanceNameVibrantDark]] || [anAppearance isEqual:[CPAppearance appearanceNamed:CPAppearanceNameVibrantLight]];
|
||||
}
|
||||
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark CPCoding
|
||||
|
||||
- (id)initWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
if (self = [super initWithCoder:aCoder])
|
||||
{
|
||||
_blendingMode = [aCoder decodeIntForKey:@"_blendingMode"] || CPVisualEffectBlendingModeWithinWindow;
|
||||
_maskImage = [aCoder decodeObjectForKey:@"_maskImage"];
|
||||
_material = [aCoder decodeIntForKey:@"_material"] || CPVisualEffectMaterialAppearanceBased;
|
||||
_state = [aCoder decodeIntForKey:@"_state"] || CPVisualEffectStateFollowsWindowActiveState;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)encodeWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
[super encodeWithCoder:aCoder];
|
||||
|
||||
[aCoder encodeObject:_maskImage forKey:@"_maskImage"];
|
||||
[aCoder encodeInt:_blendingMode forKey:@"_blendingMode"];
|
||||
[aCoder encodeInt:_material forKey:@"_material"];
|
||||
[aCoder encodeInt:_state forKey:@"_state"];
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
+493
-52
@@ -36,6 +36,7 @@
|
||||
@import "CPResponder.j"
|
||||
@import "CPScreen.j"
|
||||
@import "CPText.j"
|
||||
@import "CPTrackingArea.j"
|
||||
@import "CPView.j"
|
||||
@import "CPWindow_Constants.j"
|
||||
@import "_CPBorderlessBridgeWindowView.j"
|
||||
@@ -55,6 +56,7 @@
|
||||
@class _CPWindowFrameAnimation
|
||||
|
||||
@global CPApp
|
||||
@global _CPPlatformWindowWillCloseNotification
|
||||
|
||||
@typedef _CPWindowFullPlatformWindowSession
|
||||
|
||||
@@ -196,7 +198,16 @@ var CPWindowActionMessageKeys = [
|
||||
CPView _contentView;
|
||||
CPView _toolbarView;
|
||||
|
||||
BOOL _handlingTrackingAreaEvent;
|
||||
BOOL _restartHandlingTrackingAreaEvent;
|
||||
CPArray _previousMouseEnteredStack;
|
||||
CPArray _previousCursorUpdateStack;
|
||||
CPArray _mouseEnteredStack;
|
||||
CPArray _cursorUpdateStack;
|
||||
CPArray _queuedEvents;
|
||||
CPArray _trackingAreaViews;
|
||||
id _activeCursorTrackingArea;
|
||||
CPArray _queuedTrackingEvents;
|
||||
CPView _leftMouseDownView;
|
||||
CPView _rightMouseDownView;
|
||||
|
||||
@@ -334,6 +345,17 @@ CPTexturedBackgroundWindowMask
|
||||
|
||||
[self setLevel:CPNormalWindowLevel];
|
||||
|
||||
_handlingTrackingAreaEvent = NO;
|
||||
_restartHandlingTrackingAreaEvent = NO;
|
||||
_trackingAreaViews = [];
|
||||
_previousMouseEnteredStack = [];
|
||||
_previousCursorUpdateStack = [];
|
||||
_mouseEnteredStack = [];
|
||||
_cursorUpdateStack = [];
|
||||
_queuedEvents = [];
|
||||
_queuedTrackingEvents = [];
|
||||
_activeCursorTrackingArea = nil;
|
||||
|
||||
// Create our border view which is the actual root of our view hierarchy.
|
||||
_windowView = [[windowViewClass alloc] initWithFrame:CGRectMake(0.0, 0.0, CGRectGetWidth(_frame), CGRectGetHeight(_frame)) styleMask:aStyleMask];
|
||||
|
||||
@@ -556,6 +578,11 @@ CPTexturedBackgroundWindowMask
|
||||
}
|
||||
}
|
||||
|
||||
- (CPView)_windowView
|
||||
{
|
||||
return _windowView;
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the receiver as a full platform window. If you pass YES the CPWindow instance will fill the entire browser content area,
|
||||
otherwise the CPWindow will be a window inside of your browser window which the user can drag around, and resize (if you allow).
|
||||
@@ -1922,6 +1949,9 @@ CPTexturedBackgroundWindowMask
|
||||
|
||||
_leftMouseDownView = nil;
|
||||
|
||||
// If mouseUp ends a drag operation, send delayed events for tracking views under the mouse, then flush delayed events
|
||||
[self _flushTrackingEventQueueForMouseAt:point];
|
||||
|
||||
return;
|
||||
|
||||
case CPLeftMouseDown:
|
||||
@@ -1957,6 +1987,11 @@ CPTexturedBackgroundWindowMask
|
||||
|
||||
case CPLeftMouseDragged:
|
||||
case CPRightMouseDragged:
|
||||
// First, we search for any tracking area requesting CPTrackingEnabledDuringMouseDrag.
|
||||
// At the same time, we update the entered stack.
|
||||
[self _handleTrackingAreaEvent:anEvent];
|
||||
|
||||
// Normal mouseDragged workflow
|
||||
if (!_leftMouseDownView)
|
||||
return [[_windowView hitTest:point] mouseDragged:anEvent];
|
||||
|
||||
@@ -1975,61 +2010,12 @@ CPTexturedBackgroundWindowMask
|
||||
return [_leftMouseDownView performSelector:selector withObject:anEvent];
|
||||
|
||||
case CPMouseMoved:
|
||||
[_windowView setCursorForLocation:point resizing:NO];
|
||||
|
||||
// Ignore mouse moves for parents of sheets
|
||||
if (!_acceptsMouseMovedEvents || sheet)
|
||||
return;
|
||||
|
||||
if (!_mouseEnteredStack)
|
||||
_mouseEnteredStack = [];
|
||||
|
||||
var hitTestView = [_windowView hitTest:point];
|
||||
|
||||
if ([_mouseEnteredStack count] && [_mouseEnteredStack lastObject] === hitTestView)
|
||||
return [hitTestView mouseMoved:anEvent];
|
||||
|
||||
var view = hitTestView,
|
||||
mouseEnteredStack = [];
|
||||
|
||||
while (view)
|
||||
{
|
||||
mouseEnteredStack.unshift(view);
|
||||
|
||||
view = [view superview];
|
||||
}
|
||||
|
||||
var deviation = MIN(_mouseEnteredStack.length, mouseEnteredStack.length);
|
||||
|
||||
while (deviation--)
|
||||
if (_mouseEnteredStack[deviation] === mouseEnteredStack[deviation])
|
||||
break;
|
||||
|
||||
var index = deviation + 1,
|
||||
count = _mouseEnteredStack.length;
|
||||
|
||||
if (index < count)
|
||||
{
|
||||
var event = [CPEvent mouseEventWithType:CPMouseExited location:point modifierFlags:[anEvent modifierFlags] timestamp:[anEvent timestamp] windowNumber:_windowNumber context:nil eventNumber:-1 clickCount:1 pressure:0];
|
||||
|
||||
for (; index < count; ++index)
|
||||
[_mouseEnteredStack[index] mouseExited:event];
|
||||
}
|
||||
|
||||
index = deviation + 1;
|
||||
count = mouseEnteredStack.length;
|
||||
|
||||
if (index < count)
|
||||
{
|
||||
var event = [CPEvent mouseEventWithType:CPMouseEntered location:point modifierFlags:[anEvent modifierFlags] timestamp:[anEvent timestamp] windowNumber:_windowNumber context:nil eventNumber:-1 clickCount:1 pressure:0];
|
||||
|
||||
for (; index < count; ++index)
|
||||
[mouseEnteredStack[index] mouseEntered:event];
|
||||
}
|
||||
|
||||
_mouseEnteredStack = mouseEnteredStack;
|
||||
|
||||
[hitTestView mouseMoved:anEvent];
|
||||
[self _handleTrackingAreaEvent:anEvent];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2856,7 +2842,7 @@ CPTexturedBackgroundWindowMask
|
||||
if (delegate && endSelector)
|
||||
{
|
||||
if (_sheetContext["isAttached"])
|
||||
objj_msgSend(delegate, endSelector, _sheetContext["sheet"], _sheetContext["returnCode"],
|
||||
delegate.isa.objj_msgSend3(delegate, endSelector, _sheetContext["sheet"], _sheetContext["returnCode"],
|
||||
_sheetContext["contextInfo"]);
|
||||
else
|
||||
_sheetContext["deferDidEndSelector"] = YES;
|
||||
@@ -2920,7 +2906,8 @@ CPTexturedBackgroundWindowMask
|
||||
_sheetContext = nil;
|
||||
sheet._parentView = nil;
|
||||
|
||||
objj_msgSend(delegate, selector, sheet, returnCode, contextInfo);
|
||||
if (delegate != null)
|
||||
delegate.isa.objj_msgSend3(delegate, selector, sheet, returnCode, contextInfo);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -3842,6 +3829,460 @@ var interpolate = function(fromValue, toValue, progress)
|
||||
|
||||
@end
|
||||
|
||||
@implementation CPWindow (TrackingAreaAdditions)
|
||||
|
||||
- (void)_addTrackingAreaView:(CPView)aView
|
||||
{
|
||||
var trackingAreas = [aView trackingAreas];
|
||||
|
||||
for (var i = 0; i < trackingAreas.length; i++)
|
||||
[self _addTrackingArea:trackingAreas[i]];
|
||||
}
|
||||
|
||||
- (void)_removeTrackingAreaView:(CPView)aView
|
||||
{
|
||||
var trackingAreas = [aView trackingAreas];
|
||||
|
||||
for (var i = 0; i < trackingAreas.length; i++)
|
||||
[self _removeTrackingArea:trackingAreas[i]];
|
||||
}
|
||||
|
||||
- (void)_addTrackingArea:(CPTrackingArea)trackingArea
|
||||
{
|
||||
var trackingAreaView = [trackingArea view];
|
||||
|
||||
if (![_trackingAreaViews containsObjectIdenticalTo:trackingAreaView])
|
||||
[_trackingAreaViews addObject:trackingAreaView];
|
||||
|
||||
// If CPTrackingAssumeInside option is set, insert the tracking area in the events management system
|
||||
// in order to have the first event sent only when mouse leaves the tracking area
|
||||
|
||||
[self _insertTrackingArea:trackingArea assumeInside:([trackingArea options] & CPTrackingAssumeInside)];
|
||||
}
|
||||
|
||||
- (void)_removeTrackingArea:(CPTrackingArea)trackingArea
|
||||
{
|
||||
// If mouse is in the tracking area, we remove it from the stack to avoid to fire a future mouseExited event
|
||||
|
||||
[self _purgeTrackingArea:trackingArea];
|
||||
|
||||
var trackingAreaView = [trackingArea view];
|
||||
|
||||
[_trackingAreaViews removeObjectIdenticalTo:trackingAreaView];
|
||||
}
|
||||
|
||||
- (void)_insertTrackingArea:(CPTrackingArea)trackingArea assumeInside:(BOOL)assumeInside
|
||||
{
|
||||
if (_handlingTrackingAreaEvent)
|
||||
_restartHandlingTrackingAreaEvent = YES;
|
||||
|
||||
if (assumeInside)
|
||||
{
|
||||
if (_handlingTrackingAreaEvent)
|
||||
[_mouseEnteredStack addObject:trackingArea];
|
||||
else
|
||||
[_previousMouseEnteredStack addObject:trackingArea];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)_purgeTrackingArea:(CPTrackingArea)trackingArea
|
||||
{
|
||||
if (_handlingTrackingAreaEvent)
|
||||
{
|
||||
[_mouseEnteredStack removeObjectIdenticalTo:trackingArea];
|
||||
|
||||
var i = _queuedEvents.length;
|
||||
|
||||
while (i--)
|
||||
if ([_queuedEvents[i] trackingArea] === trackingArea)
|
||||
[_queuedEvents removeObjectAtIndex:i];
|
||||
|
||||
_cursorUpdateStack = [];
|
||||
_activeCursorTrackingArea = nil;
|
||||
}
|
||||
else
|
||||
{
|
||||
[_previousMouseEnteredStack removeObjectIdenticalTo:trackingArea];
|
||||
[_previousCursorUpdateStack removeObjectIdenticalTo:trackingArea];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)_handleTrackingAreaEvent:(CPEvent)anEvent
|
||||
{
|
||||
_handlingTrackingAreaEvent = YES;
|
||||
|
||||
var point = [anEvent locationInWindow],
|
||||
dragging = ([anEvent type] !== CPMouseMoved);
|
||||
|
||||
do
|
||||
{
|
||||
// Initialize this run
|
||||
_restartHandlingTrackingAreaEvent = NO;
|
||||
|
||||
_mouseEnteredStack = [];
|
||||
_cursorUpdateStack = [];
|
||||
|
||||
// Important remark: we must queue events to avoid running conditions when a view uses a mouse event to modify view hierarchy
|
||||
|
||||
_queuedEvents = [];
|
||||
|
||||
// Handle mouse entering tracking areas (and calc _mouseEnteredStack and _cursorUpdateStack)
|
||||
|
||||
[self _handleMouseMovedAndEnteredEventsForEvent:anEvent atPoint:point dragging:dragging];
|
||||
|
||||
// Handle mouse exiting tracking areas
|
||||
|
||||
[self _handleMouseExitedEventsForEvent:anEvent atPoint:point dragging:dragging];
|
||||
|
||||
// Cursor update
|
||||
|
||||
if (_cursorUpdateStack.length > 0)
|
||||
{
|
||||
[self _handleCursorUpdateEventsForEvent:anEvent atPoint:point dragging:dragging];
|
||||
}
|
||||
else if (!dragging)
|
||||
{
|
||||
// Here, we are outside the window content view tracking area, so let _windowView set the cursor (resize cursor, ...)
|
||||
|
||||
[_windowView setCursorForLocation:point resizing:NO];
|
||||
_activeCursorTrackingArea = nil;
|
||||
}
|
||||
|
||||
// Send all queued events
|
||||
// Important remark : as an event can modify the view hierarchy, the events queue could be modified while processing it
|
||||
|
||||
while (_queuedEvents.length > 0)
|
||||
{
|
||||
var queuedEvent = _queuedEvents[0],
|
||||
trackingArea = [queuedEvent trackingArea],
|
||||
trackingOwner = [trackingArea owner];
|
||||
|
||||
switch ([queuedEvent type])
|
||||
{
|
||||
case CPMouseEntered:
|
||||
[trackingOwner mouseEntered:queuedEvent];
|
||||
break;
|
||||
|
||||
case CPMouseExited:
|
||||
[trackingOwner mouseExited:queuedEvent];
|
||||
break;
|
||||
|
||||
case CPCursorUpdate:
|
||||
[trackingOwner cursorUpdate:queuedEvent];
|
||||
break;
|
||||
}
|
||||
|
||||
if (queuedEvent === _queuedEvents[0])
|
||||
[_queuedEvents removeObjectAtIndex:0];
|
||||
}
|
||||
|
||||
// Prepare for next call
|
||||
|
||||
_previousMouseEnteredStack = _mouseEnteredStack;
|
||||
_previousCursorUpdateStack = _cursorUpdateStack;
|
||||
}
|
||||
while (_restartHandlingTrackingAreaEvent)
|
||||
|
||||
_handlingTrackingAreaEvent = NO;
|
||||
}
|
||||
|
||||
- (void)_handleMouseMovedAndEnteredEventsForEvent:(CPEvent)anEvent atPoint:(CGPoint)point dragging:(BOOL)dragging
|
||||
{
|
||||
var isKeyWindow = [self isKeyWindow];
|
||||
|
||||
for (var i = 0; i < _trackingAreaViews.length; i++)
|
||||
{
|
||||
var aView = _trackingAreaViews[i],
|
||||
trackingAreas = [aView trackingAreas];
|
||||
|
||||
if ([aView isHidden])
|
||||
continue;
|
||||
|
||||
for (var j = 0; j < trackingAreas.length; j++)
|
||||
{
|
||||
var aTrackingArea = trackingAreas[j],
|
||||
trackingOptions = [aTrackingArea options],
|
||||
trackingImplementedMethods = [aTrackingArea implementedOwnerMethods];
|
||||
|
||||
if (!(((trackingOptions & CPTrackingActiveAlways) ||
|
||||
(trackingOptions & CPTrackingActiveInActiveApp) ||
|
||||
((trackingOptions & CPTrackingActiveInKeyWindow) && isKeyWindow) ||
|
||||
((trackingOptions & CPTrackingActiveWhenFirstResponder) && isKeyWindow && (_firstResponder === aView))) &&
|
||||
(CGRectContainsPoint([aTrackingArea windowRect], point))))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
[_mouseEnteredStack addObject:aTrackingArea];
|
||||
|
||||
if ([_previousMouseEnteredStack containsObjectIdenticalTo:aTrackingArea])
|
||||
{
|
||||
// Mouse was already in this rect so it's a mouseMoved
|
||||
|
||||
if (!dragging && (trackingOptions & CPTrackingMouseMoved) && (trackingImplementedMethods & CPTrackingOwnerImplementsMouseMoved))
|
||||
[[aTrackingArea owner] mouseMoved:anEvent];
|
||||
}
|
||||
else if ((trackingOptions & CPTrackingMouseEnteredAndExited) && (trackingImplementedMethods & CPTrackingOwnerImplementsMouseEntered))
|
||||
{
|
||||
var mouseEnteredEvent = [CPEvent enterExitEventWithType:CPMouseEntered
|
||||
location:point
|
||||
modifierFlags:[anEvent modifierFlags]
|
||||
timestamp:[anEvent timestamp]
|
||||
windowNumber:_windowNumber
|
||||
context:nil
|
||||
eventNumber:-1
|
||||
trackingArea:aTrackingArea];
|
||||
|
||||
if (dragging && !(trackingOptions & CPTrackingEnabledDuringMouseDrag))
|
||||
[self _queueTrackingEvent:mouseEnteredEvent];
|
||||
else
|
||||
[_queuedEvents addObject:mouseEnteredEvent];
|
||||
}
|
||||
|
||||
if ((trackingOptions & CPTrackingCursorUpdate) && (trackingImplementedMethods & CPTrackingOwnerImplementsCursorUpdate))
|
||||
[_cursorUpdateStack addObject:aTrackingArea];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)_handleMouseExitedEventsForEvent:(CPEvent)anEvent atPoint:(CGPoint)point dragging:(BOOL)dragging
|
||||
{
|
||||
// Search for exited views (were in _previousMouseEnteredStack but no more in _mouseEnteredStack)
|
||||
|
||||
for (var i = 0; i < _previousMouseEnteredStack.length; i++)
|
||||
{
|
||||
var aTrackingArea = _previousMouseEnteredStack[i],
|
||||
trackingOptions = [aTrackingArea options];
|
||||
|
||||
if ([_mouseEnteredStack containsObjectIdenticalTo:aTrackingArea])
|
||||
continue;
|
||||
|
||||
// Mouse is no more in this area so it's a mouseExited
|
||||
|
||||
if ((trackingOptions & CPTrackingMouseEnteredAndExited) && ([aTrackingArea implementedOwnerMethods] & CPTrackingOwnerImplementsMouseExited))
|
||||
{
|
||||
var theView = [aTrackingArea owner],
|
||||
mouseExitedEvent = [CPEvent enterExitEventWithType:CPMouseExited
|
||||
location:point
|
||||
modifierFlags:[anEvent modifierFlags]
|
||||
timestamp:[anEvent timestamp]
|
||||
windowNumber:_windowNumber
|
||||
context:nil
|
||||
eventNumber:-1
|
||||
trackingArea:aTrackingArea];
|
||||
|
||||
if (dragging && !(trackingOptions & CPTrackingEnabledDuringMouseDrag))
|
||||
[self _queueTrackingEvent:mouseExitedEvent];
|
||||
else
|
||||
[_queuedEvents addObject:mouseExitedEvent];
|
||||
}
|
||||
|
||||
// If this is the active cursor area, we reset _previousCursorUpdateStack so a new active area will be computed
|
||||
|
||||
if (aTrackingArea === _activeCursorTrackingArea)
|
||||
{
|
||||
_previousCursorUpdateStack = [];
|
||||
_activeCursorTrackingArea = nil;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)_handleCursorUpdateEventsForEvent:(CPEvent)anEvent atPoint:(CGPoint)point dragging:(BOOL)dragging
|
||||
{
|
||||
var overlappingTrackingAreas = [];
|
||||
|
||||
for (var i = 0; i < _cursorUpdateStack.length; i++)
|
||||
{
|
||||
var aTrackingArea = _cursorUpdateStack[i];
|
||||
|
||||
if ((![_previousCursorUpdateStack containsObjectIdenticalTo:aTrackingArea]) || (aTrackingArea === _activeCursorTrackingArea))
|
||||
[overlappingTrackingAreas addObject:aTrackingArea];
|
||||
}
|
||||
|
||||
var frontmostTrackingArea = overlappingTrackingAreas[0],
|
||||
frontmostView = [frontmostTrackingArea view];
|
||||
|
||||
for (var i = 1; i < overlappingTrackingAreas.length; i++)
|
||||
{
|
||||
var aTrackingArea = overlappingTrackingAreas[i],
|
||||
aView = [aTrackingArea view];
|
||||
|
||||
// First, if aView is _windowView, skip to next overlapping tracking area
|
||||
// as _windowView can't be the frontmost view if there's multiple overlapping tracking areas.
|
||||
|
||||
if (aView === _windowView)
|
||||
continue;
|
||||
|
||||
// Then, if frontmostView is _windowView, aView must become frontmostView
|
||||
|
||||
if (frontmostView === _windowView)
|
||||
{
|
||||
frontmostTrackingArea = aTrackingArea;
|
||||
frontmostView = aView;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// Next verify if aView is a subview of frontmostView
|
||||
// If so, it's our new frontmost view
|
||||
|
||||
var searchingView = aView;
|
||||
|
||||
while ((searchingView !== _contentView) && ([searchingView superview] !== frontmostView))
|
||||
searchingView = [searchingView superview];
|
||||
|
||||
if (searchingView !== _contentView)
|
||||
{
|
||||
frontmostTrackingArea = aTrackingArea;
|
||||
frontmostView = aView;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// aView is not a subview of frontmostView
|
||||
// Search in view hierarchy which one will be over the other
|
||||
// (this is done by comparing their draw order)
|
||||
|
||||
var firstView = frontmostView,
|
||||
firstSuperview = [firstView superview];
|
||||
|
||||
while (firstView !== _contentView)
|
||||
{
|
||||
var secondView = aView,
|
||||
secondSuperview = [secondView superview];
|
||||
|
||||
while ((secondSuperview !== _contentView) && (firstSuperview !== secondSuperview))
|
||||
{
|
||||
secondView = secondSuperview;
|
||||
secondSuperview = [secondView superview];
|
||||
}
|
||||
|
||||
if (firstSuperview === secondSuperview)
|
||||
break;
|
||||
|
||||
firstView = firstSuperview;
|
||||
firstSuperview = [firstView superview];
|
||||
}
|
||||
|
||||
if (firstSuperview !== secondSuperview)
|
||||
[CPException raise:CPInternalInconsistencyException reason:"Problem with view hierarchy"];
|
||||
|
||||
var firstSuperviewSubviews = [firstSuperview subviews],
|
||||
firstViewIndex = [firstSuperviewSubviews indexOfObject:firstView],
|
||||
secondViewIndex = [firstSuperviewSubviews indexOfObject:secondView];
|
||||
|
||||
if (secondViewIndex > firstViewIndex)
|
||||
{
|
||||
frontmostTrackingArea = aTrackingArea;
|
||||
frontmostView = aView;
|
||||
}
|
||||
}
|
||||
|
||||
if (frontmostTrackingArea !== _activeCursorTrackingArea)
|
||||
{
|
||||
var cursorUpdateEvent = [CPEvent enterExitEventWithType:CPCursorUpdate
|
||||
location:point
|
||||
modifierFlags:[anEvent modifierFlags]
|
||||
timestamp:[anEvent timestamp]
|
||||
windowNumber:_windowNumber
|
||||
context:nil
|
||||
eventNumber:-1
|
||||
trackingArea:frontmostTrackingArea];
|
||||
|
||||
if (dragging)
|
||||
[self _queueTrackingEvent:cursorUpdateEvent];
|
||||
else
|
||||
[_queuedEvents addObject:cursorUpdateEvent];
|
||||
|
||||
_activeCursorTrackingArea = frontmostTrackingArea;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)_queueTrackingEvent:(CPEvent)anEvent
|
||||
{
|
||||
// This will put a tracking event in the _queuedTrackingEvents queue.
|
||||
//
|
||||
// We optimize this queue with this policy :
|
||||
// - if mouseEntered, search if queue contains a previous mouseExited for the same tracking area. If so, discard both.
|
||||
// - if mouseExited, search if queue contains a previous mouseEntered for the same tracking area. If so, discard both.
|
||||
//
|
||||
// This is not Cocoa way of doing as it would send every event.
|
||||
// But final result should be the same.
|
||||
|
||||
var eventType = [anEvent type],
|
||||
trackingArea = [anEvent trackingArea];
|
||||
|
||||
switch ([anEvent type])
|
||||
{
|
||||
case CPMouseEntered:
|
||||
for (var i = 0; i < _queuedTrackingEvents.length; i++)
|
||||
{
|
||||
var queuedEvent = _queuedTrackingEvents[i];
|
||||
|
||||
if (([queuedEvent trackingArea] === trackingArea) && ([queuedEvent type] === CPMouseExited))
|
||||
{
|
||||
[_queuedTrackingEvents removeObjectAtIndex:i];
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
[_queuedTrackingEvents addObject:anEvent];
|
||||
break;
|
||||
|
||||
case CPMouseExited:
|
||||
for (var i = 0; i < _queuedTrackingEvents.length; i++)
|
||||
{
|
||||
var queuedEvent = _queuedTrackingEvents[i];
|
||||
|
||||
if (([queuedEvent trackingArea] === trackingArea) && ([queuedEvent type] === CPMouseEntered))
|
||||
{
|
||||
[_queuedTrackingEvents removeObjectAtIndex:i];
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
[_queuedTrackingEvents addObject:anEvent];
|
||||
break;
|
||||
|
||||
case CPCursorUpdate:
|
||||
[_queuedTrackingEvents addObject:anEvent];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)_flushTrackingEventQueueForMouseAt:(CGPoint)point
|
||||
{
|
||||
for (var i = 0; i < _queuedTrackingEvents.length; i++)
|
||||
{
|
||||
var queuedEvent = _queuedTrackingEvents[i],
|
||||
trackingArea = [queuedEvent trackingArea],
|
||||
trackingOwner = [trackingArea owner];
|
||||
|
||||
switch ([queuedEvent type])
|
||||
{
|
||||
case CPMouseEntered:
|
||||
[trackingOwner mouseEntered:queuedEvent];
|
||||
break;
|
||||
|
||||
case CPMouseExited:
|
||||
[trackingOwner mouseExited:queuedEvent];
|
||||
break;
|
||||
|
||||
case CPCursorUpdate:
|
||||
[trackingOwner updateTrackingAreas];
|
||||
|
||||
if (CGRectContainsPoint([trackingArea windowRect], point))
|
||||
[trackingOwner cursorUpdate:queuedEvent];
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
_queuedTrackingEvents = [];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
function _CPWindowFullPlatformWindowSessionMake(aWindowView, aContentRect, hasShadow, aLevel)
|
||||
{
|
||||
return { windowView:aWindowView, contentRect:aContentRect, hasShadow:hasShadow, level:aLevel };
|
||||
|
||||
@@ -26,7 +26,6 @@
|
||||
@import "CGGradient.j"
|
||||
@import "_CPWindowView.j"
|
||||
|
||||
@global CPPopoverAppearanceMinimal
|
||||
|
||||
var _CPPopoverWindowViewDefaultCursorSize = CGSizeMake(16, 10);
|
||||
|
||||
@@ -39,7 +38,6 @@ var _CPPopoverWindowViewDefaultCursorSize = CGSizeMake(16, 10);
|
||||
{
|
||||
float _arrowOffsetX @accessors(property=arrowOffsetX);
|
||||
float _arrowOffsetY @accessors(property=arrowOffsetY);
|
||||
int _appearance @accessors(property=appearance);
|
||||
unsigned _preferredEdge @accessors(property=preferredEdge);
|
||||
|
||||
CGSize _cursorSize;
|
||||
@@ -123,7 +121,7 @@ var _CPPopoverWindowViewDefaultCursorSize = CGSizeMake(16, 10);
|
||||
{
|
||||
_arrowOffsetX = 0.0;
|
||||
_arrowOffsetY = 0.0;
|
||||
_appearance = CPPopoverAppearanceMinimal;
|
||||
_appearance = [CPAppearance appearanceNamed:CPAppearanceNameVibrantLight];
|
||||
_cursorSize = CGSizeMakeCopy(_CPPopoverWindowViewDefaultCursorSize);
|
||||
}
|
||||
|
||||
@@ -168,7 +166,7 @@ var _CPPopoverWindowViewDefaultCursorSize = CGSizeMake(16, 10);
|
||||
gradient,
|
||||
frame = [self bounds];
|
||||
|
||||
if (_appearance == CPPopoverAppearanceMinimal)
|
||||
if ([_appearance isEqual:[CPAppearance appearanceNamed:CPAppearanceNameVibrantLight]])
|
||||
{
|
||||
gradient = [self valueForThemeAttribute:@"background-gradient"];
|
||||
strokeColor = [self valueForThemeAttribute:@"stroke-color"];
|
||||
|
||||
@@ -353,7 +353,9 @@ _CPWindowViewResizeSlop = 3;
|
||||
if ([theWindow isFullPlatformWindow] ||
|
||||
!(_styleMask & CPResizableWindowMask) ||
|
||||
(CPWindowResizeStyle !== CPWindowResizeStyleModern))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var globalPoint = [theWindow convertBaseToGlobal:aPoint],
|
||||
resizeRegion = isResizing ? _resizeRegion : [self resizeRegionForPoint:globalPoint],
|
||||
@@ -969,3 +971,18 @@ _CPWindowViewResizeSlop = 3;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation _CPWindowView (TrackingAreaAdditions)
|
||||
|
||||
- (void)updateTrackingAreas
|
||||
{
|
||||
[self removeAllTrackingAreas];
|
||||
|
||||
[self addTrackingArea:[[CPTrackingArea alloc] initWithRect:[self contentRectForFrameRect:[self frame]]
|
||||
options:CPTrackingCursorUpdate | CPTrackingActiveInActiveApp
|
||||
owner:self
|
||||
userInfo:nil]];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
+17
-17
@@ -32,6 +32,7 @@
|
||||
@import "_CPCibObjectData.j"
|
||||
@import "_CPCibProxyObject.j"
|
||||
@import "_CPCibWindowTemplate.j"
|
||||
@import "_CPLocalizableString.j"
|
||||
|
||||
CPCibOwner = @"CPCibOwner";
|
||||
CPCibTopLevelObjects = @"CPCibTopLevelObjects";
|
||||
@@ -46,10 +47,11 @@ var CPCibObjectDataKey = @"CPCibObjectDataKey";
|
||||
|
||||
@implementation CPCib : CPObject
|
||||
{
|
||||
CPData _data;
|
||||
CPBundle _bundle;
|
||||
BOOL _awakenCustomResources;
|
||||
BOOL _awakenCustomResources @accessors(property=_awakenCustomResources);
|
||||
|
||||
CPBundle _bundle;
|
||||
CPData _data;
|
||||
CPString _cibName;
|
||||
id _loadDelegate;
|
||||
}
|
||||
|
||||
@@ -59,6 +61,8 @@ var CPCibObjectDataKey = @"CPCibObjectDataKey";
|
||||
|
||||
if (self)
|
||||
{
|
||||
_cibName = [aURL lastPathComponent];
|
||||
|
||||
_data = [CPURLConnection sendSynchronousRequest:[CPURLRequest requestWithURL:aURL] returningResponse:nil];
|
||||
|
||||
if (!_data)
|
||||
@@ -76,6 +80,8 @@ var CPCibObjectDataKey = @"CPCibObjectDataKey";
|
||||
|
||||
if (self)
|
||||
{
|
||||
_cibName = [aURL lastPathComponent];
|
||||
|
||||
[CPURLConnection connectionWithRequest:[CPURLRequest requestWithURL:aURL] delegate:self];
|
||||
|
||||
_awakenCustomResources = YES;
|
||||
@@ -92,7 +98,9 @@ var CPCibObjectDataKey = @"CPCibObjectDataKey";
|
||||
aName = [aName stringByAppendingString:@".cib"];
|
||||
|
||||
// If aBundle is nil, use mainBundle, but ONLY for searching for the nib, not for resources later.
|
||||
self = [self initWithContentsOfURL:[aBundle || [CPBundle mainBundle] pathForResource:aName]];
|
||||
var bundle = aBundle || [CPBundle mainBundle];
|
||||
|
||||
self = [self initWithContentsOfURL:[bundle _cibPathForResource:aName]];
|
||||
|
||||
if (self)
|
||||
_bundle = aBundle;
|
||||
@@ -106,7 +114,9 @@ var CPCibObjectDataKey = @"CPCibObjectDataKey";
|
||||
aName = [aName stringByAppendingString:@".cib"];
|
||||
|
||||
// If aBundle is nil, use mainBundle, but ONLY for searching for the nib, not for resources later.
|
||||
self = [self initWithContentsOfURL:[aBundle || [CPBundle mainBundle] pathForResource:aName] loadDelegate:aLoadDelegate];
|
||||
var bundle = aBundle || [CPBundle mainBundle];
|
||||
|
||||
self = [self initWithContentsOfURL:[bundle _cibPathForResource:aName] loadDelegate:aLoadDelegate];
|
||||
|
||||
if (self)
|
||||
_bundle = aBundle;
|
||||
@@ -114,16 +124,6 @@ var CPCibObjectDataKey = @"CPCibObjectDataKey";
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)_setAwakenCustomResources:(BOOL)shouldAwakenCustomResources
|
||||
{
|
||||
_awakenCustomResources = shouldAwakenCustomResources;
|
||||
}
|
||||
|
||||
- (BOOL)_awakenCustomResources
|
||||
{
|
||||
return _awakenCustomResources;
|
||||
}
|
||||
|
||||
- (BOOL)instantiateCibWithExternalNameTable:(CPDictionary)anExternalNameTable
|
||||
{
|
||||
var bundle = _bundle,
|
||||
@@ -132,7 +132,7 @@ var CPCibObjectDataKey = @"CPCibObjectDataKey";
|
||||
if (!bundle && owner)
|
||||
bundle = [CPBundle bundleForClass:[owner class]];
|
||||
|
||||
var unarchiver = [[_CPCibKeyedUnarchiver alloc] initForReadingWithData:_data bundle:bundle awakenCustomResources:_awakenCustomResources],
|
||||
var unarchiver = [[_CPCibKeyedUnarchiver alloc] initForReadingWithData:_data bundle:bundle awakenCustomResources:_awakenCustomResources cibName:_cibName],
|
||||
replacementClasses = [anExternalNameTable objectForKey:CPCibReplacementClasses];
|
||||
|
||||
if (replacementClasses)
|
||||
@@ -226,4 +226,4 @@ var CPCibDataFileKey = @"CPCibDataFileKey",
|
||||
[aCoder encodeObject:[_data base64] forKey:CPCibDataFileKey];
|
||||
}
|
||||
|
||||
@end
|
||||
@end
|
||||
+31
-17
@@ -26,7 +26,11 @@
|
||||
|
||||
@import "CPCib.j"
|
||||
|
||||
var CPCibOwner = @"CPCibOwner";
|
||||
var CPCibOwner = @"CPCibOwner",
|
||||
CPBundleDefaultLanguage = @"CPBundleDefaultLanguage",
|
||||
CPBundleTypeOfLocalization = @"CPBundleTypeOfLocalization",
|
||||
CPBundleBaseLocalizationType = @"CPBundleBaseLocalizationType",
|
||||
CPBundleInterfaceBuilderLocalizationType = @"CPBundleInterfaceBuilderLocalizationType";
|
||||
|
||||
@implementation CPObject (CPCibLoading)
|
||||
|
||||
@@ -45,14 +49,7 @@ var CPCibOwner = @"CPCibOwner";
|
||||
|
||||
+ (CPCib)loadCibNamed:(CPString)aName owner:(id)anOwner
|
||||
{
|
||||
if (![aName hasSuffix:@".cib"])
|
||||
aName = [aName stringByAppendingString:@".cib"];
|
||||
|
||||
// Path is based solely on anOwner:
|
||||
var bundle = anOwner ? [CPBundle bundleForClass:[anOwner class]] : [CPBundle mainBundle],
|
||||
path = [bundle pathForResource:aName];
|
||||
|
||||
return [self loadCibFile:path externalNameTable:@{ CPCibOwner: anOwner }];
|
||||
return [self loadCibFile:[self _cibPathForName:aName withOwner:anOwner] externalNameTable:@{ CPCibOwner: anOwner }];
|
||||
}
|
||||
|
||||
- (CPCib)loadCibFile:(CPString)aFileName externalNameTable:(CPDictionary)aNameTable
|
||||
@@ -71,14 +68,7 @@ var CPCibOwner = @"CPCibOwner";
|
||||
|
||||
+ (CPCib)loadCibNamed:(CPString)aName owner:(id)anOwner loadDelegate:(id)aDelegate
|
||||
{
|
||||
if (![aName hasSuffix:@".cib"])
|
||||
aName = [aName stringByAppendingString:@".cib"];
|
||||
|
||||
// Path is based solely on anOwner:
|
||||
var bundle = anOwner ? [CPBundle bundleForClass:[anOwner class]] : [CPBundle mainBundle],
|
||||
path = [bundle pathForResource:aName];
|
||||
|
||||
return [self loadCibFile:path externalNameTable:@{ CPCibOwner: anOwner } loadDelegate:aDelegate];
|
||||
return [self loadCibFile:[self _cibPathForName:aName withOwner:anOwner] externalNameTable:@{ CPCibOwner: anOwner } loadDelegate:aDelegate];
|
||||
}
|
||||
|
||||
- (CPCib)loadCibFile:(CPString)aFileName externalNameTable:(CPDictionary)aNameTable loadDelegate:(id)aDelegate
|
||||
@@ -91,6 +81,30 @@ var CPCibOwner = @"CPCibOwner";
|
||||
externalNameTable:aNameTable]]);
|
||||
}
|
||||
|
||||
- (CPString)_cibPathForResource:(CPString)aName
|
||||
{
|
||||
var defaultBundleLanguage = [self objectForInfoDictionaryKey:CPBundleDefaultLanguage],
|
||||
typeOfLocalization = [self objectForInfoDictionaryKey:CPBundleTypeOfLocalization];
|
||||
|
||||
if (defaultBundleLanguage && (!typeOfLocalization || typeOfLocalization == CPBundleBaseLocalizationType))
|
||||
aName = @"Base.lproj/" + aName;
|
||||
else if (defaultBundleLanguage && typeOfLocalization == CPBundleInterfaceBuilderLocalizationType)
|
||||
aName = _bundle.loadedLanguage() + ".lproj/" + aName;
|
||||
|
||||
return [self pathForResource:aName];
|
||||
}
|
||||
|
||||
+ (CPString)_cibPathForName:(CPString)aName withOwner:(id)anOwner
|
||||
{
|
||||
if (![aName hasSuffix:@".cib"])
|
||||
aName = [aName stringByAppendingString:@".cib"];
|
||||
|
||||
// Path is based solely on anOwner:
|
||||
var bundle = anOwner ? [CPBundle bundleForClass:[anOwner class]] : [CPBundle mainBundle];
|
||||
|
||||
return [bundle _cibPathForResource:aName];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation _CPCibLoadDelegate : CPObject
|
||||
|
||||
@@ -21,16 +21,22 @@
|
||||
*/
|
||||
|
||||
@import <Foundation/CPKeyedUnarchiver.j>
|
||||
|
||||
@import <Foundation/CPBundle.j>
|
||||
|
||||
@implementation _CPCibKeyedUnarchiver : CPKeyedUnarchiver
|
||||
{
|
||||
CPBundle _bundle;
|
||||
BOOL _awakenCustomResources;
|
||||
CPDictionary _externalObjectsForProxyIdentifiers;
|
||||
BOOL _awakenCustomResources @accessors(getter=awakenCustomResources);
|
||||
CPBundle _bundle @accessors(getter=bundle);
|
||||
CPDictionary _externalObjectsForProxyIdentifiers @accessors(setter=setExternalObjectsForProxyIdentifiers:);
|
||||
CPString _cibName @accessors(getter=cibName);
|
||||
}
|
||||
|
||||
- (id)initForReadingWithData:(CPData)data bundle:(CPBundle)aBundle awakenCustomResources:(BOOL)shouldAwakenCustomResources
|
||||
{
|
||||
return [self initForReadingWithData:data bundle:aBundle awakenCustomResources:shouldAwakenCustomResources cibName:@""];
|
||||
}
|
||||
|
||||
- (id)initForReadingWithData:(CPData)data bundle:(CPBundle)aBundle awakenCustomResources:(BOOL)shouldAwakenCustomResources cibName:(CPString)aCibName
|
||||
{
|
||||
self = [super initForReadingWithData:data];
|
||||
|
||||
@@ -38,6 +44,7 @@
|
||||
{
|
||||
_bundle = aBundle;
|
||||
_awakenCustomResources = shouldAwakenCustomResources;
|
||||
_cibName = aCibName;
|
||||
|
||||
[self setDelegate:self];
|
||||
}
|
||||
@@ -45,21 +52,6 @@
|
||||
return self;
|
||||
}
|
||||
|
||||
- (CPBundle)bundle
|
||||
{
|
||||
return _bundle;
|
||||
}
|
||||
|
||||
- (BOOL)awakenCustomResources
|
||||
{
|
||||
return _awakenCustomResources;
|
||||
}
|
||||
|
||||
- (void)setExternalObjectsForProxyIdentifiers:(CPDictionary)externalObjectsForProxyIdentifiers
|
||||
{
|
||||
_externalObjectsForProxyIdentifiers = externalObjectsForProxyIdentifiers;
|
||||
}
|
||||
|
||||
- (id)externalObjectForProxyIdentifier:(CPString)anIdentifier
|
||||
{
|
||||
return [_externalObjectsForProxyIdentifiers objectForKey:anIdentifier];
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* _CPLocalizableString.j
|
||||
* AppKit
|
||||
*
|
||||
* Created by Alexandre Wilhelm.
|
||||
* Copyright 2015, Cappuccino Project
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
@import <Foundation/CPString.j>
|
||||
@import <Foundation/CPCharacterSet.j>
|
||||
|
||||
@implementation _CPLocalizableString : CPObject
|
||||
{
|
||||
CPString _dev @accessors(property=dev);
|
||||
CPString _key @accessors(property=key);
|
||||
CPString _value @accessors(property=value);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
var CPLocalizableStringDev = @"CPLocalizableStringDev",
|
||||
CPLocalizableStringKey = @"CPLocalizableStringKey",
|
||||
CPLocalizableStringValue = @"CPLocalizableStringValue";
|
||||
|
||||
@implementation _CPLocalizableString (CPCoding)
|
||||
|
||||
- (id)initWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
[self init];
|
||||
|
||||
_dev = [aCoder decodeObjectForKey:CPLocalizableStringDev];
|
||||
_key = [aCoder decodeObjectForKey:CPLocalizableStringKey];
|
||||
_value = [aCoder decodeObjectForKey:CPLocalizableStringValue];
|
||||
|
||||
var tableName = [[aCoder cibName] stringByTrimmingCharactersInSet:[CPCharacterSet characterSetWithCharactersInString:@".cib"]];
|
||||
|
||||
return [[aCoder bundle] localizedStringForKey:_key value:_value table:tableName];
|
||||
}
|
||||
|
||||
- (void)encodeWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
[aCoder encodeObject:_dev forKey:CPLocalizableStringDev];
|
||||
[aCoder encodeObject:_key forKey:CPLocalizableStringKey];
|
||||
[aCoder encodeObject:_value forKey:CPLocalizableStringValue];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -27,7 +27,8 @@
|
||||
|
||||
@import "CGContext.j"
|
||||
@import "CGGeometry.j"
|
||||
|
||||
@import "CPColor.j"
|
||||
@import "CPView.j"
|
||||
|
||||
#define DOM(aLayer) aLayer._DOMElement
|
||||
|
||||
@@ -494,11 +495,11 @@ var CALayerRegisteredRunLoopUpdates = nil;
|
||||
*/
|
||||
- (void)display
|
||||
{
|
||||
#if PLATFORM(DOM)
|
||||
if (!_context)
|
||||
{
|
||||
_context = CGBitmapGraphicsContextCreate();
|
||||
|
||||
#if PLATFORM(DOM)
|
||||
_DOMContentsElement = _context.DOMElement;
|
||||
|
||||
_DOMContentsElement.style.zIndex = -100;
|
||||
@@ -516,7 +517,6 @@ var CALayerRegisteredRunLoopUpdates = nil;
|
||||
_DOMContentsElement.style.height = ROUND(CGRectGetHeight(_backingStoreFrame)) + "px";
|
||||
|
||||
_DOMElement.appendChild(_DOMContentsElement);
|
||||
#endif
|
||||
}
|
||||
|
||||
if (USE_BUFFER)
|
||||
@@ -534,6 +534,7 @@ var CALayerRegisteredRunLoopUpdates = nil;
|
||||
|
||||
[self drawInContext:CABackingStoreGetContext(_contents)];
|
||||
}
|
||||
#endif
|
||||
|
||||
[self composite];
|
||||
}
|
||||
@@ -796,7 +797,6 @@ if (_DOMContentsElement && aLayer._zPosition > _DOMContentsElement.style.zIndex)
|
||||
|
||||
if (mask & CALayerDisplayUpdateMask)
|
||||
[layer display];
|
||||
|
||||
else if (mask & CALayerFrameSizeUpdateMask || mask & CALayerCompositeUpdateMask)
|
||||
[layer composite];
|
||||
|
||||
|
||||
+3
-4
@@ -1,8 +1,6 @@
|
||||
|
||||
require("../common.jake");
|
||||
|
||||
checkUlimit();
|
||||
|
||||
var framework = require("objective-j/jake").framework,
|
||||
BundleTask = require("objective-j/jake").BundleTask;
|
||||
|
||||
@@ -35,8 +33,9 @@ appKitTask = framework ("AppKit", function(appKitTask)
|
||||
return "--include \"" + aFilename + "\"";
|
||||
}).join(" ");
|
||||
|
||||
if ($CONFIGURATION === "Release")
|
||||
appKitTask.setCompilerFlags("-O " + INCLUDES);
|
||||
if ($CONFIGURATION === "Release") {
|
||||
appKitTask.setCompilerFlags("-O2 " + INCLUDES);
|
||||
}
|
||||
else
|
||||
appKitTask.setCompilerFlags("-DDEBUG -g " + INCLUDES);
|
||||
});
|
||||
|
||||
@@ -121,6 +121,8 @@ var PrimaryPlatformWindow = NULL;
|
||||
_windowLayers = @{};
|
||||
|
||||
_charCodes = {};
|
||||
|
||||
_platformPasteboard = [CPPlatformPasteboard new];
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -213,7 +215,7 @@ var PrimaryPlatformWindow = NULL;
|
||||
- (BOOL)isVisible
|
||||
{
|
||||
#if PLATFORM(DOM)
|
||||
return _DOMWindow !== NULL;
|
||||
return _DOMWindow !== NULL && _DOMWindow !== undefined;
|
||||
#else
|
||||
return NO;
|
||||
#endif
|
||||
|
||||
@@ -48,10 +48,21 @@ var screenNeedsInitialization = NO,
|
||||
document.documentElement.style.overflow = "hidden";
|
||||
|
||||
if ([CPPlatform isBrowser])
|
||||
{
|
||||
// This differ from cocoa, where shouldTerminate is called in the method terminate of CPApp
|
||||
// Cappuccino acts like this because we can not close a window openend by the user with a script (so not possible in terminate), and we can only prevent the action in the method onbeforeunload in js.
|
||||
window.onbeforeunload = function()
|
||||
{
|
||||
if ([CPApp _sendDelegateApplicationShouldTerminate] != CPTerminateNow)
|
||||
return [CPApp _sendDelegateApplicationShouldTerminateMessage];
|
||||
};
|
||||
|
||||
window.onunload = function()
|
||||
{
|
||||
[self closeAllPlatformWindows];
|
||||
[CPApp terminate:nil];
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
+ (BOOL)isBrowser
|
||||
@@ -138,4 +149,18 @@ var screenNeedsInitialization = NO,
|
||||
object:self];
|
||||
}
|
||||
|
||||
+ (void)closeAllPlatformWindows
|
||||
{
|
||||
var platformWindows = [CPPlatformWindow visiblePlatformWindows],
|
||||
primaryPlatformWindow = [CPPlatformWindow primaryPlatformWindow],
|
||||
platformWindowEnumerator = [platformWindows objectEnumerator],
|
||||
platformWindow = nil;
|
||||
|
||||
while ((platformWindow = [platformWindowEnumerator nextObject]) !== nil)
|
||||
{
|
||||
if (platformWindow != primaryPlatformWindow)
|
||||
[platformWindow orderOut:self];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -129,6 +129,33 @@ var DOMFixedWidthSpanElement = nil,
|
||||
DOMMetricsDivElement.appendChild(DOMMetricsImgElement);
|
||||
}
|
||||
|
||||
+ (int)charPositionOfString:(CPString)aString withFont:(CPFont)aFont forPoint:(CGPoint)aPoint
|
||||
{
|
||||
if (!aString)
|
||||
return 0;
|
||||
|
||||
var position = 0,
|
||||
stringLength = aString.length,
|
||||
currentString = "";
|
||||
|
||||
for (var i = 0; i < stringLength; i++)
|
||||
{
|
||||
var lastChar = aString[i];
|
||||
|
||||
currentString += lastChar;
|
||||
|
||||
var sizeOfString = [self sizeOfString:currentString withFont:aFont forWidth:nil].width,
|
||||
sizeLastChar = [self sizeOfString:lastChar withFont:aFont forWidth:nil].width;
|
||||
|
||||
if (sizeOfString - sizeLastChar / 2 < aPoint.x)
|
||||
position++;
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
return position;
|
||||
}
|
||||
|
||||
+ (CGSize)sizeOfString:(CPString)aString withFont:(CPFont)aFont forWidth:(float)aWidth
|
||||
{
|
||||
if (!DOMFixedWidthSpanElement)
|
||||
|
||||
@@ -365,6 +365,7 @@ _CPPlatformWindowWillCloseNotification = @"_CPPlatformWindowWillCloseNotificatio
|
||||
_DOMBodyElement.style["-khtml-user-select"] = "none";
|
||||
|
||||
_DOMBodyElement.webkitTouchCallout = "none";
|
||||
_DOMBodyElement.style[CPBrowserStyleProperty(@"user-select")] = @"none";
|
||||
|
||||
[self createDOMElements];
|
||||
[self _addLayers];
|
||||
@@ -567,6 +568,9 @@ _CPPlatformWindowWillCloseNotification = @"_CPPlatformWindowWillCloseNotificatio
|
||||
|
||||
_DOMWindow = window.open("about:blank", "_blank", "menubar=no,location=no,resizable=yes,scrollbars=no,status=no,left=" + CGRectGetMinX(_contentRect) + ",top=" + CGRectGetMinY(_contentRect) + ",width=" + CGRectGetWidth(_contentRect) + ",height=" + CGRectGetHeight(_contentRect));
|
||||
|
||||
if (!_DOMWindow)
|
||||
return;
|
||||
|
||||
[PlatformWindows addObject:self];
|
||||
|
||||
// FIXME: cpSetFrame?
|
||||
@@ -1413,6 +1417,9 @@ _CPPlatformWindowWillCloseNotification = @"_CPPlatformWindowWillCloseNotificatio
|
||||
|
||||
- (void)order:(CPWindowOrderingMode)orderingMode window:(CPWindow)aWindow relativeTo:(CPWindow)otherWindow
|
||||
{
|
||||
if (!_DOMWindow)
|
||||
return;
|
||||
|
||||
[CPPlatform initializeScreenIfNecessary];
|
||||
|
||||
// Grab the appropriate level for the layer, and create it if
|
||||
@@ -1850,11 +1857,10 @@ function CPWindowObjectList()
|
||||
|
||||
function CPWindowList()
|
||||
{
|
||||
var windowObjectList = CPWindowObjectList(),
|
||||
windowList = [];
|
||||
var windowObjectList = CPWindowObjectList();
|
||||
|
||||
for (var i = 0, count = [windowObjectList count]; i < count; i++)
|
||||
windowList.push([windowObjectList[i] windowNumber]);
|
||||
|
||||
return windowList;
|
||||
return [windowObjectList arrayByApplyingBlock:function(windowObject)
|
||||
{
|
||||
return [windowObject windowNumber];
|
||||
}];
|
||||
}
|
||||
@@ -384,6 +384,19 @@ var themedButtonValues = nil,
|
||||
"themedTokenFieldTokenCloseButton"];
|
||||
}
|
||||
|
||||
+ (CPColor)themedColor
|
||||
{
|
||||
var color = [CPColor blackColor],
|
||||
themedColorValues =
|
||||
[
|
||||
[@"alternate-selected-control-color", [[CPColor alloc] _initWithRGBA:[0.22, 0.46, 0.84, 1.0]]],
|
||||
[@"secondary-selected-control-color", [[CPColor alloc] _initWithRGBA:[0.83, 0.83, 0.83, 1.0]]]
|
||||
];
|
||||
|
||||
[self registerThemeValues:themedColorValues forObject:color];
|
||||
|
||||
return color;
|
||||
}
|
||||
|
||||
+ (CPButton)makeButton
|
||||
{
|
||||
@@ -2999,6 +3012,24 @@ var themedButtonValues = nil,
|
||||
return progressBar;
|
||||
}
|
||||
|
||||
+ (CPProgressIndicator)themedCircularProgressIndicator
|
||||
{
|
||||
var progressBar = [[CPProgressIndicator alloc] initWithFrame:CGRectMake(0, 0, 32, 32)];
|
||||
[progressBar setStyle:CPProgressIndicatorSpinningStyle];
|
||||
[progressBar setIndeterminate:NO];
|
||||
|
||||
var themeValues =
|
||||
[
|
||||
[@"circular-border-color", [CPColor colorWithHexString:@"C7C7C7"]],
|
||||
[@"circular-border-size", 1],
|
||||
[@"circular-color", [CPColor colorWithHexString:@"89B5CD"]]
|
||||
];
|
||||
|
||||
[self registerThemeValues:themeValues forView:progressBar];
|
||||
|
||||
return progressBar;
|
||||
}
|
||||
|
||||
+ (CPBox)themedBox
|
||||
{
|
||||
var box = [[CPBox alloc] initWithFrame:CGRectMake(0,0,100,100)],
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
@import <AppKit/CPButtonBar.j>
|
||||
@import <AppKit/CPCheckBox.j>
|
||||
@import <AppKit/CPComboBox.j>
|
||||
@import <AppKit/CPColor.j>
|
||||
@import <AppKit/CPColorWell.j>
|
||||
@import <AppKit/CPDatePicker.j>
|
||||
@import <AppKit/CPLevelIndicator.j>
|
||||
@@ -96,7 +97,22 @@ var themedButtonValues = nil,
|
||||
"themedRuleEditor",
|
||||
"themedTableDataView",
|
||||
"themedCornerview",
|
||||
"themedTokenFieldTokenCloseButton"];
|
||||
"themedTokenFieldTokenCloseButton",
|
||||
"themedColor"];
|
||||
}
|
||||
|
||||
+ (CPColor)themedColor
|
||||
{
|
||||
var color = [CPColor redColor],
|
||||
themedColorValues =
|
||||
[
|
||||
[@"alternate-selected-control-color", [[CPColor alloc] _initWithRGBA:[0.22, 0.46, 0.84, 1.0]]],
|
||||
[@"secondary-selected-control-color", [[CPColor alloc] _initWithRGBA:[0.83, 0.83, 0.83, 1.0]]]
|
||||
];
|
||||
|
||||
[self registerThemeValues:themedColorValues forObject:color];
|
||||
|
||||
return color;
|
||||
}
|
||||
|
||||
+ (CPButton)makeButton
|
||||
@@ -310,7 +326,7 @@ var themedButtonValues = nil,
|
||||
var button = [self button];
|
||||
|
||||
[button setTitle:@"OK"];
|
||||
[button setThemeState:[CPButtonStateBezelStyleRounded, CPThemeStateDefault]];
|
||||
[button setThemeStates:[CPButtonStateBezelStyleRounded, CPThemeStateDefault]];
|
||||
|
||||
return button;
|
||||
}
|
||||
@@ -688,7 +704,8 @@ var themedButtonValues = nil,
|
||||
[@"text-color", [CPColor blackColor], [CPThemeStateTableDataView, CPThemeStateSelectedDataView, CPThemeStateEditable, CPThemeStateFirstResponder, CPThemeStateKeyWindow]],
|
||||
[@"content-inset", CGInsetMake(7.0, 7.0, 5.0, 10.0), [CPThemeStateTableDataView, CPThemeStateEditable]],
|
||||
[@"font", [CPFont systemFontOfSize:CPFontCurrentSystemSize], [CPThemeStateTableDataView, CPThemeStateEditing]],
|
||||
[@"bezel-inset", CGInsetMake(-2.0, -2.0, -2.0, -2.0), [CPThemeStateTableDataView, CPThemeStateEditing]],
|
||||
[@"bezel-inset", CGInsetMake(-2.0, -2.0, -2.0, -2.0), [CPThemeStateTableDataView, CPThemeStateEditable, CPThemeStateEditing]],
|
||||
[@"bezel-inset", CGInsetMake(1.0, 1.0, 1.0, 1.0), [CPThemeStateTableDataView, CPThemeStateEditable]],
|
||||
|
||||
[@"text-color", [CPColor colorWithCalibratedWhite:125.0 / 255.0 alpha:1.0], [CPThemeStateTableDataView, CPThemeStateGroupRow]],
|
||||
[@"text-color", [CPColor whiteColor], [CPThemeStateTableDataView, CPThemeStateGroupRow, CPThemeStateSelectedDataView, CPThemeStateFirstResponder, CPThemeStateKeyWindow]],
|
||||
@@ -2499,6 +2516,24 @@ var themedButtonValues = nil,
|
||||
return progressBar;
|
||||
}
|
||||
|
||||
+ (CPProgressIndicator)themedCircularProgressIndicator
|
||||
{
|
||||
var progressBar = [[CPProgressIndicator alloc] initWithFrame:CGRectMake(0, 0, 32, 32)];
|
||||
[progressBar setStyle:CPProgressIndicatorSpinningStyle];
|
||||
[progressBar setIndeterminate:NO];
|
||||
|
||||
var themeValues =
|
||||
[
|
||||
[@"circular-border-color", [CPColor colorWithHexString:@"A0A0A0"]],
|
||||
[@"circular-border-size", 1],
|
||||
[@"circular-color", [CPColor colorWithHexString:@"5982DA"]]
|
||||
];
|
||||
|
||||
[self registerThemeValues:themeValues forView:progressBar];
|
||||
|
||||
return progressBar;
|
||||
}
|
||||
|
||||
+ (CPBox)themedBox
|
||||
{
|
||||
var box = [[CPBox alloc] initWithFrame:CGRectMake(0, 0, 100, 100)],
|
||||
|
||||
@@ -217,6 +217,11 @@ var ItemSizes = { },
|
||||
return [[self themeName] compare:[aThemeDescriptor themeName]];
|
||||
}
|
||||
|
||||
+ (void)registerThemeValues:(CPArray)themeValues forObject:(id)anObject
|
||||
{
|
||||
[self registerThemeValues:themeValues forView:anObject];
|
||||
}
|
||||
|
||||
+ (void)registerThemeValues:(CPArray)themeValues forView:(CPView)aView
|
||||
{
|
||||
for (var i = 0; i < themeValues.length; ++i)
|
||||
@@ -227,12 +232,22 @@ var ItemSizes = { },
|
||||
state = attributeValueState[2];
|
||||
|
||||
if (state)
|
||||
[aView setValue:value forThemeAttribute:attribute inState:state];
|
||||
{
|
||||
if (state.isa && [state isKindOfClass:CPArray])
|
||||
[aView setValue:value forThemeAttribute:attribute inStates:state];
|
||||
else
|
||||
[aView setValue:value forThemeAttribute:attribute inState:state];
|
||||
}
|
||||
else
|
||||
[aView setValue:value forThemeAttribute:attribute];
|
||||
}
|
||||
}
|
||||
|
||||
+ (void)registerThemeValues:(CPArray)themeValues forObject:(id)anObject inherit:(CPArray)inheritedValues
|
||||
{
|
||||
[self registerThemeValues:themeValues forView:anObject inherit:inheritedValues];
|
||||
}
|
||||
|
||||
+ (void)registerThemeValues:(CPArray)themeValues forView:(CPView)aView inherit:(CPArray)inheritedValues
|
||||
{
|
||||
// Register inherited values first, then override those with the subtheme values.
|
||||
@@ -294,7 +309,12 @@ var ItemSizes = { },
|
||||
}
|
||||
|
||||
if (state)
|
||||
[aView setValue:value forThemeAttribute:attribute inState:state];
|
||||
{
|
||||
if (state.isa && [state isKindOfClass:CPArray])
|
||||
[aView setValue:value forThemeAttribute:attribute inStates:state];
|
||||
else
|
||||
[aView setValue:value forThemeAttribute:attribute inState:state];
|
||||
}
|
||||
else
|
||||
[aView setValue:value forThemeAttribute:attribute];
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ blendKitTask = framework ("BlendKit", function(blendKitTask)
|
||||
blendKitTask.setFlattensSources(true); // FIXME: how do we non flatten?
|
||||
|
||||
if ($CONFIGURATION === "Release")
|
||||
blendKitTask.setCompilerFlags("-O");
|
||||
blendKitTask.setCompilerFlags("-O2");
|
||||
else
|
||||
blendKitTask.setCompilerFlags("-DDEBUG -g");
|
||||
});
|
||||
|
||||
@@ -171,9 +171,9 @@ var _CPAutocompleteMenuMaximumHeight = 307;
|
||||
|
||||
var dataView = [tableColumn dataView],
|
||||
fontNormal = [dataView valueForThemeAttribute:@"font" inState:CPThemeStateTableDataView],
|
||||
fontSelected = [dataView valueForThemeAttribute:@"font" inState:[CPThemeStateTableDataView, CPThemeStateSelectedDataView]],
|
||||
fontSelected = [dataView valueForThemeAttribute:@"font" inStates:[CPThemeStateTableDataView, CPThemeStateSelectedDataView]],
|
||||
contentInsetNormal = [dataView valueForThemeAttribute:@"content-inset" inState:CPThemeStateTableDataView],
|
||||
contentInsetSelected = [dataView valueForThemeAttribute:@"content-inset" inState:[CPThemeStateTableDataView, CPThemeStateSelectedDataView]];
|
||||
contentInsetSelected = [dataView valueForThemeAttribute:@"content-inset" inStates:[CPThemeStateTableDataView, CPThemeStateSelectedDataView]];
|
||||
|
||||
var mergedString = contentArray.join("\n");
|
||||
|
||||
|
||||
@@ -0,0 +1,480 @@
|
||||
/*
|
||||
* AppKit.j
|
||||
* AppKit
|
||||
*
|
||||
* Created by Alexandre Wilhelm.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
@import <Foundation/Foundation.j>
|
||||
|
||||
@import "CPTheme.j"
|
||||
|
||||
var CPViewThemeClassKey = @"CPViewThemeClassKey",
|
||||
CPViewThemeStateKey = @"CPViewThemeStateKey";
|
||||
|
||||
@protocol CPTheme
|
||||
|
||||
- (CPString)themeClass;
|
||||
- (void)setThemeClass:(CPString)theClass;
|
||||
|
||||
- (CPTheme)theme;
|
||||
- (void)setTheme:(CPTheme)aTheme;
|
||||
|
||||
- (unsigned)themeState;
|
||||
- (BOOL)hasThemeState:(ThemeState)aState;
|
||||
- (BOOL)hasThemeStates:(CPArray)states;
|
||||
- (BOOL)setThemeState:(ThemeState)aState;
|
||||
- (BOOL)setThemeStates:(CPArray)aState;
|
||||
- (BOOL)unsetThemeState:(ThemeState)aState;
|
||||
- (BOOL)unsetThemeStates:(CPArray)aState;
|
||||
- (BOOL)hasThemeAttribute:(CPString)aName;
|
||||
|
||||
- (void)objectDidChangeTheme;
|
||||
|
||||
- (void)setValue:(id)aValue forThemeAttribute:(CPString)aName inState:(ThemeState)aState;
|
||||
- (void)setValue:(id)aValue forThemeAttribute:(CPString)aName;
|
||||
- (id)valueForThemeAttribute:(CPString)aName inState:(ThemeState)aState;
|
||||
- (id)valueForThemeAttribute:(CPString)aName;
|
||||
- (id)currentValueForThemeAttribute:(CPString)aName;
|
||||
- (void)registerThemeValues:(CPArray)themeValues;
|
||||
- (void)registerThemeValues:(CPArray)themeValues inherit:(CPArray)inheritedValues;
|
||||
|
||||
@end
|
||||
|
||||
@implementation CPObject (ObjectTheming)
|
||||
{
|
||||
// Theming Support
|
||||
CPTheme _theme;
|
||||
CPString _themeClass;
|
||||
JSObject _themeAttributes;
|
||||
unsigned _themeState;
|
||||
}
|
||||
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Theme State
|
||||
|
||||
- (unsigned)themeState
|
||||
{
|
||||
return _themeState;
|
||||
}
|
||||
|
||||
- (BOOL)hasThemeState:(ThemeState)aState
|
||||
{
|
||||
#if DEBUG
|
||||
// TODO: To allow aState to be an array is now deprecated. An exception is thrown only in Debug version as
|
||||
// the preformance cost for this check is to high. We should remove this check in a future release.
|
||||
if (aState && aState.isa && [aState isKindOfClass:CPArray])
|
||||
[CPException raise:CPInvalidArgumentException reason:@"aState can't be an array. Please use 'hasThemeStates: instead: " + aState];
|
||||
#endif
|
||||
|
||||
return _themeState.hasThemeState(aState);
|
||||
}
|
||||
|
||||
- (BOOL)hasThemeStates:(CPArray)states
|
||||
{
|
||||
var i = [states count],
|
||||
aState = [states objectAtIndex:--i];
|
||||
|
||||
while (i > 0)
|
||||
aState = aState.and([states objectAtIndex:--i]);
|
||||
|
||||
return _themeState.hasThemeState(aState);
|
||||
}
|
||||
|
||||
- (BOOL)setThemeState:(ThemeState)aState
|
||||
{
|
||||
#if DEBUG
|
||||
// TODO: To allow aState to be an array is now deprecated. An exception is thrown only in Debug version as
|
||||
// the preformance cost for this check is to high. We should remove this check in a future release.
|
||||
if (aState && aState.isa && [aState isKindOfClass:CPArray])
|
||||
[CPException raise:CPInvalidArgumentException reason:@"aState can't be an array. Please use 'setThemeStates: instead: " + aState];
|
||||
#endif
|
||||
|
||||
if (_themeState.hasThemeState(aState))
|
||||
return NO;
|
||||
|
||||
_themeState = _themeState.and(aState);
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (BOOL)unsetThemeState:(ThemeState)aState
|
||||
{
|
||||
#if DEBUG
|
||||
// TODO: To allow aState to be an array is now deprecated. An exception is thrown only in Debug version as
|
||||
// the preformance cost for this check is to high. We should remove this check in a future release.
|
||||
if (aState && aState.isa && [aState isKindOfClass:CPArray])
|
||||
[CPException raise:CPInvalidArgumentException reason:@"aState can't be an array. Please use 'unsetThemeStates: instead: " + aState];
|
||||
#endif
|
||||
|
||||
var oldThemeState = _themeState;
|
||||
|
||||
_themeState = _themeState.without(aState);
|
||||
|
||||
if (oldThemeState === _themeState)
|
||||
return NO;
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (BOOL)setThemeStates:(CPArray)states
|
||||
{
|
||||
var i = [states count],
|
||||
aState = [states objectAtIndex:--i];
|
||||
|
||||
while (i > 0)
|
||||
aState = aState.and([states objectAtIndex:--i]);
|
||||
|
||||
return [self setThemeState:aState];
|
||||
}
|
||||
|
||||
- (BOOL)unsetThemeStates:(CPArray)states
|
||||
{
|
||||
var i = [states count],
|
||||
aState = [states objectAtIndex:--i];
|
||||
|
||||
while (i > 0)
|
||||
aState = aState.and([states objectAtIndex:--i]);
|
||||
|
||||
return [self unsetThemeState:aState];
|
||||
}
|
||||
|
||||
#pragma mark Theme Attributes
|
||||
|
||||
+ (CPString)defaultThemeClass
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
+ (CPDictionary)themeAttributes
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (CPString)themeClass
|
||||
{
|
||||
if (_themeClass)
|
||||
return _themeClass;
|
||||
|
||||
return [[self class] defaultThemeClass];
|
||||
}
|
||||
|
||||
- (void)setThemeClass:(CPString)theClass
|
||||
{
|
||||
_themeClass = theClass;
|
||||
|
||||
[self _loadThemeAttributes];
|
||||
}
|
||||
|
||||
var NULL_THEME = {};
|
||||
|
||||
+ (CPArray)_themeAttributesForTheme:(CPTheme)theme andThemeClass:(CPString)themeClassName
|
||||
{
|
||||
var theClass = [self class],
|
||||
theClassName = class_getName(theClass),
|
||||
themeClassNameCache = theme != nil ? theme._cachedThemeAttributes || (theme._cachedThemeAttributes = {}) : NULL_THEME,
|
||||
themedCacheAttributes = themeClassNameCache[themeClassName] || (themeClassNameCache[themeClassName] = {}),
|
||||
attributes = themedCacheAttributes[theClassName];
|
||||
|
||||
if (attributes)
|
||||
return attributes;
|
||||
else
|
||||
attributes = [];
|
||||
|
||||
var CPObjectClass = [CPObject class];
|
||||
|
||||
for (; theClass && theClass !== CPObjectClass; theClass = [theClass superclass])
|
||||
{
|
||||
var cachedAttributes = themedCacheAttributes[class_getName(theClass)];
|
||||
|
||||
if (cachedAttributes)
|
||||
{
|
||||
attributes = attributes.length ? attributes.concat(cachedAttributes) : attributes;
|
||||
break;
|
||||
}
|
||||
|
||||
var attributeDictionary = [theClass themeAttributes];
|
||||
|
||||
if (!attributeDictionary)
|
||||
continue;
|
||||
|
||||
var attributeKeys = [attributeDictionary allKeys],
|
||||
attributeCount = attributeKeys.length;
|
||||
|
||||
while (attributeCount--)
|
||||
{
|
||||
var attributeName = attributeKeys[attributeCount],
|
||||
attributeValue = [attributeDictionary objectForKey:attributeName],
|
||||
themeAttribute = [[_CPThemeAttribute alloc] initWithName:attributeName defaultValue:attributeValue defaultAttribute:[theme attributeWithName:attributeName forClass:themeClassName]];
|
||||
|
||||
attributes.push(themeAttribute);
|
||||
}
|
||||
}
|
||||
|
||||
themedCacheAttributes[theClassName] = attributes;
|
||||
|
||||
return attributes;
|
||||
}
|
||||
|
||||
- (void)_loadThemeAttributes
|
||||
{
|
||||
var theClass = [self class],
|
||||
attributes = [theClass _themeAttributesForTheme:[self theme] andThemeClass:[self themeClass]],
|
||||
count = attributes.length;
|
||||
|
||||
if (!count)
|
||||
return;
|
||||
|
||||
_themeAttributes = {};
|
||||
|
||||
while (count--)
|
||||
{
|
||||
var attribute = attributes[count];
|
||||
|
||||
_themeAttributes[attribute._name] = attribute;
|
||||
}
|
||||
}
|
||||
|
||||
- (CPTheme)theme
|
||||
{
|
||||
return _theme;
|
||||
}
|
||||
|
||||
- (void)setTheme:(CPTheme)aTheme
|
||||
{
|
||||
if (_theme === aTheme)
|
||||
return;
|
||||
|
||||
_theme = aTheme;
|
||||
|
||||
[self objectDidChangeTheme];
|
||||
}
|
||||
|
||||
- (void)objectDidChangeTheme
|
||||
{
|
||||
if (!_themeAttributes)
|
||||
return;
|
||||
|
||||
var theme = [self theme],
|
||||
themeClass = [self themeClass];
|
||||
|
||||
for (var attributeName in _themeAttributes)
|
||||
{
|
||||
if (_themeAttributes.hasOwnProperty(attributeName))
|
||||
_themeAttributes[attributeName] = [_themeAttributes[attributeName] attributeBySettingParentAttribute:[theme attributeWithName:attributeName forClass:themeClass]];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
- (CPDictionary)_themeAttributeDictionary
|
||||
{
|
||||
var dictionary = @{};
|
||||
|
||||
if (_themeAttributes)
|
||||
{
|
||||
var theme = [self theme];
|
||||
|
||||
for (var attributeName in _themeAttributes)
|
||||
{
|
||||
if (_themeAttributes.hasOwnProperty(attributeName))
|
||||
[dictionary setObject:_themeAttributes[attributeName] forKey:attributeName];
|
||||
}
|
||||
}
|
||||
|
||||
return dictionary;
|
||||
}
|
||||
|
||||
- (void)setValue:(id)aValue forThemeAttribute:(CPString)aName inState:(ThemeState)aState
|
||||
{
|
||||
#if DEBUG
|
||||
// TODO: To allow aState to be an array is now deprecated. An exception is thrown only in Debug version as
|
||||
// the preformance cost for this check is to high. We should remove this check in a future release.
|
||||
if (aState.isa && [aState isKindOfClass:CPArray])
|
||||
[CPException raise:CPInvalidArgumentException reason:self + @": aState can't be an array. Please use 'setValue:forThemeAttribute:inStates:' instead: " + aState];
|
||||
#endif
|
||||
|
||||
var themeAttr = _themeAttributes && _themeAttributes[aName];
|
||||
|
||||
if (!themeAttr)
|
||||
[CPException raise:CPInvalidArgumentException reason:[self className] + " does not contain theme attribute '" + aName + "'"];
|
||||
|
||||
_themeAttributes[aName] = [themeAttr attributeBySettingValue:aValue forState:aState];
|
||||
}
|
||||
|
||||
- (void)setValue:(id)aValue forThemeAttribute:(CPString)aName inStates:(CPArray)states
|
||||
{
|
||||
var i = [states count],
|
||||
aState = [states objectAtIndex:--i];
|
||||
|
||||
while (i > 0)
|
||||
aState = aState.and([states objectAtIndex:--i]);
|
||||
|
||||
var themeAttr = _themeAttributes && _themeAttributes[aName];
|
||||
|
||||
if (!themeAttr)
|
||||
[CPException raise:CPInvalidArgumentException reason:[self className] + " does not contain theme attribute '" + aName + "'"];
|
||||
|
||||
_themeAttributes[aName] = [themeAttr attributeBySettingValue:aValue forState:aState];
|
||||
}
|
||||
|
||||
- (void)setValue:(id)aValue forThemeAttribute:(CPString)aName
|
||||
{
|
||||
var themeAttr = _themeAttributes && _themeAttributes[aName];
|
||||
|
||||
if (!themeAttr)
|
||||
[CPException raise:CPInvalidArgumentException reason:[self className] + " does not contain theme attribute '" + aName + "'"];
|
||||
|
||||
_themeAttributes[aName] = [themeAttr attributeBySettingValue:aValue];
|
||||
}
|
||||
|
||||
- (id)valueForThemeAttribute:(CPString)aName inState:(ThemeState)aState
|
||||
{
|
||||
#if DEBUG
|
||||
if (aState.isa && [aState isKindOfClass:CPArray])
|
||||
[CPException raise:CPInvalidArgumentException reason:@"aState can't be an array. Please use 'valueForThemeAttribute:inStates:' instead: " + aState];
|
||||
#endif
|
||||
|
||||
var themeAttr = _themeAttributes && _themeAttributes[aName];
|
||||
|
||||
if (!themeAttr)
|
||||
[CPException raise:CPInvalidArgumentException reason:[self className] + " does not contain theme attribute '" + aName + "'"];
|
||||
|
||||
return [themeAttr valueForState:aState];
|
||||
}
|
||||
|
||||
- (id)valueForThemeAttribute:(CPString)aName inStates:(CPArray)states
|
||||
{
|
||||
var i = [states count],
|
||||
aState = [states objectAtIndex:--i];
|
||||
|
||||
while (i > 0)
|
||||
aState = aState.and([states objectAtIndex:--i]);
|
||||
|
||||
var themeAttr = _themeAttributes && _themeAttributes[aName];
|
||||
|
||||
if (!themeAttr)
|
||||
[CPException raise:CPInvalidArgumentException reason:[self className] + " does not contain theme attribute '" + aName + "'"];
|
||||
|
||||
return [themeAttr valueForState:aState];
|
||||
}
|
||||
|
||||
- (id)valueForThemeAttribute:(CPString)aName
|
||||
{
|
||||
var themeAttr = _themeAttributes && _themeAttributes[aName];
|
||||
|
||||
if (!themeAttr)
|
||||
[CPException raise:CPInvalidArgumentException reason:[self className] + " does not contain theme attribute '" + aName + "'"];
|
||||
|
||||
return [themeAttr value];
|
||||
}
|
||||
|
||||
- (id)currentValueForThemeAttribute:(CPString)aName
|
||||
{
|
||||
var themeAttr = _themeAttributes && _themeAttributes[aName];
|
||||
|
||||
if (!themeAttr)
|
||||
[CPException raise:CPInvalidArgumentException reason:[self className] + " does not contain theme attribute '" + aName + "'"];
|
||||
|
||||
return [themeAttr valueForState:_themeState];
|
||||
}
|
||||
|
||||
- (BOOL)hasThemeAttribute:(CPString)aName
|
||||
{
|
||||
return (_themeAttributes && _themeAttributes[aName] !== undefined);
|
||||
}
|
||||
|
||||
/*!
|
||||
Registers theme values encoded in an array at runtime. The format of the data in the array
|
||||
is the same as that used by ThemeDescriptors.j, with the exception that you need to use
|
||||
CPColorWithImages() in place of PatternColor(). For more information see the comments
|
||||
at the top of ThemeDescriptors.j.
|
||||
|
||||
@param themeValues array of theme values
|
||||
*/
|
||||
- (void)registerThemeValues:(CPArray)themeValues
|
||||
{
|
||||
for (var i = 0; i < themeValues.length; ++i)
|
||||
{
|
||||
var attributeValueState = themeValues[i],
|
||||
attribute = attributeValueState[0],
|
||||
value = attributeValueState[1],
|
||||
state = attributeValueState[2];
|
||||
|
||||
if (state)
|
||||
if (state.isa && [state isKindOfClass:CPArray])
|
||||
[self setValue:value forThemeAttribute:attribute inStates:state];
|
||||
else
|
||||
[self setValue:value forThemeAttribute:attribute inState:state];
|
||||
else
|
||||
[self setValue:value forThemeAttribute:attribute];
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
Registers theme values encoded in an array at runtime. The format of the data in the array
|
||||
is the same as that used by ThemeDescriptors.j, with the exception that you need to use
|
||||
CPColorWithImages() in place of PatternColor(). The values in \c inheritedValues are
|
||||
registered first, then those in \c themeValues override/augment the inherited values.
|
||||
For more information see the comments at the top of ThemeDescriptors.j.
|
||||
|
||||
@param themeValues array of base theme values
|
||||
@param inheritedValues array of overridden/additional theme values
|
||||
*/
|
||||
- (void)registerThemeValues:(CPArray)themeValues inherit:(CPArray)inheritedValues
|
||||
{
|
||||
// Register inherited values first, then override those with the subtheme values.
|
||||
if (inheritedValues)
|
||||
[self registerThemeValues:inheritedValues];
|
||||
|
||||
if (themeValues)
|
||||
[self registerThemeValues:themeValues];
|
||||
}
|
||||
|
||||
- (void)_encodeThemeObjectsWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
[aCoder encodeObject:[self themeClass] forKey:CPViewThemeClassKey];
|
||||
[aCoder encodeObject:String(_themeState) forKey:CPViewThemeStateKey];
|
||||
|
||||
for (var attributeName in _themeAttributes)
|
||||
{
|
||||
if (_themeAttributes.hasOwnProperty(attributeName))
|
||||
CPThemeAttributeEncode(aCoder, _themeAttributes[attributeName]);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)_decodeThemeObjectsWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
_theme = [CPTheme defaultTheme];
|
||||
_themeClass = [aCoder decodeObjectForKey:CPViewThemeClassKey];
|
||||
_themeState = CPThemeState([aCoder decodeObjectForKey:CPViewThemeStateKey]);
|
||||
_themeAttributes = {};
|
||||
|
||||
var theClass = [self class],
|
||||
themeClass = [self themeClass],
|
||||
attributes = [theClass _themeAttributesForTheme:_theme andThemeClass:themeClass],
|
||||
count = attributes.length;
|
||||
|
||||
while (count--)
|
||||
{
|
||||
var attribute = attributes[count];
|
||||
|
||||
_themeAttributes[attribute._name] = CPThemeAttributeDecode(aCoder, attribute);
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
+27
-15
@@ -141,7 +141,12 @@ var _CPPopoverWindow_shouldClose_ = 1 << 4,
|
||||
if (_appearance === anAppearance)
|
||||
return;
|
||||
|
||||
[_windowView setAppearance:anAppearance];
|
||||
_appearance = anAppearance;
|
||||
|
||||
var appearanceName = _appearance == CPPopoverAppearanceMinimal ? CPAppearanceNameVibrantLight : CPAppearanceNameVibrantDark,
|
||||
viewAppearance = [CPAppearance appearanceNamed:appearanceName];
|
||||
|
||||
[_windowView setAppearance:viewAppearance];
|
||||
}
|
||||
|
||||
- (void)setStyleMask:(unsigned)aStyleMask
|
||||
@@ -548,29 +553,36 @@ var _CPPopoverWindow_shouldClose_ = 1 << 4,
|
||||
|
||||
// Now set up the pop-in to normal size transition.
|
||||
// Because we are watching the -webkit-transform, it will occur now.
|
||||
[self setCSS3Property:@"Transform" value:@"scale(1)"];
|
||||
[self setCSS3Property:@"Transition" value:CPBrowserCSSProperty('transform') + @" 50ms linear"];
|
||||
|
||||
_transitionCompleteFunction = function()
|
||||
window.setTimeout(function()
|
||||
{
|
||||
[self setCSS3Property:@"Transform" value:@"scale(1)"];
|
||||
[self setCSS3Property:@"Transition" value:CPBrowserCSSProperty('transform') + @" 50ms linear"];
|
||||
|
||||
_transitionCompleteFunction = function()
|
||||
{
|
||||
#if PLATFORM(DOM)
|
||||
_DOMElement.removeEventListener(CPBrowserStyleProperty('transitionend'), _transitionCompleteFunction, YES);
|
||||
_DOMElement.removeEventListener(CPBrowserStyleProperty('transitionend'), _transitionCompleteFunction, YES);
|
||||
|
||||
// Make sure to clear these properties when the animation is done. Without this,
|
||||
// the window becomes blurry in Chrome, presumably because the browser composits
|
||||
// a layer with a transform differently even when it's an identity transform.
|
||||
[self setCSS3Property:@"Transform" value:nil];
|
||||
[self setCSS3Property:@"TransformOrigin" value:nil];
|
||||
[self setCSS3Property:@"Transition" value:nil];
|
||||
// Make sure to clear these properties when the animation is done. Without this,
|
||||
// the window becomes blurry in Chrome, presumably because the browser composits
|
||||
// a layer with a transform differently even when it's an identity transform.
|
||||
[self setCSS3Property:@"Transform" value:nil];
|
||||
[self setCSS3Property:@"TransformOrigin" value:nil];
|
||||
[self setCSS3Property:@"Transition" value:nil];
|
||||
#endif
|
||||
_isOpening = NO;
|
||||
_isOpening = NO;
|
||||
|
||||
[_delegate _popoverWindowDidShow];
|
||||
}
|
||||
[_delegate _popoverWindowDidShow];
|
||||
}
|
||||
|
||||
#if PLATFORM(DOM)
|
||||
_DOMElement.addEventListener(CPBrowserStyleProperty('transitionend'), _transitionCompleteFunction, YES);
|
||||
_DOMElement.addEventListener(CPBrowserStyleProperty('transitionend'), _transitionCompleteFunction, YES);
|
||||
#endif
|
||||
}, 0); // There are some weird random conditions happening in Chrome 44. If we don't put a timeout to 0 for the end of
|
||||
// the transition, the popover is blinking. It happens on Opera as well...
|
||||
// When the condition is met, the popover will lag a bit when opening...nothing crazy ;)
|
||||
// An issue has been opened here : https://code.google.com/p/chromium/issues/detail?id=523044&thanks=523044&ts=1440095724
|
||||
};
|
||||
|
||||
#if PLATFORM(DOM)
|
||||
|
||||
@@ -176,6 +176,8 @@ var _CPToolTipHeight = 24.0,
|
||||
_toolTipWindow = aWindow;
|
||||
_constrainsToUsableScreen = NO;
|
||||
|
||||
[self setPlatformWindow:[_toolTipWindow platformWindow]];
|
||||
|
||||
textFrameSize.height += 4;
|
||||
|
||||
_content = [CPTextField labelWithTitle:aString];
|
||||
@@ -193,6 +195,7 @@ var _CPToolTipHeight = 24.0,
|
||||
[self setAlphaValue:0.9];
|
||||
|
||||
[_windowView setNeedsDisplay:YES];
|
||||
|
||||
}
|
||||
|
||||
return self;
|
||||
|
||||
+25
-10
@@ -366,23 +366,38 @@ function pressEnvironment(rootPath, outputFiles, environment, options) {
|
||||
return {executable:includedBytes, data:dataBytes, mhtml:mhtmlBytes};
|
||||
}
|
||||
|
||||
function pngcrushDirectory(directory) {
|
||||
var directoryPath = FILE.path(directory);
|
||||
var pngs = directoryPath.glob("**/*.png");
|
||||
function pngcrushDirectory(directory)
|
||||
{
|
||||
var directoryPath = FILE.path(directory),
|
||||
pngs = directoryPath.glob("**/*.png");
|
||||
|
||||
system.stderr.print("Running pngcrush on " + pngs.length + " pngs:");
|
||||
pngs.forEach(function(dstPath) {
|
||||
pngs.forEach(function(dstPath)
|
||||
{
|
||||
var tmpPath = FILE.path(dstPath+".tmp");
|
||||
|
||||
var p = OS.popen(["pngcrush", "-rem", "alla", "-reduce", /*"-brute",*/ dstPath, tmpPath]);
|
||||
if (p.wait()) {
|
||||
CPLog.warn("pngcrush failed. Ensure it's installed and on your PATH.");
|
||||
try
|
||||
{
|
||||
var p = OS.popen(["pngcrush", "-rem", "alla", "-reduce", /*"-brute",*/ dstPath, tmpPath]);
|
||||
|
||||
if (p.wait())
|
||||
{
|
||||
CPLog.warn("pngcrush failed. Ensure it's installed and on your PATH.");
|
||||
}
|
||||
else
|
||||
{
|
||||
FILE.move(tmpPath, dstPath);
|
||||
system.stderr.write(".").flush();
|
||||
}
|
||||
}
|
||||
else {
|
||||
FILE.move(tmpPath, dstPath);
|
||||
system.stderr.write(".").flush();
|
||||
finally
|
||||
{
|
||||
p.stdin.close();
|
||||
p.stdout.close();
|
||||
p.stderr.close();
|
||||
}
|
||||
});
|
||||
|
||||
system.stderr.print("");
|
||||
}
|
||||
|
||||
|
||||
@@ -3,10 +3,20 @@ var OS = require("os");
|
||||
|
||||
exports.fontinfo = function(name, size)
|
||||
{
|
||||
var p = OS.popen(["fontinfo", "-n", name, size || 12]);
|
||||
var result;
|
||||
|
||||
if (p.wait() === 0)
|
||||
return JSON.parse(p.stdout.read());
|
||||
else
|
||||
return null;
|
||||
try
|
||||
{
|
||||
var p = OS.popen(["fontinfo", "-n", name, size || 12]);
|
||||
if (p.wait() === 0)
|
||||
result = p.stdout.read();
|
||||
}
|
||||
finally
|
||||
{
|
||||
p.stdin.close();
|
||||
p.stdout.close();
|
||||
p.stderr.close();
|
||||
}
|
||||
|
||||
return result ? JSON.parse(result) : null;
|
||||
};
|
||||
@@ -3,10 +3,20 @@ var OS = require("os");
|
||||
|
||||
exports.imagesize = function(path)
|
||||
{
|
||||
var p = OS.popen(["imagesize", "-n", path]);
|
||||
var result;
|
||||
|
||||
if (p.wait() === 0)
|
||||
return JSON.parse(p.stdout.read());
|
||||
else
|
||||
return null;
|
||||
try
|
||||
{
|
||||
var p = OS.popen(["imagesize", "-n", path]);
|
||||
if (p.wait() === 0)
|
||||
result = p.stdout.read();
|
||||
}
|
||||
finally
|
||||
{
|
||||
p.stdin.close();
|
||||
p.stdout.close();
|
||||
p.stderr.close();
|
||||
}
|
||||
|
||||
return result ? JSON.parse(result) : null;
|
||||
};
|
||||
@@ -880,6 +880,25 @@ Returns a hash for the object. Unlike Cocoa, the hash value does not take conten
|
||||
return join.call([self _javaScriptArrayCopy], aString);
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns an Array formed by applying a function to each object in the receiver.
|
||||
@param aFunction a function taking two arguments: (element, index).
|
||||
@return an Array containing the transformed elements.
|
||||
*/
|
||||
- (CPArray)arrayByApplyingBlock:(Function/*element, index*/)aFunction
|
||||
{
|
||||
var result = [],
|
||||
count = [self count];
|
||||
|
||||
for (var idx = 0; idx < count; idx++)
|
||||
{
|
||||
var obj = aFunction([self objectAtIndex:idx], idx);
|
||||
[result addObject:obj];
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Creating a description of the array
|
||||
|
||||
/*!
|
||||
|
||||
@@ -91,7 +91,7 @@ var concat = Array.prototype.concat,
|
||||
- (id)initWithObjects:(CPArray)objects count:(CPUInteger)aCount
|
||||
{
|
||||
if ([objects isKindOfClass:_CPJavaScriptArray])
|
||||
return slice.call(objects, 0);
|
||||
return slice.call(objects, 0, aCount);
|
||||
|
||||
var array = [],
|
||||
index = 0;
|
||||
@@ -233,6 +233,19 @@ var concat = Array.prototype.concat,
|
||||
return join.call(self, aString);
|
||||
}
|
||||
|
||||
- (CPArray)arrayByApplyingBlock:(Function/*element, index*/)aFunction
|
||||
{
|
||||
var result = [];
|
||||
|
||||
for (var idx = 0; idx < self.length; idx++)
|
||||
{
|
||||
var obj = aFunction(self[idx], idx);
|
||||
result.push(obj);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
- (void)insertObject:(id)anObject atIndex:(CPUInteger)anIndex
|
||||
{
|
||||
if (anIndex > self.length || anIndex < 0)
|
||||
@@ -335,7 +348,6 @@ var concat = Array.prototype.concat,
|
||||
[super addObjectsFromArray:anArray];
|
||||
}
|
||||
|
||||
|
||||
- (id)copy
|
||||
{
|
||||
return slice.call(self, 0);
|
||||
|
||||
@@ -763,13 +763,10 @@
|
||||
- (void)setAttributedString:(CPAttributedString)aString
|
||||
{
|
||||
_string = aString._string;
|
||||
_rangeEntries = [];
|
||||
|
||||
var i = 0,
|
||||
count = aString._rangeEntries.length;
|
||||
|
||||
for (; i < count; i++)
|
||||
_rangeEntries.push(copyRangeEntry(aString._rangeEntries[i]));
|
||||
_rangeEntries = [aString._rangeEntries arrayByApplyingBlock:function(entry)
|
||||
{
|
||||
return copyRangeEntry(entry);
|
||||
}];
|
||||
}
|
||||
|
||||
//Private methods
|
||||
|
||||
+63
-11
@@ -25,8 +25,18 @@
|
||||
@import "CPNotificationCenter.j"
|
||||
@import "CPObject.j"
|
||||
|
||||
@global CFBundleCopyBundleLocalizations
|
||||
@global CFBundleCopyLocalizedString
|
||||
|
||||
CPBundleDidLoadNotification = @"CPBundleDidLoadNotification";
|
||||
|
||||
@protocol CPBundleDelegate <CPObject>
|
||||
|
||||
@required
|
||||
- (void)bundleDidFinishLoading:(CPBundle)aBundle;
|
||||
|
||||
@end
|
||||
|
||||
/*!
|
||||
@class CPBundle
|
||||
@ingroup foundation
|
||||
@@ -37,8 +47,8 @@ var CPBundlesForURLStrings = { };
|
||||
|
||||
@implementation CPBundle : CPObject
|
||||
{
|
||||
CFBundle _bundle;
|
||||
id _delegate;
|
||||
CFBundle _bundle;
|
||||
id <CPBundleDelegate> _delegate;
|
||||
}
|
||||
|
||||
+ (CPBundle)bundleWithURL:(CPURL)aURL
|
||||
@@ -94,6 +104,7 @@ var CPBundlesForURLStrings = { };
|
||||
if (self)
|
||||
{
|
||||
_bundle = new CFBundle(aURL);
|
||||
|
||||
CPBundlesForURLStrings[URLString] = self;
|
||||
}
|
||||
|
||||
@@ -154,6 +165,21 @@ var CPBundlesForURLStrings = { };
|
||||
return _bundle.pathForResource(aFilename);
|
||||
}
|
||||
|
||||
- (CPString)pathForResource:(CPString)aFilename ofType:(CPString)extension
|
||||
{
|
||||
return _bundle.pathForResource(aFilename, extension);
|
||||
}
|
||||
|
||||
- (CPString)pathForResource:(CPString)aFilename ofType:(CPString)extension inDirectory:(CPString)subpath
|
||||
{
|
||||
return _bundle.pathForResource(aFilename, extension, subpath);
|
||||
}
|
||||
|
||||
- (CPString)pathForResource:(CPString)aFilename ofType:(CPString)extension inDirectory:(CPString)subpath forLocalization:(CPString)localizationName
|
||||
{
|
||||
return _bundle.pathForResource(aFilename, extension, subpath, localizationName);
|
||||
}
|
||||
|
||||
- (CPDictionary)infoDictionary
|
||||
{
|
||||
return _bundle.infoDictionary();
|
||||
@@ -164,7 +190,7 @@ var CPBundlesForURLStrings = { };
|
||||
return _bundle.valueForInfoDictionaryKey(aKey);
|
||||
}
|
||||
|
||||
- (void)loadWithDelegate:(id)aDelegate
|
||||
- (void)loadWithDelegate:(id <CPBundleDelegate>)aDelegate
|
||||
{
|
||||
_delegate = aDelegate;
|
||||
|
||||
@@ -186,15 +212,12 @@ var CPBundlesForURLStrings = { };
|
||||
|
||||
- (CPArray)staticResourceURLs
|
||||
{
|
||||
var staticResourceURLs = [],
|
||||
staticResources = _bundle.staticResources(),
|
||||
index = 0,
|
||||
count = [staticResources count];
|
||||
var staticResources = _bundle.staticResources();
|
||||
|
||||
for (; index < count; ++index)
|
||||
[staticResourceURLs addObject:staticResources[index].URL()];
|
||||
|
||||
return staticResourceURLs;
|
||||
return [staticResources arrayByApplyingBlock:function(resource)
|
||||
{
|
||||
return resource.URL();
|
||||
}];
|
||||
}
|
||||
|
||||
- (CPArray)environments
|
||||
@@ -212,4 +235,33 @@ var CPBundlesForURLStrings = { };
|
||||
return [super description] + "(" + [self bundlePath] + ")";
|
||||
}
|
||||
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Localization
|
||||
|
||||
- (CPArray)localizations
|
||||
{
|
||||
return CFBundleCopyBundleLocalizations(_bundle);
|
||||
}
|
||||
|
||||
- (CPString)localizedStringForKey:(CPString)aKey value:(CPString)aValue table:(CPString)aTable
|
||||
{
|
||||
return CFBundleCopyLocalizedString(_bundle, aKey, aValue, aTable);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
function CPLocalizedString(key, comment)
|
||||
{
|
||||
return CFCopyLocalizedString(key, comment);
|
||||
}
|
||||
|
||||
function CPLocalizedStringFromTable(key, table, comment)
|
||||
{
|
||||
return CFCopyLocalizedStringFromTable(key, table, comment);
|
||||
}
|
||||
|
||||
function CPCopyLocalizedStringFromTableInBundle(key, table, bundle, comment)
|
||||
{
|
||||
return CFCopyLocalizedStringFromTableInBundle(key, table, bundle._bundle, comment);
|
||||
}
|
||||
@@ -1552,7 +1552,7 @@ var defaultDateFormatterBehavior = CPDateFormatterBehavior10_4,
|
||||
month = [[self monthSymbols] indexOfObject:dateComponent] + 1;
|
||||
}
|
||||
|
||||
if (month > 11 || length >= 5)
|
||||
if (month > 12 || length >= 5)
|
||||
return nil;
|
||||
|
||||
dateArray[1] = month;
|
||||
@@ -1580,7 +1580,7 @@ var defaultDateFormatterBehavior = CPDateFormatterBehavior10_4,
|
||||
month = [[self standaloneMonthSymbols] indexOfObject:dateComponent] + 1;
|
||||
}
|
||||
|
||||
if (month > 11 || length >= 5)
|
||||
if (month > 12 || length >= 5)
|
||||
return nil;
|
||||
|
||||
dateArray[1] = month;
|
||||
|
||||
@@ -222,13 +222,13 @@ var CPDictionaryMaxDescriptionRecursion = 10;
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
if ([objects count] != [keyArray count])
|
||||
var i = [keyArray count];
|
||||
|
||||
if ([objects count] != i)
|
||||
[CPException raise:CPInvalidArgumentException reason:[CPString stringWithFormat:@"Counts are different.(%d != %d)", [objects count], [keyArray count]]];
|
||||
|
||||
if (self)
|
||||
{
|
||||
var i = [keyArray count];
|
||||
|
||||
while (i--)
|
||||
{
|
||||
var value = objects[i],
|
||||
@@ -273,18 +273,16 @@ var CPDictionaryMaxDescriptionRecursion = 10;
|
||||
if (self)
|
||||
{
|
||||
// The arguments array contains self and _cmd, so the first object is at position 2.
|
||||
var index = 2;
|
||||
|
||||
for (; index < argCount; index += 2)
|
||||
while (argCount-- > 2)
|
||||
{
|
||||
var value = arguments[index],
|
||||
key = arguments[index + 1];
|
||||
var key = arguments[argCount--],
|
||||
value = arguments[argCount]
|
||||
|
||||
if (value === nil)
|
||||
[CPException raise:CPInvalidArgumentException reason:@"Attempt to insert nil object from objects[" + ((index / 2) - 1) + @"]"];
|
||||
[CPException raise:CPInvalidArgumentException reason:@"Attempt to insert nil object from objects[" + ((argCount / 2) - 1) + @"]"];
|
||||
|
||||
if (key === nil)
|
||||
[CPException raise:CPInvalidArgumentException reason:@"Attempt to insert nil key from keys[" + ((index / 2) - 1) + @"]"];
|
||||
[CPException raise:CPInvalidArgumentException reason:@"Attempt to insert nil key from keys[" + ((argCount / 2) - 1) + @"]"];
|
||||
|
||||
[self setObject:value forKey:key];
|
||||
}
|
||||
|
||||
@@ -24,6 +24,8 @@
|
||||
@import "CPObject.j"
|
||||
@import "CPString.j"
|
||||
|
||||
@class CPString
|
||||
|
||||
CPInvalidArgumentException = @"CPInvalidArgumentException";
|
||||
CPUnsupportedMethodException = @"CPUnsupportedMethodException";
|
||||
CPRangeException = @"CPRangeException";
|
||||
|
||||
@@ -1018,14 +1018,13 @@ var CPIndexSetCountKey = @"CPIndexSetCountKey",
|
||||
if (self)
|
||||
{
|
||||
_count = [aCoder decodeIntForKey:CPIndexSetCountKey];
|
||||
_ranges = [];
|
||||
|
||||
var rangeStrings = [aCoder decodeObjectForKey:CPIndexSetRangeStringsKey],
|
||||
index = 0,
|
||||
count = rangeStrings.length;
|
||||
var rangeStrings = [aCoder decodeObjectForKey:CPIndexSetRangeStringsKey];
|
||||
|
||||
for (; index < count; ++index)
|
||||
_ranges.push(CPRangeFromString(rangeStrings[index]));
|
||||
_ranges = [rangeStrings arrayByApplyingBlock:function(range)
|
||||
{
|
||||
return CPRangeFromString(range);
|
||||
}];
|
||||
}
|
||||
|
||||
return self;
|
||||
|
||||
@@ -164,10 +164,11 @@
|
||||
+ (BOOL)automaticallyNotifiesObserversForKey:(CPString)aKey
|
||||
{
|
||||
var capitalizedKey = aKey.charAt(0).toUpperCase() + aKey.substring(1),
|
||||
selector = "automaticallyNotifiesObserversOf" + capitalizedKey;
|
||||
selector = "automaticallyNotifiesObserversOf" + capitalizedKey,
|
||||
aClass = [self class];
|
||||
|
||||
if ([[self class] respondsToSelector:selector])
|
||||
return objj_msgSend([self class], selector);
|
||||
if ([aClass respondsToSelector:selector])
|
||||
return aClass.isa.objj_msgSend0(aClass, selector);
|
||||
|
||||
return YES;
|
||||
}
|
||||
@@ -175,10 +176,11 @@
|
||||
+ (CPSet)keyPathsForValuesAffectingValueForKey:(CPString)aKey
|
||||
{
|
||||
var capitalizedKey = aKey.charAt(0).toUpperCase() + aKey.substring(1),
|
||||
selector = "keyPathsForValuesAffecting" + capitalizedKey;
|
||||
selector = "keyPathsForValuesAffecting" + capitalizedKey,
|
||||
aClass = [self class];
|
||||
|
||||
if ([[self class] respondsToSelector:selector])
|
||||
return objj_msgSend([self class], selector);
|
||||
if ([aClass respondsToSelector:selector])
|
||||
return aClass.isa.objj_msgSend0(aClass, selector);
|
||||
|
||||
return [CPSet set];
|
||||
}
|
||||
@@ -421,7 +423,7 @@ var kvoNewAndOld = CPKeyValueObservingOptionNew | CPKeyValueObservingOpti
|
||||
setKey_method_imp(self, _cmd, anObject);
|
||||
|
||||
[self didChangeValueForKey:aKey];
|
||||
}, "");
|
||||
}, setKey_method.method_types);
|
||||
}
|
||||
|
||||
// FIXME: Deprecated.
|
||||
@@ -439,7 +441,7 @@ var kvoNewAndOld = CPKeyValueObservingOptionNew | CPKeyValueObservingOpti
|
||||
_setKey_method_imp(self, _cmd, anObject);
|
||||
|
||||
[self didChangeValueForKey:aKey];
|
||||
}, "");
|
||||
}, _setKey_method.method_types);
|
||||
}
|
||||
|
||||
// Ordered To-Many Relationships
|
||||
@@ -476,7 +478,7 @@ var kvoNewAndOld = CPKeyValueObservingOptionNew | CPKeyValueObservingOpti
|
||||
[self didChange:CPKeyValueChangeInsertion
|
||||
valuesAtIndexes:[CPIndexSet indexSetWithIndex:anIndex]
|
||||
forKey:aKey];
|
||||
}, "");
|
||||
}, insertObject_inKeyAtIndex_method.method_types);
|
||||
}
|
||||
|
||||
if (insertKey_atIndexes_method)
|
||||
@@ -494,7 +496,7 @@ var kvoNewAndOld = CPKeyValueObservingOptionNew | CPKeyValueObservingOpti
|
||||
[self didChange:CPKeyValueChangeInsertion
|
||||
valuesAtIndexes:[indexes copy]
|
||||
forKey:aKey];
|
||||
}, "");
|
||||
}, insertKey_atIndexes_method.method_types);
|
||||
}
|
||||
|
||||
if (removeObjectFromKeyAtIndex_method)
|
||||
@@ -512,7 +514,7 @@ var kvoNewAndOld = CPKeyValueObservingOptionNew | CPKeyValueObservingOpti
|
||||
[self didChange:CPKeyValueChangeRemoval
|
||||
valuesAtIndexes:[CPIndexSet indexSetWithIndex:anIndex]
|
||||
forKey:aKey];
|
||||
}, "");
|
||||
}, removeObjectFromKeyAtIndex_method.method_types);
|
||||
}
|
||||
|
||||
if (removeKeyAtIndexes_method)
|
||||
@@ -530,7 +532,7 @@ var kvoNewAndOld = CPKeyValueObservingOptionNew | CPKeyValueObservingOpti
|
||||
[self didChange:CPKeyValueChangeRemoval
|
||||
valuesAtIndexes:[indexes copy]
|
||||
forKey:aKey];
|
||||
}, "");
|
||||
}, removeKeyAtIndexes_method.method_types);
|
||||
}
|
||||
|
||||
// These are optional.
|
||||
@@ -556,7 +558,7 @@ var kvoNewAndOld = CPKeyValueObservingOptionNew | CPKeyValueObservingOpti
|
||||
[self didChange:CPKeyValueChangeReplacement
|
||||
valuesAtIndexes:[CPIndexSet indexSetWithIndex:anIndex]
|
||||
forKey:aKey];
|
||||
}, "");
|
||||
}, replaceObjectInKeyAtIndex_withObject_method.method_types);
|
||||
}
|
||||
|
||||
var replaceKeyAtIndexes_withKey_selector =
|
||||
@@ -579,7 +581,7 @@ var kvoNewAndOld = CPKeyValueObservingOptionNew | CPKeyValueObservingOpti
|
||||
[self didChange:CPKeyValueChangeReplacement
|
||||
valuesAtIndexes:[indexes copy]
|
||||
forKey:aKey];
|
||||
}, "");
|
||||
}, replaceKeyAtIndexes_withKey_method.method_types);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -613,7 +615,7 @@ var kvoNewAndOld = CPKeyValueObservingOptionNew | CPKeyValueObservingOpti
|
||||
[self didChangeValueForKey:aKey
|
||||
withSetMutation:CPKeyValueUnionSetMutation
|
||||
usingObjects:[CPSet setWithObject:anObject]];
|
||||
}, "");
|
||||
}, addKeyObject_method.method_types);
|
||||
}
|
||||
|
||||
if (addKey_method)
|
||||
@@ -631,7 +633,7 @@ var kvoNewAndOld = CPKeyValueObservingOptionNew | CPKeyValueObservingOpti
|
||||
[self didChangeValueForKey:aKey
|
||||
withSetMutation:CPKeyValueUnionSetMutation
|
||||
usingObjects:[objects copy]];
|
||||
}, "");
|
||||
}, addKey_method.method_types);
|
||||
}
|
||||
|
||||
if (removeKeyObject_method)
|
||||
@@ -649,7 +651,7 @@ var kvoNewAndOld = CPKeyValueObservingOptionNew | CPKeyValueObservingOpti
|
||||
[self didChangeValueForKey:aKey
|
||||
withSetMutation:CPKeyValueMinusSetMutation
|
||||
usingObjects:[CPSet setWithObject:anObject]];
|
||||
}, "");
|
||||
}, removeKeyObject_method.method_types);
|
||||
}
|
||||
|
||||
if (removeKey_method)
|
||||
@@ -667,7 +669,7 @@ var kvoNewAndOld = CPKeyValueObservingOptionNew | CPKeyValueObservingOpti
|
||||
[self didChangeValueForKey:aKey
|
||||
withSetMutation:CPKeyValueMinusSetMutation
|
||||
usingObjects:[objects copy]];
|
||||
}, "");
|
||||
}, removeKey_method.method_types);
|
||||
}
|
||||
|
||||
// intersect<Key>: is optional.
|
||||
@@ -689,7 +691,7 @@ var kvoNewAndOld = CPKeyValueObservingOptionNew | CPKeyValueObservingOpti
|
||||
[self didChangeValueForKey:aKey
|
||||
withSetMutation:CPKeyValueIntersectSetMutation
|
||||
usingObjects:[aSet copy]];
|
||||
}, "");
|
||||
}, intersectKey_method.method_types);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -380,12 +380,10 @@ var _CPKeyedArchiverStringClass = Nil,
|
||||
/* @ignore */
|
||||
- (void)_encodeArrayOfObjects:(CPArray)objects forKey:(CPString)aKey
|
||||
{
|
||||
var i = 0,
|
||||
count = objects.length,
|
||||
references = [];
|
||||
|
||||
for (; i < count; ++i)
|
||||
[references addObject:_CPKeyedArchiverEncodeObject(self, objects[i], NO)];
|
||||
var references = [objects arrayByApplyingBlock:function(object)
|
||||
{
|
||||
return _CPKeyedArchiverEncodeObject(self, object, NO);
|
||||
}];
|
||||
|
||||
[_plistObject setObject:references forKey:aKey];
|
||||
}
|
||||
|
||||
@@ -332,7 +332,7 @@ var CPArrayClass = Ni
|
||||
*/
|
||||
- (id)decodeObjectForKey:(CPString)aKey
|
||||
{
|
||||
var object = _plistObject.valueForKey(aKey),
|
||||
var object = _plistObject && _plistObject.valueForKey(aKey),
|
||||
objectClass = (object != nil) && object.isa;
|
||||
|
||||
if (objectClass === CPDictionaryClass || objectClass === CPMutableDictionaryClass)
|
||||
|
||||
@@ -25,6 +25,8 @@
|
||||
@import "CPException.j"
|
||||
@import "CPNotification.j"
|
||||
@import "CPNull.j"
|
||||
@import "CPOperationQueue.j"
|
||||
@import "CPOperation.j"
|
||||
@import "CPSet.j"
|
||||
|
||||
@class _CPNotificationRegistry
|
||||
@@ -94,13 +96,13 @@ var CPNotificationDefaultCenter = nil;
|
||||
Adds an entry to the receiver’s dispatch table with a block, and optional criteria: notification name and sender.
|
||||
@param aNotificationName the name of the notification the observer wants to watch
|
||||
@param anObject the object in the notification the observer wants to watch
|
||||
@param queue is ignored for the moment
|
||||
@param The operation queue to which block should be added. If you pass nil, the block is run synchronously on the posting thread.
|
||||
@param block the block to be executed when the notification is received.
|
||||
*/
|
||||
- (id <CPObject>)addObserverForName:(CPString)aNotificationName object:(id)anObject queue:(id)queue usingBlock:(Function)block
|
||||
- (id <CPObject>)addObserverForName:(CPString)aNotificationName object:(id)anObject queue:(CPOperationQueue)queue usingBlock:(Function)block
|
||||
{
|
||||
var registry = [self _registryForNotificationName:aNotificationName],
|
||||
observer = [[_CPNotificationObserver alloc] initWithBlock:block];
|
||||
observer = [[_CPNotificationObserver alloc] initWithBlock:block queue:queue];
|
||||
|
||||
[registry addObserver:observer object:anObject];
|
||||
|
||||
@@ -344,9 +346,10 @@ var _CPNotificationCenterPostNotification = function(/* CPNotificationCenter */
|
||||
/* @ignore */
|
||||
@implementation _CPNotificationObserver : CPObject
|
||||
{
|
||||
id _observer;
|
||||
Function _block;
|
||||
SEL _selector;
|
||||
CPOperationQueue _operationQueue;
|
||||
id _observer;
|
||||
Function _block;
|
||||
SEL _selector;
|
||||
}
|
||||
|
||||
- (id)initWithObserver:(id)anObserver selector:(SEL)aSelector
|
||||
@@ -360,11 +363,12 @@ var _CPNotificationCenterPostNotification = function(/* CPNotificationCenter */
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id)initWithBlock:(Function)aBlock
|
||||
- (id)initWithBlock:(Function)aBlock queue:(CPOperationQueue)aQueue
|
||||
{
|
||||
if (self)
|
||||
{
|
||||
_block = aBlock;
|
||||
_operationQueue = aQueue;
|
||||
}
|
||||
|
||||
return self;
|
||||
@@ -384,7 +388,11 @@ var _CPNotificationCenterPostNotification = function(/* CPNotificationCenter */
|
||||
{
|
||||
if (_block)
|
||||
{
|
||||
_block(aNotification);
|
||||
if (!_operationQueue)
|
||||
_block(aNotification);
|
||||
else
|
||||
[_operationQueue addOperation:[[_CPNotificationObserverOperation alloc] initWithBlock:_block notification:aNotification]];
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -392,3 +400,38 @@ var _CPNotificationCenterPostNotification = function(/* CPNotificationCenter */
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
/* @ignore */
|
||||
@implementation _CPNotificationObserverOperation : CPOperation
|
||||
{
|
||||
CPNotification _notification;
|
||||
Function _block;
|
||||
}
|
||||
|
||||
/* @ignore */
|
||||
- (id)initWithBlock:(Function)aBlock notification:(CPNotification)aNotification
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
if (self)
|
||||
{
|
||||
_block = aBlock;
|
||||
_notification = aNotification;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
/* @ignore */
|
||||
- (void)main
|
||||
{
|
||||
_block(_notification);
|
||||
}
|
||||
|
||||
/* @ignore */
|
||||
- (BOOL)isReady
|
||||
{
|
||||
return YES;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,328 @@
|
||||
/*
|
||||
* CPNotificationQueue.j
|
||||
* Foundation
|
||||
*
|
||||
* Created by Alexandre Wilhelm.
|
||||
* Copyright 2015 <alexandre.wilhelmfr@gmail.com>
|
||||
*
|
||||
* 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 "CPObject.j"
|
||||
@import "CPNotification.j"
|
||||
@import "CPNotificationCenter.j"
|
||||
|
||||
@typedef CPPostingStyle
|
||||
|
||||
/*
|
||||
@global
|
||||
@group CPViewAutoresizingMasks
|
||||
The default resizingMask, the view will not resize or reposition itself.
|
||||
*/
|
||||
CPPostWhenIdle = 1;
|
||||
/*
|
||||
@global
|
||||
@group CPPostingStyle
|
||||
The notification is posted at the end of the current notification callout or timer.
|
||||
*/
|
||||
CPPostASAP = 2;
|
||||
/*
|
||||
@global
|
||||
@group CPPostingStyle
|
||||
The notification is posted immediately after coalescing.
|
||||
*/
|
||||
CPPostNow = 3;
|
||||
|
||||
|
||||
@typedef CPNotificationCoalescing
|
||||
|
||||
/*
|
||||
@global
|
||||
@group CPNotificationCoalescing
|
||||
Do not coalesce notifications in the queue.
|
||||
*/
|
||||
CPNotificationNoCoalescing = 1 << 0;
|
||||
/*
|
||||
@global
|
||||
@group CPNotificationCoalescing
|
||||
Coalesce notifications with the same name.
|
||||
*/
|
||||
CPNotificationCoalescingOnName = 1 << 1;
|
||||
/*
|
||||
@global
|
||||
@group CPNotificationCoalescing
|
||||
Coalesce notifications with the same object.
|
||||
*/
|
||||
CPNotificationCoalescingOnSender = 1 << 2;
|
||||
|
||||
|
||||
var CPNotificationDefaultQueue;
|
||||
|
||||
var runLoop = [CPRunLoop mainRunLoop];
|
||||
|
||||
/*!
|
||||
@class CPPostingStyle
|
||||
@ingroup foundation
|
||||
@brief CPNotificationQueue objects act as buffers for notification centers (instances of CPNotificationCenter).
|
||||
|
||||
Cappuccino provides a framework for sending messages between objects within
|
||||
a process called notifications. CPNotificationQueue objects (or simply notification queues)
|
||||
act as buffers for notification centers (instances of CPNotificationCenter).
|
||||
Whereas a notification center distributes notifications when posted,
|
||||
notifications placed into the queue can be delayed until the end of the current pass through the run loop
|
||||
or until the run loop is idle. Duplicate notifications can also be coalesced so that only one notification
|
||||
is sent although multiple notifications are posted. A notification queue maintains notifications
|
||||
(instances of C¨Notification) generally in a first in first out (FIFO) order.
|
||||
When a notification rises to the front of the queue, the queue posts it to the notification center,
|
||||
which in turn dispatches the notification to all objects registered as observers.
|
||||
*/
|
||||
@implementation CPNotificationQueue : CPObject
|
||||
{
|
||||
BOOL _runLoopLaunched;
|
||||
CPMutableArray _postNowNotifications;
|
||||
CPMutableArray _postIdleNotifications;
|
||||
CPMutableArray _postASAPNotifications;
|
||||
CPNotificationCenter _notificationCenter;
|
||||
}
|
||||
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Class methods
|
||||
|
||||
/*!
|
||||
Returns the application's notification queue. This notification queue uses the default notification center
|
||||
*/
|
||||
+ (id)defaultQueue
|
||||
{
|
||||
if (!CPNotificationDefaultQueue)
|
||||
CPNotificationDefaultQueue = [[CPNotificationQueue alloc] initWithNotificationCenter:[CPNotificationCenter defaultCenter]];
|
||||
|
||||
return CPNotificationDefaultQueue;
|
||||
}
|
||||
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Init methods
|
||||
|
||||
/*!
|
||||
Initializes and returns a notification queue for the specified notification center.
|
||||
@param anObserver the specified notification center
|
||||
@return a new CPNotificationQueue
|
||||
*/
|
||||
- (id)initWithNotificationCenter:(CPNotificationCenter)aNotificationCenter
|
||||
{
|
||||
if (self = [super init])
|
||||
{
|
||||
_notificationCenter = aNotificationCenter;
|
||||
_postNowNotifications = [CPMutableArray new];
|
||||
_postIdleNotifications = [CPMutableArray new];
|
||||
_postASAPNotifications = [CPMutableArray new];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Enqueue methods
|
||||
|
||||
/*!
|
||||
Adds a notification to the notification queue with a specified posting style.
|
||||
@param notification the notification to add to the queue
|
||||
@param postingStyle the posting style for the notification
|
||||
*/
|
||||
- (void)enqueueNotification:(CPNotification)notification postingStyle:(CPPostingStyle)postingStyle
|
||||
{
|
||||
[self enqueueNotification:notification postingStyle:postingStyle coalesceMask:CPNotificationCoalescingOnName|CPNotificationCoalescingOnSender forModes:[CPDefaultRunLoopMode]];
|
||||
}
|
||||
|
||||
/*!
|
||||
Adds a notification to the notification queue with a specified posting style, criteria for coalescing, and runloop mode.
|
||||
@param notification the notification to add to the queue
|
||||
@param postingStyle the posting style for the notification
|
||||
@param coalesceMask a mask indicating what criteria to use when matching attributes of notification to attributes notifications in the queue.
|
||||
@modes modes the list of modes the notification may be posted in.
|
||||
*/
|
||||
- (void)enqueueNotification:(CPNotification)notification postingStyle:(CPPostingStyle)postingStyle coalesceMask:(CPNotificationCoalescing)coalesceMask forModes:(CPArray)modes
|
||||
{
|
||||
[self _removeNotification:notification coalesceMask:coalesceMask];
|
||||
|
||||
switch (postingStyle)
|
||||
{
|
||||
case CPPostWhenIdle:
|
||||
[_postIdleNotifications addObject:notification];
|
||||
break;
|
||||
|
||||
case CPPostASAP:
|
||||
[_postASAPNotifications addObject:notification];
|
||||
break;
|
||||
|
||||
case CPPostNow:
|
||||
[_postNowNotifications addObject:notification];
|
||||
break;
|
||||
}
|
||||
|
||||
if ([_postIdleNotifications count] || [_postASAPNotifications count] || [_postNowNotifications count])
|
||||
[self _runRunLoop];
|
||||
|
||||
if (postingStyle == CPPostNow)
|
||||
{
|
||||
for (var i = [modes count] - 1; i >= 0; i--)
|
||||
[[CPRunLoop currentRunLoop] limitDateForMode:modes[i]];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Dequeue methods
|
||||
|
||||
/*!
|
||||
Removes all notifications from the queue that match a provided notification using provided matching criteria.
|
||||
@param notification the notification to add to the queue
|
||||
@param coalesceMask mask indicating what criteria to use when matching attributes of notification to remove notifications in the queue.
|
||||
*/
|
||||
- (void)dequeueNotificationsMatching:(CPNotification)notification coalesceMask:(CPUInteger)coalesceMask
|
||||
{
|
||||
[self _removeNotification:notification coalesceMask:coalesceMask];
|
||||
}
|
||||
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark RunLoop methods
|
||||
|
||||
/*!
|
||||
@ignore
|
||||
*/
|
||||
- (void)_runRunLoop
|
||||
{
|
||||
if (!_runLoopLaunched)
|
||||
{
|
||||
[runLoop performSelector:@selector(_launchNotificationsInQueue) target:self argument:nil order:0 modes:[CPDefaultRunLoopMode]];
|
||||
_runLoopLaunched = YES;
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
@ignore
|
||||
*/
|
||||
- (void)_launchNotificationsInQueue
|
||||
{
|
||||
_runLoopLaunched = NO;
|
||||
|
||||
if ([_postNowNotifications count])
|
||||
{
|
||||
[self _launchNotificationsForArray:_postNowNotifications];
|
||||
[self _runRunLoop];
|
||||
return;
|
||||
}
|
||||
|
||||
if ([_postASAPNotifications count])
|
||||
{
|
||||
[self _launchNotificationsForArray:_postASAPNotifications];
|
||||
[self _runRunLoop];
|
||||
return;
|
||||
}
|
||||
|
||||
if ([_postIdleNotifications count])
|
||||
{
|
||||
[self _launchNotificationsForArray:_postIdleNotifications];
|
||||
[self _runRunLoop];
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Posting methods
|
||||
|
||||
/*!
|
||||
@ignore
|
||||
*/
|
||||
- (void)_launchNotificationsForArray:(CPArray)anArray
|
||||
{
|
||||
for (var i = [anArray count] - 1; i >= 0; i--)
|
||||
{
|
||||
var notification = anArray[i];
|
||||
[_notificationCenter postNotification:notification];
|
||||
}
|
||||
|
||||
[anArray removeAllObjects];
|
||||
}
|
||||
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Remove methods
|
||||
|
||||
/*!
|
||||
@ignore
|
||||
*/
|
||||
- (void)_removeNotification:(CPNotification)notification coalesceMask:(CPUInteger)coalesceMask
|
||||
{
|
||||
[self _removeNotification:notification coalesceMask:coalesceMask inNotifications:_postNowNotifications];
|
||||
[self _removeNotification:notification coalesceMask:coalesceMask inNotifications:_postASAPNotifications];
|
||||
[self _removeNotification:notification coalesceMask:coalesceMask inNotifications:_postIdleNotifications];
|
||||
}
|
||||
|
||||
/*!
|
||||
@ignore
|
||||
*/
|
||||
- (void)_removeNotification:(CPNotification)aNotification coalesceMask:(CPUInteger)coalesceMask inNotifications:(CPArray)notifications
|
||||
{
|
||||
var notificationsToRemove = [],
|
||||
name = [aNotification name],
|
||||
sender = [aNotification object];
|
||||
|
||||
for (var i = [notifications count] - 1; i >= 0; i--)
|
||||
{
|
||||
var notification = notifications[i];
|
||||
|
||||
if (notification == aNotification)
|
||||
{
|
||||
[notificationsToRemove addObject:notification];
|
||||
continue;
|
||||
}
|
||||
|
||||
if (coalesceMask & CPNotificationNoCoalescing)
|
||||
continue;
|
||||
|
||||
if (coalesceMask & CPNotificationCoalescingOnName && coalesceMask & CPNotificationCoalescingOnSender)
|
||||
{
|
||||
if ([notification object] == sender && [notification name] == name)
|
||||
[notificationsToRemove addObject:notification]
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (coalesceMask & CPNotificationCoalescingOnName)
|
||||
{
|
||||
if ([notification name] == name)
|
||||
[notificationsToRemove addObject:notification]
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (coalesceMask & CPNotificationCoalescingOnSender)
|
||||
{
|
||||
if ([notification object] == sender)
|
||||
[notificationsToRemove addObject:notification]
|
||||
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
[notifications removeObjectsInArray:notificationsToRemove];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -28,3 +28,5 @@
|
||||
@import "_CPAggregateExpression.j"
|
||||
@import "_CPSetExpression.j"
|
||||
@import "_CPSubqueryExpression.j"
|
||||
@import "_CPBlockExpression.j"
|
||||
@import "_CPConditionalExpression.j"
|
||||
@@ -25,7 +25,7 @@
|
||||
|
||||
@implementation _CPAggregateExpression : CPExpression
|
||||
{
|
||||
CPArray _aggregate;
|
||||
CPArray _aggregate @accessors(getter=collection);
|
||||
}
|
||||
|
||||
- (id)initWithAggregate:(CPArray)collection
|
||||
@@ -34,6 +34,7 @@
|
||||
|
||||
if (self)
|
||||
_aggregate = collection;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@@ -48,48 +49,30 @@
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (id)collection
|
||||
{
|
||||
return _aggregate;
|
||||
}
|
||||
|
||||
- (id)expressionValueWithObject:(id)object context:(CPDictionary)context
|
||||
{
|
||||
var eval_array = [CPArray array],
|
||||
collection = [_aggregate objectEnumerator],
|
||||
exp;
|
||||
|
||||
while ((exp = [collection nextObject]) !== nil)
|
||||
return [_aggregate arrayByApplyingBlock:function(exp)
|
||||
{
|
||||
var eval = [exp expressionValueWithObject:object context:context];
|
||||
[eval_array addObject:eval];
|
||||
}
|
||||
|
||||
return eval_array;
|
||||
return [exp expressionValueWithObject:object context:context];
|
||||
}];
|
||||
}
|
||||
|
||||
- (CPString)description
|
||||
{
|
||||
var i = 0,
|
||||
count = [_aggregate count],
|
||||
result = "{";
|
||||
{
|
||||
var descriptions = [_aggregate arrayByApplyingBlock:function(exp)
|
||||
{
|
||||
return [exp description];
|
||||
}];
|
||||
|
||||
for (; i < count; i++)
|
||||
result = result + [CPString stringWithFormat:@"%s%s", [[_aggregate objectAtIndex:i] description], (i + 1 < count) ? @", " : @""];
|
||||
|
||||
result = result + "}";
|
||||
|
||||
return result;
|
||||
return "{" + [descriptions componentsJoinedByString:","] + "}" ;
|
||||
}
|
||||
|
||||
- (CPExpression)_expressionWithSubstitutionVariables:(CPDictionary)variables
|
||||
{
|
||||
var subst_array = [CPArray array],
|
||||
count = [_aggregate count],
|
||||
i = 0;
|
||||
|
||||
for (; i < count; i++)
|
||||
[subst_array addObject:[[_aggregate objectAtIndex:i] _expressionWithSubstitutionVariables:variables]];
|
||||
var subst_array = [_aggregate arrayByApplyingBlock:function(exp)
|
||||
{
|
||||
return [exp _expressionWithSubstitutionVariables:variables];
|
||||
}];
|
||||
|
||||
return [CPExpression expressionForAggregate:subst_array];
|
||||
}
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* _CPBlockExpression.j
|
||||
*
|
||||
* Created by cacaodev.
|
||||
* Copyright 2015.
|
||||
*
|
||||
* 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 "_CPExpression.j"
|
||||
|
||||
@implementation _CPBlockExpression : CPExpression
|
||||
{
|
||||
Function _block @accessors(getter=expressionBlock);
|
||||
CPArray _arguments @accessors(getter=arguments);
|
||||
}
|
||||
|
||||
- (id)initWithBlock:(Function)aBlock arguments:(CPArray)arguments
|
||||
{
|
||||
self = [super initWithExpressionType:CPBlockExpressionType];
|
||||
|
||||
if (self)
|
||||
{
|
||||
_block = aBlock;
|
||||
_arguments = arguments;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (BOOL)isEqual:(id)object
|
||||
{
|
||||
if (self === object)
|
||||
return YES;
|
||||
|
||||
if (object === nil || object.isa !== self.isa || [object expressionBlock] !== _block || ![[object arguments] isEqual:_arguments])
|
||||
return NO;
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (id)expressionValueWithObject:(id)object context:(CPDictionary)context
|
||||
{
|
||||
var args = [_arguments arrayByApplyingBlock:function(exp)
|
||||
{
|
||||
return [exp expressionValueWithObject:object context:context];
|
||||
}];
|
||||
|
||||
return _block(object, args, context);
|
||||
}
|
||||
|
||||
- (CPExpression)_expressionWithSubstitutionVariables:(CPDictionary)bindings
|
||||
{
|
||||
var args = [_arguments arrayByApplyingBlock:function(exp)
|
||||
{
|
||||
return [exp _expressionWithSubstitutionVariables:bindings];
|
||||
}];
|
||||
|
||||
return [[_CPBlockExpression alloc] initWithBlock:_block arguments:args];
|
||||
}
|
||||
|
||||
- (CPString)description
|
||||
{
|
||||
return [CPString stringWithFormat:@"Block(function, %@)", [_arguments description]];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* _CPConditionalExpression.j
|
||||
*
|
||||
* Created by cacaodev.
|
||||
* Copyright 2015.
|
||||
*
|
||||
* 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 "_CPPredicate.j"
|
||||
@import "_CPExpression.j"
|
||||
|
||||
@implementation _CPConditionalExpression : CPExpression
|
||||
{
|
||||
CPPredicate _predicate @accessors(getter=predicate);
|
||||
CPExpression _trueExpression @accessors(getter=trueExpression);
|
||||
CPExpression _falseExpression @accessors(getter=falseExpression);
|
||||
}
|
||||
|
||||
- (id)initWithPredicate:(CPPredicate)aPredicate trueExpression:(CPExpression)trueExpression falseExpression:(CPExpression)falseExpression
|
||||
{
|
||||
self = [super initWithExpressionType:CPConditionalExpressionType];
|
||||
|
||||
if (self)
|
||||
{
|
||||
_predicate = aPredicate;
|
||||
_trueExpression = trueExpression;
|
||||
_falseExpression = falseExpression;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (BOOL)isEqual:(id)object
|
||||
{
|
||||
if (self === object)
|
||||
return YES;
|
||||
|
||||
if (object === nil || object.isa !== self.isa || ![[object predicate] isEqual:_predicate] || ![[object trueExpression] isEqual:_trueExpression] || ![[object falseExpression] isEqual:_falseExpression])
|
||||
return NO;
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (id)expressionValueWithObject:(id)object context:(CPDictionary)context
|
||||
{
|
||||
var eval = [_predicate evaluateWithObject:object substitutionVariables:context],
|
||||
exp = eval ? _trueExpression : _falseExpression;
|
||||
|
||||
return [exp expressionValueWithObject:object context:context];
|
||||
}
|
||||
|
||||
- (CPExpression)_expressionWithSubstitutionVariables:(CPDictionary)bindings
|
||||
{
|
||||
var predicate = [_predicate predicateWithSubstitutionVariables:bindings],
|
||||
trueExp = [_trueExpression _expressionWithSubstitutionVariables:bindings],
|
||||
falseExp = [_falseExpression _expressionWithSubstitutionVariables:bindings];
|
||||
|
||||
return [[_CPConditionalExpression alloc] initWithPredicate:predicate trueExpression:trueExp falseExpression:falseExp];
|
||||
}
|
||||
|
||||
- (CPString)description
|
||||
{
|
||||
return [CPString stringWithFormat:@"TERNARY(%@,%@,%@)", [_predicate predicateFormat], [_trueExpression description], [_falseExpression description]];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -27,7 +27,7 @@
|
||||
|
||||
@implementation _CPConstantValueExpression : CPExpression
|
||||
{
|
||||
id _value;
|
||||
id _value @accessors(getter=constantValue);
|
||||
}
|
||||
|
||||
- (id)initWithValue:(id)value
|
||||
@@ -51,11 +51,6 @@
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (id)constantValue
|
||||
{
|
||||
return _value;
|
||||
}
|
||||
|
||||
- (id)expressionValueWithObject:(id)object context:(CPDictionary)context
|
||||
{
|
||||
return _value;
|
||||
@@ -66,6 +61,9 @@
|
||||
if ([_value isKindOfClass:[CPString class]])
|
||||
return @"\"" + _value + @"\"";
|
||||
|
||||
if (_value === [CPNull null])
|
||||
return @"nil";
|
||||
|
||||
return [_value description];
|
||||
}
|
||||
|
||||
|
||||
@@ -65,6 +65,14 @@ CPIntersectSetExpressionType = 8;
|
||||
An expression that combines two nested expression results by set subtraction.
|
||||
*/
|
||||
CPMinusSetExpressionType = 9;
|
||||
/*!
|
||||
An expression that returns the result of evaluating a block.
|
||||
*/
|
||||
CPBlockExpressionType = 10;
|
||||
/*!
|
||||
An expression that returns an expression that depends on the evaluation of a predicate.
|
||||
*/
|
||||
CPConditionalExpressionType = 11;
|
||||
|
||||
/*!
|
||||
@ingroup foundation
|
||||
@@ -259,6 +267,32 @@ CPMinusSetExpressionType = 9;
|
||||
return [[_CPSubqueryExpression alloc] initWithExpression:expression usingIteratorVariable:variable predicate:predicate];
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns Creates an NSExpression object that will use the Block for evaluating objects.
|
||||
@param aBlock The Block is applied to the object to be evaluated.
|
||||
|
||||
The Block takes three arguments and returns a value:
|
||||
|
||||
evaluatedObject
|
||||
The object to be evaluated.
|
||||
expressions
|
||||
An array of predicate expressions that evaluates to a collection.
|
||||
context
|
||||
A dictionary that the expression can use to store temporary state for one predicate evaluation.
|
||||
|
||||
@discussion Note that context is mutable, and that it can only be accessed during the evaluation of the expression.
|
||||
@param arguments An array containing NSExpression objects that will be used as parameters during the invocation of the block.
|
||||
*/
|
||||
+ (CPExpression)expressionForBlock:(Function)aBlock arguments:(CPArray)args
|
||||
{
|
||||
return [[_CPBlockExpression alloc] initWithBlock:aBlock arguments:args];
|
||||
}
|
||||
|
||||
+ (CPExpression)expressionForConditional:(CPPredicate)aPredicate trueExpression:(CPExpression)trueExpression falseExpression:(CPExpression)falseExpression
|
||||
{
|
||||
return [[_CPConditionalExpression alloc] initWithPredicate:aPredicate trueExpression:trueExpression falseExpression:falseExpression];
|
||||
}
|
||||
|
||||
// Getting Information About an Expression
|
||||
/*!
|
||||
Returns the expression type for the receiver.
|
||||
@@ -316,7 +350,7 @@ CPMinusSetExpressionType = 9;
|
||||
|
||||
/*!
|
||||
Returns the arguments for the receiver.
|
||||
@return The arguments for the receiver—that is, the array of expressions that will be passed as parameters during invocation of the selector on the operand of a function expression.
|
||||
@return The arguments for the receiver—that is, the array of expressions that will be passed as parameters during invocation of the selector on the operand of a function expression or as a parameter of the block of a block expression.
|
||||
This method raises an exception if it is not applicable to the receiver.
|
||||
*/
|
||||
- (CPArray)arguments
|
||||
@@ -337,8 +371,8 @@ CPMinusSetExpressionType = 9;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the predicate in a subquery expression.
|
||||
@return The predicate in a subquery expression..
|
||||
Returns the predicate in a subquery expression or a conditional expression.
|
||||
@return The predicate in a subquery expression or a conditional expression.
|
||||
This method raises an exception if it is not applicable to the receiver.
|
||||
*/
|
||||
- (CPPredicate)predicate
|
||||
@@ -380,6 +414,39 @@ CPMinusSetExpressionType = 9;
|
||||
return nil;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the block of a block expression.
|
||||
@return The block of a block expression.
|
||||
This method raises an exception if it is not applicable to the receiver.
|
||||
*/
|
||||
- (Function)expressionBlock
|
||||
{
|
||||
_CPRaiseInvalidAbstractInvocation(self, _cmd);
|
||||
return nil;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the true expression of a conditional expression.
|
||||
@return The true expression of a conditional expression.
|
||||
This method raises an exception if it is not applicable to the receiver.
|
||||
*/
|
||||
- (CPExpression)trueExpression
|
||||
{
|
||||
_CPRaiseInvalidAbstractInvocation(self, _cmd);
|
||||
return nil;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the false expression of a conditional expression.
|
||||
@return The false expression of a conditional expression.
|
||||
This method raises an exception if it is not applicable to the receiver.
|
||||
*/
|
||||
- (CPExpression)falseExpression
|
||||
{
|
||||
_CPRaiseInvalidAbstractInvocation(self, _cmd);
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (CPExpression)_expressionWithSubstitutionVariables:(CPDictionary)variables
|
||||
{
|
||||
return self;
|
||||
|
||||
@@ -28,9 +28,9 @@
|
||||
|
||||
@implementation _CPFunctionExpression : CPExpression
|
||||
{
|
||||
CPExpression _operand;
|
||||
CPExpression _operand @accessors(getter=operand);
|
||||
SEL _selector;
|
||||
CPArray _arguments;
|
||||
CPArray _arguments @accessors(getter=arguments);
|
||||
int _argc;
|
||||
int _maxargs;
|
||||
}
|
||||
@@ -88,16 +88,6 @@
|
||||
return [self _function];
|
||||
}
|
||||
|
||||
- (CPArray)arguments
|
||||
{
|
||||
return _arguments;
|
||||
}
|
||||
|
||||
- (CPExpression)operand
|
||||
{
|
||||
return _operand;
|
||||
}
|
||||
|
||||
- (id)expressionValueWithObject:(id)object context:(CPDictionary)context
|
||||
{
|
||||
var target = [_operand expressionValueWithObject:object context:context],
|
||||
@@ -143,11 +133,10 @@
|
||||
- (CPExpression)_expressionWithSubstitutionVariables:(CPDictionary)variables
|
||||
{
|
||||
var operand = [[self operand] _expressionWithSubstitutionVariables:variables],
|
||||
args = [CPArray array],
|
||||
i = 0;
|
||||
|
||||
for (; i < _argc; i++)
|
||||
[args addObject:[_arguments[i] _expressionWithSubstitutionVariables:variables]];
|
||||
args = [_arguments arrayByApplyingBlock:function(arg)
|
||||
{
|
||||
return [arg _expressionWithSubstitutionVariables:variables];
|
||||
}];
|
||||
|
||||
return [CPExpression expressionForFunction:operand selectorName:[self _function] arguments:args];
|
||||
}
|
||||
|
||||
@@ -605,11 +605,11 @@
|
||||
}
|
||||
if ([self scanPredicateKeyword:@"TRUE"] || [self scanPredicateKeyword:@"YES"])
|
||||
{
|
||||
return [CPExpression expressionForConstantValue:[CPNumber numberWithBool:YES]];
|
||||
return [CPExpression expressionForConstantValue:YES];
|
||||
}
|
||||
if ([self scanPredicateKeyword:@"FALSE"] || [self scanPredicateKeyword:@"NO"])
|
||||
{
|
||||
return [CPExpression expressionForConstantValue:[CPNumber numberWithBool:NO]];
|
||||
return [CPExpression expressionForConstantValue:NO];
|
||||
}
|
||||
if ([self scanPredicateKeyword:@"SELF"])
|
||||
{
|
||||
@@ -744,16 +744,43 @@
|
||||
|
||||
if (![self scanString:@"," intoString:NULL])
|
||||
CPRaiseParseError(self, @"expression");
|
||||
|
||||
variableExpression = [self parseExpression];
|
||||
|
||||
if (![self scanString:@"," intoString:NULL])
|
||||
CPRaiseParseError(self, @"expression");
|
||||
|
||||
subpredicate = [self parsePredicate];
|
||||
|
||||
if (![self scanString:@")" intoString:NULL])
|
||||
CPRaiseParseError(self, @"predicate");
|
||||
|
||||
return [[_CPSubqueryExpression alloc] initWithExpression:collection usingIteratorExpression:variableExpression predicate:subpredicate];
|
||||
}
|
||||
|
||||
if ([self scanString:@"TERNARY" intoString:NULL])
|
||||
{
|
||||
if (![self scanString:@"(" intoString:NULL])
|
||||
CPRaiseParseError(self, @"expression");
|
||||
|
||||
var predicate = [self parsePredicate],
|
||||
trueExpression,
|
||||
falseExpression;
|
||||
|
||||
if (![self scanString:@"," intoString:NULL])
|
||||
CPRaiseParseError(self, @"predicate");
|
||||
|
||||
trueExpression = [self parseExpression];
|
||||
|
||||
if (![self scanString:@"," intoString:NULL])
|
||||
CPRaiseParseError(self, @"expression");
|
||||
|
||||
falseExpression = [self parseExpression];
|
||||
|
||||
if (![self scanString:@")" intoString:NULL])
|
||||
CPRaiseParseError(self, @"expression");
|
||||
|
||||
return [[_CPSubqueryExpression alloc] initWithExpression:collection usingIteratorExpression:variableExpression predicate:subpredicate];
|
||||
return [CPExpression expressionForConditional:predicate trueExpression:trueExpression falseExpression:falseExpression];
|
||||
}
|
||||
|
||||
if ([self scanString:@"FUNCTION" intoString:NULL])
|
||||
|
||||
@@ -25,8 +25,8 @@
|
||||
|
||||
@implementation _CPSetExpression : CPExpression
|
||||
{
|
||||
CPExpression _left;
|
||||
CPExpression _right;
|
||||
CPExpression _left @accessors(getter=leftExpression);
|
||||
CPExpression _right @accessors(getter=rightExpression);
|
||||
}
|
||||
|
||||
- (id)initWithType:(int)type left:(CPExpression)left right:(CPExpression)right
|
||||
@@ -88,16 +88,6 @@
|
||||
return self;
|
||||
}
|
||||
|
||||
- (CPExpression)leftExpression
|
||||
{
|
||||
return _left;
|
||||
}
|
||||
|
||||
- (CPExpression)rightExpression
|
||||
{
|
||||
return _right;
|
||||
}
|
||||
|
||||
- (CPString)description
|
||||
{
|
||||
var desc;
|
||||
|
||||
@@ -26,9 +26,9 @@
|
||||
|
||||
@implementation _CPSubqueryExpression : CPExpression
|
||||
{
|
||||
CPExpression _collection;
|
||||
CPExpression _collection @accessors(getter=collection);
|
||||
CPExpression _variableExpression;
|
||||
CPPredicate _subpredicate;
|
||||
CPPredicate _subpredicate @accessors(getter=predicate);
|
||||
}
|
||||
|
||||
- (id)initWithExpression:(CPExpression)collection usingIteratorVariable:(CPString)variable predicate:(CPPredicate)subpredicate
|
||||
@@ -47,23 +47,25 @@
|
||||
_collection = collection;
|
||||
_variableExpression = variableExpression;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id)expressionValueWithObject:(id)object context:(id)context
|
||||
- (id)expressionValueWithObject:(id)object context:(id)aContext
|
||||
{
|
||||
var collection = [_collection expressionValueWithObject:object context:context],
|
||||
count = [collection count],
|
||||
var collection = [_collection expressionValueWithObject:object context:aContext],
|
||||
result = [CPArray array],
|
||||
bindings = @{ [self variable]: [CPExpression expressionForEvaluatedObject] },
|
||||
i = 0;
|
||||
variable = [self variable],
|
||||
context = aContext || @{};
|
||||
|
||||
for (; i < count; i++)
|
||||
if ([context objectForKey:variable] == nil)
|
||||
[context setObject:[CPExpression expressionForEvaluatedObject] forKey:variable];
|
||||
|
||||
[collection enumerateObjectsUsingBlock:function(exp, idx, stop)
|
||||
{
|
||||
var item = [collection objectAtIndex:i];
|
||||
if ([_subpredicate evaluateWithObject:item substitutionVariables:bindings])
|
||||
[result addObject:item];
|
||||
}
|
||||
if ([_subpredicate evaluateWithObject:exp substitutionVariables:context])
|
||||
[result addObject:exp];
|
||||
}];
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -79,21 +81,11 @@
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (CPExpression)collection
|
||||
{
|
||||
return _collection;
|
||||
}
|
||||
|
||||
- (id)copy
|
||||
{
|
||||
return [[_CPSubqueryExpression alloc] initWithExpression:[_collection copy] usingIteratorExpression:[_variableExpression copy] predicate:[_subpredicate copy]];
|
||||
}
|
||||
|
||||
- (CPPredicate)predicate
|
||||
{
|
||||
return _subpredicate;
|
||||
}
|
||||
|
||||
- (CPString)description
|
||||
{
|
||||
return [self predicateFormat];
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
|
||||
@implementation _CPVariableExpression : CPExpression
|
||||
{
|
||||
CPString _variable;
|
||||
CPString _variable @accessors(getter=variable);
|
||||
}
|
||||
|
||||
- (id)initWithVariable:(CPString)variable
|
||||
@@ -54,11 +54,6 @@
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (CPString)variable
|
||||
{
|
||||
return _variable;
|
||||
}
|
||||
|
||||
- (id)expressionValueWithObject:object context:(CPDictionary)context
|
||||
{
|
||||
var expression = [self _expressionWithSubstitutionVariables:context];
|
||||
|
||||
+59
-1
@@ -42,6 +42,7 @@ var _CPRunLoopPerformPool = [],
|
||||
/* @ignore */
|
||||
@implementation _CPRunLoopPerform : CPObject
|
||||
{
|
||||
Function _block;
|
||||
id _target;
|
||||
SEL _selector;
|
||||
id _argument;
|
||||
@@ -64,6 +65,7 @@ var _CPRunLoopPerformPool = [],
|
||||
{
|
||||
var perform = _CPRunLoopPerformPool.pop();
|
||||
|
||||
perform._block = nil;
|
||||
perform._target = aTarget;
|
||||
perform._selector = aSelector;
|
||||
perform._argument = anArgument;
|
||||
@@ -77,6 +79,26 @@ var _CPRunLoopPerformPool = [],
|
||||
return [[self alloc] initWithSelector:aSelector target:aTarget argument:anArgument order:anOrder modes:modes];
|
||||
}
|
||||
|
||||
+ (_CPRunLoopPerform)performWithBlock:(Function)aBlock argument:(id)anArgument order:(unsigned)anOrder modes:(CPArray)modes
|
||||
{
|
||||
if (_CPRunLoopPerformPool.length)
|
||||
{
|
||||
var perform = _CPRunLoopPerformPool.pop();
|
||||
|
||||
perform._target = nil;
|
||||
perform._selector = nil;
|
||||
perform._block = aBlock;
|
||||
perform._argument = anArgument;
|
||||
perform._order = anOrder;
|
||||
perform._runLoopModes = modes;
|
||||
perform._isValid = YES;
|
||||
|
||||
return perform;
|
||||
}
|
||||
|
||||
return [[self alloc] initWithBlock:aBlock argument:anArgument order:anOrder modes:modes];
|
||||
}
|
||||
|
||||
- (id)initWithSelector:(SEL)aSelector target:(SEL)aTarget argument:(id)anArgument order:(unsigned)anOrder modes:(CPArray)modes
|
||||
{
|
||||
self = [super init];
|
||||
@@ -94,6 +116,22 @@ var _CPRunLoopPerformPool = [],
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id)initWithBlock:(Function)aBlock argument:(id)anArgument order:(unsigned)anOrder modes:(CPArray)modes
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
if (self)
|
||||
{
|
||||
_block = aBlock;
|
||||
_argument = anArgument;
|
||||
_order = anOrder;
|
||||
_runLoopModes = modes;
|
||||
_isValid = YES;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (SEL)selector
|
||||
{
|
||||
return _selector;
|
||||
@@ -121,7 +159,10 @@ var _CPRunLoopPerformPool = [],
|
||||
|
||||
if ([_runLoopModes containsObject:aRunLoopMode])
|
||||
{
|
||||
[_target performSelector:_selector withObject:_argument];
|
||||
if (_block)
|
||||
_block(_argument);
|
||||
else
|
||||
[_target performSelector:_selector withObject:_argument];
|
||||
|
||||
return YES;
|
||||
}
|
||||
@@ -227,6 +268,23 @@ var CPRunLoopLastNativeRunLoop = 0;
|
||||
_orderedPerforms.splice(count + 1, 0, perform);
|
||||
}
|
||||
|
||||
|
||||
/*!
|
||||
@ignore
|
||||
*/
|
||||
- (void)performBlock:(Function)aBlock argument:(id)anArgument order:(int)anOrder modes:(CPArray)modes
|
||||
{
|
||||
var perform = [_CPRunLoopPerform performWithBlock:aBlock argument:anArgument order:anOrder modes:modes],
|
||||
count = _orderedPerforms.length;
|
||||
|
||||
// We sort ourselves in reverse because we iterate this list backwards.
|
||||
while (count--)
|
||||
if (anOrder < [_orderedPerforms[count] order])
|
||||
break;
|
||||
|
||||
_orderedPerforms.splice(count + 1, 0, perform);
|
||||
}
|
||||
|
||||
/*!
|
||||
Cancels the specified selector and target.
|
||||
@param aSelector the selector of the method to invoke
|
||||
|
||||
Loaded 100 of 508 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user