mirror of
https://github.com/cappuccino/cappuccino.git
synced 2026-09-05 10:23:39 +00:00
Merge branch 'master' into 010-CPApplicationOSBehavior
This commit is contained in:
+319
-74
@@ -22,13 +22,14 @@
|
||||
|
||||
@import "CPTextField.j"
|
||||
@import "CPView.j"
|
||||
@import <Foundation/CPGeometry.j>
|
||||
|
||||
// CPBoxType
|
||||
@typedef CPBoxType
|
||||
CPBoxPrimary = 0;
|
||||
CPBoxSecondary = 1;
|
||||
CPBoxSecondary = 1; // Deprecated
|
||||
CPBoxSeparator = 2;
|
||||
CPBoxOldStyle = 3;
|
||||
CPBoxOldStyle = 3; // Deprecated
|
||||
CPBoxCustom = 4;
|
||||
|
||||
// CPBorderType
|
||||
@@ -58,11 +59,13 @@ CPBelowBottom = 6;
|
||||
@implementation CPBox : CPView
|
||||
{
|
||||
CPBoxType _boxType;
|
||||
CPBorderType _borderType;
|
||||
CPBorderType _borderType; // deprecated
|
||||
CPView _contentView;
|
||||
CPView _boxView; // needed for CSS theming, will be transparent for non CSS themes
|
||||
BOOL _transparent @accessors(getter=isTransparent);
|
||||
|
||||
CPString _title @accessors(getter=title);
|
||||
int _titlePosition @accessors(getter=titlePosition);
|
||||
CPString _title @accessors(getter=title);
|
||||
int _titlePosition @accessors(getter=titlePosition);
|
||||
CPTextField _titleView;
|
||||
}
|
||||
|
||||
@@ -90,8 +93,15 @@ CPBelowBottom = 6;
|
||||
@"inner-shadow-size": 6.0,
|
||||
@"inner-shadow-color": [CPNull null],
|
||||
@"content-margin": CGSizeMakeZero(),
|
||||
@"nib2cib-adjustment-primary-frame": CGRectMake(4, -4, -8, -6)
|
||||
};
|
||||
@"title-font": [CPNull null],
|
||||
@"title-left-offset": 5.0,
|
||||
@"title-top-offset": 0.0,
|
||||
@"title-color": [CPNull null],
|
||||
@"nib2cib-adjustment-primary-frame": CGRectMake(4, -4, -8, -6),
|
||||
@"content-adjustment": CGRectMakeZero(),
|
||||
@"min-y-correction-no-title": 0,
|
||||
@"min-y-correction-title": 0
|
||||
};
|
||||
}
|
||||
|
||||
+ (id)boxEnclosingView:(CPView)aView
|
||||
@@ -115,16 +125,26 @@ CPBelowBottom = 6;
|
||||
|
||||
if (self)
|
||||
{
|
||||
_borderType = CPBezelBorder;
|
||||
_borderType = CPGrooveBorder; // Was CPBezelBorder but Cocoa default is CPGrooveBorder
|
||||
_boxType = CPBoxPrimary;
|
||||
|
||||
_titlePosition = CPNoTitle;
|
||||
_titleView = [CPTextField labelWithTitle:@""];
|
||||
[_titleView setFont:[self titleFont]];
|
||||
[_titleView setTextColor:[self titleColor]];
|
||||
|
||||
_boxView = [[CPView alloc] initWithFrame:[self bounds]];
|
||||
[_boxView setAutoresizingMask:CPViewWidthSizable | CPViewHeightSizable];
|
||||
|
||||
_contentView = [[CPView alloc] initWithFrame:[self bounds]];
|
||||
[_contentView setAutoresizingMask:CPViewWidthSizable | CPViewHeightSizable];
|
||||
|
||||
[self setAutoresizesSubviews:YES];
|
||||
[self addSubview:_contentView];
|
||||
[self addSubview:_boxView];
|
||||
[_boxView setAutoresizesSubviews:YES];
|
||||
[_boxView addSubview:_contentView];
|
||||
|
||||
[self sizeToFit];
|
||||
}
|
||||
|
||||
return self;
|
||||
@@ -156,6 +176,8 @@ CPBelowBottom = 6;
|
||||
*/
|
||||
- (CPBorderType)borderType
|
||||
{
|
||||
CPLog.warn("CPBox borderType is deprecated.");
|
||||
|
||||
return _borderType;
|
||||
}
|
||||
|
||||
@@ -178,7 +200,8 @@ CPBelowBottom = 6;
|
||||
return;
|
||||
|
||||
_borderType = aBorderType;
|
||||
[self setNeedsDisplay:YES];
|
||||
|
||||
[self refreshDisplay];
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -218,13 +241,30 @@ CPBelowBottom = 6;
|
||||
*/
|
||||
- (void)setBoxType:(CPBoxType)aBoxType
|
||||
{
|
||||
if ((aBoxType == CPBoxSecondary) || (aBoxType == CPBoxOldStyle))
|
||||
CPLog.warn("CPBox setBoxType: CPBoxSecondary and CPBoxOldStyle are deprecated.");
|
||||
|
||||
if (_boxType === aBoxType)
|
||||
return;
|
||||
|
||||
_boxType = aBoxType;
|
||||
[self setNeedsDisplay:YES];
|
||||
|
||||
[self refreshDisplay];
|
||||
}
|
||||
|
||||
- (void)setTransparent:(BOOL)shouldBeTransparent
|
||||
{
|
||||
if (_transparent == shouldBeTransparent)
|
||||
return;
|
||||
|
||||
_transparent = shouldBeTransparent;
|
||||
|
||||
[self _manageTitlePositioning];
|
||||
}
|
||||
|
||||
/*!
|
||||
The receiver’s border color. It must be a custom box (that is, it has a type of CPBoxCustom) and it must have a border style of CPLineBorder.
|
||||
*/
|
||||
- (CPColor)borderColor
|
||||
{
|
||||
return [self valueForThemeAttribute:@"border-color"];
|
||||
@@ -232,12 +272,21 @@ CPBelowBottom = 6;
|
||||
|
||||
- (void)setBorderColor:(CPColor)color
|
||||
{
|
||||
if ((_boxType !== CPBoxCustom) || (_borderType !== CPLineBorder))
|
||||
{
|
||||
CPLog.warn("CPBox setBorderColor: the box must be of type CPBoxCustom AND border of type CPLineBorder in order to use setBorderColor. Ignored.");
|
||||
return;
|
||||
}
|
||||
|
||||
if ([color isEqual:[self borderColor]])
|
||||
return;
|
||||
|
||||
[self setValue:color forThemeAttribute:@"border-color"];
|
||||
}
|
||||
|
||||
/*!
|
||||
The receiver’s border width. It must be a custom box (that is, it has a type of CPBoxCustom) and it must have a border style of CPLineBorder.
|
||||
*/
|
||||
- (float)borderWidth
|
||||
{
|
||||
return [self valueForThemeAttribute:@"border-width"];
|
||||
@@ -245,12 +294,21 @@ CPBelowBottom = 6;
|
||||
|
||||
- (void)setBorderWidth:(float)width
|
||||
{
|
||||
if ((_boxType !== CPBoxCustom) || (_borderType !== CPLineBorder))
|
||||
{
|
||||
CPLog.warn("CPBox setBorderWidth: the box must be of type CPBoxCustom AND border of type CPLineBorder in order to use setBorderWidth. Ignored.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (width === [self borderWidth])
|
||||
return;
|
||||
|
||||
[self setValue:width forThemeAttribute:@"border-width"];
|
||||
}
|
||||
|
||||
/*!
|
||||
The receiver’s corner radius. It must be a custom box (that is, it has a type of CPBoxCustom) and it must have a border style of CPLineBorder.
|
||||
*/
|
||||
- (float)cornerRadius
|
||||
{
|
||||
return [self valueForThemeAttribute:@"corner-radius"];
|
||||
@@ -258,12 +316,21 @@ CPBelowBottom = 6;
|
||||
|
||||
- (void)setCornerRadius:(float)radius
|
||||
{
|
||||
if ((_boxType !== CPBoxCustom) || (_borderType !== CPLineBorder))
|
||||
{
|
||||
CPLog.warn("CPBox setCornerRadius: the box must be of type CPBoxCustom AND border of type CPLineBorder in order to use setCornerRadius. Ignored.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (radius === [self cornerRadius])
|
||||
return;
|
||||
|
||||
[self setValue:radius forThemeAttribute:@"corner-radius"];
|
||||
}
|
||||
|
||||
/*!
|
||||
The receiver’s background color. It must be a custom box (that is, it has a type of CPBoxCustom) and it must have a border style of CPLineBorder.
|
||||
*/
|
||||
- (CPColor)fillColor
|
||||
{
|
||||
return [self valueForThemeAttribute:@"background-color"];
|
||||
@@ -271,6 +338,12 @@ CPBelowBottom = 6;
|
||||
|
||||
- (void)setFillColor:(CPColor)color
|
||||
{
|
||||
if ((_boxType !== CPBoxCustom) || (_borderType !== CPLineBorder))
|
||||
{
|
||||
CPLog.warn("CPBox setFillColor: the box must be of type CPBoxCustom AND border of type CPLineBorder in order to use setFillColor. Ignored.");
|
||||
return;
|
||||
}
|
||||
|
||||
if ([color isEqual:[self fillColor]])
|
||||
return;
|
||||
|
||||
@@ -287,21 +360,20 @@ CPBelowBottom = 6;
|
||||
if (aView === _contentView)
|
||||
return;
|
||||
|
||||
var borderWidth = [self borderWidth],
|
||||
contentMargin = [self valueForThemeAttribute:@"content-margin"];
|
||||
|
||||
[aView setFrame:CGRectInset([self bounds], contentMargin.width + borderWidth, contentMargin.height + borderWidth)];
|
||||
[aView setAutoresizingMask:CPViewWidthSizable | CPViewHeightSizable];
|
||||
|
||||
// A nil contentView is allowed (tested in Cocoa 2013-02-22).
|
||||
if (!aView)
|
||||
[_contentView removeFromSuperview];
|
||||
else if (_contentView)
|
||||
[self replaceSubview:_contentView with:aView];
|
||||
[_boxView replaceSubview:_contentView with:aView];
|
||||
else
|
||||
[self addSubview:aView];
|
||||
[_boxView addSubview:aView];
|
||||
|
||||
_contentView = aView;
|
||||
|
||||
[self sizeToFit];
|
||||
[self refreshDisplay];
|
||||
}
|
||||
|
||||
- (CGSize)contentViewMargins
|
||||
@@ -321,9 +393,14 @@ CPBelowBottom = 6;
|
||||
{
|
||||
var offset = [self _titleHeightOffset],
|
||||
borderWidth = [self borderWidth],
|
||||
contentMargin = [self valueForThemeAttribute:@"content-margin"];
|
||||
contentMargin = [self valueForThemeAttribute:@"content-margin"],
|
||||
contentAdjustment = [self valueForThemeAttribute:@"content-adjustment"],
|
||||
minYCorrection = [self valueForThemeAttribute:(_titlePosition === CPNoTitle ? @"min-y-correction-no-title" : @"min-y-correction-title")];
|
||||
|
||||
[self setFrame:CGRectInset(aRect, -(contentMargin.width + borderWidth), -(contentMargin.height + offset[0] + borderWidth))];
|
||||
[self setFrame:CGRectMake(aRect.origin.x - contentAdjustment.origin.x - contentMargin.width + borderWidth,
|
||||
aRect.origin.y - contentAdjustment.origin.y - contentMargin.height + borderWidth - minYCorrection,
|
||||
aRect.size.width + 2 * contentMargin.width - contentAdjustment.size.width,
|
||||
aRect.size.height + 2 * contentMargin.height - contentAdjustment.size.height)];
|
||||
}
|
||||
|
||||
- (void)setTitle:(CPString)aTitle
|
||||
@@ -348,14 +425,42 @@ CPBelowBottom = 6;
|
||||
|
||||
- (CPFont)titleFont
|
||||
{
|
||||
return [_titleView font];
|
||||
if ([self hasThemeAttribute:@"title-font"])
|
||||
return [self valueForThemeAttribute:@"title-font"];
|
||||
else
|
||||
return [_titleView font];
|
||||
}
|
||||
|
||||
- (void)setTitleFont:(CPFont)aFont
|
||||
{
|
||||
if ([aFont isEqual:[self titleFont]])
|
||||
return;
|
||||
|
||||
if ([self hasThemeAttribute:@"title-font"])
|
||||
[self setValue:aFont forThemeAttribute:@"title-font"];
|
||||
|
||||
[_titleView setFont:aFont];
|
||||
}
|
||||
|
||||
- (CPColor)titleColor
|
||||
{
|
||||
if ([self hasThemeAttribute:@"title-color"])
|
||||
return [self valueForThemeAttribute:@"title-color"];
|
||||
else
|
||||
return [_titleView textColor];
|
||||
}
|
||||
|
||||
- (void)setTitleColor:(CPColor)aColor
|
||||
{
|
||||
if ([aColor isEqual:[self titleColor]])
|
||||
return;
|
||||
|
||||
if ([self hasThemeAttribute:@"title-color"])
|
||||
[self setValue:aColor forThemeAttribute:@"title-color"];
|
||||
|
||||
[_titleView setTextColor:aColor];
|
||||
}
|
||||
|
||||
/*!
|
||||
Return the text field used to display the receiver's title.
|
||||
|
||||
@@ -366,25 +471,39 @@ CPBelowBottom = 6;
|
||||
return _titleView;
|
||||
}
|
||||
|
||||
/*!
|
||||
Return the rectangle in which the receiver’s title is drawn.
|
||||
*/
|
||||
- (CGRect)titleRect
|
||||
{
|
||||
return [_titleView frame];
|
||||
}
|
||||
|
||||
- (void)_manageTitlePositioning
|
||||
{
|
||||
if (_titlePosition == CPNoTitle)
|
||||
if ((_titlePosition == CPNoTitle) || _transparent)
|
||||
{
|
||||
[_titleView removeFromSuperview];
|
||||
[self setNeedsDisplay:YES];
|
||||
|
||||
if (_boxType !== CPBoxSeparator)
|
||||
[self sizeToFit];
|
||||
|
||||
[self refreshDisplay];
|
||||
return;
|
||||
}
|
||||
|
||||
[_titleView setStringValue:_title];
|
||||
[_titleView sizeToFit];
|
||||
[self addSubview:_titleView];
|
||||
|
||||
var titleLeftOffset = [self valueForThemeAttribute:@"title-left-offset"],
|
||||
titleTopOffset = [self valueForThemeAttribute:@"title-top-offset"];
|
||||
|
||||
switch (_titlePosition)
|
||||
{
|
||||
case CPAtTop:
|
||||
case CPAboveTop:
|
||||
case CPBelowTop:
|
||||
[_titleView setFrameOrigin:CGPointMake(5.0, 0.0)];
|
||||
[_titleView setFrameOrigin:CGPointMake(titleLeftOffset, titleTopOffset)]; // FIXME: was 0.0
|
||||
[_titleView setAutoresizingMask:CPViewNotSizable];
|
||||
break;
|
||||
|
||||
@@ -392,39 +511,51 @@ CPBelowBottom = 6;
|
||||
case CPAtBottom:
|
||||
case CPBelowBottom:
|
||||
var h = [_titleView frameSize].height;
|
||||
[_titleView setFrameOrigin:CGPointMake(5.0, [self frameSize].height - h)];
|
||||
[_titleView setFrameOrigin:CGPointMake(titleLeftOffset, [self frameSize].height - h - titleTopOffset)];
|
||||
[_titleView setAutoresizingMask:CPViewMinYMargin];
|
||||
break;
|
||||
}
|
||||
|
||||
if (!_transparent)
|
||||
[self addSubview:_titleView];
|
||||
|
||||
[self sizeToFit];
|
||||
[self setNeedsDisplay:YES];
|
||||
[self refreshDisplay];
|
||||
}
|
||||
|
||||
- (void)sizeToFit
|
||||
{
|
||||
var contentFrame = [_contentView frame],
|
||||
offset = [self _titleHeightOffset],
|
||||
contentMargin = [self valueForThemeAttribute:@"content-margin"];
|
||||
var offset = [self _titleHeightOffset],
|
||||
size = [self frameSize];
|
||||
|
||||
if (!contentFrame)
|
||||
[_boxView setFrame:CGRectMake(0, offset[1], size.width, size.height - offset[0])];
|
||||
|
||||
if (!_contentView)
|
||||
return;
|
||||
|
||||
[_contentView setFrameOrigin:CGPointMake(contentMargin.width, contentMargin.height + offset[1])];
|
||||
var boxSize = [_boxView frameSize],
|
||||
contentMargin = [self valueForThemeAttribute:@"content-margin"],
|
||||
contentAdjustment = [self valueForThemeAttribute:@"content-adjustment"],
|
||||
borderWidth = [self valueForThemeAttribute:@"border-width"],
|
||||
minYCorrection = [self valueForThemeAttribute:(_titlePosition === CPNoTitle ? @"min-y-correction-no-title" : @"min-y-correction-title")];
|
||||
|
||||
[_contentView setFrame:CGRectMake(contentAdjustment.origin.x + contentMargin.width - borderWidth,
|
||||
contentAdjustment.origin.y + contentMargin.height - borderWidth + minYCorrection,
|
||||
boxSize.width - 2 * contentMargin.width + contentAdjustment.size.width,
|
||||
boxSize.height - 2 * contentMargin.height + contentAdjustment.size.height)];
|
||||
}
|
||||
|
||||
- (float)_titleHeightOffset
|
||||
- (CPArray)_titleHeightOffset
|
||||
{
|
||||
if (_titlePosition == CPNoTitle)
|
||||
return [0.0, 0.0];
|
||||
var titleTopOffset = [self valueForThemeAttribute:@"title-top-offset"];
|
||||
|
||||
switch (_titlePosition)
|
||||
{
|
||||
case CPAtTop:
|
||||
return [[_titleView frameSize].height, [_titleView frameSize].height];
|
||||
return [[_titleView frameSize].height + titleTopOffset, [_titleView frameSize].height + titleTopOffset];
|
||||
|
||||
case CPAtBottom:
|
||||
return [[_titleView frameSize].height, 0.0];
|
||||
return [[_titleView frameSize].height + titleTopOffset, 0.0];
|
||||
|
||||
default:
|
||||
return [0.0, 0.0];
|
||||
@@ -441,21 +572,24 @@ CPBelowBottom = 6;
|
||||
|
||||
- (void)drawRect:(CGRect)rect
|
||||
{
|
||||
if ([self isCSSBased] && (_boxType !== CPBoxCustom))
|
||||
return;
|
||||
|
||||
var bounds = [self bounds];
|
||||
|
||||
switch (_boxType)
|
||||
if (_boxType === CPBoxSeparator)
|
||||
{
|
||||
case CPBoxSeparator:
|
||||
// NSBox does not include a horizontal flag for the separator type. We have to determine
|
||||
// the type of separator to draw by the width and height of the frame.
|
||||
if (CGRectGetWidth(bounds) === 5.0)
|
||||
return [self _drawVerticalSeparatorInRect:bounds];
|
||||
else if (CGRectGetHeight(bounds) === 5.0)
|
||||
return [self _drawHorizontalSeparatorInRect:bounds];
|
||||
|
||||
break;
|
||||
// NSBox does not include a horizontal flag for the separator type. We have to determine
|
||||
// the type of separator to draw by the width and height of the frame.
|
||||
if (CGRectGetWidth(bounds) === 5.0)
|
||||
return [self _drawVerticalSeparatorInRect:bounds];
|
||||
else if (CGRectGetHeight(bounds) === 5.0)
|
||||
return [self _drawHorizontalSeparatorInRect:bounds];
|
||||
}
|
||||
|
||||
if (_transparent)
|
||||
return;
|
||||
|
||||
if (_titlePosition == CPAtTop)
|
||||
{
|
||||
bounds.origin.y += [_titleView frameSize].height;
|
||||
@@ -476,9 +610,6 @@ CPBelowBottom = 6;
|
||||
switch (_borderType)
|
||||
{
|
||||
case CPBezelBorder:
|
||||
[self _drawBezelBorderInRect:bounds];
|
||||
break;
|
||||
|
||||
case CPGrooveBorder:
|
||||
case CPLineBorder:
|
||||
[self _drawLineBorderInRect:bounds];
|
||||
@@ -595,12 +726,70 @@ CPBelowBottom = 6;
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark -
|
||||
|
||||
@implementation CPBox (CSSTheming)
|
||||
|
||||
- (void)layoutSubviews
|
||||
{
|
||||
if (![self isCSSBased] || (_boxType === CPBoxCustom))
|
||||
return;
|
||||
|
||||
var bounds = [self bounds];
|
||||
|
||||
if (_boxType === CPBoxSeparator)
|
||||
{
|
||||
if (bounds.size.width === 5.0)
|
||||
{
|
||||
// Vertical separator
|
||||
[_boxView setFrame:CGRectMake(2,0,1,bounds.size.height)];
|
||||
}
|
||||
else
|
||||
{
|
||||
// Horizontal separator
|
||||
[_boxView setFrame:CGRectMake(0,2,bounds.size.width,1)];
|
||||
}
|
||||
|
||||
[_boxView setBackgroundColor:[self valueForThemeAttribute:@"border-color"]];
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// All types of boxes (beside custom which is not covered here) always draw the same way, unless they are CPNoBorder.
|
||||
if ((_borderType !== CPNoBorder) && !_transparent)
|
||||
{
|
||||
[_boxView setBackgroundColor:[self valueForThemeAttribute:@"background-color"]];
|
||||
return;
|
||||
}
|
||||
|
||||
// No border or transparent
|
||||
[_boxView setBackgroundColor:nil];
|
||||
}
|
||||
|
||||
- (BOOL)isCSSBased
|
||||
{
|
||||
return [[self theme] isCSSBased];
|
||||
}
|
||||
|
||||
- (void)refreshDisplay
|
||||
{
|
||||
if ([self isCSSBased] && (_boxType !== CPBoxCustom))
|
||||
[self setNeedsLayout:YES];
|
||||
else
|
||||
[self setNeedsDisplay:YES];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark -
|
||||
|
||||
var CPBoxTypeKey = @"CPBoxTypeKey",
|
||||
CPBoxBorderTypeKey = @"CPBoxBorderTypeKey",
|
||||
CPBoxTitle = @"CPBoxTitle",
|
||||
CPBoxTitlePosition = @"CPBoxTitlePosition",
|
||||
CPBoxTitleView = @"CPBoxTitleView",
|
||||
CPBoxContentView = @"CPBoxContentView";
|
||||
CPBoxTitleKey = @"CPBoxTitleKey",
|
||||
CPBoxTitlePositionKey = @"CPBoxTitlePositionKey",
|
||||
CPBoxTitleViewKey = @"CPBoxTitleViewKey",
|
||||
CPBoxContentViewKey = @"CPBoxContentViewKey",
|
||||
CPBoxBoxViewKey = @"CPBoxBoxViewKey";
|
||||
|
||||
@implementation CPBox (CPCoding)
|
||||
|
||||
@@ -613,33 +802,56 @@ var CPBoxTypeKey = @"CPBoxTypeKey",
|
||||
_boxType = [aCoder decodeIntForKey:CPBoxTypeKey];
|
||||
_borderType = [aCoder decodeIntForKey:CPBoxBorderTypeKey];
|
||||
|
||||
_title = [aCoder decodeObjectForKey:CPBoxTitle];
|
||||
_titlePosition = [aCoder decodeIntForKey:CPBoxTitlePosition];
|
||||
_titleView = [aCoder decodeObjectForKey:CPBoxTitleView] || [CPTextField labelWithTitle:_title];
|
||||
_title = [aCoder decodeObjectForKey:CPBoxTitleKey];
|
||||
_titlePosition = [aCoder decodeIntForKey:CPBoxTitlePositionKey];
|
||||
|
||||
if (_boxType != CPBoxSeparator)
|
||||
// Important : see comment on encodeWithCoder below
|
||||
|
||||
_boxView = [aCoder decodeObjectForKey:CPBoxBoxViewKey];
|
||||
|
||||
if (!_boxView)
|
||||
{
|
||||
// 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
|
||||
// We're coming from nib2cib.
|
||||
|
||||
_contentView = [aCoder decodeObjectForKey:CPBoxContentView] || [[CPView alloc] initWithFrame:[self bounds]];
|
||||
var malformedContentView = [self subviews][0];
|
||||
[_contentView setSubviews:[malformedContentView subviews]];
|
||||
[self replaceSubview:malformedContentView with:_contentView];
|
||||
_boxView = [[CPView alloc] initWithFrame:[self bounds]];
|
||||
_titleView = [CPTextField labelWithTitle:_title];
|
||||
}
|
||||
else
|
||||
{
|
||||
_titlePosition = CPNoTitle;
|
||||
// We're coming from elsewhere
|
||||
|
||||
_titleView = [aCoder decodeObjectForKey:CPBoxTitleViewKey];
|
||||
}
|
||||
|
||||
[self setAutoresizesSubviews:YES];
|
||||
|
||||
_contentView = [aCoder decodeObjectForKey:CPBoxContentViewKey];
|
||||
|
||||
// FIXME: super-mega-hyper-trick : _contentView has a superview which is not normal !
|
||||
// FIXME: (see encodeWithCoder to understand why this is not possible)
|
||||
// FIXME: we fix this by hand. This is horrible so please find a structural solution !
|
||||
|
||||
if (_contentView)
|
||||
_contentView._superview = nil;
|
||||
|
||||
[_contentView setAutoresizingMask:CPViewWidthSizable | CPViewHeightSizable];
|
||||
[_boxView setAutoresizingMask:CPViewWidthSizable | CPViewHeightSizable];
|
||||
[_boxView setAutoresizesSubviews:YES];
|
||||
[self setAutoresizesSubviews:YES];
|
||||
|
||||
if (_contentView)
|
||||
[_boxView setSubviews:@[_contentView]];
|
||||
|
||||
[self addSubview:_boxView];
|
||||
[self addSubview:_titleView];
|
||||
|
||||
if (_boxType === CPBoxSeparator)
|
||||
_titlePosition = CPNoTitle;
|
||||
|
||||
[_titleView setFont:[self titleFont]];
|
||||
[_titleView setTextColor:[self titleColor]];
|
||||
|
||||
[self _manageTitlePositioning];
|
||||
|
||||
[self refreshDisplay];
|
||||
}
|
||||
|
||||
return self;
|
||||
@@ -647,14 +859,47 @@ var CPBoxTypeKey = @"CPBoxTypeKey",
|
||||
|
||||
- (void)encodeWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
// We have to distinguish between 2 cases :
|
||||
// - we come from nib2cib
|
||||
// - we come from elsewhere
|
||||
//
|
||||
// When coming from nib2cib, we have no _boxView, _contentView, _titleView.
|
||||
// We fix _contentView to be the first (and only) subview.
|
||||
// They will have to be added on decoding.
|
||||
//
|
||||
// When coming from elsewhere, we remove _boxView (and thus _contentView) and _titleView
|
||||
// from the view hierarchy as we'll already encode them via variables.
|
||||
// They will be putted back during decoding. This way, we reduce the space and speed needed for coding.
|
||||
|
||||
var subviews = [self subviews];
|
||||
|
||||
if (!_boxView)
|
||||
{
|
||||
// We're coming from nib2cib.
|
||||
|
||||
_contentView = subviews[0];
|
||||
|
||||
[_contentView removeFromSuperview];
|
||||
}
|
||||
else
|
||||
{
|
||||
// We're coming from elsewhere.
|
||||
|
||||
[_boxView removeFromSuperview];
|
||||
[_titleView removeFromSuperview];
|
||||
}
|
||||
|
||||
[super encodeWithCoder:aCoder];
|
||||
|
||||
[self setSubviews:subviews];
|
||||
|
||||
[aCoder encodeInt:_boxType forKey:CPBoxTypeKey];
|
||||
[aCoder encodeInt:_borderType forKey:CPBoxBorderTypeKey];
|
||||
[aCoder encodeObject:_title forKey:CPBoxTitle];
|
||||
[aCoder encodeInt:_titlePosition forKey:CPBoxTitlePosition];
|
||||
[aCoder encodeObject:_titleView forKey:CPBoxTitleView];
|
||||
[aCoder encodeObject:_contentView forKey:CPBoxContentView];
|
||||
[aCoder encodeObject:_title forKey:CPBoxTitleKey];
|
||||
[aCoder encodeInt:_titlePosition forKey:CPBoxTitlePositionKey];
|
||||
[aCoder encodeConditionalObject:_contentView forKey:CPBoxContentViewKey];
|
||||
[aCoder encodeConditionalObject:_titleView forKey:CPBoxTitleViewKey];
|
||||
[aCoder encodeConditionalObject:_boxView forKey:CPBoxBoxViewKey];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
+258
-166
@@ -44,6 +44,7 @@ CPTexturedRoundedBezelStyle = 11; // Round Textured
|
||||
CPRoundRectBezelStyle = 12; // Round Rect
|
||||
CPRecessedBezelStyle = 13; // Recessed
|
||||
CPRoundedDisclosureBezelStyle = 14; // Disclosure
|
||||
CPInlineBezelStyle = 15; // Inline
|
||||
CPHUDBezelStyle = -1;
|
||||
|
||||
|
||||
@@ -72,14 +73,32 @@ CPPushInCellMask = CPPushInButtonMask;
|
||||
CPChangeGrayCellMask = CPGrayButtonMask;
|
||||
CPChangeBackgroundCellMask = CPBackgroundButtonMask;
|
||||
|
||||
CPButtonStateMixed = CPThemeState("mixed");
|
||||
CPButtonStateBezelStyleRounded = CPThemeState("rounded");
|
||||
CPButtonStateBezelStyleRoundRect = CPThemeState("roundRect");
|
||||
CPButtonStateMixed = CPThemeState("mixed");
|
||||
CPButtonStateBezelStyleRounded = CPThemeState("rounded"); // IB style : Push
|
||||
CPButtonStateBezelStyleShadowlessSquare = CPThemeState("square"); // IB style : Square
|
||||
CPButtonStateBezelStyleSmallSquare = CPThemeState("gradient"); // IB style : Gradient
|
||||
CPButtonStateBezelStyleTexturedRounded = CPThemeState("textured-rounded"); // IB style : Textured rounded
|
||||
CPButtonStateBezelStyleRoundRect = CPThemeState("roundRect"); // IB style : Round rect
|
||||
CPButtonStateBezelStyleRecessed = CPThemeState("recessed"); // IB style : Recessed
|
||||
CPButtonStateBezelStyleInline = CPThemeState("inline"); // IB style : Inline
|
||||
CPButtonStateBezelStyleRegularSquare = CPThemeState("bevel"); // IB style : Bevel
|
||||
CPButtonStateBezelStyleTextured = CPThemeState("textured"); // IB style : Textured
|
||||
CPButtonStateBezelStyleDisclosure = CPThemeState("disclosure"); // IB style : Disclosure triangle
|
||||
CPButtonStateBezelStyleRoundedDisclosure = CPThemeState("rounded-disclosure"); // IB style : Rounded disclosure
|
||||
|
||||
// add all future correspondance between bezel styles and theme state here.
|
||||
var CPButtonBezelStyleStateMap = @{
|
||||
CPRoundedBezelStyle: CPButtonStateBezelStyleRounded,
|
||||
CPRoundRectBezelStyle: CPButtonStateBezelStyleRoundRect,
|
||||
CPRoundedBezelStyle: CPButtonStateBezelStyleRounded,
|
||||
CPShadowlessSquareBezelStyle: CPButtonStateBezelStyleShadowlessSquare,
|
||||
CPSmallSquareBezelStyle: CPButtonStateBezelStyleSmallSquare,
|
||||
CPTexturedRoundedBezelStyle: CPButtonStateBezelStyleTexturedRounded,
|
||||
CPRoundRectBezelStyle: CPButtonStateBezelStyleRoundRect,
|
||||
CPRecessedBezelStyle: CPButtonStateBezelStyleRecessed,
|
||||
CPInlineBezelStyle: CPButtonStateBezelStyleInline,
|
||||
CPRegularSquareBezelStyle: CPButtonStateBezelStyleRegularSquare,
|
||||
CPTexturedSquareBezelStyle: CPButtonStateBezelStyleTextured,
|
||||
CPDisclosureBezelStyle: CPButtonStateBezelStyleDisclosure,
|
||||
CPRoundedDisclosureBezelStyle: CPButtonStateBezelStyleRoundedDisclosure
|
||||
};
|
||||
|
||||
/// @cond IGNORE
|
||||
@@ -108,6 +127,7 @@ CPButtonImageOffset = 3.0;
|
||||
|
||||
// NS-style Display Properties
|
||||
CPBezelStyle _bezelStyle;
|
||||
ThemeState _bezelState;
|
||||
|
||||
CPString _keyEquivalent;
|
||||
unsigned _keyEquivalentModifierMask;
|
||||
@@ -117,7 +137,7 @@ CPButtonImageOffset = 3.0;
|
||||
float _periodicDelay;
|
||||
float _periodicInterval;
|
||||
|
||||
BOOL _isTracking;
|
||||
BOOL _isHighlighted;
|
||||
}
|
||||
|
||||
+ (Class)_binderClassForBinding:(CPString)aBinding
|
||||
@@ -157,6 +177,13 @@ CPButtonImageOffset = 3.0;
|
||||
@"bezel-inset": CGInsetMakeZero(),
|
||||
@"content-inset": CGInsetMakeZero(),
|
||||
@"bezel-color": [CPNull null],
|
||||
@"image-position": CPImageLeft,
|
||||
@"vertical-alignment": CPCenterVerticalTextAlignment,
|
||||
@"alignment": CPCenterTextAlignment,
|
||||
@"image-scaling": CPImageScaleNone,
|
||||
@"invert-image": NO,
|
||||
@"invert-image-on-push": NO,
|
||||
@"image-color": [CPNull null] // If null, image color follows text color
|
||||
};
|
||||
}
|
||||
|
||||
@@ -172,12 +199,12 @@ CPButtonImageOffset = 3.0;
|
||||
if (self)
|
||||
{
|
||||
// Should we instead override the defaults?
|
||||
[self setValue:CPCenterTextAlignment forThemeAttribute:@"alignment"];
|
||||
[self setValue:CPCenterVerticalTextAlignment forThemeAttribute:@"vertical-alignment"];
|
||||
[self setValue:CPImageLeft forThemeAttribute:@"image-position"];
|
||||
[self setValue:CPImageScaleNone forThemeAttribute:@"image-scaling"];
|
||||
// [self setValue:CPCenterTextAlignment forThemeAttribute:@"alignment"];
|
||||
// [self setValue:CPCenterVerticalTextAlignment forThemeAttribute:@"vertical-alignment"];
|
||||
// [self setValue:CPImageLeft forThemeAttribute:@"image-position"];
|
||||
// [self setValue:CPImageScaleNone forThemeAttribute:@"image-scaling"];
|
||||
|
||||
[self setBezelStyle:CPRoundRectBezelStyle];
|
||||
[self setBezelStyle:CPRoundedBezelStyle];
|
||||
[self setBordered:YES];
|
||||
|
||||
[self _init];
|
||||
@@ -258,30 +285,6 @@ CPButtonImageOffset = 3.0;
|
||||
anObjectValue = CPOnState;
|
||||
|
||||
[super setObjectValue:anObjectValue];
|
||||
|
||||
switch ([self objectValue])
|
||||
{
|
||||
case CPMixedState:
|
||||
[self unsetThemeState:CPThemeStateSelected];
|
||||
[self setThemeState:CPButtonStateMixed];
|
||||
if (_showsStateBy & (CPChangeGrayCellMask | CPChangeBackgroundCellMask))
|
||||
[self setThemeState:CPThemeStateHighlighted];
|
||||
else
|
||||
[self unsetThemeState:CPThemeStateHighlighted];
|
||||
break;
|
||||
|
||||
case CPOnState:
|
||||
[self unsetThemeState:CPButtonStateMixed];
|
||||
[self setThemeState:CPThemeStateSelected];
|
||||
if (_showsStateBy & (CPChangeGrayCellMask | CPChangeBackgroundCellMask))
|
||||
[self setThemeState:CPThemeStateHighlighted];
|
||||
else
|
||||
[self unsetThemeState:CPThemeStateHighlighted];
|
||||
break;
|
||||
|
||||
case CPOffState:
|
||||
[self unsetThemeStates:[CPThemeStateSelected, CPButtonStateMixed, CPThemeStateHighlighted]];
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -378,12 +381,19 @@ CPButtonImageOffset = 3.0;
|
||||
|
||||
- (void)setImage:(CPImage)anImage
|
||||
{
|
||||
[self setValue:anImage forThemeAttribute:@"image" inState:CPThemeStateNormal];
|
||||
// This is needed when compiling themes
|
||||
if (!_bezelState)
|
||||
_bezelState = CPThemeStateNormal;
|
||||
|
||||
[self setValue:anImage forThemeAttribute:@"image" inState:_bezelState];
|
||||
}
|
||||
|
||||
- (CPImage)image
|
||||
{
|
||||
return [self valueForThemeAttribute:@"image" inState:CPThemeStateNormal];
|
||||
if (!_bezelState)
|
||||
_bezelState = CPThemeStateNormal;
|
||||
|
||||
return [self valueForThemeAttribute:@"image" inState:_bezelState];
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -392,7 +402,8 @@ CPButtonImageOffset = 3.0;
|
||||
*/
|
||||
- (void)setAlternateImage:(CPImage)anImage
|
||||
{
|
||||
[self setValue:anImage forThemeAttribute:@"image" inState:CPThemeStateHighlighted];
|
||||
[self setValue:anImage forThemeAttribute:@"image" inState:_bezelState.and(CPThemeStateHighlighted)];
|
||||
[self setValue:anImage forThemeAttribute:@"image" inState:_bezelState.and(CPThemeStateSelected)];
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -400,7 +411,17 @@ CPButtonImageOffset = 3.0;
|
||||
*/
|
||||
- (CPImage)alternateImage
|
||||
{
|
||||
return [self valueForThemeAttribute:@"image" inState:CPThemeStateHighlighted];
|
||||
return [self valueForThemeAttribute:@"image" inState:_bezelState.and(CPThemeStateSelected)];
|
||||
}
|
||||
|
||||
- (void)setHoveredImage:(CPImage)anImage
|
||||
{
|
||||
[self setValue:anImage forThemeAttribute:@"image" inState:_bezelState.and(CPThemeStateHovered)];
|
||||
}
|
||||
|
||||
- (CPImage)hoveredImage
|
||||
{
|
||||
return [self valueForThemeAttribute:@"image" inState:_bezelState.and(CPThemeStateHovered)];
|
||||
}
|
||||
|
||||
- (void)setImageOffset:(float)theImageOffset
|
||||
@@ -423,11 +444,6 @@ CPButtonImageOffset = 3.0;
|
||||
|
||||
_showsStateBy = aMask;
|
||||
|
||||
if (_showsStateBy & (CPChangeGrayCellMask | CPChangeBackgroundCellMask) && [self state] != CPOffState)
|
||||
[self setThemeState:CPThemeStateHighlighted];
|
||||
else
|
||||
[self unsetThemeState:CPThemeStateHighlighted];
|
||||
|
||||
[self setNeedsDisplay:YES];
|
||||
[self setNeedsLayout];
|
||||
}
|
||||
@@ -444,11 +460,8 @@ CPButtonImageOffset = 3.0;
|
||||
|
||||
_highlightsBy = aMask;
|
||||
|
||||
if ([self hasThemeState:CPThemeStateHighlighted])
|
||||
{
|
||||
[self setNeedsDisplay:YES];
|
||||
[self setNeedsLayout];
|
||||
}
|
||||
[self setNeedsDisplay:YES];
|
||||
[self setNeedsLayout];
|
||||
}
|
||||
|
||||
- (CPInteger)highlightsBy
|
||||
@@ -533,6 +546,17 @@ CPButtonImageOffset = 3.0;
|
||||
_periodicInterval = anInterval;
|
||||
}
|
||||
|
||||
- (void)highlight:(BOOL)shouldHighlight
|
||||
{
|
||||
if (_isHighlighted == shouldHighlight)
|
||||
return;
|
||||
|
||||
_isHighlighted = shouldHighlight;
|
||||
|
||||
[self setNeedsLayout];
|
||||
[self setNeedsDisplay:YES];
|
||||
}
|
||||
|
||||
- (void)mouseDown:(CPEvent)anEvent
|
||||
{
|
||||
if ([self isEnabled] && [self isContinuous])
|
||||
@@ -555,46 +579,12 @@ CPButtonImageOffset = 3.0;
|
||||
[_target performSelector:_action withObject:self];
|
||||
}
|
||||
|
||||
- (BOOL)startTrackingAt:(CGPoint)aPoint
|
||||
{
|
||||
_isTracking = YES;
|
||||
|
||||
var startedTracking = [super startTrackingAt:aPoint];
|
||||
|
||||
if (_highlightsBy & (CPPushInCellMask | CPChangeGrayCellMask))
|
||||
{
|
||||
if (_showsStateBy & (CPChangeGrayCellMask | CPChangeBackgroundCellMask))
|
||||
[self highlight:[self state] == CPOffState];
|
||||
else
|
||||
[self highlight:YES];
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_showsStateBy & (CPChangeGrayCellMask | CPChangeBackgroundCellMask))
|
||||
[self highlight:[self state] != CPOffState];
|
||||
else
|
||||
[self highlight:NO];
|
||||
}
|
||||
|
||||
return startedTracking;
|
||||
}
|
||||
|
||||
- (void)stopTracking:(CGPoint)lastPoint at:(CGPoint)aPoint mouseIsUp:(BOOL)mouseIsUp
|
||||
{
|
||||
_isTracking = NO;
|
||||
|
||||
if (mouseIsUp && CGRectContainsPoint([self bounds], aPoint))
|
||||
[self setNextState];
|
||||
else
|
||||
{
|
||||
if (_showsStateBy & (CPChangeGrayCellMask | CPChangeBackgroundCellMask))
|
||||
[self highlight:[self state] != CPOffState];
|
||||
else
|
||||
[self highlight:NO];
|
||||
}
|
||||
|
||||
[self setNeedsLayout];
|
||||
[self setNeedsDisplay:YES];
|
||||
[self highlight:NO];
|
||||
[self invalidateTimers];
|
||||
}
|
||||
|
||||
@@ -615,7 +605,7 @@ CPButtonImageOffset = 3.0;
|
||||
|
||||
- (CGRect)contentRectForBounds:(CGRect)bounds
|
||||
{
|
||||
var contentInset = [self currentValueForThemeAttribute:@"content-inset"];
|
||||
var contentInset = [self valueForThemeAttribute:@"content-inset" inState:[self _contentVisualState]];
|
||||
|
||||
return CGRectInsetByInset(bounds, contentInset);
|
||||
}
|
||||
@@ -626,7 +616,7 @@ CPButtonImageOffset = 3.0;
|
||||
if (![self isBordered])
|
||||
return bounds;
|
||||
|
||||
var bezelInset = [self currentValueForThemeAttribute:@"bezel-inset"];
|
||||
var bezelInset = [self currentValueForThemeAttribute:@"bezel-inset"]; // FIXME: faire comme au dessus ?
|
||||
|
||||
return CGRectInsetByInset(bounds, bezelInset);
|
||||
}
|
||||
@@ -642,7 +632,7 @@ CPButtonImageOffset = 3.0;
|
||||
size = [contentView frameSize];
|
||||
}
|
||||
else
|
||||
size = [([self title] || " ") sizeWithFont:[self currentValueForThemeAttribute:@"font"]];
|
||||
size = [([self title] || " ") sizeWithFont:[self font]]; //[self currentValueForThemeAttribute:@"font"]];
|
||||
|
||||
var contentInset = [self currentValueForThemeAttribute:@"content-inset"],
|
||||
minSize = [self currentValueForThemeAttribute:@"min-size"],
|
||||
@@ -698,87 +688,164 @@ CPButtonImageOffset = 3.0;
|
||||
return [[_CPImageAndTextView alloc] initWithFrame:CGRectMakeZero()];
|
||||
}
|
||||
|
||||
- (CPThemeState)_backgroundVisualState
|
||||
{
|
||||
var visualState = [self themeState] || CPThemeStateNormal, // Needed during theme compilation
|
||||
currentState = [self state],
|
||||
buttonIsOn = (currentState !== CPOffState);
|
||||
|
||||
if (_isHighlighted && (_highlightsBy & (CPPushInCellMask | CPChangeGrayCellMask))) // FIXME: quid background ?
|
||||
visualState = visualState.and(CPThemeStateHighlighted);
|
||||
else
|
||||
visualState = visualState.without(CPThemeStateHighlighted);
|
||||
|
||||
if (buttonIsOn && (_showsStateBy & (CPPushInCellMask | CPChangeGrayCellMask))) // FIXME: quid background ?
|
||||
visualState = visualState.and((currentState === CPOnState) ? CPThemeStateSelected : CPButtonStateMixed);
|
||||
else
|
||||
visualState = visualState.without(CPThemeStateSelected);
|
||||
|
||||
return visualState;
|
||||
}
|
||||
|
||||
// Note : We have to split content and image visual states as, for example, radio buttons don't follow push buttons behavior
|
||||
- (CPThemeState)_contentVisualState
|
||||
{
|
||||
var visualState = [self themeState] || CPThemeStateNormal, // Needed during theme compilation
|
||||
currentState = [self state],
|
||||
buttonIsOn = (currentState !== CPOffState);
|
||||
|
||||
// If the button is pushed (_isHighlighted), always add the highlighted state
|
||||
if (_isHighlighted || (((_showsStateBy & CPChangeGrayCellMask) || (_showsStateBy & CPChangeBackgroundCellMask)) && buttonIsOn))
|
||||
visualState = visualState.and(CPThemeStateHighlighted);
|
||||
else
|
||||
visualState = visualState.without(CPThemeStateHighlighted);
|
||||
|
||||
if (buttonIsOn && (_showsStateBy & CPContentsCellMask))
|
||||
visualState = visualState.and((currentState === CPOnState) ? CPThemeStateSelected : CPButtonStateMixed);
|
||||
else
|
||||
visualState = visualState.without(CPThemeStateSelected);
|
||||
|
||||
return visualState;
|
||||
}
|
||||
|
||||
- (CPThemeState)_imageVisualState
|
||||
{
|
||||
var visualState = [self themeState] || CPThemeStateNormal, // Needed during theme compilation
|
||||
currentState = [self state],
|
||||
buttonIsOn = (currentState !== CPOffState);
|
||||
|
||||
// Remove highlighted & selected theme states
|
||||
visualState = visualState.without(CPThemeStateHighlighted);
|
||||
visualState = visualState.without(CPThemeStateSelected);
|
||||
|
||||
// Note : We have to deal with special case where button is ON, highlightsBy and showsStateBy use content, and button is pushed
|
||||
// BUT this should not be used for disclosure buttons !
|
||||
if (_isHighlighted && buttonIsOn && (_highlightsBy & CPContentsCellMask) && (_showsStateBy & CPContentsCellMask) && (_bezelStyle !== CPDisclosureBezelStyle))
|
||||
return visualState;
|
||||
|
||||
if (_isHighlighted && ((_highlightsBy & CPContentsCellMask) || (_highlightsBy & CPChangeGrayCellMask)))
|
||||
visualState = visualState.and(CPThemeStateHighlighted);
|
||||
|
||||
if (buttonIsOn && (_showsStateBy & CPContentsCellMask))
|
||||
visualState = visualState.and((currentState === CPOnState) ? CPThemeStateSelected : CPButtonStateMixed);
|
||||
|
||||
return visualState;
|
||||
}
|
||||
|
||||
- (CPString)_currentTitle
|
||||
{
|
||||
var buttonIsOn = ([self state] !== CPOffState);
|
||||
|
||||
// Note : We have to deal with special case where button is ON, highlightsBy and showsStateBy use content, and button is pushed
|
||||
if (_isHighlighted && buttonIsOn && (_highlightsBy & CPContentsCellMask) && (_showsStateBy & CPContentsCellMask))
|
||||
return _title;
|
||||
|
||||
else if (_alternateTitle && ((_isHighlighted && (_highlightsBy & CPContentsCellMask)) || (buttonIsOn && (_showsStateBy & CPContentsCellMask))))
|
||||
return _alternateTitle;
|
||||
|
||||
else
|
||||
return _title;
|
||||
}
|
||||
|
||||
- (CPImage)_currentImage
|
||||
{
|
||||
var visualState = [self _imageVisualState],
|
||||
currentImage = [self valueForThemeAttribute:@"image" inState:visualState],
|
||||
imageColor = [self valueForThemeAttribute:@"image-color" inState:visualState],
|
||||
buttonIsOn = ([self state] !== CPOffState);
|
||||
|
||||
if ([currentImage isMaterialIconImage])
|
||||
{
|
||||
// FIXME: Keep this ?
|
||||
if (([self valueForThemeAttribute:@"invert-image" inState:visualState] || ([self valueForThemeAttribute:@"invert-image-on-push" inState:visualState] && (_isHighlighted || (((_showsStateBy & CPChangeGrayCellMask) || (_showsStateBy & CPChangeBackgroundCellMask)) && buttonIsOn)))))
|
||||
currentImage = [currentImage invertedImage];
|
||||
|
||||
else if (imageColor && [imageColor isKindOfClass:CPColor])
|
||||
// In some buttons, image color doesn't follow text color !
|
||||
currentImage = [currentImage imageVersionWithColor:imageColor];
|
||||
|
||||
else
|
||||
// By default, image color follows text color
|
||||
currentImage = [currentImage imageVersionWithColor:[self valueForThemeAttribute:@"text-color" inState:[self _contentVisualState]]];
|
||||
}
|
||||
|
||||
return currentImage;
|
||||
}
|
||||
|
||||
- (void)layoutSubviews
|
||||
{
|
||||
var bezelView = [self layoutEphemeralSubviewNamed:@"bezel-view"
|
||||
positioned:CPWindowBelow
|
||||
relativeToEphemeralSubviewNamed:@"content-view"];
|
||||
var bezelView = [self layoutEphemeralSubviewNamed:@"bezel-view"
|
||||
positioned:CPWindowBelow
|
||||
relativeToEphemeralSubviewNamed:@"content-view"],
|
||||
|
||||
[bezelView setBackgroundColor:[self currentValueForThemeAttribute:@"bezel-color"]];
|
||||
|
||||
var contentView = [self layoutEphemeralSubviewNamed:@"content-view"
|
||||
contentView = [self layoutEphemeralSubviewNamed:@"content-view"
|
||||
positioned:CPWindowAbove
|
||||
relativeToEphemeralSubviewNamed:@"bezel-view"];
|
||||
relativeToEphemeralSubviewNamed:@"bezel-view"],
|
||||
|
||||
if (contentView)
|
||||
{
|
||||
var title = nil,
|
||||
image = nil;
|
||||
image = [self _currentImage],
|
||||
contentVisualState = [self _contentVisualState];
|
||||
|
||||
if (_isTracking)
|
||||
{
|
||||
if (_highlightsBy & CPContentsCellMask)
|
||||
{
|
||||
if (_showsStateBy & CPContentsCellMask)
|
||||
{
|
||||
title = ([self state] == CPOffState && _alternateTitle) ? _alternateTitle : _title;
|
||||
image = ([self state] == CPOffState && [self alternateImage]) ? [self alternateImage] : [self image];
|
||||
}
|
||||
else
|
||||
{
|
||||
title = [self alternateTitle];
|
||||
image = [self alternateImage];
|
||||
}
|
||||
}
|
||||
else if (_showsStateBy & CPContentsCellMask)
|
||||
{
|
||||
title = ([self state] != CPOffState && _alternateTitle) ? _alternateTitle : _title;
|
||||
image = ([self state] != CPOffState && [self alternateImage]) ? [self alternateImage] : [self image];
|
||||
}
|
||||
else
|
||||
{
|
||||
title = _title;
|
||||
image = [self image];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_showsStateBy & CPContentsCellMask)
|
||||
{
|
||||
title = ([self state] != CPOffState && _alternateTitle) ? _alternateTitle : _title;
|
||||
image = ([self state] != CPOffState && [self alternateImage]) ? [self alternateImage] : [self image];
|
||||
}
|
||||
else
|
||||
{
|
||||
title = _title;
|
||||
image = [self image];
|
||||
}
|
||||
}
|
||||
[bezelView setBackgroundColor:[self valueForThemeAttribute:@"bezel-color" inState:[self _backgroundVisualState]]];
|
||||
[contentView setText:[self _currentTitle]];
|
||||
[contentView setImage:image];
|
||||
|
||||
[contentView setText:title];
|
||||
[contentView setImage:image];
|
||||
[contentView setImageOffset:[self currentValueForThemeAttribute:@"image-offset"]];
|
||||
[contentView setImageOffset:[self valueForThemeAttribute:@"image-offset" inState:contentVisualState]];
|
||||
|
||||
[contentView setFont:[self currentValueForThemeAttribute:@"font"]];
|
||||
[contentView setTextColor:[self currentValueForThemeAttribute:@"text-color"]];
|
||||
[contentView setAlignment:[self currentValueForThemeAttribute:@"alignment"]];
|
||||
[contentView setVerticalAlignment:[self currentValueForThemeAttribute:@"vertical-alignment"]];
|
||||
[contentView setLineBreakMode:[self currentValueForThemeAttribute:@"line-break-mode"]];
|
||||
[contentView _setUsesSingleLineMode:YES];
|
||||
[contentView setTextShadowColor:[self currentValueForThemeAttribute:@"text-shadow-color"]];
|
||||
[contentView setTextShadowOffset:[self currentValueForThemeAttribute:@"text-shadow-offset"]];
|
||||
[contentView setImagePosition:[self currentValueForThemeAttribute:@"image-position"]];
|
||||
[contentView setImageScaling:[self currentValueForThemeAttribute:@"image-scaling"]];
|
||||
[contentView setDimsImage:[self hasThemeState:CPThemeStateDisabled] && _imageDimsWhenDisabled];
|
||||
}
|
||||
[contentView setFont:[self font]]; //[self currentValueForThemeAttribute:@"font"]];
|
||||
[contentView setTextColor:[self valueForThemeAttribute:@"text-color" inState:contentVisualState]];
|
||||
[contentView setAlignment:[self valueForThemeAttribute:@"alignment" inState:contentVisualState]];
|
||||
[contentView setVerticalAlignment:[self valueForThemeAttribute:@"vertical-alignment" inState:contentVisualState]];
|
||||
[contentView setLineBreakMode:[self valueForThemeAttribute:@"line-break-mode" inState:contentVisualState]];
|
||||
[contentView _setUsesSingleLineMode:YES];
|
||||
[contentView setTextShadowColor:[self valueForThemeAttribute:@"text-shadow-color" inState:contentVisualState]];
|
||||
[contentView setTextShadowOffset:[self valueForThemeAttribute:@"text-shadow-offset" inState:contentVisualState]];
|
||||
[contentView setImagePosition:[self valueForThemeAttribute:@"image-position"]];
|
||||
[contentView setImageScaling:[self valueForThemeAttribute:@"image-scaling"]];
|
||||
|
||||
// We don't automatically dim material icon images as the color is driven by the theme
|
||||
[contentView setDimsImage:[self hasThemeState:CPThemeStateDisabled] && _imageDimsWhenDisabled && ![image isMaterialIconImage]];
|
||||
}
|
||||
|
||||
- (void)setBordered:(BOOL)shouldBeBordered
|
||||
{
|
||||
if (shouldBeBordered)
|
||||
{
|
||||
[self setThemeState:CPThemeStateBordered];
|
||||
|
||||
if (_bezelState)
|
||||
_bezelState = _bezelState.and(CPThemeStateBordered);
|
||||
else
|
||||
_bezelState = CPThemeStateBordered;
|
||||
}
|
||||
else
|
||||
{
|
||||
[self unsetThemeState:CPThemeStateBordered];
|
||||
|
||||
if (_bezelState)
|
||||
_bezelState = _bezelState.without(CPThemeStateBordered);
|
||||
else
|
||||
_bezelState = CPThemeStateNormal;
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)isBordered
|
||||
@@ -874,17 +941,19 @@ CPButtonImageOffset = 3.0;
|
||||
|
||||
[self setState:[self nextState]];
|
||||
|
||||
var shouldHighlight = NO;
|
||||
|
||||
if (_highlightsBy & (CPPushInCellMask | CPChangeGrayCellMask))
|
||||
{
|
||||
if (_showsStateBy & (CPChangeGrayCellMask | CPChangeBackgroundCellMask))
|
||||
shouldHighlight = [self state] == CPOffState;
|
||||
else
|
||||
shouldHighlight = YES;
|
||||
}
|
||||
|
||||
[self highlight:shouldHighlight];
|
||||
// FIXME: Here
|
||||
// var shouldHighlight = NO;
|
||||
//
|
||||
// if (_highlightsBy & (CPPushInCellMask | CPChangeGrayCellMask))
|
||||
// {
|
||||
// if (_showsStateBy & (CPChangeGrayCellMask | CPChangeBackgroundCellMask))
|
||||
// shouldHighlight = [self state] == CPOffState;
|
||||
// else
|
||||
// shouldHighlight = YES;
|
||||
// }
|
||||
//
|
||||
// [self highlight:shouldHighlight];
|
||||
[self highlight:YES];
|
||||
|
||||
try
|
||||
{
|
||||
@@ -896,7 +965,8 @@ CPButtonImageOffset = 3.0;
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (shouldHighlight)
|
||||
// FIXME: Here
|
||||
// if (shouldHighlight)
|
||||
[CPTimer scheduledTimerWithTimeInterval:0.1 target:self selector:@selector(unhighlightButtonTimerDidFinish:) userInfo:nil repeats:NO];
|
||||
}
|
||||
}
|
||||
@@ -920,6 +990,18 @@ CPButtonImageOffset = 3.0;
|
||||
[self setThemeState:newState];
|
||||
|
||||
_bezelStyle = aBezelStyle;
|
||||
|
||||
if (_bezelState && newState)
|
||||
_bezelState = _bezelState.and(newState);
|
||||
else
|
||||
_bezelState = newState || CPThemeStateNormal;
|
||||
|
||||
// For disclosure triangle and rounded, we have to move away from
|
||||
// what Xcode tells us as we implement visual behavior with images (so content)
|
||||
// and not background
|
||||
|
||||
if ((_bezelStyle === CPDisclosureBezelStyle) || (_bezelStyle === CPRoundedDisclosureBezelStyle))
|
||||
[self setShowsStateBy:CPContentsCellMask];
|
||||
}
|
||||
|
||||
- (unsigned)bezelStyle
|
||||
@@ -930,7 +1012,7 @@ CPButtonImageOffset = 3.0;
|
||||
@end
|
||||
|
||||
|
||||
var CPButtonImageKey = @"CPButtonImageKey",
|
||||
var CPButtonImageKey = @"CPButtonImageKey", // FIXME: pas utilisé ??????
|
||||
CPButtonAlternateImageKey = @"CPButtonAlternateImageKey",
|
||||
CPButtonTitleKey = @"CPButtonTitleKey",
|
||||
CPButtonAlternateTitleKey = @"CPButtonAlternateTitleKey",
|
||||
@@ -943,7 +1025,8 @@ var CPButtonImageKey = @"CPButtonImageKey",
|
||||
CPButtonPeriodicDelayKey = @"CPButtonPeriodicDelayKey",
|
||||
CPButtonPeriodicIntervalKey = @"CPButtonPeriodicIntervalKey",
|
||||
CPButtonHighlightsByKey = @"CPButtonHighlightsByKey",
|
||||
CPButtonShowsStateByKey = @"CPButtonShowsStateByKey";
|
||||
CPButtonShowsStateByKey = @"CPButtonShowsStateByKey",
|
||||
CPButtonBezelStyleKey = @"CPButtonBezelStyleKey";
|
||||
|
||||
@implementation CPButton (CPCoding)
|
||||
|
||||
@@ -992,6 +1075,12 @@ var CPButtonImageKey = @"CPButtonImageKey",
|
||||
|
||||
_keyEquivalentModifierMask = [aCoder decodeIntForKey:CPButtonKeyEquivalentMaskKey];
|
||||
|
||||
if ([aCoder containsValueForKey:CPButtonIsBorderedKey])
|
||||
[self setBordered:[aCoder decodeBoolForKey:CPButtonIsBorderedKey]];
|
||||
|
||||
if ([aCoder containsValueForKey:CPButtonBezelStyleKey])
|
||||
[self setBezelStyle:[aCoder decodeIntForKey:CPButtonBezelStyleKey]];
|
||||
|
||||
[self setNeedsLayout];
|
||||
[self setNeedsDisplay:YES];
|
||||
}
|
||||
@@ -1026,6 +1115,9 @@ var CPButtonImageKey = @"CPButtonImageKey",
|
||||
|
||||
[aCoder encodeObject:_periodicDelay forKey:CPButtonPeriodicDelayKey];
|
||||
[aCoder encodeObject:_periodicInterval forKey:CPButtonPeriodicIntervalKey];
|
||||
|
||||
[aCoder encodeBool:[self isBordered] forKey:CPButtonIsBorderedKey];
|
||||
[aCoder encodeInt: [self bezelStyle] forKey:CPButtonBezelStyleKey];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
+30
-17
@@ -94,23 +94,6 @@ CPCheckBoxImageOffset = 4.0;
|
||||
[self takeStateFromKeyPath:aKeyPath ofObjects:objects];
|
||||
}
|
||||
|
||||
- (CPImage)image
|
||||
{
|
||||
return [self currentValueForThemeAttribute:@"image"];
|
||||
}
|
||||
|
||||
- (CPImage)alternateImage
|
||||
{
|
||||
return [self currentValueForThemeAttribute:@"image"];
|
||||
}
|
||||
|
||||
- (BOOL)startTrackingAt:(CGPoint)aPoint
|
||||
{
|
||||
var startedTracking = [super startTrackingAt:aPoint];
|
||||
[self highlight:YES];
|
||||
return startedTracking;
|
||||
}
|
||||
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Override methods from CPButton
|
||||
@@ -135,6 +118,36 @@ CPCheckBoxImageOffset = 4.0;
|
||||
return size;
|
||||
}
|
||||
|
||||
- (CPThemeState)_contentVisualState
|
||||
{
|
||||
// Note : Behavior differs from CPButton as title doesn't follow the highlightsBy content flag
|
||||
|
||||
var visualState = [self themeState] || CPThemeStateNormal, // Needed during theme compilation
|
||||
currentState = [self state];
|
||||
|
||||
if ((currentState !== CPOffState) && (_showsStateBy & CPContentsCellMask))
|
||||
visualState = visualState.and((currentState === CPOnState) ? CPThemeStateSelected : CPButtonStateMixed);
|
||||
|
||||
return visualState;
|
||||
}
|
||||
|
||||
- (CPThemeState)_imageVisualState
|
||||
{
|
||||
// Note : Behavior differs from CPButton as we don't force "not selected" theme state
|
||||
// when button is highglighted and selected
|
||||
|
||||
var visualState = [self themeState] || CPThemeStateNormal, // Needed during theme compilation
|
||||
currentState = [self state];
|
||||
|
||||
if (_isHighlighted && (_highlightsBy & CPContentsCellMask))
|
||||
visualState = visualState.and(CPThemeStateHighlighted);
|
||||
|
||||
if ((currentState !== CPOffState) && (_showsStateBy & CPContentsCellMask))
|
||||
visualState = visualState.and((currentState === CPOnState) ? CPThemeStateSelected : CPButtonStateMixed);
|
||||
|
||||
return visualState;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation _CPCheckBoxValueBinder : CPBinder
|
||||
|
||||
+10
-3
@@ -561,7 +561,11 @@ var cachedBlackColor,
|
||||
|
||||
- (void)_initCSSStringFromComponents
|
||||
{
|
||||
var hasAlpha = CPFeatureIsCompatible(CPCSSRGBAFeature) && _components[3] != 1.0;
|
||||
// Fix to avoid a problem when compiling a theme (missing the alpha component as theme compiler doesn't have CSS rgba capability)
|
||||
var hasAlpha = YES;
|
||||
#if PLATFORM(DOM)
|
||||
hasAlpha = CPFeatureIsCompatible(CPCSSRGBAFeature) && _components[3] != 1.0;
|
||||
#endif
|
||||
|
||||
_cssString = (hasAlpha ? "rgba(" : "rgb(") +
|
||||
parseInt(_components[0] * 255.0) + ", " +
|
||||
@@ -800,6 +804,9 @@ url("data:image/png;base64,BASE64ENCODEDDATA") // if there is a pattern image
|
||||
var description = [super description],
|
||||
patternImage = [self patternImage];
|
||||
|
||||
if ([self isCSSBased])
|
||||
return description + "\n" + [self cssDictionary]+ "\nBefore:\n" + [self cssBeforeDictionary] + "\nAfter:\n" + [self cssAfterDictionary];
|
||||
|
||||
if (!patternImage)
|
||||
return description + " " + [self cssString];
|
||||
|
||||
@@ -933,7 +940,7 @@ url("data:image/png;base64,BASE64ENCODEDDATA") // if there is a pattern image
|
||||
// You can use -(BOOL)isCSSBased to determine how to cope with it in your code.
|
||||
// -(BOOL)hasCSSDictionary, -(BOOL)hasCSSBeforeDictionary and -(BOOL)hasCSSAfterDictionary are convience methods you can use.
|
||||
//
|
||||
// Remark : -(void)restorePreviousCSSState and -(DOMElement)applyCSSColorForView are meant to be used by low level UI widgets (like CPView) to implement
|
||||
// Remark : +(void)restorePreviousCSSState and -(DOMElement)applyCSSColorForView are meant to be used by low level UI widgets (like CPView) to implement
|
||||
// CSS theme support.
|
||||
|
||||
@implementation CPColor (CSSTheming)
|
||||
@@ -992,7 +999,7 @@ url("data:image/png;base64,BASE64ENCODEDDATA") // if there is a pattern image
|
||||
return ([_cssAfterDictionary count] > 0);
|
||||
}
|
||||
|
||||
- (void)restorePreviousCSSState:(CPArrayRef)aPreviousStateRef forDOMElement:(DOMElement)aDOMElement
|
||||
+ (void)restorePreviousCSSState:(CPArrayRef)aPreviousStateRef forDOMElement:(DOMElement)aDOMElement
|
||||
{
|
||||
#if PLATFORM(DOM)
|
||||
var aPreviousState = @deref(aPreviousStateRef);
|
||||
|
||||
+15
-4
@@ -23,12 +23,13 @@
|
||||
@import <Foundation/CPFormatter.j>
|
||||
@import <Foundation/CPTimer.j>
|
||||
|
||||
@import "CPFont.j"
|
||||
@import "CPShadow.j"
|
||||
@import "CPText.j"
|
||||
@import "CPKeyValueBinding.j"
|
||||
@import "CPTrackingArea.j"
|
||||
|
||||
@class CPFont
|
||||
|
||||
@global CPApp
|
||||
|
||||
@protocol CPControlTextEditingDelegate <CPObject>
|
||||
@@ -48,6 +49,9 @@ CPRegularControlSize = 0;
|
||||
CPSmallControlSize = 1;
|
||||
CPMiniControlSize = 2;
|
||||
|
||||
// To get the theme state corresponding to a control size, use CPControlSizeThemeStates[controlSize]
|
||||
CPControlSizeThemeStates = @[CPThemeStateControlSizeRegular, CPThemeStateControlSizeSmall, CPThemeStateControlSizeMini];
|
||||
|
||||
@typedef CPLineBreakMode
|
||||
CPLineBreakByWordWrapping = 0;
|
||||
CPLineBreakByCharWrapping = 1;
|
||||
@@ -131,7 +135,7 @@ var CPControlBlackColor = [CPColor blackColor];
|
||||
@"vertical-alignment": CPTopVerticalTextAlignment,
|
||||
@"line-break-mode": CPLineBreakByClipping,
|
||||
@"text-color": [CPColor blackColor],
|
||||
@"font": [CPFont systemFontOfSize:CPFontCurrentSystemSize],
|
||||
@"font": [CPNull null],
|
||||
@"text-shadow-color": [CPNull null],
|
||||
@"text-shadow-offset": CGSizeMakeZero(),
|
||||
@"image-position": CPImageLeft,
|
||||
@@ -195,7 +199,8 @@ var CPControlBlackColor = [CPColor blackColor];
|
||||
{
|
||||
_sendActionOn = CPLeftMouseUpMask;
|
||||
_trackingMouseDownFlags = 0;
|
||||
|
||||
|
||||
[self setControlSize:CPThemeStateControlSizeRegular];
|
||||
[self updateTrackingAreas];
|
||||
}
|
||||
|
||||
@@ -852,6 +857,11 @@ var CPControlBlackColor = [CPColor blackColor];
|
||||
[self setValue:aColor forThemeAttribute:@"text-color" inState:[self themeState]];
|
||||
}
|
||||
|
||||
- (void)setTextColor:(CPColor)aColor inThemeStates:(CPArray)themeStates
|
||||
{
|
||||
[self setValue:aColor forThemeAttribute:@"text-color" inStates:themeStates];
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the text color of the receiver.
|
||||
*/
|
||||
@@ -907,7 +917,7 @@ var CPControlBlackColor = [CPColor blackColor];
|
||||
*/
|
||||
- (CPFont)font
|
||||
{
|
||||
return [self valueForThemeAttribute:@"font"];
|
||||
return [self currentValueForThemeAttribute:@"font"] || [CPFont systemFontForControlSize:_controlSize];
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -1117,6 +1127,7 @@ var CPControlActionKey = @"CPControlActionKey",
|
||||
[self setControlSize:[aCoder decodeIntForKey:CPControlControlSizeKey]];
|
||||
|
||||
[self setBaseWritingDirection:[aCoder decodeIntForKey:CPControlBaseWrittingDirectionKey]];
|
||||
[self updateTrackingAreas];
|
||||
}
|
||||
|
||||
return self;
|
||||
|
||||
+115
-11
@@ -25,6 +25,8 @@
|
||||
|
||||
@import "CPView.j"
|
||||
@import "CPFontDescriptor.j"
|
||||
@import "_CPObject+Theme.j"
|
||||
@import "CPControl.j"
|
||||
|
||||
CPFontDefaultSystemFontFace = @"Arial, sans-serif";
|
||||
CPFontDefaultSystemFontSize = 12;
|
||||
@@ -38,12 +40,15 @@ CPFontCurrentSystemSize = -1;
|
||||
// For internal use only by this class and subclasses
|
||||
_CPFontSystemFacePlaceholder = "_CPFontSystemFacePlaceholder";
|
||||
|
||||
var _CPFontCache = {},
|
||||
_CPSystemFontCache = {},
|
||||
_CPFontSystemFontFace = CPFontDefaultSystemFontFace,
|
||||
_CPFontSystemFontSize = 12,
|
||||
_CPFontFallbackFaces = CPFontDefaultSystemFontFace.split(", "),
|
||||
_CPFontStripRegExp = new RegExp("(^\\s*[\"']?|[\"']?\\s*$)", "g");
|
||||
var _CPFontCache = {},
|
||||
_CPSystemFontCache = {},
|
||||
_CPFontSystemFontFace = CPFontDefaultSystemFontFace,
|
||||
_CPFontSystemFontSize = CPFontDefaultSystemFontSize,
|
||||
_CPFontSystemFontSizeSmall = CPFontDefaultSystemFontSize - 1,
|
||||
_CPFontSystemFontSizeMini = CPFontDefaultSystemFontSize - 2,
|
||||
_CPFontFallbackFaces = CPFontDefaultSystemFontFace.split(", "),
|
||||
_CPFontStripRegExp = new RegExp("(^\\s*[\"']?|[\"']?\\s*$)", "g"),
|
||||
_CPFontSystemFontFaceSpecified = NO;
|
||||
|
||||
|
||||
#define _CPRealFontSize(aSize) (aSize <= 0 ? _CPFontSystemFontSize : aSize)
|
||||
@@ -109,7 +114,7 @@ following:
|
||||
<string>Asap</string>
|
||||
@endcode
|
||||
*/
|
||||
@implementation CPFont : CPObject
|
||||
@implementation CPFont : CPObject <CPTheme>
|
||||
{
|
||||
CPString _name;
|
||||
float _size;
|
||||
@@ -123,6 +128,22 @@ following:
|
||||
CPString _cssString;
|
||||
}
|
||||
|
||||
+ (CPString)defaultThemeClass
|
||||
{
|
||||
return @"font";
|
||||
}
|
||||
|
||||
+ (CPDictionary)themeAttributes
|
||||
{
|
||||
return @{
|
||||
@"system-font-face": [CPNull null],
|
||||
@"system-font-style": [CPNull null],
|
||||
@"system-font-size-regular": [CPNull null],
|
||||
@"system-font-size-small": [CPNull null],
|
||||
@"system-font-size-mini": [CPNull null]
|
||||
};
|
||||
}
|
||||
|
||||
+ (void)initialize
|
||||
{
|
||||
if (self !== [CPFont class])
|
||||
@@ -134,7 +155,10 @@ following:
|
||||
systemFontFace = [[CPBundle bundleForClass:[CPView class]] objectForInfoDictionaryKey:@"CPSystemFontFace"];
|
||||
|
||||
if (systemFontFace)
|
||||
{
|
||||
_CPFontSystemFontFace = _CPFontNormalizedNames(systemFontFace);
|
||||
_CPFontSystemFontFaceSpecified = YES;
|
||||
}
|
||||
|
||||
var systemFontSize = [[CPBundle mainBundle] objectForInfoDictionaryKey:@"CPSystemFontSize"];
|
||||
|
||||
@@ -142,7 +166,62 @@ following:
|
||||
systemFontSize = [[CPBundle bundleForClass:[CPView class]] objectForInfoDictionaryKey:@"CPSystemFontSize"];
|
||||
|
||||
if (systemFontSize)
|
||||
{
|
||||
_CPFontSystemFontSize = systemFontSize;
|
||||
_CPFontSystemFontFaceSpecified = YES;
|
||||
}
|
||||
}
|
||||
|
||||
+ (void)initializeSystemFontFromTheme:(CPTheme)aTheme
|
||||
{
|
||||
// If something was specified via +initialize (from an Info.plist file), don't do anything
|
||||
if (_CPFontSystemFontFaceSpecified)
|
||||
return;
|
||||
|
||||
// Reset all system font caches
|
||||
_CPSystemFontCache = {};
|
||||
|
||||
// Now, try to get information from the theme
|
||||
var systemFontFace = [aTheme valueForAttributeWithName:@"system-font-face" forClass:[CPFont class]];
|
||||
|
||||
if (systemFontFace)
|
||||
{
|
||||
[self _invalidateSystemFontCache];
|
||||
_CPFontSystemFontFace = _CPFontNormalizedNames(systemFontFace);
|
||||
}
|
||||
|
||||
var systemFontSize = [aTheme valueForAttributeWithName:@"system-font-size-regular" forClass:[CPFont class]];
|
||||
|
||||
if (systemFontSize)
|
||||
{
|
||||
[self _invalidateSystemFontCache];
|
||||
_CPFontSystemFontSize = systemFontSize;
|
||||
}
|
||||
|
||||
systemFontSize = [aTheme valueForAttributeWithName:@"system-font-size-small" forClass:[CPFont class]];
|
||||
|
||||
if (systemFontSize)
|
||||
{
|
||||
[self _invalidateSystemFontCache];
|
||||
_CPFontSystemFontSizeSmall = systemFontSize;
|
||||
}
|
||||
|
||||
systemFontSize = [aTheme valueForAttributeWithName:@"system-font-size-mini" forClass:[CPFont class]];
|
||||
|
||||
if (systemFontSize)
|
||||
{
|
||||
[self _invalidateSystemFontCache];
|
||||
_CPFontSystemFontSizeMini = systemFontSize;
|
||||
}
|
||||
|
||||
// Is there something to add to the global syle definition ?
|
||||
var systemFontStyle = [aTheme valueForAttributeWithName:@"system-font-style" forClass:[CPFont class]];
|
||||
|
||||
if (systemFontStyle)
|
||||
{
|
||||
// Yes, so install it in the DOM Style element
|
||||
document.getElementsByTagName("STYLE")[0].innerHTML += "\n" + [aTheme setCSSResourcesPathInString:systemFontStyle];
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -177,14 +256,13 @@ following:
|
||||
|
||||
+ (CPFont)systemFontForControlSize:(CPControlSize)aSize
|
||||
{
|
||||
// TODO These sizes should be themable or made less arbitrary in some other way.
|
||||
switch (aSize)
|
||||
{
|
||||
case CPSmallControlSize:
|
||||
return [self systemFontOfSize:_CPFontSystemFontSize - 1];
|
||||
return [self systemFontOfSize:_CPFontSystemFontSizeSmall];
|
||||
|
||||
case CPMiniControlSize:
|
||||
return [self systemFontOfSize:_CPFontSystemFontSize - 2];
|
||||
return [self systemFontOfSize:_CPFontSystemFontSizeMini];
|
||||
|
||||
case CPRegularControlSize:
|
||||
default:
|
||||
@@ -315,6 +393,10 @@ following:
|
||||
_isItalic = isItalic;
|
||||
_isSystem = isSystem;
|
||||
|
||||
_theme = [CPTheme defaultTheme];
|
||||
_themeState = CPThemeStateNormal;
|
||||
[self _loadThemeAttributes];
|
||||
|
||||
if (isSystem)
|
||||
{
|
||||
_name = aName;
|
||||
@@ -432,6 +514,22 @@ following:
|
||||
_lineHeight = [metrics objectForKey:@"lineHeight"];
|
||||
}
|
||||
|
||||
- (CPControlSize)controlSizeCorrespondingToFontSize
|
||||
{
|
||||
switch (_size)
|
||||
{
|
||||
case _CPFontSystemFontSizeSmall:
|
||||
return CPSmallControlSize;
|
||||
|
||||
case _CPFontSystemFontSizeMini:
|
||||
return CPMiniControlSize;
|
||||
|
||||
default:
|
||||
// If we can't find a corresponding size, return regular control size
|
||||
return CPRegularControlSize;
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation CPFont(DescriptorAdditions)
|
||||
@@ -492,7 +590,11 @@ var CPFontNameKey = @"CPFontNameKey",
|
||||
isItalic = [aCoder decodeBoolForKey:CPFontIsItalicKey],
|
||||
isSystem = [aCoder decodeBoolForKey:CPFontIsSystemKey];
|
||||
|
||||
return [self _initWithName:fontName size:size bold:isBold italic:isItalic system:isSystem];
|
||||
self = [self _initWithName:fontName size:size bold:isBold italic:isItalic system:isSystem];
|
||||
|
||||
[self _decodeThemeObjectsWithCoder:aCoder];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -506,6 +608,8 @@ var CPFontNameKey = @"CPFontNameKey",
|
||||
[aCoder encodeBool:_isBold forKey:CPFontIsBoldKey];
|
||||
[aCoder encodeBool:_isItalic forKey:CPFontIsItalicKey];
|
||||
[aCoder encodeBool:_isSystem forKey:CPFontIsSystemKey];
|
||||
|
||||
[self _encodeThemeObjectsWithCoder:aCoder];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
+200
-4
@@ -26,10 +26,13 @@
|
||||
@import <Foundation/CPRunLoop.j>
|
||||
@import <Foundation/CPString.j>
|
||||
@import <Foundation/CPData.j>
|
||||
@import <Foundation/CPKeyedArchiver.j>
|
||||
@import <Foundation/CPKeyedUnarchiver.j>
|
||||
|
||||
@import "CGGeometry.j"
|
||||
@import "CPCompatibility.j"
|
||||
|
||||
@class CPColor
|
||||
|
||||
@protocol CPImageDelegate <CPObject>
|
||||
|
||||
@@ -85,9 +88,7 @@ function CPImageInBundle()
|
||||
|
||||
if (typeof(arguments[1]) === "number")
|
||||
{
|
||||
if (arguments[1] != nil)
|
||||
size = CGSizeMake(arguments[1], arguments[2]);
|
||||
|
||||
size = CGSizeMake(arguments[1], arguments[2]);
|
||||
bundle = arguments[3];
|
||||
}
|
||||
else if (typeof(arguments[1]) === "object")
|
||||
@@ -446,6 +447,11 @@ function CPAppKitImage(aFilename, aSize)
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (BOOL)isMaterialIconImage
|
||||
{
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (CPString)description
|
||||
{
|
||||
var filename = [self filename],
|
||||
@@ -564,6 +570,7 @@ function CPAppKitImage(aFilename, aSize)
|
||||
CPDictionary _cssDictionary @accessors(property=cssDictionary);
|
||||
CPDictionary _cssBeforeDictionary @accessors(property=cssBeforeDictionary);
|
||||
CPDictionary _cssAfterDictionary @accessors(property=cssAfterDictionary);
|
||||
CGSize _displaySize;
|
||||
}
|
||||
|
||||
+ (CPImage)imageWithCSSDictionary:(CPDictionary)aDictionary size:(CGSize)aSize
|
||||
@@ -582,6 +589,16 @@ function CPAppKitImage(aFilename, aSize)
|
||||
return [[CPImage alloc] initWithCSSDictionary:@{} beforeDictionary:nil afterDictionary:nil size:aSize];
|
||||
}
|
||||
|
||||
+ (CPImage)imageWithMaterialIconNamed:(CPString)iconName size:(CGSize)size
|
||||
{
|
||||
return [_CPMaterialIconImage imageWithIconNamed:iconName size:size];
|
||||
}
|
||||
|
||||
+ (CPImage)imageWithMaterialIconNamed:(CPString)iconName size:(CGSize)size color:(CPColor)color
|
||||
{
|
||||
return [_CPMaterialIconImage imageWithIconNamed:iconName size:size color:color];
|
||||
}
|
||||
|
||||
- (id)initWithCSSDictionary:(CPDictionary)aDictionary beforeDictionary:(CPDictionary)beforeDictionary afterDictionary:(CPDictionary)afterDictionary size:(CGSize)aSize
|
||||
{
|
||||
self = [super init];
|
||||
@@ -594,6 +611,7 @@ function CPAppKitImage(aFilename, aSize)
|
||||
_cssDictionary = aDictionary;
|
||||
_cssBeforeDictionary = beforeDictionary;
|
||||
_cssAfterDictionary = afterDictionary;
|
||||
_displaySize = CGSizeMakeCopy(aSize);
|
||||
}
|
||||
|
||||
return self;
|
||||
@@ -685,7 +703,7 @@ function CPAppKitImage(aFilename, aSize)
|
||||
aStyleNode.replaceChild(styleDescription, aStyleNode.firstChild);
|
||||
}
|
||||
|
||||
[aView setDOMClassName:@"CP"+[aView UID]];
|
||||
aDOMElement.className = @"CP"+[aView UID];
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -707,6 +725,11 @@ function CPAppKitImage(aFilename, aSize)
|
||||
#endif
|
||||
}
|
||||
|
||||
- (BOOL)_shouldBeResized
|
||||
{
|
||||
return NO;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
var CPImageCSSDictionaryKey = @"CPImageCSSDictionaryKey",
|
||||
@@ -750,6 +773,168 @@ var CPImageCSSDictionaryKey = @"CPImageCSSDictionaryKey",
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark -
|
||||
|
||||
@implementation _CPMaterialIconImage : CPImage
|
||||
{
|
||||
CPMutableDictionary _cachedColorVersions;
|
||||
CPColor _cachedInvertedColor;
|
||||
}
|
||||
|
||||
+ (_CPMaterialIconImage)imageWithIconNamed:(CPString)iconName size:(CGSize)size
|
||||
{
|
||||
return [[_CPMaterialIconImage alloc] initWithIconName:iconName size:size];
|
||||
}
|
||||
|
||||
+ (_CPMaterialIconImage)imageWithIconNamed:(CPString)iconName size:(CGSize)size color:(CPColor)color
|
||||
{
|
||||
return [[_CPMaterialIconImage alloc] initWithIconName:iconName size:size color:color];
|
||||
}
|
||||
|
||||
+ (_CPMaterialIconImage)imageWithIconNamed:(CPString)iconName size:(CGSize)size color:(CPColor)color additionalCSSDictionary:(CPDictionary)additionalCSSDictionary
|
||||
{
|
||||
return [[_CPMaterialIconImage alloc] initWithIconName:iconName size:size color:color additionalCSSDictionary:additionalCSSDictionary];
|
||||
}
|
||||
|
||||
- (CPDictionary)_baseMaterialIconCSSDictionaryForIconName:(CPString)iconName size:(CGSize)size
|
||||
{
|
||||
return @{
|
||||
@"width": size.width + @"px",
|
||||
@"height": size.height + @"px",
|
||||
@"top": @"0px",
|
||||
@"left": @"0px",
|
||||
@"content": @"'" + iconName + @"'",
|
||||
|
||||
@"position": @"absolute",
|
||||
@"z-index": @"300",
|
||||
|
||||
@"font-family": @"'Material Icons'",
|
||||
@"font-weight": @"normal",
|
||||
@"font-style": @"normal",
|
||||
@"font-size": MIN(size.width, size.height) + @"px",
|
||||
@"display": @"inline-block",
|
||||
@"line-height": @"1",
|
||||
@"text-transform": @"none",
|
||||
@"letter-spacing": @"normal",
|
||||
@"word-wrap": @"normal",
|
||||
@"white-space": @"nowrap",
|
||||
@"direction": @"ltr",
|
||||
@"-webkit-font-smoothing": @"antialiased",
|
||||
@"text-rendering": @"optimizeLegibility",
|
||||
@"-moz-osx-font-smoothing": @"grayscale",
|
||||
@"font-feature-settings": @"'liga'"
|
||||
};
|
||||
}
|
||||
|
||||
- (_CPMaterialIconImage)initWithIconName:(CPString)iconName size:(CGSize)size
|
||||
{
|
||||
return [super initWithCSSDictionary:@{}
|
||||
beforeDictionary:@{}
|
||||
afterDictionary:[self _baseMaterialIconCSSDictionaryForIconName:iconName size:size]
|
||||
size:size];
|
||||
}
|
||||
|
||||
- (_CPMaterialIconImage)initWithIconName:(CPString)iconName size:(CGSize)size color:(CPColor)color
|
||||
{
|
||||
var materialIconCSSDictionary = [self _baseMaterialIconCSSDictionaryForIconName:iconName size:size];
|
||||
|
||||
[materialIconCSSDictionary setObject:[color cssString] forKey:@"color"];
|
||||
|
||||
return [super initWithCSSDictionary:@{}
|
||||
beforeDictionary:@{}
|
||||
afterDictionary:materialIconCSSDictionary
|
||||
size:size];
|
||||
}
|
||||
|
||||
- (void)addRotationEffectWithAngle:(float)angle
|
||||
{
|
||||
[self addCSSDictionary:@{
|
||||
@"transform": @"rotate("+angle+"deg)",
|
||||
@"transition": @"transform 0.35s ease"
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)addCSSDictionary:(CPDictionary)additionalCSSDictionary
|
||||
{
|
||||
[_cssAfterDictionary addEntriesFromDictionary:additionalCSSDictionary];
|
||||
}
|
||||
|
||||
- (void)setSize:(CGSize)aSize
|
||||
{
|
||||
[self _setDisplaySize:aSize];
|
||||
[super setSize:aSize];
|
||||
}
|
||||
|
||||
- (void)_setDisplaySize:(CGSize)aSize
|
||||
{
|
||||
if (CGSizeEqualToSize(_displaySize, aSize))
|
||||
return;
|
||||
|
||||
_displaySize = CGSizeMakeCopy(aSize);
|
||||
|
||||
[_cssAfterDictionary setObject:(aSize.width + @"px") forKey:@"width"];
|
||||
[_cssAfterDictionary setObject:(aSize.height + @"px") forKey:@"height"];
|
||||
[_cssAfterDictionary setObject:(MIN(aSize.width, aSize.height) + @"px") forKey:@"font-size"];
|
||||
}
|
||||
|
||||
- (BOOL)_shouldBeResized
|
||||
{
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (BOOL)isMaterialIconImage
|
||||
{
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (_CPMaterialIconImage)invertedImage
|
||||
{
|
||||
if (!_cachedInvertedColor)
|
||||
{
|
||||
var sourceCSSColor = [_cssAfterDictionary objectForKey:@"color"] || @"rgba(0,0,0,1)",
|
||||
sourceColor = [CPColor colorWithCSSString:sourceCSSColor];
|
||||
|
||||
_cachedInvertedColor = [CPColor colorWithRed:(1-[sourceColor redComponent])
|
||||
green:(1-[sourceColor greenComponent])
|
||||
blue:(1-[sourceColor blueComponent])
|
||||
alpha:[sourceColor alphaComponent]];
|
||||
}
|
||||
|
||||
return [self imageVersionWithColor:_cachedInvertedColor];
|
||||
}
|
||||
|
||||
- (_CPMaterialIconImage)imageVersionWithColor:(CPColor)aColor
|
||||
{
|
||||
// We can't just set the color in the cssAfterDictionary as this would not be noticed as a new image,
|
||||
// so -setImage won't do anything, so no visual refresh won't occur.
|
||||
// The trick here is to keep in cache a clone of this image for each needed color.
|
||||
var colorCSSString = [aColor cssString];
|
||||
|
||||
if (!_cachedColorVersions)
|
||||
_cachedColorVersions = @{};
|
||||
|
||||
var cachedColorVersion = [_cachedColorVersions objectForKey:colorCSSString];
|
||||
|
||||
if (!cachedColorVersion)
|
||||
{
|
||||
cachedColorVersion = [self duplicate];
|
||||
[cachedColorVersion _setCSSColor:colorCSSString];
|
||||
|
||||
[_cachedColorVersions setObject:cachedColorVersion forKey:colorCSSString];
|
||||
}
|
||||
|
||||
return cachedColorVersion;
|
||||
}
|
||||
|
||||
- (void)_setCSSColor:(CPString)aCSSColor
|
||||
{
|
||||
[_cssAfterDictionary setObject:aCSSColor forKey:@"color"];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark -
|
||||
|
||||
@implementation CPThreePartImage : CPObject
|
||||
{
|
||||
CPArray _imageSlices;
|
||||
@@ -890,3 +1075,14 @@ var CPNinePartImageImageSlicesKey = @"CPNinePartImageImageSlicesKey";
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark -
|
||||
|
||||
@implementation CPImage (Duplication)
|
||||
|
||||
- (CPImage)duplicate
|
||||
{
|
||||
return [CPKeyedUnarchiver unarchiveObjectWithData:[CPKeyedArchiver archivedDataWithRootObject:self]];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
+14
-2
@@ -99,8 +99,8 @@ var CPImageViewEmptyPlaceholderImage = nil;
|
||||
{
|
||||
#if PLATFORM(DOM)
|
||||
var image = [self objectValue],
|
||||
isCSSBasedImage = [image isCSSBased],
|
||||
isIMGImageElement = _DOMImageElement && (_DOMImageElement.nodeName == "IMG");
|
||||
isCSSBasedImage = [image isCSSBased],
|
||||
isIMGImageElement = _DOMImageElement && (_DOMImageElement.nodeName == "IMG");
|
||||
|
||||
// First, check if we need to destroy a current DOM image element. This is the case if :
|
||||
// - we have one but not the right one (that is a DIV but needing an IMG, and vice versa)
|
||||
@@ -444,6 +444,18 @@ var CPImageViewEmptyPlaceholderImage = nil;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if PLATFORM(DOM)
|
||||
if ([image isCSSBased] && [image _shouldBeResized])
|
||||
{
|
||||
[image _setDisplaySize:CGSizeMake(ROUND(width), ROUND(height))];
|
||||
|
||||
_cssStyleNode = [image applyCSSImageForView:self
|
||||
onDOMElement:_DOMImageElement
|
||||
styleNode:_cssStyleNode
|
||||
previousState:@ref(_cssStylePreviousState)];
|
||||
}
|
||||
#endif
|
||||
|
||||
_imageRect = CGRectMake(x, y, width, height);
|
||||
|
||||
if (_hasShadow)
|
||||
|
||||
@@ -22,15 +22,10 @@
|
||||
|
||||
@import "CPControl.j"
|
||||
@import "CPWindow_Constants.j"
|
||||
@import "CPSlider.j"
|
||||
|
||||
@global CPApp
|
||||
|
||||
@typedef CPTickMarkPosition
|
||||
CPTickMarkBelow = 0;
|
||||
CPTickMarkAbove = 1;
|
||||
CPTickMarkLeft = CPTickMarkAbove;
|
||||
CPTickMarkRight = CPTickMarkBelow;
|
||||
|
||||
@typedef CPLevelIndicatorStyle
|
||||
CPRelevancyLevelIndicatorStyle = 0;
|
||||
CPContinuousCapacityLevelIndicatorStyle = 1;
|
||||
|
||||
@@ -1293,6 +1293,37 @@ var CPMenuTitleKey = @"CPMenuTitleKey",
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark -
|
||||
|
||||
@implementation CPMenu (CSSTheming)
|
||||
|
||||
+ (void)setNamedValue:(CPString)aName forKey:(CPString)aKey inAttributes:(CPDictionary)aDictionary forTheme:(CPTheme)aTheme
|
||||
{
|
||||
var value = [aTheme valueForAttributeWithName:aName forClass:_CPMenuView];
|
||||
|
||||
if (value)
|
||||
[aDictionary setObject:value forKey:aKey];
|
||||
else
|
||||
[aDictionary removeObjectForKey:aKey];
|
||||
}
|
||||
|
||||
+ (void)updateMenuBarAttributesWithTheme:(CPTheme)aTheme
|
||||
{
|
||||
var newAttributes = @{};
|
||||
|
||||
[CPMenu setNamedValue:@"menu-bar-text-color" forKey:@"CPMenuBarTextColor" inAttributes:newAttributes forTheme:aTheme];
|
||||
[CPMenu setNamedValue:@"menu-bar-title-color" forKey:@"CPMenuBarTitleColor" inAttributes:newAttributes forTheme:aTheme];
|
||||
[CPMenu setNamedValue:@"menu-bar-text-shadow-color" forKey:@"CPMenuBarTextShadowColor" inAttributes:newAttributes forTheme:aTheme];
|
||||
[CPMenu setNamedValue:@"menu-bar-title-shadow-color" forKey:@"CPMenuBarTitleShadowColor" inAttributes:newAttributes forTheme:aTheme];
|
||||
[CPMenu setNamedValue:@"menu-bar-highlight-color" forKey:@"CPMenuBarHighlightColor" inAttributes:newAttributes forTheme:aTheme];
|
||||
[CPMenu setNamedValue:@"menu-bar-highlight-text-color" forKey:@"CPMenuBarHighlightTextColor" inAttributes:newAttributes forTheme:aTheme];
|
||||
[CPMenu setNamedValue:@"menu-bar-highlight-text-shadow-color" forKey:@"CPMenuBarHighlightTextShadowColor" inAttributes:newAttributes forTheme:aTheme];
|
||||
|
||||
[CPMenu setMenuBarAttributes:newAttributes];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@import "_CPMenuBarWindow.j"
|
||||
@import "_CPMenuWindow.j"
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
|
||||
@class _CPMenuView
|
||||
@class CPMenu
|
||||
@class CPMenuItem
|
||||
|
||||
@global CPMenuDidAddItemNotification
|
||||
@global CPMenuDidChangeItemNotification
|
||||
@@ -129,10 +130,12 @@
|
||||
|
||||
- (void)setColor:(CPColor)aColor
|
||||
{
|
||||
var targetView = [[CPTheme defaultTheme] valueForAttributeWithName:@"css-based" forClass:CPView] ? [[self contentView] superview] : [self contentView];
|
||||
|
||||
if (!aColor)
|
||||
[[self contentView] setBackgroundColor:[[CPTheme defaultTheme] valueForAttributeWithName:@"menu-bar-window-background-color" forClass:_CPMenuView]];
|
||||
[targetView setBackgroundColor:[[CPTheme defaultTheme] valueForAttributeWithName:@"menu-bar-window-background-color" forClass:_CPMenuView]];
|
||||
else
|
||||
[[self contentView] setBackgroundColor:aColor];
|
||||
[targetView setBackgroundColor:aColor];
|
||||
}
|
||||
|
||||
- (void)setTextColor:(CPColor)aColor
|
||||
@@ -277,6 +280,16 @@
|
||||
[menuItemView setTextColor:_textColor];
|
||||
[menuItemView setHidden:[item isHidden]];
|
||||
|
||||
// If first menu item has tag -1 and if there is a special theme value menu-bar-window-first-item-font,
|
||||
// set the corresponding font. This is used to set bold on the first item of the menubar (à la Cocoa)
|
||||
if ((index == 0) && ([item tag] == -1))
|
||||
{
|
||||
var firstItemFont = [[CPTheme defaultTheme] valueForAttributeWithName:@"menu-bar-window-first-item-font" forClass:_CPMenuView];
|
||||
|
||||
if (firstItemFont)
|
||||
[item setFont:firstItemFont];
|
||||
}
|
||||
|
||||
[menuItemView synchronizeWithMenuItem];
|
||||
|
||||
[contentView addSubview:menuItemView];
|
||||
|
||||
@@ -28,6 +28,8 @@
|
||||
|
||||
@class CPWindow
|
||||
@class _CPMenuWindow
|
||||
@class _CPMenuView
|
||||
@class CPMenuItem
|
||||
|
||||
|
||||
@global CPApp
|
||||
@@ -223,7 +225,7 @@ var STICKY_TIME_INTERVAL = 0.4,
|
||||
_lastGlobalLocation = globalLocation;
|
||||
|
||||
// If the item isn't enabled its as if we clicked on nothing.
|
||||
if (![activeItem isEnabled] || [activeItem _isMenuBarButton])
|
||||
if ([activeItem _isMenuBarButton])
|
||||
{
|
||||
activeItemIndex = CPNotFound;
|
||||
activeItem = nil;
|
||||
@@ -352,7 +354,23 @@ var STICKY_TIME_INTERVAL = 0.4,
|
||||
if ([activeMenuContainer isMenuBar])
|
||||
newMenuOrigin = CGPointMake(CGRectGetMinX(activeItemRect), CGRectGetMaxY(activeItemRect));
|
||||
else
|
||||
newMenuOrigin = CGPointMake(CGRectGetMaxX(activeItemRect), CGRectGetMinY(activeItemRect));
|
||||
{
|
||||
// New theme attributes to have more precise submenus positioning
|
||||
|
||||
var defaultTheme = [CPTheme defaultTheme],
|
||||
themeDeltaX = [defaultTheme valueForAttributeWithName:@"menu-window-submenu-delta-x" forClass:_CPMenuView],
|
||||
themeDeltaY = [defaultTheme valueForAttributeWithName:@"menu-window-submenu-delta-y" forClass:_CPMenuView],
|
||||
themeFirstDeltaY = [defaultTheme valueForAttributeWithName:@"menu-window-submenu-first-level-delta-y" forClass:_CPMenuView],
|
||||
activeMenuIndex = [_menuContainerStack indexOfObject:activeMenuContainer],
|
||||
|
||||
deltaX = themeDeltaX ? themeDeltaX : 0,
|
||||
deltaY = themeDeltaY ? themeDeltaY : 0;
|
||||
|
||||
if (themeFirstDeltaY && (activeMenuIndex == 1) && [_menuContainerStack[0] isMenuBar])
|
||||
deltaY += themeFirstDeltaY;
|
||||
|
||||
newMenuOrigin = CGPointMake(CGRectGetMaxX(activeItemRect)+deltaX, CGRectGetMinY(activeItemRect)+deltaY);
|
||||
}
|
||||
|
||||
newMenuOrigin = [activeMenuContainer convertBaseToGlobal:newMenuOrigin];
|
||||
|
||||
@@ -580,6 +598,8 @@ var STICKY_TIME_INTERVAL = 0.4,
|
||||
[self selectNextItemBeginningWith:_keyBuffer inMenu:menu];
|
||||
_lastGlobalLocation = nil;
|
||||
}
|
||||
|
||||
[[[anEvent window] platformWindow] _propagateCurrentDOMEvent:NO];
|
||||
}
|
||||
|
||||
- (void)selectNextItemBeginningWith:(CPString)characters inMenu:(CPMenu)menu
|
||||
@@ -725,39 +745,32 @@ var STICKY_TIME_INTERVAL = 0.4,
|
||||
|
||||
- (void)moveDown:(CPMenu)menu
|
||||
{
|
||||
var index = menu._highlightedIndex + 1;
|
||||
var index = menu._highlightedIndex + 1,
|
||||
item;
|
||||
|
||||
// Search for the next enabled item
|
||||
while ((index < [menu numberOfItems]) && (item = [menu itemAtIndex:index]) && ([item isSeparatorItem] || [item isHidden] || ![item isEnabled]))
|
||||
index++;
|
||||
|
||||
if (index < [menu numberOfItems])
|
||||
{
|
||||
[menu _highlightItemAtIndex:index];
|
||||
|
||||
var item = [menu highlightedItem];
|
||||
|
||||
if ([item isSeparatorItem] || [item isHidden] || ![item isEnabled])
|
||||
[self moveDown:menu];
|
||||
}
|
||||
else if (menu == [CPApp mainMenu])
|
||||
[menu _highlightItemAtIndex:0];
|
||||
}
|
||||
|
||||
- (void)moveUp:(CPMenu)menu
|
||||
{
|
||||
var index = menu._highlightedIndex - 1;
|
||||
var index = menu._highlightedIndex - 1,
|
||||
item;
|
||||
|
||||
if (index < 0)
|
||||
{
|
||||
if (index != CPNotFound || menu == [CPApp mainMenu])
|
||||
[menu _highlightItemAtIndex:[menu numberOfItems] - 1];
|
||||
// Search for the previous enabled item
|
||||
while ((index >= 0) && (item = [menu itemAtIndex:index]) && ([item isSeparatorItem] || [item isHidden] || ![item isEnabled]))
|
||||
index--;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
[menu _highlightItemAtIndex:index];
|
||||
|
||||
var item = [menu highlightedItem];
|
||||
|
||||
if ([item isSeparatorItem] || [item isHidden] || ![item isEnabled])
|
||||
[self moveUp:menu];
|
||||
if (index >= 0)
|
||||
[menu _highlightItemAtIndex:index];
|
||||
else if (menu == [CPApp mainMenu])
|
||||
[menu _highlightItemAtIndex:[menu numberOfItems] - 1];
|
||||
}
|
||||
|
||||
- (void)insertNewline:(CPMenu)menu
|
||||
|
||||
@@ -465,6 +465,7 @@ _CPMenuWindowAttachedMenuBackgroundStyle = 2;
|
||||
@"menu-bar-window-background-color": [CPNull null],
|
||||
@"menu-bar-window-background-selected-color": [CPNull null],
|
||||
@"menu-bar-window-font": [CPNull null],
|
||||
@"menu-bar-window-first-item-font": [CPNull null],
|
||||
@"menu-bar-window-height": 30.0,
|
||||
@"menu-bar-window-margin": 10.0,
|
||||
@"menu-bar-window-left-margin": 10.0,
|
||||
@@ -482,6 +483,9 @@ _CPMenuWindowAttachedMenuBackgroundStyle = 2;
|
||||
@"menu-general-icon-new": [CPNull null],
|
||||
@"menu-general-icon-save": [CPNull null],
|
||||
@"menu-general-icon-open": [CPNull null],
|
||||
@"menu-window-submenu-delta-x": 0.0,
|
||||
@"menu-window-submenu-delta-y": 0.0,
|
||||
@"menu-window-submenu-first-level-delta-y": 0.0
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -31,7 +31,9 @@
|
||||
|
||||
+ (id)view
|
||||
{
|
||||
return [[self alloc] initWithFrame:CGRectMake(0.0, 0.0, 0.0, 10.0)];
|
||||
var themedHeight = [[CPTheme defaultTheme] valueForAttributeWithName:@"menu-item-separator-view-height" forClass:_CPMenuItemStandardView];
|
||||
|
||||
return [[self alloc] initWithFrame:CGRectMake(0.0, 0.0, 0.0, (themedHeight ? themedHeight : 10.0))];
|
||||
}
|
||||
|
||||
- (id)initWithFrame:(CGRect)aFrame
|
||||
@@ -47,12 +49,25 @@
|
||||
- (void)drawRect:(CGRect)aRect
|
||||
{
|
||||
var context = [[CPGraphicsContext currentContext] graphicsPort],
|
||||
bounds = [self bounds];
|
||||
bounds = [self bounds],
|
||||
height = CGRectGetMaxY(bounds),
|
||||
themedHeight = [[CPTheme defaultTheme] valueForAttributeWithName:@"menu-item-separator-height" forClass:_CPMenuItemStandardView],
|
||||
lineHeight = themedHeight ? themedHeight : 1.0;
|
||||
|
||||
CGContextBeginPath(context);
|
||||
|
||||
CGContextMoveToPoint(context, CGRectGetMinX(bounds), FLOOR(CGRectGetMidY(bounds)) - 0.5);
|
||||
CGContextAddLineToPoint(context, CGRectGetMaxX(bounds), FLOOR(CGRectGetMidY(bounds)) - 0.5);
|
||||
CGContextSetLineWidth(context, lineHeight);
|
||||
|
||||
if (!!((height - lineHeight) % 2))
|
||||
{
|
||||
CGContextMoveToPoint(context, CGRectGetMinX(bounds), FLOOR(CGRectGetMidY(bounds)) - 0.5);
|
||||
CGContextAddLineToPoint(context, CGRectGetMaxX(bounds), FLOOR(CGRectGetMidY(bounds)) - 0.5);
|
||||
}
|
||||
else
|
||||
{
|
||||
CGContextMoveToPoint(context, CGRectGetMinX(bounds), FLOOR(CGRectGetMidY(bounds)));
|
||||
CGContextAddLineToPoint(context, CGRectGetMaxX(bounds), FLOOR(CGRectGetMidY(bounds)));
|
||||
}
|
||||
|
||||
CGContextSetStrokeColor(context, [[CPTheme defaultTheme] valueForAttributeWithName:@"menu-item-separator-color" forClass:_CPMenuItemStandardView]);
|
||||
CGContextStrokePath(context);
|
||||
|
||||
@@ -41,6 +41,8 @@
|
||||
_CPImageAndTextView _imageAndTextView;
|
||||
_CPImageAndTextView _keyEquivalentView;
|
||||
CPView _submenuIndicatorView;
|
||||
|
||||
BOOL _hasSubmenuIndicatorImage;
|
||||
}
|
||||
|
||||
+ (CPString)defaultThemeClass
|
||||
@@ -63,12 +65,17 @@
|
||||
@"menu-item-default-mixed-state-image": [CPNull null],
|
||||
@"menu-item-default-mixed-state-highlighted-image": [CPNull null],
|
||||
@"menu-item-separator-color": [CPNull null],
|
||||
@"menu-item-separator-height": 1.0,
|
||||
@"menu-item-separator-view-height": 10.0,
|
||||
@"left-margin": 3.0,
|
||||
@"right-margin": 17.0,
|
||||
@"state-column-width": 14.0,
|
||||
@"indentation-width": 17.0,
|
||||
@"vertical-margin": 4.0,
|
||||
@"vertical-offset": 0.0,
|
||||
@"right-columns-margin": 30.0,
|
||||
@"submenu-indicator-image": [CPNull null],
|
||||
@"submenu-indicator-highlighted-image": [CPNull null]
|
||||
};
|
||||
}
|
||||
|
||||
@@ -91,6 +98,7 @@
|
||||
_stateView = [[CPImageView alloc] initWithFrame:CGRectMake(0.0, 0.0, 0.0, 0.0)];
|
||||
|
||||
[_stateView setImageScaling:CPImageScaleNone];
|
||||
[_stateView setImageAlignment:CPImageAlignCenter];
|
||||
|
||||
[self addSubview:_stateView];
|
||||
|
||||
@@ -109,9 +117,24 @@
|
||||
|
||||
[self addSubview:_keyEquivalentView];
|
||||
|
||||
_submenuIndicatorView = [[_CPMenuItemSubmenuIndicatorView alloc] initWithFrame:CGRectMake(0.0, 0.0, 8.0, 10.0)];
|
||||
// Do we have a submenu indicator image specified in the theme ?
|
||||
_hasSubmenuIndicatorImage = !![self valueForThemeAttribute:@"submenu-indicator-image"];
|
||||
|
||||
if (_hasSubmenuIndicatorImage)
|
||||
{
|
||||
// Yes, then use an imageView
|
||||
_submenuIndicatorView = [[CPImageView alloc] initWithFrame:CGRectMakeZero()];
|
||||
|
||||
[_submenuIndicatorView setImageAlignment:CPImageAlignCenter];
|
||||
}
|
||||
else
|
||||
{
|
||||
// No, then use self drawing _CPMenuItemSubmenuIndicatorView
|
||||
_submenuIndicatorView = [[_CPMenuItemSubmenuIndicatorView alloc] initWithFrame:CGRectMake(0.0, 0.0, 8.0, 10.0)];
|
||||
|
||||
[_submenuIndicatorView setColor:[self valueForThemeAttribute:@"submenu-indicator-color"]];
|
||||
}
|
||||
|
||||
[_submenuIndicatorView setColor:[self valueForThemeAttribute:@"submenu-indicator-color"]];
|
||||
[_submenuIndicatorView setAutoresizingMask:CPViewMinXMargin];
|
||||
|
||||
[self addSubview:_submenuIndicatorView];
|
||||
@@ -149,11 +172,24 @@
|
||||
_font = aFont;
|
||||
}
|
||||
|
||||
- (CPFont)font
|
||||
{
|
||||
// Menu item font is forced local font or _menuItem font or system font
|
||||
return _font || [_menuItem font] || [CPFont systemFontOfSize:CPFontCurrentSystemSize];
|
||||
}
|
||||
|
||||
// FIXME: update is called 2 times at each display. Find why and fix.
|
||||
- (void)update
|
||||
{
|
||||
var x = [self valueForThemeAttribute:@"left-margin"] + [_menuItem indentationLevel] * [self valueForThemeAttribute:@"indentation-width"],
|
||||
height = 0.0,
|
||||
hasStateColumn = [[_menuItem menu] showsStateColumn];
|
||||
hasStateColumn = [[_menuItem menu] showsStateColumn],
|
||||
myFont = [self font],
|
||||
|
||||
// When possible, use specific vertical margin/offset value based on font size (which could have been set by control size)
|
||||
correspondingControlSize = [myFont controlSizeCorrespondingToFontSize],
|
||||
verticalMargin = [self valueForThemeAttribute:@"vertical-margin" inState:CPControlSizeThemeStates[correspondingControlSize]],
|
||||
verticalOffset = [self valueForThemeAttribute:@"vertical-offset" inState:CPControlSizeThemeStates[correspondingControlSize]];
|
||||
|
||||
if (hasStateColumn)
|
||||
{
|
||||
@@ -163,15 +199,15 @@
|
||||
switch ([_menuItem state])
|
||||
{
|
||||
case CPOnState:
|
||||
[_stateView setImage:[self valueForThemeAttribute:@"menu-item-default-on-state-image"]];
|
||||
[_stateView setImage:[self valueForThemeAttribute:@"menu-item-default-on-state-image" inState:CPControlSizeThemeStates[correspondingControlSize]]];
|
||||
break;
|
||||
|
||||
case CPOffState:
|
||||
[_stateView setImage:[self valueForThemeAttribute:@"menu-item-default-off-state-image"]];
|
||||
[_stateView setImage:[self valueForThemeAttribute:@"menu-item-default-off-state-image" inState:CPControlSizeThemeStates[correspondingControlSize]]];
|
||||
break;
|
||||
|
||||
case CPMixedState:
|
||||
[_stateView setImage:[self valueForThemeAttribute:@"menu-item-default-mixed-state-image"]];
|
||||
[_stateView setImage:[self valueForThemeAttribute:@"menu-item-default-mixed-state-image" inState:CPControlSizeThemeStates[correspondingControlSize]]];
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -188,7 +224,7 @@
|
||||
else
|
||||
[_stateView setHidden:YES];
|
||||
|
||||
[_imageAndTextView setFont:[_menuItem font] || _font];
|
||||
[_imageAndTextView setFont:myFont];
|
||||
[_imageAndTextView setVerticalAlignment:CPCenterVerticalTextAlignment];
|
||||
[_imageAndTextView setImage:[_menuItem image]];
|
||||
[_imageAndTextView setText:[_menuItem title]];
|
||||
@@ -201,7 +237,7 @@
|
||||
|
||||
imageAndTextViewFrame.origin.x = x;
|
||||
x += CGRectGetWidth(imageAndTextViewFrame);
|
||||
height = MAX(height, CGRectGetHeight(imageAndTextViewFrame));
|
||||
height = MAX(height, CGRectGetHeight(imageAndTextViewFrame)); // FIXME: here, height = 0 -> MAX useless
|
||||
|
||||
var hasKeyEquivalent = !![_menuItem keyEquivalent],
|
||||
hasSubmenu = [_menuItem hasSubmenu];
|
||||
@@ -211,14 +247,14 @@
|
||||
|
||||
if (hasKeyEquivalent)
|
||||
{
|
||||
[_keyEquivalentView setFont:[_menuItem font] || _font];
|
||||
[_keyEquivalentView setFont:myFont];
|
||||
[_keyEquivalentView setVerticalAlignment:CPCenterVerticalTextAlignment];
|
||||
[_keyEquivalentView setImage:[_menuItem image]];
|
||||
[_keyEquivalentView setText:[_menuItem keyEquivalentStringRepresentation]];
|
||||
[_keyEquivalentView setTextColor:[self textColor]];
|
||||
[_keyEquivalentView setTextShadowColor:[self textShadowColor]];
|
||||
[_keyEquivalentView setTextShadowOffset:CGSizeMake(0, 1)];
|
||||
[_keyEquivalentView setFrameOrigin:CGPointMake(x, [self valueForThemeAttribute:@"vertical-margin"])];
|
||||
[_keyEquivalentView setFrameOrigin:CGPointMake(x, verticalMargin)];
|
||||
[_keyEquivalentView sizeToFit];
|
||||
|
||||
var keyEquivalentViewFrame = [_keyEquivalentView frame];
|
||||
@@ -235,6 +271,14 @@
|
||||
|
||||
if (hasSubmenu)
|
||||
{
|
||||
if (_hasSubmenuIndicatorImage)
|
||||
{
|
||||
var submenuIndicatorImage = [self valueForThemeAttribute:@"submenu-indicator-image" inState:CPControlSizeThemeStates[correspondingControlSize]];
|
||||
|
||||
[_submenuIndicatorView setImage:submenuIndicatorImage];
|
||||
[_submenuIndicatorView setFrameSize:[submenuIndicatorImage size]];
|
||||
}
|
||||
|
||||
[_submenuIndicatorView setHidden:NO];
|
||||
|
||||
var submenuViewFrame = [_submenuIndicatorView frame];
|
||||
@@ -247,9 +291,9 @@
|
||||
else
|
||||
[_submenuIndicatorView setHidden:YES];
|
||||
|
||||
height += 2.0 * [self valueForThemeAttribute:@"vertical-margin"];
|
||||
height += 2.0 * verticalMargin;
|
||||
|
||||
imageAndTextViewFrame.origin.y = FLOOR((height - CGRectGetHeight(imageAndTextViewFrame)) / 2.0);
|
||||
imageAndTextViewFrame.origin.y = FLOOR((height - CGRectGetHeight(imageAndTextViewFrame)) / 2.0) + verticalOffset;
|
||||
[_imageAndTextView setFrame:imageAndTextViewFrame];
|
||||
|
||||
if (hasStateColumn)
|
||||
@@ -257,7 +301,7 @@
|
||||
|
||||
if (hasKeyEquivalent)
|
||||
{
|
||||
keyEquivalentViewFrame.origin.y = FLOOR((height - CGRectGetHeight(keyEquivalentViewFrame)) / 2.0);
|
||||
keyEquivalentViewFrame.origin.y = FLOOR((height - CGRectGetHeight(keyEquivalentViewFrame)) / 2.0) + verticalOffset;
|
||||
[_keyEquivalentView setFrame:keyEquivalentViewFrame];
|
||||
}
|
||||
|
||||
@@ -282,6 +326,8 @@
|
||||
|
||||
_highlighted = shouldHighlight;
|
||||
|
||||
var correspondingControlSize = [[self font] controlSizeCorrespondingToFontSize];
|
||||
|
||||
[_imageAndTextView setTextColor:[self textColor]];
|
||||
[_keyEquivalentView setTextColor:[self textColor]];
|
||||
[_imageAndTextView setTextShadowColor:[self textShadowColor]];
|
||||
@@ -291,13 +337,21 @@
|
||||
{
|
||||
[self setBackgroundColor:[self valueForThemeAttribute:@"menu-item-selection-color"]];
|
||||
[_imageAndTextView setImage:[_menuItem alternateImage] || [_menuItem image]];
|
||||
[_submenuIndicatorView setColor:[self textColor]];
|
||||
|
||||
if (_hasSubmenuIndicatorImage)
|
||||
[_submenuIndicatorView setImage:[self valueForThemeAttribute:@"submenu-indicator-highlighted-image" inState:CPControlSizeThemeStates[correspondingControlSize]]];
|
||||
else
|
||||
[_submenuIndicatorView setColor:[self textColor]];
|
||||
}
|
||||
else
|
||||
{
|
||||
[self setBackgroundColor:nil];
|
||||
[_imageAndTextView setImage:[_menuItem image]];
|
||||
[_submenuIndicatorView setColor:[self valueForThemeAttribute:@"submenu-indicator-color"]];
|
||||
|
||||
if (_hasSubmenuIndicatorImage)
|
||||
[_submenuIndicatorView setImage:[self valueForThemeAttribute:@"submenu-indicator-image" inState:CPControlSizeThemeStates[correspondingControlSize]]];
|
||||
else
|
||||
[_submenuIndicatorView setColor:[self valueForThemeAttribute:@"submenu-indicator-color"]];
|
||||
}
|
||||
|
||||
if ([[_menuItem menu] showsStateColumn])
|
||||
@@ -307,15 +361,15 @@
|
||||
switch ([_menuItem state])
|
||||
{
|
||||
case CPOnState:
|
||||
[_stateView setImage:[self valueForThemeAttribute:@"menu-item-default-on-state-highlighted-image"]];
|
||||
[_stateView setImage:[self valueForThemeAttribute:@"menu-item-default-on-state-highlighted-image" inState:CPControlSizeThemeStates[correspondingControlSize]]];
|
||||
break;
|
||||
|
||||
case CPOffState:
|
||||
[_stateView setImage:[self valueForThemeAttribute:@"menu-item-default-off-state-highlighted-image"]];
|
||||
[_stateView setImage:[self valueForThemeAttribute:@"menu-item-default-off-state-highlighted-image" inState:CPControlSizeThemeStates[correspondingControlSize]]];
|
||||
break;
|
||||
|
||||
case CPMixedState:
|
||||
[_stateView setImage:[self valueForThemeAttribute:@"menu-item-default-mixed-state-highlighted-image"]];
|
||||
[_stateView setImage:[self valueForThemeAttribute:@"menu-item-default-mixed-state-highlighted-image" inState:CPControlSizeThemeStates[correspondingControlSize]]];
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -327,15 +381,15 @@
|
||||
switch ([_menuItem state])
|
||||
{
|
||||
case CPOnState:
|
||||
[_stateView setImage:[self valueForThemeAttribute:@"menu-item-default-on-state-image"]];
|
||||
[_stateView setImage:[self valueForThemeAttribute:@"menu-item-default-on-state-image" inState:CPControlSizeThemeStates[correspondingControlSize]]];
|
||||
break;
|
||||
|
||||
case CPOffState:
|
||||
[_stateView setImage:[self valueForThemeAttribute:@"menu-item-default-off-state-image"]];
|
||||
[_stateView setImage:[self valueForThemeAttribute:@"menu-item-default-off-state-image" inState:CPControlSizeThemeStates[correspondingControlSize]]];
|
||||
break;
|
||||
|
||||
case CPMixedState:
|
||||
[_stateView setImage:[self valueForThemeAttribute:@"menu-item-default-mixed-state-image"]];
|
||||
[_stateView setImage:[self valueForThemeAttribute:@"menu-item-default-mixed-state-image" inState:CPControlSizeThemeStates[correspondingControlSize]]];
|
||||
break;
|
||||
|
||||
default:
|
||||
|
||||
+100
-10
@@ -67,6 +67,18 @@ CPRadioImageOffset = 4.0;
|
||||
[[button1 radioGroup] selectedRadio] returns the currently selected
|
||||
option.
|
||||
|
||||
UPDATE 09/2020 : Implementation of modern Cocoa behavior :
|
||||
|
||||
As in Cocoa, radio buttons grouping is now automatic.
|
||||
|
||||
To be associated in a common group (and so being mutually exclusive),
|
||||
radio buttons must combine 2 criteria :
|
||||
|
||||
- same superview (enclosing view)
|
||||
- same action
|
||||
|
||||
TODO: This first implementation uses "as is" CPRadioGroup. This could
|
||||
be simplified (no more need for radio group action, for example)
|
||||
*/
|
||||
@implementation CPRadio : CPButton
|
||||
{
|
||||
@@ -160,6 +172,63 @@ CPRadioImageOffset = 4.0;
|
||||
[CPApp sendAction:[_radioGroup action] to:[_radioGroup target] from:_radioGroup];
|
||||
}
|
||||
|
||||
- (void)viewDidMoveToSuperview
|
||||
{
|
||||
[self _setRadioGroup];
|
||||
[super viewDidMoveToSuperview];
|
||||
}
|
||||
|
||||
- (void)setAction:(SEL)anAction
|
||||
{
|
||||
if (anAction === _action)
|
||||
return;
|
||||
|
||||
[super setAction:anAction];
|
||||
[self _setRadioGroup];
|
||||
}
|
||||
|
||||
#pragma mark Private methods
|
||||
|
||||
- (void)_setRadioGroup
|
||||
{
|
||||
// Implementation of modern Cocoa behavior : automatic radio group
|
||||
|
||||
// If no action is set or no superview, no grouping can be done.
|
||||
if (![self action] || ![self superview])
|
||||
{
|
||||
// If I'm in a group (size > 1), remove me.
|
||||
if ([[self radioGroup] size] > 1)
|
||||
[self setRadioGroup:[CPRadioGroup new]];
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Search in superview subviews for other radio buttons having the same action.
|
||||
// Take the one with the radio group having the greatest number of members.
|
||||
|
||||
var radioGroup;
|
||||
|
||||
for (var i = 0, superviewSubviews = [[self superview] subviews], count = [superviewSubviews count], aSubview, myAction = [self action], radioGroupSize = -1; (i < count); i++)
|
||||
{
|
||||
aSubview = superviewSubviews[i];
|
||||
|
||||
if ([aSubview isKindOfClass:CPRadio] && (aSubview !== self) && ([aSubview action] === myAction) && ([[aSubview radioGroup] size] > radioGroupSize))
|
||||
{
|
||||
radioGroup = [aSubview radioGroup];
|
||||
radioGroupSize = [radioGroup size];
|
||||
}
|
||||
}
|
||||
|
||||
if (radioGroup)
|
||||
[self setRadioGroup:radioGroup];
|
||||
else
|
||||
// No other radio buttons to group with found.
|
||||
// It may be because this radio button was in a radio group and its action was changed.
|
||||
// If this is the case, we must reisolate it in a new radio group.
|
||||
if ([_radioGroup size] > 1)
|
||||
[self setRadioGroup:[CPRadioGroup new]];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
var CPRadioRadioGroupKey = @"CPRadioRadioGroupKey";
|
||||
@@ -183,21 +252,37 @@ var CPRadioRadioGroupKey = @"CPRadioRadioGroupKey";
|
||||
[aCoder encodeObject:_radioGroup forKey:CPRadioRadioGroupKey];
|
||||
}
|
||||
|
||||
- (CPImage)image
|
||||
#pragma mark -
|
||||
#pragma mark Override methods from CPButton
|
||||
|
||||
- (CPThemeState)_contentVisualState
|
||||
{
|
||||
return [self currentValueForThemeAttribute:@"image"];
|
||||
// Note : Behavior differs from CPButton as title doesn't follow the highlightsBy content flag
|
||||
|
||||
var visualState = [self themeState] || CPThemeStateNormal, // Needed during theme compilation
|
||||
currentState = [self state];
|
||||
|
||||
if ((currentState !== CPOffState) && (_showsStateBy & CPContentsCellMask))
|
||||
visualState = visualState.and((currentState === CPOnState) ? CPThemeStateSelected : CPButtonStateMixed);
|
||||
|
||||
return visualState;
|
||||
}
|
||||
|
||||
- (CPImage)alternateImage
|
||||
- (CPThemeState)_imageVisualState
|
||||
{
|
||||
return [self currentValueForThemeAttribute:@"image"];
|
||||
}
|
||||
// Note : Behavior differs from CPButton as we don't force "not selected" theme state
|
||||
// when button is highglighted and selected
|
||||
|
||||
- (BOOL)startTrackingAt:(CGPoint)aPoint
|
||||
{
|
||||
var startedTracking = [super startTrackingAt:aPoint];
|
||||
[self highlight:YES];
|
||||
return startedTracking;
|
||||
var visualState = [self themeState] || CPThemeStateNormal, // Needed during theme compilation
|
||||
currentState = [self state];
|
||||
|
||||
if (_isHighlighted && (_highlightsBy & CPContentsCellMask))
|
||||
visualState = visualState.and(CPThemeStateHighlighted);
|
||||
|
||||
if ((currentState !== CPOffState) && (_showsStateBy & CPContentsCellMask))
|
||||
visualState = visualState.and((currentState === CPOnState) ? CPThemeStateSelected : CPButtonStateMixed);
|
||||
|
||||
return visualState;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -304,6 +389,11 @@ var CPRadioRadioGroupKey = @"CPRadioRadioGroupKey";
|
||||
return _radios;
|
||||
}
|
||||
|
||||
- (int)size
|
||||
{
|
||||
return [_radios count];
|
||||
}
|
||||
|
||||
- (void)setEnabled:(BOOL)enabled
|
||||
{
|
||||
[_radios makeObjectsPerformSelector:@selector(setEnabled:) withObject:enabled];
|
||||
|
||||
+802
-150
File diff suppressed because it is too large
Load Diff
+6
-6
@@ -188,12 +188,12 @@
|
||||
[_buttonUp setFrame:upFrame];
|
||||
[_buttonDown setFrame:downFrame];
|
||||
|
||||
[_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]];
|
||||
[_buttonUp setValue:[self valueForThemeAttribute:@"bezel-color-up-button" inStates:[controlSizeThemeState, CPThemeStateBordered]] forThemeAttribute:@"bezel-color" inStates:[CPThemeStateBordered, CPButtonStateBezelStyleRounded]];
|
||||
[_buttonUp setValue:[self valueForThemeAttribute:@"bezel-color-up-button" inStates:[controlSizeThemeState, CPThemeStateBordered, CPThemeStateDisabled]] forThemeAttribute:@"bezel-color" inStates:[CPThemeStateBordered, CPThemeStateDisabled, CPButtonStateBezelStyleRounded]];
|
||||
[_buttonUp setValue:[self valueForThemeAttribute:@"bezel-color-up-button" inStates:[controlSizeThemeState, CPThemeStateBordered, CPThemeStateHighlighted]] forThemeAttribute:@"bezel-color" inStates:[CPThemeStateBordered, CPThemeStateHighlighted, CPButtonStateBezelStyleRounded]];
|
||||
[_buttonDown setValue:[self valueForThemeAttribute:@"bezel-color-down-button" inStates:[controlSizeThemeState, CPThemeStateBordered]] forThemeAttribute:@"bezel-color" inStates:[CPThemeStateBordered, CPButtonStateBezelStyleRounded]];
|
||||
[_buttonDown setValue:[self valueForThemeAttribute:@"bezel-color-down-button" inStates:[controlSizeThemeState, CPThemeStateBordered, CPThemeStateDisabled]] forThemeAttribute:@"bezel-color" inStates:[CPThemeStateBordered, CPThemeStateDisabled, CPButtonStateBezelStyleRounded]];
|
||||
[_buttonDown setValue:[self valueForThemeAttribute:@"bezel-color-down-button" inStates:[controlSizeThemeState, CPThemeStateBordered, CPThemeStateHighlighted]] forThemeAttribute:@"bezel-color" inStates:[CPThemeStateBordered, CPThemeStateHighlighted, CPButtonStateBezelStyleRounded]];
|
||||
}
|
||||
|
||||
- (void)_sizeToFit
|
||||
|
||||
+90
-20
@@ -233,7 +233,8 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
|
||||
@"bezel-inset": CGInsetMakeZero(),
|
||||
@"content-inset": CGInsetMake(1.0, 0.0, 0.0, 0.0),
|
||||
@"bezel-color": [CPNull null],
|
||||
@"min-size": CGSizeMake(0, 29)
|
||||
@"min-size": CGSizeMake(0, 29),
|
||||
@"background-inset": CGInsetMakeZero()
|
||||
};
|
||||
}
|
||||
|
||||
@@ -368,8 +369,6 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
|
||||
[self setPlaceholderString:@""];
|
||||
|
||||
_sendActionOn = CPKeyUpMask | CPKeyDownMask;
|
||||
|
||||
[self setValue:CPNaturalTextAlignment forThemeAttribute:@"alignment"];
|
||||
}
|
||||
|
||||
return self;
|
||||
@@ -560,9 +559,9 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
|
||||
Sets the background color, which is shown for non-bezeled text fields with drawsBackground set to YES
|
||||
@param aColor The background color
|
||||
*/
|
||||
- (void)setTextFieldBackgroundColor:(CPColor)aColor
|
||||
- (void)setBackgroundColor:(CPColor)aColor
|
||||
{
|
||||
if (_textFieldBackgroundColor == aColor)
|
||||
if (_backgroundColor == aColor)
|
||||
return;
|
||||
|
||||
_textFieldBackgroundColor = aColor;
|
||||
@@ -574,7 +573,7 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
|
||||
/*!
|
||||
Returns the background color.
|
||||
*/
|
||||
- (CPColor)textFieldBackgroundColor
|
||||
- (CPColor)backgroundColor
|
||||
{
|
||||
return _textFieldBackgroundColor;
|
||||
}
|
||||
@@ -759,7 +758,7 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
|
||||
#if PLATFORM(DOM)
|
||||
|
||||
var element = [self _inputElement],
|
||||
font = [self currentValueForThemeAttribute:@"font"],
|
||||
font = [self font],
|
||||
lineHeight = [font defaultLineHeightForFont],
|
||||
contentRect = [self contentRectForBounds:[self bounds]],
|
||||
verticalAlign = [self currentValueForThemeAttribute:"vertical-alignment"],
|
||||
@@ -1462,7 +1461,7 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
|
||||
lineBreakMode = [self lineBreakMode],
|
||||
text = (_stringValue || @" "),
|
||||
textSize = CGSizeMakeCopy(frameSize),
|
||||
font = [self currentValueForThemeAttribute:@"font"];
|
||||
font = [self font];
|
||||
|
||||
textSize.width -= contentInset.left + contentInset.right;
|
||||
textSize.height -= contentInset.top + contentInset.bottom;
|
||||
@@ -1866,6 +1865,12 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
|
||||
if (aName === "bezel-view")
|
||||
return [self bezelRectForBounds:[self bounds]];
|
||||
|
||||
else if (aName === "background-view")
|
||||
{
|
||||
var backgroundInset = [self currentValueForThemeAttribute:@"background-inset"];
|
||||
|
||||
return CGRectInsetByInset([self bounds], backgroundInset);
|
||||
}
|
||||
else if (aName === "content-view")
|
||||
return [self contentRectForBounds:[self bounds]];
|
||||
|
||||
@@ -1882,6 +1887,14 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
|
||||
|
||||
return view;
|
||||
}
|
||||
else if (aName === "background-view")
|
||||
{
|
||||
var view = [[CPView alloc] initWithFrame:CGRectMakeZero()];
|
||||
|
||||
[view setHitTests:NO];
|
||||
|
||||
return view;
|
||||
}
|
||||
else
|
||||
{
|
||||
var view = [[_CPImageAndTextView alloc] initWithFrame:CGRectMakeZero()];
|
||||
@@ -1896,16 +1909,37 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
|
||||
|
||||
- (void)layoutSubviews
|
||||
{
|
||||
var bezelView = [self layoutEphemeralSubviewNamed:@"bezel-view"
|
||||
positioned:CPWindowBelow
|
||||
relativeToEphemeralSubviewNamed:@"content-view"];
|
||||
var bezelColor = [self currentValueForThemeAttribute:@"bezel-color"];
|
||||
|
||||
if ([bezelColor isCSSBased])
|
||||
{
|
||||
// CSS Styling
|
||||
// We don't need bezelView as we apply CSS styling directly on the text field view itself
|
||||
|
||||
if (bezelView)
|
||||
[bezelView setBackgroundColor:[self currentValueForThemeAttribute:@"bezel-color"]];
|
||||
// We need to call [super setBackgroundColor:] as we have redefined it here
|
||||
[super setBackgroundColor:bezelColor];
|
||||
|
||||
var contentView = [self layoutEphemeralSubviewNamed:@"content-view"
|
||||
positioned:CPWindowAbove
|
||||
relativeToEphemeralSubviewNamed:nil],
|
||||
backgroundView = [self layoutEphemeralSubviewNamed:@"background-view"
|
||||
positioned:CPWindowBelow
|
||||
relativeToEphemeralSubviewNamed:@"content-view"];
|
||||
|
||||
var contentView = [self layoutEphemeralSubviewNamed:@"content-view"
|
||||
positioned:CPWindowAbove
|
||||
relativeToEphemeralSubviewNamed:@"bezel-view"];
|
||||
[backgroundView setBackgroundColor:(_drawsBackground ? _textFieldBackgroundColor : [CPColor clearColor])];
|
||||
}
|
||||
else
|
||||
{
|
||||
var bezelView = [self layoutEphemeralSubviewNamed:@"bezel-view"
|
||||
positioned:CPWindowBelow
|
||||
relativeToEphemeralSubviewNamed:@"content-view"];
|
||||
|
||||
[bezelView setBackgroundColor:bezelColor];
|
||||
|
||||
var contentView = [self layoutEphemeralSubviewNamed:@"content-view"
|
||||
positioned:CPWindowAbove
|
||||
relativeToEphemeralSubviewNamed:@"bezel-view"];
|
||||
}
|
||||
|
||||
if (contentView)
|
||||
{
|
||||
@@ -1926,7 +1960,7 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
|
||||
[contentView setText:string];
|
||||
|
||||
[contentView setTextColor:[self currentValueForThemeAttribute:@"text-color"]];
|
||||
[contentView setFont:[self currentValueForThemeAttribute:@"font"]];
|
||||
[contentView setFont:[self font]];
|
||||
[contentView setAlignment:[self currentValueForThemeAttribute:@"alignment"]];
|
||||
[contentView setVerticalAlignment:[self currentValueForThemeAttribute:@"vertical-alignment"]];
|
||||
[contentView setLineBreakMode:[self currentValueForThemeAttribute:@"line-break-mode"]];
|
||||
@@ -1966,7 +2000,25 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
|
||||
// We don't want to change the text-color of the placeHolder of the textField
|
||||
var placeholderColor = [self valueForThemeAttribute:@"text-color" inState:CPTextFieldStatePlaceholder];
|
||||
|
||||
[super setTextColor:aColor];
|
||||
// If the text field is a cell based table data view, we need to fix the color for all possible states
|
||||
if ([self hasThemeState:CPThemeStateTableDataView])
|
||||
{
|
||||
[self setTextColor:aColor inThemeStates:[CPThemeStateTableDataView]];
|
||||
[self setTextColor:aColor inThemeStates:[CPThemeStateTableDataView, CPThemeStateSelectedDataView]];
|
||||
[self setTextColor:aColor inThemeStates:[CPThemeStateTableDataView, CPThemeStateSelectedDataView, CPThemeStateFirstResponder, CPThemeStateKeyWindow]];
|
||||
}
|
||||
else
|
||||
{
|
||||
if ([self hasThemeState:CPTextFieldStateRounded])
|
||||
{
|
||||
[self setTextColor:aColor inThemeStates:[CPTextFieldStateRounded]];
|
||||
[self setTextColor:aColor inThemeStates:[CPTextFieldStateRounded, CPThemeStateEditing]];
|
||||
}
|
||||
|
||||
[self setTextColor:aColor inThemeStates:[CPThemeStateNormal]];
|
||||
[self setTextColor:aColor inThemeStates:[CPThemeStateEditing]];
|
||||
}
|
||||
|
||||
[self setValue:placeholderColor forThemeAttribute:@"text-color" inState:CPTextFieldStatePlaceholder];
|
||||
}
|
||||
|
||||
@@ -2109,8 +2161,7 @@ var CPTextFieldIsEditableKey = "CPTextFieldIsEditableKey",
|
||||
[self setSelectable:[aCoder decodeBoolForKey:CPTextFieldIsSelectableKey]];
|
||||
|
||||
[self setDrawsBackground:[aCoder decodeBoolForKey:CPTextFieldDrawsBackgroundKey]];
|
||||
|
||||
[self setTextFieldBackgroundColor:[aCoder decodeObjectForKey:CPTextFieldBackgroundColorKey]];
|
||||
[self setBackgroundColor:[aCoder decodeObjectForKey:CPTextFieldBackgroundColorKey]];
|
||||
|
||||
[self setLineBreakMode:[aCoder decodeIntForKey:CPTextFieldLineBreakModeKey]];
|
||||
[self setAlignment:[aCoder decodeIntForKey:CPTextFieldAlignmentKey]];
|
||||
@@ -2120,6 +2171,7 @@ var CPTextFieldIsEditableKey = "CPTextFieldIsEditableKey",
|
||||
[self _setUsesSingleLineMode:[aCoder decodeBoolForKey:CPTextFieldUsesSingleLineMode]];
|
||||
[self _setWraps:[aCoder decodeBoolForKey:CPTextFieldWraps]];
|
||||
[self _setScrolls:[aCoder decodeBoolForKey:CPTextFieldScrolls]];
|
||||
[self updateTrackingAreas];
|
||||
}
|
||||
|
||||
return self;
|
||||
@@ -2319,4 +2371,22 @@ var CPTextFieldIsEditableKey = "CPTextFieldIsEditableKey",
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark -
|
||||
|
||||
@implementation CPTextField (Deprecated)
|
||||
|
||||
- (void)setTextFieldBackgroundColor:(CPColor)aColor
|
||||
{
|
||||
CPLog.error("[CPTextField setTextFieldBackgroundColor:] is deprecated, use [CPTextField setBackgroundColor:] instead.");
|
||||
|
||||
[self setBackgroundColor:aColor];
|
||||
}
|
||||
|
||||
- (CPColor)textFieldBackgroundColor
|
||||
{
|
||||
CPLog.info("[CPTextField textFieldBackgroundColor] is deprecated, use [CPTextField backgroundColor] instead.");
|
||||
|
||||
return [self backgroundColor];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -49,6 +49,8 @@ var CPThemesByName = { },
|
||||
+ (void)setDefaultTheme:(CPTheme)aTheme
|
||||
{
|
||||
CPThemeDefaultTheme = aTheme;
|
||||
|
||||
[CPFont initializeSystemFontFromTheme:aTheme];
|
||||
}
|
||||
|
||||
+ (CPTheme)defaultTheme
|
||||
|
||||
+1
-1
@@ -1995,7 +1995,7 @@ var CPViewHighDPIDrawingEnabled = YES;
|
||||
|
||||
#if PLATFORM(DOM)
|
||||
if (_backgroundType === BackgroundCSSStyling)
|
||||
[_backgroundColor restorePreviousCSSState:@ref(_cssStylePreviousState) forDOMElement:_DOMElement];
|
||||
[CPColor restorePreviousCSSState:@ref(_cssStylePreviousState) forDOMElement:_DOMElement];
|
||||
|
||||
var patternImage = [_backgroundColor patternImage],
|
||||
colorExists = _backgroundColor && ([_backgroundColor patternImage] || [_backgroundColor alphaComponent] > 0.0),
|
||||
|
||||
+69
-19
@@ -193,7 +193,7 @@ var CPWindowActionMessageKeys = [
|
||||
BOOL _isDocumentEdited;
|
||||
BOOL _isDocumentSaving;
|
||||
|
||||
CPImageView _shadowView;
|
||||
_CPShadowWindowView _shadowView;
|
||||
|
||||
CPView _windowView;
|
||||
CPView _contentView;
|
||||
@@ -369,6 +369,23 @@ CPTexturedBackgroundWindowMask
|
||||
[_windowView _setWindow:self];
|
||||
[_windowView setNextResponder:self];
|
||||
|
||||
// CSS Styling
|
||||
#if PLATFORM(DOM)
|
||||
var radius;
|
||||
|
||||
if (radius = [_windowView actualValueForThemeAttribute:@"border-top-left-radius"])
|
||||
_windowView._DOMElement.style.borderTopLeftRadius = radius;
|
||||
|
||||
if (radius = [_windowView actualValueForThemeAttribute:@"border-top-right-radius"])
|
||||
_windowView._DOMElement.style.borderTopRightRadius = radius;
|
||||
|
||||
if (radius = [_windowView actualValueForThemeAttribute:@"border-bottom-left-radius"])
|
||||
_windowView._DOMElement.style.borderBottomLeftRadius = radius;
|
||||
|
||||
if (radius = [_windowView actualValueForThemeAttribute:@"border-bottom-right-radius"])
|
||||
_windowView._DOMElement.style.borderBottomRightRadius = radius;
|
||||
#endif
|
||||
|
||||
// Size calculation needs _windowView
|
||||
_minSize = [self _calculateMinSizeForProposedSize:CGSizeMake(0.0, 0.0)];
|
||||
_maxSize = CGSizeMake(1000000.0, 1000000.0);
|
||||
@@ -1410,25 +1427,51 @@ CPTexturedBackgroundWindowMask
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (_hasShadow && !_shadowView)
|
||||
|
||||
if ([[self actualTheme] isCSSBased])
|
||||
{
|
||||
_shadowView = [[_CPShadowWindowView alloc] initWithFrame:CGRectMakeZero()];
|
||||
|
||||
[_shadowView setWindowView:_windowView];
|
||||
[_shadowView setAutoresizingMask:CPViewWidthSizable | CPViewHeightSizable];
|
||||
[_shadowView setNeedsLayout];
|
||||
|
||||
// When using CSS theming, we get rid of _shadowView and add CSS shadowing directly
|
||||
// on the _windowView
|
||||
|
||||
#if PLATFORM(DOM)
|
||||
CPDOMDisplayServerInsertBefore(_DOMElement, _shadowView._DOMElement, _windowView._DOMElement);
|
||||
// We must check that _windowView exists as _updateShadow can be called by
|
||||
// _setSharesChromeWithPlatformWindow whereas _windowView is not yet created
|
||||
if (_windowView && _windowView._DOMElement)
|
||||
{
|
||||
var currentBoxShadow = _windowView._DOMElement.style.boxShadow;
|
||||
|
||||
if (_hasShadow && (currentBoxShadow.length == 0))
|
||||
{
|
||||
_windowView._DOMElement.style.boxShadow = [_windowView actualValueForThemeAttribute:@"window-shadow-color"];
|
||||
}
|
||||
else if (!_hasShadow && (currentBoxShadow.length > 0))
|
||||
{
|
||||
_windowView._DOMElement.style.boxShadow = "";
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
else if (!_hasShadow && _shadowView)
|
||||
else
|
||||
{
|
||||
if (_hasShadow && !_shadowView)
|
||||
{
|
||||
_shadowView = [[_CPShadowWindowView alloc] initWithFrame:CGRectMakeZero()];
|
||||
|
||||
[_shadowView setWindowView:_windowView];
|
||||
[_shadowView setAutoresizingMask:CPViewWidthSizable | CPViewHeightSizable];
|
||||
[_shadowView setNeedsLayout];
|
||||
|
||||
#if PLATFORM(DOM)
|
||||
CPDOMDisplayServerRemoveChild(_DOMElement, _shadowView._DOMElement);
|
||||
CPDOMDisplayServerInsertBefore(_DOMElement, _shadowView._DOMElement, _windowView._DOMElement);
|
||||
#endif
|
||||
_shadowView = nil;
|
||||
}
|
||||
else if (!_hasShadow && _shadowView)
|
||||
{
|
||||
#if PLATFORM(DOM)
|
||||
CPDOMDisplayServerRemoveChild(_DOMElement, _shadowView._DOMElement);
|
||||
#endif
|
||||
_shadowView = nil;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2814,7 +2857,7 @@ CPTexturedBackgroundWindowMask
|
||||
contentRect = [_contentView frame],
|
||||
sheetFrame = CGRectMakeCopy([attachedSheet frame]);
|
||||
|
||||
sheetFrame.origin.y = CGRectGetMinY(_frame) + CGRectGetMinY(contentRect);
|
||||
sheetFrame.origin.y = [[attachedSheet _windowView] _sheetVerticalOffset]+ CGRectGetMinY(_frame) + CGRectGetMinY(contentRect);
|
||||
sheetFrame.origin.x = CGRectGetMinX(_frame) + FLOOR((CGRectGetWidth(_frame) - CGRectGetWidth(sheetFrame)) / 2.0);
|
||||
|
||||
[attachedSheet setFrame:sheetFrame display:YES animate:NO];
|
||||
@@ -3013,13 +3056,12 @@ CPTexturedBackgroundWindowMask
|
||||
}
|
||||
|
||||
// The sheet starts hidden just above the top of a clip rect
|
||||
// TODO : Make properly for the -1 in endY
|
||||
var sheetFrame = [sheet frame],
|
||||
sheetShadowFrame = sheet._hasShadow ? [sheet._shadowView frame] : sheetFrame,
|
||||
sheetShadowFrame = [sheet _shadowFrame],
|
||||
frame = [self frame],
|
||||
originX = frame.origin.x + FLOOR((frame.size.width - sheetFrame.size.width) / 2),
|
||||
startFrame = CGRectMake(originX, -sheetShadowFrame.size.height, sheetFrame.size.width, sheetFrame.size.height),
|
||||
endY = -1 + [_windowView bodyOffset] - [[self contentView] frame].origin.y,
|
||||
endY = [[sheet _windowView] _sheetVerticalOffset] + [_windowView bodyOffset] - [[self contentView] frame].origin.y,
|
||||
endFrame = CGRectMake(originX, endY, sheetFrame.size.width, sheetFrame.size.height);
|
||||
|
||||
if (_toolbar && [_windowView showsToolbar] && [self isFullPlatformWindow])
|
||||
@@ -3058,7 +3100,7 @@ CPTexturedBackgroundWindowMask
|
||||
{
|
||||
var sheet = _sheetContext["sheet"],
|
||||
sheetFrame = [sheet frame],
|
||||
fullHeight = sheet._hasShadow ? [sheet._shadowView frame].size.height : sheetFrame.size.height,
|
||||
fullHeight = [sheet _shadowFrame].size.height,
|
||||
endFrame = CGRectMakeCopy(sheetFrame),
|
||||
contentOrigin = [self convertBaseToGlobal:[[self contentView] frame].origin];
|
||||
|
||||
@@ -3090,7 +3132,7 @@ CPTexturedBackgroundWindowMask
|
||||
sheetOrigin = CGPointMakeCopy(sheetFrame.origin);
|
||||
|
||||
[self _removeClipForSheet:sheet];
|
||||
[sheet setFrameOrigin:CGPointMake(sheetOrigin.x, [sheet frame].origin.y + sheetOrigin.y)];
|
||||
[self _setAttachedSheetFrameOrigin];
|
||||
|
||||
// we wanted to close the sheet while it animated in, do that now
|
||||
if (_sheetContext["shouldClose"] === YES)
|
||||
@@ -3137,6 +3179,14 @@ CPTexturedBackgroundWindowMask
|
||||
return _isSheet;
|
||||
}
|
||||
|
||||
- (CGRect)_shadowFrame
|
||||
{
|
||||
if (_hasShadow)
|
||||
return _shadowView ? [_shadowView frame] : [self frame];
|
||||
|
||||
return [self frame];
|
||||
}
|
||||
|
||||
//
|
||||
/*
|
||||
Used privately.
|
||||
|
||||
@@ -41,6 +41,7 @@ var _CPPopoverWindowViewDefaultCursorSize = CGSizeMake(16, 10);
|
||||
unsigned _preferredEdge @accessors(property=preferredEdge);
|
||||
|
||||
CGSize _cursorSize;
|
||||
CPColor _contentViewSavedBackgroundColor;
|
||||
}
|
||||
|
||||
+ (CPString)defaultThemeClass
|
||||
@@ -123,6 +124,7 @@ var _CPPopoverWindowViewDefaultCursorSize = CGSizeMake(16, 10);
|
||||
_arrowOffsetY = 0.0;
|
||||
_appearance = [CPAppearance appearanceNamed:CPAppearanceNameVibrantLight];
|
||||
_cursorSize = CGSizeMakeCopy(_CPPopoverWindowViewDefaultCursorSize);
|
||||
_contentViewSavedBackgroundColor = nil;
|
||||
}
|
||||
|
||||
return self;
|
||||
@@ -177,6 +179,24 @@ var _CPPopoverWindowViewDefaultCursorSize = CGSizeMake(16, 10);
|
||||
strokeColor = [self valueForThemeAttribute:@"stroke-color-hud"];
|
||||
}
|
||||
|
||||
// Cappuccino special behavior :
|
||||
// If the content view has a background color, we use it as gradient color
|
||||
var subviews = [self subviews],
|
||||
contentView = [subviews count] > 0 ? subviews[0] : nil,
|
||||
contentViewBackgroundColor = [contentView backgroundColor];
|
||||
|
||||
if (_contentViewSavedBackgroundColor)
|
||||
{
|
||||
gradient = _contentViewSavedBackgroundColor;
|
||||
}
|
||||
else if (contentViewBackgroundColor)
|
||||
{
|
||||
gradient = _contentViewSavedBackgroundColor = contentViewBackgroundColor;
|
||||
|
||||
// We remove the content view background color (it will be restored when the popover closes)
|
||||
[contentView setBackgroundColor:nil];
|
||||
}
|
||||
|
||||
// fix rect to take care of stroke and shadow
|
||||
frame.origin.x += halfStrokeWidth + shadowBlur;
|
||||
frame.origin.y += halfStrokeWidth + (shadowBlur + shadowSize.height / 2);
|
||||
@@ -188,7 +208,14 @@ var _CPPopoverWindowViewDefaultCursorSize = CGSizeMake(16, 10);
|
||||
|
||||
CGContextBeginPath(context);
|
||||
CGContextSetShadowWithColor(context, shadowSize, shadowBlur, shadowColor);
|
||||
CGContextDrawLinearGradient(context, gradient, CGPointMake(CGRectGetMidX(frame), 0.0), CGPointMake(CGRectGetMidX(frame), frame.size.height), 0);
|
||||
|
||||
// Is gradient a CGGradient or a CPColor ?
|
||||
if ('locations' in gradient)
|
||||
// CGGradient
|
||||
CGContextDrawLinearGradient(context, gradient, CGPointMake(CGRectGetMidX(frame), 0.0), CGPointMake(CGRectGetMidX(frame), frame.size.height), 0);
|
||||
else
|
||||
// CPColor or CGColor
|
||||
CGContextSetFillColor(context, gradient);
|
||||
|
||||
var xMin = CGRectGetMinX(frame),
|
||||
xMax = CGRectGetMaxX(frame),
|
||||
@@ -401,4 +428,18 @@ var _CPPopoverWindowViewDefaultCursorSize = CGSizeMake(16, 10);
|
||||
CGContextFillPath(context);
|
||||
}
|
||||
|
||||
- (void)_restoreViewBackgroundColor
|
||||
{
|
||||
if (_contentViewSavedBackgroundColor)
|
||||
{
|
||||
// We restore the saved background color in the content view
|
||||
var subviews = [self subviews],
|
||||
contentView = [subviews count] > 0 ? subviews[0] : nil;
|
||||
|
||||
[contentView setBackgroundColor:_contentViewSavedBackgroundColor];
|
||||
|
||||
_contentViewSavedBackgroundColor = nil;
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -70,12 +70,40 @@ _CPWindowViewResizeSlop = 3;
|
||||
|
||||
+ (CGRect)contentRectForFrameRect:(CGRect)aFrameRect
|
||||
{
|
||||
return CGRectMakeCopy(aFrameRect);
|
||||
// First, we have to check if we are compiling a theme or running an application because if working on a theme,
|
||||
// we can't use theme attributes to determine the inset ! This would be a kind of circular reference...
|
||||
|
||||
var compilingATheme = [[[CPBundle mainBundle] objectForInfoDictionaryKey:@"CPApplicationDelegateClass"] isEqualToString:@"BKShowcaseController"],
|
||||
frameOutset = compilingATheme ? CGInsetMakeZero() : [[CPTheme defaultTheme] valueForAttributeWithName:@"frame-outset" forClass:_CPWindowView];
|
||||
|
||||
if (!frameOutset)
|
||||
// No value found in theme. Get default value.
|
||||
frameOutset = [[self themeAttributes] objectForKey:@"frame-outset"];
|
||||
|
||||
if (!frameOutset)
|
||||
// No value found in defaults. Get default value for _CPWindowView (we know here that the window class is not _CPWindowView)
|
||||
frameOutset = [[_CPWindowView themeAttributes] objectForKey:@"frame-outset"];
|
||||
|
||||
return CGRectMake(aFrameRect.origin.x - frameOutset.left, aFrameRect.origin.y - frameOutset.top, aFrameRect.size.width + frameOutset.left + frameOutset.right, aFrameRect.size.height + frameOutset.top + frameOutset.bottom);
|
||||
}
|
||||
|
||||
+ (CGRect)frameRectForContentRect:(CGRect)aContentRect
|
||||
{
|
||||
return CGRectMakeCopy(aContentRect);
|
||||
// First, we have to check if we are compiling a theme or running an application because if working on a theme,
|
||||
// we can't use theme attributes to determine the inset ! This would be a kind of circular reference...
|
||||
|
||||
var compilingATheme = [[[CPBundle mainBundle] objectForInfoDictionaryKey:@"CPApplicationDelegateClass"] isEqualToString:@"BKShowcaseController"],
|
||||
frameOutset = compilingATheme ? CGInsetMakeZero() : [[CPTheme defaultTheme] valueForAttributeWithName:@"frame-outset" forClass:[self class]]; // _CPWindowView - [self class]
|
||||
|
||||
if (!frameOutset)
|
||||
// No value found in theme. Get default value.
|
||||
frameOutset = [[self themeAttributes] objectForKey:@"frame-outset"];
|
||||
|
||||
if (!frameOutset)
|
||||
// No value found in theme. Get default value for _CPWindowView
|
||||
frameOutset = [[_CPWindowView themeAttributes] objectForKey:@"frame-outset"];
|
||||
|
||||
return CGRectMake(aContentRect.origin.x - frameOutset.left, aContentRect.origin.y - frameOutset.top, aContentRect.size.width + frameOutset.left + frameOutset.right, aContentRect.size.height + frameOutset.top + frameOutset.bottom);
|
||||
}
|
||||
|
||||
+ (CPString)defaultThemeClass
|
||||
@@ -94,6 +122,7 @@ _CPWindowViewResizeSlop = 3;
|
||||
@"resize-indicator": [CPNull null],
|
||||
@"attached-sheet-shadow-color": [CPColor blackColor],
|
||||
@"shadow-height": 8,
|
||||
@"shadown-horizontal-offset": 0,
|
||||
@"close-image-origin": [CPNull null],
|
||||
@"close-image-size": [CPNull null],
|
||||
@"close-image": [CPNull null],
|
||||
@@ -107,10 +136,16 @@ _CPWindowViewResizeSlop = 3;
|
||||
@"title-line-break-mode": CPLineBreakByTruncatingTail,
|
||||
@"title-vertical-alignment": CPTopVerticalTextAlignment,
|
||||
@"title-margin": 20,
|
||||
@"border-top-left-radius": @"0px",
|
||||
@"border-top-right-radius": @"0px",
|
||||
@"border-bottom-left-radius": @"0px",
|
||||
@"border-bottom-right-radius": @"0px",
|
||||
@"minimize-image-origin": [CPNull null],
|
||||
@"minimize-image-size": [CPNull null],
|
||||
@"zoom-image-origin": [CPNull null],
|
||||
@"zoom-image-size": [CPNull null]
|
||||
@"zoom-image-size": [CPNull null],
|
||||
@"frame-outset": CGInsetMakeZero(),
|
||||
@"sheet-vertical-offset": -1
|
||||
};
|
||||
}
|
||||
|
||||
@@ -937,9 +972,14 @@ _CPWindowViewResizeSlop = 3;
|
||||
[self setNeedsLayout];
|
||||
}
|
||||
|
||||
- (CGRect)_shadowViewWidthForParentWindow:(CPWindow)parentWindow
|
||||
- (int)_sheetVerticalOffset
|
||||
{
|
||||
var myWidth = [self bounds].size.width,
|
||||
return [self currentValueForThemeAttribute:@"sheet-vertical-offset"];
|
||||
}
|
||||
|
||||
- (int)_shadowViewWidthForParentWindow:(CPWindow)parentWindow
|
||||
{
|
||||
var myWidth = [self bounds].size.width - [self currentValueForThemeAttribute:@"shadown-horizontal-offset"],
|
||||
parentWidth = [[parentWindow contentView] bounds].size.width;
|
||||
|
||||
return MIN(myWidth, parentWidth);
|
||||
|
||||
@@ -54,8 +54,9 @@ var _CPCibCustomResourceClassNameKey = @"_CPCibCustomResourceClassNameKey",
|
||||
return;
|
||||
|
||||
_CPCibCustomResourceTemplateImageMap = @{
|
||||
"CPAddTemplate": "button-image-plus",
|
||||
"CPRemoveTemplate": "button-image-minus"
|
||||
"CPAddTemplate": "button-image-plus",
|
||||
"CPRemoveTemplate": "button-image-minus",
|
||||
"CPActionTemplate": "button-image-action"
|
||||
};
|
||||
}
|
||||
+ (id)imageResourceWithName:(CPString)aResourceName size:(CGSize)aSize
|
||||
@@ -111,6 +112,19 @@ var _CPCibCustomResourceClassNameKey = @"_CPCibCustomResourceClassNameKey",
|
||||
(![aCoder respondsToSelector:@selector(awakenCustomResources)] || [aCoder awakenCustomResources]))
|
||||
if (_className === @"CPImage")
|
||||
{
|
||||
// Is this a material icon reference ?
|
||||
if ([_resourceName hasSuffix:@"@MaterialIcons"])
|
||||
{
|
||||
var range = [_resourceName rangeOfString:@"@MaterialIcons"];
|
||||
|
||||
if (range.location <= 0)
|
||||
[CPException raise:CPRangeException reason:"Malformed Material Icon reference ("+_resourceName+")"];
|
||||
|
||||
var iconName = [_resourceName substringToIndex:range.location];
|
||||
|
||||
return [CPImage imageWithMaterialIconNamed:iconName size:CGSizeMake(16,16)];
|
||||
}
|
||||
|
||||
var templateImage = [_CPCibCustomResourceTemplateImageMap objectForKey:_resourceName];
|
||||
|
||||
if (templateImage)
|
||||
|
||||
@@ -37,7 +37,7 @@ var DEFAULT_CSS_PROPERTIES = nil,
|
||||
[self _setTargetValue:YES withKeyPath:@"CPAnimationTriggerOrderOut" setter:_cmd];
|
||||
}
|
||||
|
||||
- (void)setAlphaValue:(CGPoint)alphaValue
|
||||
- (void)setAlphaValue:(float)alphaValue
|
||||
{
|
||||
[self _setTargetValue:alphaValue withKeyPath:@"alphaValue" setter:_cmd];
|
||||
}
|
||||
|
||||
BIN
Binary file not shown.
+1
File diff suppressed because one or more lines are too long
+2373
File diff suppressed because it is too large
Load Diff
|
After Width: | Height: | Size: 275 KiB |
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Executable
+36
@@ -0,0 +1,36 @@
|
||||
@font-face {
|
||||
font-family: 'Material Icons';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: url(MaterialIcons-Regular.eot); /* For IE6-8 */
|
||||
src: local('Material Icons'),
|
||||
local('MaterialIcons-Regular'),
|
||||
url(MaterialIcons-Regular.woff2) format('woff2'),
|
||||
url(MaterialIcons-Regular.woff) format('woff'),
|
||||
url(MaterialIcons-Regular.ttf) format('truetype');
|
||||
}
|
||||
|
||||
.material-icons {
|
||||
font-family: 'Material Icons';
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
font-size: 24px; /* Preferred icon size */
|
||||
display: inline-block;
|
||||
line-height: 1;
|
||||
text-transform: none;
|
||||
letter-spacing: normal;
|
||||
word-wrap: normal;
|
||||
white-space: nowrap;
|
||||
direction: ltr;
|
||||
|
||||
/* Support for all WebKit browsers. */
|
||||
-webkit-font-smoothing: antialiased;
|
||||
/* Support for Safari and Chrome. */
|
||||
text-rendering: optimizeLegibility;
|
||||
|
||||
/* Support for Firefox. */
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
|
||||
/* Support for IE. */
|
||||
font-feature-settings: 'liga';
|
||||
}
|
||||
@@ -325,6 +325,15 @@ var ItemSizes = { },
|
||||
[self registerThemeValues:themeValues forView:aView];
|
||||
}
|
||||
|
||||
+ (void)registerThemeValues:(CPArray)themeValues forView:(CPView)aView inheritFrom:(CPView)anotherView
|
||||
{
|
||||
if (anotherView)
|
||||
// We take all theme attributes values from anotherView
|
||||
[aView _addThemeAttributeDictionary:[anotherView _themeAttributeDictionary]];
|
||||
|
||||
[self registerThemeValues:themeValues forView:aView];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
function BKLabelFromIdentifier(anIdentifier)
|
||||
|
||||
@@ -662,17 +662,6 @@ var _CPimageAndTextViewFrameSizeChangedFlag = 1 << 0,
|
||||
if (!imageStyle)
|
||||
var imageStyle = _DOMImageElement.style;
|
||||
|
||||
if (_flags & _CPImageAndTextViewImageChangedFlag)
|
||||
{
|
||||
if (isCSSBasedImage)
|
||||
_cssStyleNode = [_image applyCSSImageForView:self
|
||||
onDOMElement:_DOMImageElement
|
||||
styleNode:_cssStyleNode
|
||||
previousState:@ref(_cssStylePreviousState)];
|
||||
else
|
||||
_DOMImageElement.src = [_image filename];
|
||||
}
|
||||
|
||||
var centerX = size.width / 2.0,
|
||||
centerY = size.height / 2.0,
|
||||
imageSize = [_image size],
|
||||
@@ -737,6 +726,24 @@ var _CPimageAndTextViewFrameSizeChangedFlag = 1 << 0,
|
||||
imageStyle.top = FLOOR(centerY - imageHeight / 2.0) + "px";
|
||||
imageStyle.left = FLOOR(centerX - imageWidth / 2.0) + "px";
|
||||
}
|
||||
|
||||
if (_flags & _CPImageAndTextViewImageChangedFlag)
|
||||
{
|
||||
if (isCSSBasedImage)
|
||||
{
|
||||
// For material icons images & co.
|
||||
if ([_image _shouldBeResized])
|
||||
[_image _setDisplaySize:CGSizeMake(imageWidth, imageHeight)];
|
||||
|
||||
_cssStyleNode = [_image applyCSSImageForView:self
|
||||
onDOMElement:_DOMImageElement
|
||||
styleNode:_cssStyleNode
|
||||
previousState:@ref(_cssStylePreviousState)];
|
||||
}
|
||||
else
|
||||
_DOMImageElement.src = [_image filename];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (hasDOMTextElement)
|
||||
|
||||
@@ -205,7 +205,7 @@ var NULL_THEME = {};
|
||||
|
||||
if (cachedAttributes)
|
||||
{
|
||||
attributes = attributes.length ? attributes.concat(cachedAttributes) : attributes;
|
||||
attributes = cachedAttributes.length ? attributes.concat(cachedAttributes) : attributes;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -300,6 +300,22 @@ var NULL_THEME = {};
|
||||
return dictionary;
|
||||
}
|
||||
|
||||
- (void)_addThemeAttributeDictionary:(CPDictionary)themeAttributeDictionary
|
||||
{
|
||||
if (!themeAttributeDictionary)
|
||||
return;
|
||||
|
||||
var keys = [themeAttributeDictionary allKeys];
|
||||
|
||||
for (var i = 0, count = [keys count], key, value; i < count; i++)
|
||||
{
|
||||
key = keys[i];
|
||||
value = [themeAttributeDictionary objectForKey:key];
|
||||
|
||||
_themeAttributes[key] = value;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setValue:(id)aValue forThemeAttribute:(CPString)aName inState:(ThemeState)aState
|
||||
{
|
||||
#if DEBUG
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
@import "CPButton.j"
|
||||
@import "CPMenu.j"
|
||||
@import "CPPanel.j"
|
||||
@import "CPAnimationContext.j"
|
||||
|
||||
// Use forward declaration because this file is imported by CPPopover
|
||||
@class CPPopover
|
||||
@@ -117,7 +118,11 @@ var _CPPopoverWindow_shouldClose_ = 1 << 4,
|
||||
_isClosing = NO;
|
||||
_browserAnimates = [self browserSupportsAnimation];
|
||||
_shouldPerformAnimation = YES;
|
||||
_orderOutTransitionFunction = function() { [self _orderOutRecursively:YES]; };
|
||||
_orderOutTransitionFunction = function()
|
||||
{
|
||||
[self _orderOutRecursively:YES];
|
||||
[_windowView _restoreViewBackgroundColor];
|
||||
};
|
||||
_isOpening = YES;
|
||||
|
||||
[self setStyleMask:aStyleMask];
|
||||
|
||||
@@ -191,7 +191,7 @@ var _CPKeyedArchiverStringClass = Nil,
|
||||
*/
|
||||
- (void)finishEncoding
|
||||
{
|
||||
if (_delegate && _delegateSelectors & _CPKeyedArchiverDidFinishEncodingSelector)
|
||||
if (_delegate && _delegateSelectors & _CPKeyedArchiverWillFinishEncodingSelector)
|
||||
[_delegate archiverWillFinish:self];
|
||||
|
||||
var i = 0,
|
||||
@@ -299,10 +299,10 @@ var _CPKeyedArchiverStringClass = Nil,
|
||||
if ([_delegate respondsToSelector:@selector(archiver:willReplaceObject:withObject:)])
|
||||
_delegateSelectors |= _CPKeyedArchiverWillReplaceObjectWithObjectSelector;
|
||||
|
||||
if ([_delegate respondsToSelector:@selector(archiver:didFinishEncoding:)])
|
||||
if ([_delegate respondsToSelector:@selector(archiverDidFinish:)])
|
||||
_delegateSelectors |= _CPKeyedArchiverDidFinishEncodingSelector;
|
||||
|
||||
if ([_delegate respondsToSelector:@selector(archiver:willFinishEncoding:)])
|
||||
if ([_delegate respondsToSelector:@selector(archiverWillFinish:)])
|
||||
_delegateSelectors |= _CPKeyedArchiverWillFinishEncodingSelector;
|
||||
|
||||
}
|
||||
|
||||
@@ -132,15 +132,16 @@
|
||||
[self assert:CPMixedState equals:[button state] message:@"Mixed state is allowed, state should be CPMixedState"];
|
||||
}
|
||||
|
||||
- (void)testThemeStateWhenSettingObjectValue
|
||||
{
|
||||
[button unsetThemeState:[button themeState]];
|
||||
[button setObjectValue:CPOnState];
|
||||
[self assert:String(CPThemeStateSelected) equals:String([button themeState]) message:@"object should be in the selected themestate"];
|
||||
|
||||
[button setObjectValue:CPOffState];
|
||||
[self assert:String(CPThemeStateNormal) equals:String([button themeState]) message:@"object should be in the normal themestate"];
|
||||
}
|
||||
// PR #2920 modifies how theme states are used. This test is no more usable.
|
||||
//- (void)testThemeStateWhenSettingObjectValue
|
||||
//{
|
||||
// [button unsetThemeState:[button themeState]];
|
||||
// [button setObjectValue:CPOnState];
|
||||
// [self assert:String(CPThemeStateSelected) equals:String([button themeState]) message:@"object should be in the selected themestate"];
|
||||
//
|
||||
// [button setObjectValue:CPOffState];
|
||||
// [self assert:String(CPThemeStateNormal) equals:String([button themeState]) message:@"object should be in the normal themestate"];
|
||||
//}
|
||||
|
||||
- (void)testThemeAttributes
|
||||
{
|
||||
@@ -189,6 +190,69 @@
|
||||
[self assertTrue:wasClicked message:@"a user click on a radio button should fire the group action"];
|
||||
}
|
||||
|
||||
- (void)testAutomaticRadioGroup
|
||||
{
|
||||
var radioButton1 = [CPRadio radioWithTitle:@"Radio 1"],
|
||||
radioButton2 = [CPRadio radioWithTitle:@"Radio 2"],
|
||||
radioButton3 = [CPRadio radioWithTitle:@"Radio 3"],
|
||||
simpleView1 = [[CPView alloc] initWithFrame:CGRectMakeZero()],
|
||||
simpleView2 = [[CPView alloc] initWithFrame:CGRectMakeZero()];
|
||||
|
||||
// Initially, buttons are isolated
|
||||
[self assertFalse:([radioButton1 radioGroup] === [radioButton2 radioGroup]) message:@"initially, buttons should be isolated"];
|
||||
|
||||
[simpleView1 addSubview:radioButton1];
|
||||
[simpleView1 addSubview:radioButton2];
|
||||
|
||||
// As no actions are defined, buttons are still isolated
|
||||
[self assertFalse:([radioButton1 radioGroup] === [radioButton2 radioGroup]) message:@"no actions defined, buttons should be isolated"];
|
||||
|
||||
[radioButton1 setAction:@selector(dummyAction1:)];
|
||||
[radioButton2 setAction:@selector(dummyAction2:)];
|
||||
|
||||
// As different actions are defined, buttons are still isolatdd
|
||||
[self assertFalse:([radioButton1 radioGroup] === [radioButton2 radioGroup]) message:@"different actions defined, buttons should be isolated"];
|
||||
|
||||
[radioButton2 setAction:@selector(dummyAction1:)];
|
||||
|
||||
// As the same action is defined, buttons must be grouped
|
||||
[self assertTrue:([radioButton1 radioGroup] === [radioButton2 radioGroup]) message:@"same action defined, buttons should be grouped"];
|
||||
|
||||
[radioButton3 setAction:@selector(dummyAction1:)];
|
||||
|
||||
// As radioButton3 is not inserted in a view, it's isolated
|
||||
[self assertTrue:([[radioButton3 radioGroup] size] === 1) message:@"not in a view, button should be isolated"];
|
||||
|
||||
[simpleView2 addSubview:radioButton3];
|
||||
|
||||
// As radioButton3 is in another view, it's isolated from radioButton1 & 2
|
||||
[self assertTrue:([[radioButton3 radioGroup] size] === 1) message:@"alone in a view, button should be isolated"];
|
||||
|
||||
[simpleView1 addSubview:radioButton3];
|
||||
|
||||
// As all 3 buttons are in the same view, with the same action, they are grouped
|
||||
[self assertTrue:([[radioButton3 radioGroup] size] === 3) message:@"after moving to the same view, buttons should be grouped"];
|
||||
|
||||
[radioButton3 setAction:@selector(dummyAction2:)];
|
||||
|
||||
// As the action of button 3 is now different, it's isolated
|
||||
[self assertTrue:([[radioButton3 radioGroup] size] === 1) message:@"after changing the action, button 3 should be isolated"];
|
||||
|
||||
// And buttons 1 & 2 are still grouped
|
||||
[self assertTrue:([radioButton1 radioGroup] === [radioButton2 radioGroup]) message:@"buttons 1 & 2 should remain grouped"];
|
||||
[self assertTrue:([[radioButton1 radioGroup] size] === 2) message:@"radio group should contain only buttons 1 & 2"];
|
||||
}
|
||||
|
||||
- (IBAction)dummyAction1:(id)sender
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
- (IBAction)dummyAction2:(id)sender
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
- (void)testTypeMasks
|
||||
{
|
||||
button = [[CPButton alloc] initWithFrame:CGRectMakeZero()];
|
||||
|
||||
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
@@ -629,6 +629,28 @@ var sharedObject = [CPObject new];
|
||||
[self assertTrue:[[self stringForTesting] isEqual:string] message:"setAttributedString should have made strings equal, but they were not"];
|
||||
}
|
||||
|
||||
- (void)testReplaceAttributedString
|
||||
{
|
||||
var string = [[CPMutableAttributedString alloc] initWithString:"HELLO <br> THERE"];
|
||||
[string replaceOccurrencesOfString:"<br>"
|
||||
withString:"<tr>"
|
||||
options:0
|
||||
range:nil];
|
||||
|
||||
[self assertTrue:[string._string isEqual:"HELLO <tr> THERE"] message:"replaceOccurrencesOfString:withString:options:range: did not properly replace the search string with the replacement string. Result is: " + string._string];
|
||||
}
|
||||
|
||||
- (void)testReplaceAttributedStringWithRange
|
||||
{
|
||||
var string = [[CPMutableAttributedString alloc] initWithString:"HELLO <br> THERE"];
|
||||
[string replaceOccurrencesOfString:"<br>"
|
||||
withString:"<tr>"
|
||||
options:0
|
||||
range:CPMakeRange(0, 4)];
|
||||
|
||||
[self assertTrue:[string._string isEqual:"HELLO <br> THERE"] message:"replaceOccurrencesOfString:withString:options:range: did not respect the range restriction. Result is: " + string._string];
|
||||
}
|
||||
|
||||
- (void)testEncoding
|
||||
{
|
||||
// We can't test using [self stringForTesting] because it contains attributes without coding support.
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* AppController.j
|
||||
* Nib2CibSubviewsOrderTest
|
||||
*
|
||||
* Created by You on September 19, 2020.
|
||||
* Copyright 2020, Your Company All rights reserved.
|
||||
*/
|
||||
|
||||
@import <Foundation/Foundation.j>
|
||||
@import <AppKit/AppKit.j>
|
||||
|
||||
|
||||
@implementation AppController : CPObject
|
||||
{
|
||||
@outlet CPWindow theWindow;
|
||||
}
|
||||
|
||||
- (void)applicationDidFinishLaunching:(CPNotification)aNotification
|
||||
{
|
||||
// This is called when the application is done loading.
|
||||
}
|
||||
|
||||
- (void)awakeFromCib
|
||||
{
|
||||
// This is called when the cib is done loading.
|
||||
// You can implement this method on any object instantiated from a Cib.
|
||||
// It's a useful hook for setting up current UI values, and other things.
|
||||
|
||||
// In this case, we want the window from Cib to become our full browser window
|
||||
[theWindow setFullPlatformWindow:NO];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>Main cib file base name</key>
|
||||
<string>MainMenu.cib</string>
|
||||
<key>CPBundleName</key>
|
||||
<string>Nib2CibSubviewsOrderTest</string>
|
||||
<key>CPBundleVersion</key>
|
||||
<string>1.0</string>
|
||||
<key>CPHumanReadableCopyright</key>
|
||||
<string>Copyright © 2020, Your Company All rights reserved.</string>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -0,0 +1,172 @@
|
||||
/*
|
||||
* Jakefile
|
||||
* Nib2CibSubviewsOrderTest
|
||||
*
|
||||
* Created by You on September 19, 2020.
|
||||
* Copyright 2020, Your Company All rights reserved.
|
||||
*/
|
||||
|
||||
var ENV = require("system").env,
|
||||
FILE = require("file"),
|
||||
JAKE = require("jake"),
|
||||
task = JAKE.task,
|
||||
FileList = JAKE.FileList,
|
||||
app = require("cappuccino/jake").app,
|
||||
configuration = ENV["CONFIG"] || ENV["CONFIGURATION"] || ENV["c"] || "Debug",
|
||||
OS = require("os"),
|
||||
projectName = "Nib2CibSubviewsOrderTest";
|
||||
|
||||
app (projectName, function(task)
|
||||
{
|
||||
ENV["OBJJ_INCLUDE_PATHS"] = "Frameworks";
|
||||
|
||||
if (configuration === "Debug")
|
||||
ENV["OBJJ_INCLUDE_PATHS"] = FILE.join(ENV["OBJJ_INCLUDE_PATHS"], configuration);
|
||||
|
||||
task.setBuildIntermediatesPath(FILE.join("Build", "Nib2CibSubviewsOrderTest.build", configuration));
|
||||
task.setBuildPath(FILE.join("Build", configuration));
|
||||
|
||||
task.setProductName("Nib2CibSubviewsOrderTest");
|
||||
task.setIdentifier("com.yourcompany.Nib2CibSubviewsOrderTest");
|
||||
task.setVersion("1.0");
|
||||
task.setAuthor("Your Company");
|
||||
task.setEmail("feedback @nospam@ yourcompany.com");
|
||||
task.setSummary("Nib2CibSubviewsOrderTest");
|
||||
task.setSources(new FileList("**/*.j").exclude(FILE.join("Build", "**")).exclude(FILE.join("Frameworks", "Source", "**")));
|
||||
task.setResources(new FileList("Resources/**"));
|
||||
task.setIndexFilePath("index.html");
|
||||
task.setInfoPlistPath("Info.plist");
|
||||
|
||||
if (configuration === "Debug")
|
||||
task.setCompilerFlags("-DDEBUG -g -S --inline-msg-send");
|
||||
else
|
||||
task.setCompilerFlags("-O2");
|
||||
});
|
||||
|
||||
task ("default", [projectName], function()
|
||||
{
|
||||
printResults(configuration);
|
||||
});
|
||||
|
||||
task ("build", ["default"], function()
|
||||
{
|
||||
updateApplicationSize();
|
||||
});
|
||||
|
||||
task ("debug", function()
|
||||
{
|
||||
configuration = ENV["CONFIGURATION"] = "Debug";
|
||||
JAKE.subjake(["."], "build", ENV);
|
||||
});
|
||||
|
||||
task ("release", function()
|
||||
{
|
||||
configuration = ENV["CONFIGURATION"] = "Release";
|
||||
JAKE.subjake(["."], "build", ENV);
|
||||
});
|
||||
|
||||
task ("run", ["debug"], function()
|
||||
{
|
||||
OS.system(["open", FILE.join("Build", "Debug", projectName, "index.html")]);
|
||||
});
|
||||
|
||||
task ("run-release", ["release"], function()
|
||||
{
|
||||
OS.system(["open", FILE.join("Build", "Release", projectName, "index.html")]);
|
||||
});
|
||||
|
||||
task ("deploy", ["release"], function()
|
||||
{
|
||||
FILE.mkdirs(FILE.join("Build", "Deployment", projectName));
|
||||
OS.system(["press", "-f", FILE.join("Build", "Release", projectName), FILE.join("Build", "Deployment", projectName)]);
|
||||
printResults("Deployment")
|
||||
});
|
||||
|
||||
function printResults(configuration)
|
||||
{
|
||||
print("----------------------------");
|
||||
print(configuration+" app built at path: "+FILE.join("Build", configuration, projectName));
|
||||
print("----------------------------");
|
||||
}
|
||||
|
||||
function updateApplicationSize()
|
||||
{
|
||||
print("Calculating application file sizes...");
|
||||
|
||||
var contents = FILE.read(FILE.join("Build", configuration, projectName, "Info.plist"), { charset:"UTF-8" }),
|
||||
format = CFPropertyList.sniffedFormatOfString(contents),
|
||||
plist = CFPropertyList.propertyListFromString(contents),
|
||||
totalBytes = {executable:0, data:0, mhtml:0};
|
||||
|
||||
// Get the size of all framework executables and sprite data
|
||||
var frameworksDir = "Frameworks";
|
||||
|
||||
if (configuration === "Debug")
|
||||
frameworksDir = FILE.join(frameworksDir, "Debug");
|
||||
|
||||
var frameworks = FILE.list(frameworksDir);
|
||||
|
||||
frameworks.forEach(function(framework)
|
||||
{
|
||||
if (framework !== "Source")
|
||||
addBundleFileSizes(FILE.join(frameworksDir, framework), totalBytes);
|
||||
});
|
||||
|
||||
// Read in the default theme name, and attempt to get its size
|
||||
var themeName = plist.valueForKey("CPDefaultTheme") || "Aristo2",
|
||||
themePath = nil;
|
||||
|
||||
if (themeName === "Aristo" || themeName === "Aristo2")
|
||||
themePath = FILE.join(frameworksDir, "AppKit", "Resources", themeName + ".blend");
|
||||
else
|
||||
themePath = FILE.join("Frameworks", "Resources", themeName + ".blend");
|
||||
|
||||
if (FILE.isDirectory(themePath))
|
||||
addBundleFileSizes(themePath, totalBytes);
|
||||
|
||||
// Add sizes for the app
|
||||
addBundleFileSizes(FILE.join("Build", configuration, projectName), totalBytes);
|
||||
|
||||
print("Executables: " + totalBytes.executable + ", sprite data: " + totalBytes.data + ", total: " + (totalBytes.executable + totalBytes.data));
|
||||
|
||||
var dict = new CFMutableDictionary();
|
||||
|
||||
dict.setValueForKey("executable", totalBytes.executable);
|
||||
dict.setValueForKey("data", totalBytes.data);
|
||||
dict.setValueForKey("mhtml", totalBytes.mhtml);
|
||||
|
||||
plist.setValueForKey("CPApplicationSize", dict);
|
||||
|
||||
FILE.write(FILE.join("Build", configuration, projectName, "Info.plist"), CFPropertyList.stringFromPropertyList(plist, format), { charset:"UTF-8" });
|
||||
}
|
||||
|
||||
function addBundleFileSizes(bundlePath, totalBytes)
|
||||
{
|
||||
var bundleName = FILE.basename(bundlePath),
|
||||
environment = bundleName === "Foundation" ? "Objj" : "Browser",
|
||||
bundlePath = FILE.join(bundlePath, environment + ".environment");
|
||||
|
||||
if (FILE.isDirectory(bundlePath))
|
||||
{
|
||||
var filename = bundleName + ".sj",
|
||||
filePath = new FILE.Path(FILE.join(bundlePath, filename));
|
||||
|
||||
if (filePath.exists())
|
||||
totalBytes.executable += filePath.size();
|
||||
|
||||
filePath = new FILE.Path(FILE.join(bundlePath, "dataURLs.txt"));
|
||||
|
||||
if (filePath.exists())
|
||||
totalBytes.data += filePath.size();
|
||||
|
||||
filePath = new FILE.Path(FILE.join(bundlePath, "MHTMLData.txt"));
|
||||
|
||||
if (filePath.exists())
|
||||
totalBytes.mhtml += filePath.size();
|
||||
|
||||
filePath = new FILE.Path(FILE.join(bundlePath, "MHTMLPaths.txt"));
|
||||
|
||||
if (filePath.exists())
|
||||
totalBytes.mhtml += filePath.size();
|
||||
}
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,375 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="14460.31" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none">
|
||||
<dependencies>
|
||||
<deployment version="1050" identifier="macosx"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="14460.31"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<customObject id="-2" userLabel="File's Owner" customClass="NSApplication">
|
||||
<connections>
|
||||
<outlet property="delegate" destination="450" id="451"/>
|
||||
</connections>
|
||||
</customObject>
|
||||
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
|
||||
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
|
||||
<menu title="AMainMenu" systemMenu="main" id="29" userLabel="MainMenu">
|
||||
<items>
|
||||
<menuItem title="NewApplication" id="56">
|
||||
<menu key="submenu" title="NewApplication" systemMenu="apple" id="57">
|
||||
<items>
|
||||
<menuItem title="About NewApplication" id="58">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="orderFrontStandardAboutPanel:" target="-2" id="142"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem isSeparatorItem="YES" id="236">
|
||||
<modifierMask key="keyEquivalentModifierMask" command="YES"/>
|
||||
</menuItem>
|
||||
<menuItem title="Preferences…" keyEquivalent="," id="129"/>
|
||||
<menuItem isSeparatorItem="YES" id="143">
|
||||
<modifierMask key="keyEquivalentModifierMask" command="YES"/>
|
||||
</menuItem>
|
||||
<menuItem title="Quit NewApplication" keyEquivalent="q" id="136">
|
||||
<connections>
|
||||
<action selector="terminate:" target="-3" id="449"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
</items>
|
||||
</menu>
|
||||
</menuItem>
|
||||
<menuItem title="File" id="83">
|
||||
<menu key="submenu" title="File" id="81">
|
||||
<items>
|
||||
<menuItem title="New" keyEquivalent="n" id="82">
|
||||
<connections>
|
||||
<action selector="newDocument:" target="-1" id="373"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Open…" keyEquivalent="o" id="72">
|
||||
<connections>
|
||||
<action selector="openDocument:" target="-1" id="374"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Open Recent" id="124">
|
||||
<menu key="submenu" title="Open Recent" systemMenu="recentDocuments" id="125">
|
||||
<items>
|
||||
<menuItem title="Clear Menu" id="126">
|
||||
<connections>
|
||||
<action selector="clearRecentDocuments:" target="-1" id="127"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
</items>
|
||||
</menu>
|
||||
</menuItem>
|
||||
<menuItem isSeparatorItem="YES" id="79">
|
||||
<modifierMask key="keyEquivalentModifierMask" command="YES"/>
|
||||
</menuItem>
|
||||
<menuItem title="Close" keyEquivalent="w" id="73">
|
||||
<connections>
|
||||
<action selector="performClose:" target="-1" id="193"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Save" keyEquivalent="s" id="75">
|
||||
<connections>
|
||||
<action selector="saveDocument:" target="-1" id="362"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Save As…" keyEquivalent="S" id="80">
|
||||
<modifierMask key="keyEquivalentModifierMask" shift="YES" command="YES"/>
|
||||
<connections>
|
||||
<action selector="saveDocumentAs:" target="-1" id="363"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Revert to Saved" id="112">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="revertDocumentToSaved:" target="-1" id="364"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
</items>
|
||||
</menu>
|
||||
</menuItem>
|
||||
<menuItem title="Edit" id="217">
|
||||
<menu key="submenu" title="Edit" id="205">
|
||||
<items>
|
||||
<menuItem title="Undo" keyEquivalent="z" id="207">
|
||||
<connections>
|
||||
<action selector="undo:" target="-1" id="223"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Redo" keyEquivalent="Z" id="215">
|
||||
<modifierMask key="keyEquivalentModifierMask" shift="YES" command="YES"/>
|
||||
<connections>
|
||||
<action selector="redo:" target="-1" id="231"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem isSeparatorItem="YES" id="206">
|
||||
<modifierMask key="keyEquivalentModifierMask" command="YES"/>
|
||||
</menuItem>
|
||||
<menuItem title="Cut" keyEquivalent="x" id="199">
|
||||
<connections>
|
||||
<action selector="cut:" target="-1" id="228"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Copy" keyEquivalent="c" id="197">
|
||||
<connections>
|
||||
<action selector="copy:" target="-1" id="224"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Paste" keyEquivalent="v" id="203">
|
||||
<connections>
|
||||
<action selector="paste:" target="-1" id="226"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Delete" id="202">
|
||||
<connections>
|
||||
<action selector="delete:" target="-1" id="235"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Select All" keyEquivalent="a" id="198">
|
||||
<connections>
|
||||
<action selector="selectAll:" target="-1" id="232"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem isSeparatorItem="YES" id="214">
|
||||
<modifierMask key="keyEquivalentModifierMask" command="YES"/>
|
||||
</menuItem>
|
||||
<menuItem title="Find" id="218">
|
||||
<menu key="submenu" title="Find" id="220">
|
||||
<items>
|
||||
<menuItem title="Find…" tag="1" keyEquivalent="f" id="209">
|
||||
<connections>
|
||||
<action selector="performFindPanelAction:" target="-1" id="241"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Find Next" tag="2" keyEquivalent="g" id="208"/>
|
||||
<menuItem title="Find Previous" tag="3" keyEquivalent="G" id="213">
|
||||
<modifierMask key="keyEquivalentModifierMask" shift="YES" command="YES"/>
|
||||
</menuItem>
|
||||
<menuItem title="Use Selection for Find" tag="7" keyEquivalent="e" id="221"/>
|
||||
<menuItem title="Jump to Selection" keyEquivalent="j" id="210">
|
||||
<connections>
|
||||
<action selector="centerSelectionInVisibleArea:" target="-1" id="245"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
</items>
|
||||
</menu>
|
||||
</menuItem>
|
||||
<menuItem title="Spelling and Grammar" id="216">
|
||||
<menu key="submenu" title="Spelling and Grammar" id="200">
|
||||
<items>
|
||||
<menuItem title="Show Spelling…" keyEquivalent=":" id="204">
|
||||
<connections>
|
||||
<action selector="showGuessPanel:" target="-1" id="230"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Check Spelling" keyEquivalent=";" id="201">
|
||||
<connections>
|
||||
<action selector="checkSpelling:" target="-1" id="225"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Check Spelling While Typing" id="219">
|
||||
<connections>
|
||||
<action selector="toggleContinuousSpellChecking:" target="-1" id="222"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Check Grammar With Spelling" id="346">
|
||||
<connections>
|
||||
<action selector="toggleGrammarChecking:" target="-1" id="347"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
</items>
|
||||
</menu>
|
||||
</menuItem>
|
||||
<menuItem title="Substitutions" id="348">
|
||||
<menu key="submenu" title="Substitutions" id="349">
|
||||
<items>
|
||||
<menuItem title="Smart Copy/Paste" tag="1" keyEquivalent="f" id="350">
|
||||
<connections>
|
||||
<action selector="toggleSmartInsertDelete:" target="-1" id="355"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Smart Quotes" tag="2" keyEquivalent="g" id="351">
|
||||
<connections>
|
||||
<action selector="toggleAutomaticQuoteSubstitution:" target="-1" id="356"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Smart Links" tag="3" keyEquivalent="G" id="354">
|
||||
<modifierMask key="keyEquivalentModifierMask" shift="YES" command="YES"/>
|
||||
<connections>
|
||||
<action selector="toggleAutomaticLinkDetection:" target="-1" id="357"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
</items>
|
||||
</menu>
|
||||
</menuItem>
|
||||
<menuItem title="Speech" id="211">
|
||||
<menu key="submenu" title="Speech" id="212">
|
||||
<items>
|
||||
<menuItem title="Start Speaking" id="196">
|
||||
<connections>
|
||||
<action selector="startSpeaking:" target="-1" id="233"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Stop Speaking" id="195">
|
||||
<connections>
|
||||
<action selector="stopSpeaking:" target="-1" id="227"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
</items>
|
||||
</menu>
|
||||
</menuItem>
|
||||
</items>
|
||||
</menu>
|
||||
</menuItem>
|
||||
<menuItem title="View" id="295">
|
||||
<menu key="submenu" title="View" id="296">
|
||||
<items>
|
||||
<menuItem title="Show Toolbar" keyEquivalent="t" id="297">
|
||||
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
|
||||
<connections>
|
||||
<action selector="toggleToolbarShown:" target="-1" id="366"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Customize Toolbar…" id="298">
|
||||
<connections>
|
||||
<action selector="runToolbarCustomizationPalette:" target="-1" id="365"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
</items>
|
||||
</menu>
|
||||
</menuItem>
|
||||
<menuItem title="Window" id="19">
|
||||
<menu key="submenu" title="Window" systemMenu="window" id="24">
|
||||
<items>
|
||||
<menuItem title="Minimize" keyEquivalent="m" id="23">
|
||||
<connections>
|
||||
<action selector="performMiniaturize:" target="-1" id="37"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Zoom" id="239">
|
||||
<connections>
|
||||
<action selector="performZoom:" target="-1" id="240"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem isSeparatorItem="YES" id="92">
|
||||
<modifierMask key="keyEquivalentModifierMask" command="YES"/>
|
||||
</menuItem>
|
||||
<menuItem title="Bring All to Front" id="5">
|
||||
<connections>
|
||||
<action selector="arrangeInFront:" target="-1" id="39"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
</items>
|
||||
</menu>
|
||||
</menuItem>
|
||||
<menuItem title="Help" id="103">
|
||||
<menu key="submenu" title="Help" id="106">
|
||||
<items>
|
||||
<menuItem title="NewApplication Help" keyEquivalent="?" id="111">
|
||||
<connections>
|
||||
<action selector="showHelp:" target="-1" id="360"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
</items>
|
||||
</menu>
|
||||
</menuItem>
|
||||
</items>
|
||||
</menu>
|
||||
<window title="Window" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" animationBehavior="default" id="371">
|
||||
<windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES"/>
|
||||
<windowPositionMask key="initialPositionMask" leftStrut="YES" bottomStrut="YES"/>
|
||||
<rect key="contentRect" x="335" y="390" width="630" height="447"/>
|
||||
<rect key="screenRect" x="0.0" y="0.0" width="1440" height="878"/>
|
||||
<view key="contentView" id="372">
|
||||
<rect key="frame" x="0.0" y="0.0" width="630" height="447"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<customView id="kko-W5-z1c">
|
||||
<rect key="frame" x="20" y="114" width="590" height="313"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<subviews>
|
||||
<customView id="oBo-tL-UGm">
|
||||
<rect key="frame" x="20" y="197" width="163" height="96"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<subviews>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" id="a7l-jw-yQG">
|
||||
<rect key="frame" x="71" y="28" width="20" height="39"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<textFieldCell key="cell" lineBreakMode="clipping" title="1" id="lcN-NH-TwV">
|
||||
<font key="font" metaFont="systemBold" size="32"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
</subviews>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="color" keyPath="backgroundColor">
|
||||
<color key="value" red="0.75626586289999997" green="0.12995527479999999" blue="0.16144353450000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</userDefinedRuntimeAttribute>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</customView>
|
||||
<customView id="XOc-Em-h1C">
|
||||
<rect key="frame" x="120" y="141" width="163" height="96"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<subviews>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" id="qjc-ru-kbm">
|
||||
<rect key="frame" x="71" y="28" width="24" height="39"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<textFieldCell key="cell" lineBreakMode="clipping" title="2" id="AIm-5j-LYJ">
|
||||
<font key="font" metaFont="systemBold" size="32"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
</subviews>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="color" keyPath="backgroundColor">
|
||||
<color key="value" red="0.13940883970000001" green="0.75626586289999997" blue="0.17305829089999999" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</userDefinedRuntimeAttribute>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</customView>
|
||||
<customView id="ptL-tL-Ce1">
|
||||
<rect key="frame" x="227" y="81" width="163" height="96"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<subviews>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" id="IRq-b6-aBj">
|
||||
<rect key="frame" x="71" y="28" width="25" height="39"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<textFieldCell key="cell" lineBreakMode="clipping" title="3" id="ZBF-2n-Xph">
|
||||
<font key="font" metaFont="systemBold" size="32"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
</subviews>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="color" keyPath="backgroundColor">
|
||||
<color key="value" red="0.2146045047" green="0.31081067029999998" blue="0.75626586289999997" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</userDefinedRuntimeAttribute>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</customView>
|
||||
</subviews>
|
||||
</customView>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" id="z23-TS-k6B">
|
||||
<rect key="frame" x="18" y="89" width="594" height="17"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<textFieldCell key="cell" lineBreakMode="clipping" alignment="center" title="You should see, backmost to frontmost, red view (1), green view (2) and blue view (3)" id="XbE-hd-Dmt">
|
||||
<font key="font" metaFont="systemBold"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
</subviews>
|
||||
</view>
|
||||
<point key="canvasLocation" x="438" y="235.5"/>
|
||||
</window>
|
||||
<customObject id="450" customClass="AppController">
|
||||
<connections>
|
||||
<outlet property="theWindow" destination="371" id="459"/>
|
||||
</connections>
|
||||
</customObject>
|
||||
</objects>
|
||||
</document>
|
||||
@@ -0,0 +1,204 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<!--
|
||||
index-debug.html
|
||||
Nib2CibSubviewsOrderTest
|
||||
|
||||
Created by You on September 19, 2020.
|
||||
Copyright 2020, Your Company All rights reserved.
|
||||
-->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
|
||||
<!--[if lte IE 8]>
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=EmulateIE7, chrome=1">
|
||||
<![endif]-->
|
||||
<!--[if gte IE 9]>
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge, chrome=1">
|
||||
<![endif]-->
|
||||
|
||||
<meta name="viewport" content="initial-scale=1.0, user-scalable=no">
|
||||
|
||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="black">
|
||||
|
||||
<link rel="apple-touch-icon" href="Resources/icon.png">
|
||||
<link rel="apple-touch-startup-image" href="Resources/default.png">
|
||||
|
||||
<title>Nib2CibSubviewsOrderTest</title>
|
||||
|
||||
<!-- Custom javascript goes here -->
|
||||
<!-- End custom javascript -->
|
||||
|
||||
<script type="text/javascript">
|
||||
OBJJ_MAIN_FILE = "main.j";
|
||||
OBJJ_INCLUDE_PATHS = ["Frameworks/Debug", "Frameworks"];
|
||||
// The below will tell the compiler to generate debug symbols, type signatures and not inline objj_msgSend functions.
|
||||
// This will affect only Objective-J code that is compiled when loading the application. It will not affect precompiled
|
||||
// code like the Cappuccino frameworks.
|
||||
//
|
||||
// Debug symbols will give each Objective-J method a Javascript function name. Without a name it will be very hard to find
|
||||
// the methods in the debugger.
|
||||
// Type Signatures will give type information to the Objective-J runtime for instance variables and methods.
|
||||
// Inline objj_msgSend will give a speed increase but decorators will not work. Check below on debug options for
|
||||
// more information on decorators.
|
||||
//
|
||||
// Uncomment or comment on the line below to change the flags
|
||||
OBJJ_COMPILER_FLAGS = ["IncludeDebugSymbols", "IncludeTypeSignatures", "SourceMap", "InlineMsgSend"];
|
||||
|
||||
var progressBar = null;
|
||||
|
||||
OBJJ_PROGRESS_CALLBACK = function(percent, appSize, path)
|
||||
{
|
||||
percent = percent * 100;
|
||||
|
||||
if (!progressBar)
|
||||
progressBar = document.getElementById("progress-bar");
|
||||
|
||||
if (progressBar)
|
||||
progressBar.style.width = Math.min(percent, 100) + "%";
|
||||
}
|
||||
|
||||
var loadingHTML =
|
||||
'<div id="loading">' +
|
||||
' <div id="loading-text">Loading...</div>' +
|
||||
' <div id="progress-indicator">' +
|
||||
' <span id="progress-bar" style="width:0%"></span>' +
|
||||
' </div>' +
|
||||
'</div>';
|
||||
</script>
|
||||
|
||||
<script type="text/javascript" src="Frameworks/Debug/Objective-J/Objective-J.js" charset="UTF-8"></script>
|
||||
|
||||
<script type="text/javascript">
|
||||
objj_msgSend_reset();
|
||||
|
||||
// DEBUG OPTIONS:
|
||||
// Decorators will only work when the code is compiled without the compiler option 'InlineMsgSend'. Check above.
|
||||
|
||||
// Uncomment to enable printing of backtraces on exceptions:
|
||||
//objj_msgSend_decorate(objj_backtrace_decorator);
|
||||
|
||||
// Uncomment to supress exceptions that take place inside a message
|
||||
//objj_msgSend_decorate(objj_supress_exceptions_decorator)
|
||||
|
||||
// Uncomment to enable runtime type checking:
|
||||
//objj_msgSend_decorate(objj_typecheck_decorator);
|
||||
|
||||
// Uncomment (along with both above) to print backtraces on type check errors:
|
||||
//objj_typecheck_prints_backtrace = true;
|
||||
|
||||
// Uncomment to disable the default logger (CPLogConsole if window.console exists, CPLogPopup otherwise):
|
||||
//CPLogUnregister(CPLogDefault);
|
||||
|
||||
// Uncomment to enable a specific logger:
|
||||
//CPLogRegister(CPLogConsole);
|
||||
//CPLogRegister(CPLogPopup);
|
||||
|
||||
// Tag view DOM elements with a "data-cappuccino-view" attribute that contains
|
||||
// the class name of the view that created them. Comment this or set to false to disable.
|
||||
appkit_tag_dom_elements = true;
|
||||
</script>
|
||||
|
||||
<style type="text/css">
|
||||
html, body, h1, p {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/* We need a body wrapper because Cappuccino is unhappy if we change the body element */
|
||||
#cappuccino-body {
|
||||
/* Position it absolutely so it will fill the height without content */
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
|
||||
/* Put it at the bottom of the stack so it doesn't interfere with UI */
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
#cappuccino-body .container {
|
||||
display: table;
|
||||
margin: 0 auto;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
#cappuccino-body .content {
|
||||
display: table-cell;
|
||||
height: 100%;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
#loading {
|
||||
position: relative;
|
||||
top: 35%;
|
||||
}
|
||||
|
||||
#loading-text {
|
||||
height: 1.5em;
|
||||
color: #555;
|
||||
font: normal bold 36px/36px Arial, sans-serif;
|
||||
}
|
||||
|
||||
#progress-indicator {
|
||||
padding: 0px;
|
||||
height: 16px;
|
||||
border: 5px solid #555;
|
||||
border-radius: 18px;
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
#progress-bar {
|
||||
position: relative;
|
||||
top: -1px;
|
||||
left: -1px;
|
||||
display: block;
|
||||
height: 18px;
|
||||
|
||||
/* Compensate for moving the bar left 1px to overlap the indicator border */
|
||||
border-right: 1px solid #555;
|
||||
background-color: #555;
|
||||
}
|
||||
|
||||
#noscript {
|
||||
position: relative;
|
||||
top: 35%;
|
||||
padding: 1em 1.5em;
|
||||
border: 5px solid #555;
|
||||
border-radius: 16px;
|
||||
background-color: white;
|
||||
color: #555;
|
||||
text-align: center;
|
||||
font: bold 24px Arial, sans-serif;
|
||||
}
|
||||
|
||||
#noscript a {
|
||||
color: #98c0ff;
|
||||
text-decoration: none;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="cappuccino-body">
|
||||
<div class="container">
|
||||
<div class="content">
|
||||
<script type="text/javascript">
|
||||
document.write(loadingHTML);
|
||||
</script>
|
||||
</div>
|
||||
</div>
|
||||
<noscript style="position:absolute; top:0; left:0; width:100%; height:100%">
|
||||
<div class="container">
|
||||
<div class="content">
|
||||
<div id="noscript">
|
||||
<p style="font-size:120%; margin-bottom:.75em">JavaScript is required for this site.</p>
|
||||
<p><a href="http://www.enable-javascript.com" target="_blank">Enable JavaScript</a></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</noscript>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,166 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<!--
|
||||
index.html
|
||||
Nib2CibSubviewsOrderTest
|
||||
|
||||
Created by You on September 19, 2020.
|
||||
Copyright 2020, Your Company All rights reserved.
|
||||
-->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
|
||||
<!--[if lte IE 8]>
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=EmulateIE7, chrome=1">
|
||||
<![endif]-->
|
||||
<!--[if gte IE 9]>
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge, chrome=1">
|
||||
<![endif]-->
|
||||
|
||||
<meta name="viewport" content="initial-scale=1.0, user-scalable=no">
|
||||
|
||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="black">
|
||||
|
||||
<link rel="apple-touch-icon" href="Resources/icon.png">
|
||||
<link rel="apple-touch-startup-image" href="Resources/default.png">
|
||||
|
||||
<title>Nib2CibSubviewsOrderTest</title>
|
||||
|
||||
<!-- Custom javascript goes here -->
|
||||
<!-- End custom javascript -->
|
||||
|
||||
<script type="text/javascript">
|
||||
OBJJ_MAIN_FILE = "main.j";
|
||||
// The below will tell the compiler to not generate debug symbols but will generate type signatures and inline objj_msgSend functions.
|
||||
// This will affect only Objective-J code that is compiled when loading the application. It will not affect precompiled
|
||||
// code like the Cappuccino frameworks.
|
||||
// Uncomment or comment on the line below to change the flags
|
||||
OBJJ_COMPILER_FLAGS = [/*"IncludeDebugSymbols"*/, "IncludeTypeSignatures"/*, "SourceMap"*/, "InlineMsgSend"];
|
||||
|
||||
var progressBar = null;
|
||||
|
||||
OBJJ_PROGRESS_CALLBACK = function(percent, appSize, path)
|
||||
{
|
||||
percent = percent * 100;
|
||||
|
||||
if (!progressBar)
|
||||
progressBar = document.getElementById("progress-bar");
|
||||
|
||||
if (progressBar)
|
||||
progressBar.style.width = Math.min(percent, 100) + "%";
|
||||
}
|
||||
|
||||
var loadingHTML =
|
||||
'<div id="loading">' +
|
||||
' <div id="loading-text">Loading...</div>' +
|
||||
' <div id="progress-indicator">' +
|
||||
' <span id="progress-bar" style="width:0%"></span>' +
|
||||
' </div>' +
|
||||
'</div>';
|
||||
</script>
|
||||
|
||||
<script type="text/javascript" src="Frameworks/Objective-J/Objective-J.js" charset="UTF-8"></script>
|
||||
|
||||
<style type="text/css">
|
||||
html, body, h1, p {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/* We need a body wrapper because Cappuccino is unhappy if we change the body element */
|
||||
#cappuccino-body {
|
||||
/* Position it absolutely so it will fill the height without content */
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
|
||||
/* Put it at the bottom of the stack so it doesn't interfere with UI */
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
#cappuccino-body .container {
|
||||
display: table;
|
||||
margin: 0 auto;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
#cappuccino-body .content {
|
||||
display: table-cell;
|
||||
height: 100%;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
#loading {
|
||||
position: relative;
|
||||
top: 35%;
|
||||
}
|
||||
|
||||
#loading-text {
|
||||
height: 1.5em;
|
||||
color: #555;
|
||||
font: normal bold 36px/36px Arial, sans-serif;
|
||||
}
|
||||
|
||||
#progress-indicator {
|
||||
padding: 0px;
|
||||
height: 16px;
|
||||
border: 5px solid #555;
|
||||
border-radius: 18px;
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
#progress-bar {
|
||||
position: relative;
|
||||
top: -1px;
|
||||
left: -1px;
|
||||
display: block;
|
||||
height: 18px;
|
||||
|
||||
/* Compensate for moving the bar left 1px to overlap the indicator border */
|
||||
border-right: 1px solid #555;
|
||||
background-color: #555;
|
||||
}
|
||||
|
||||
#noscript {
|
||||
position: relative;
|
||||
top: 35%;
|
||||
padding: 1em 1.5em;
|
||||
border: 5px solid #555;
|
||||
border-radius: 16px;
|
||||
background-color: white;
|
||||
color: #555;
|
||||
text-align: center;
|
||||
font: bold 24px Arial, sans-serif;
|
||||
}
|
||||
|
||||
#noscript a {
|
||||
color: #98c0ff;
|
||||
text-decoration: none;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="cappuccino-body">
|
||||
<div class="container">
|
||||
<div class="content">
|
||||
<script type="text/javascript">
|
||||
document.write(loadingHTML);
|
||||
</script>
|
||||
</div>
|
||||
</div>
|
||||
<noscript style="position:absolute; top:0; left:0; width:100%; height:100%">
|
||||
<div class="container">
|
||||
<div class="content">
|
||||
<div id="noscript">
|
||||
<p style="font-size:120%; margin-bottom:.75em">JavaScript is required for this site.</p>
|
||||
<p><a href="http://www.enable-javascript.com" target="_blank">Enable JavaScript</a></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</noscript>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
* AppController.j
|
||||
* Nib2CibSubviewsOrderTest
|
||||
*
|
||||
* Created by You on September 19, 2020.
|
||||
* Copyright 2020, Your Company All rights reserved.
|
||||
*/
|
||||
|
||||
@import <Foundation/Foundation.j>
|
||||
@import <AppKit/AppKit.j>
|
||||
|
||||
@import "AppController.j"
|
||||
|
||||
|
||||
function main(args, namedArgs)
|
||||
{
|
||||
CPApplicationMain(args, namedArgs);
|
||||
}
|
||||
@@ -32,6 +32,7 @@
|
||||
{
|
||||
_boxType = [aCoder decodeIntForKey:@"NSBoxType"];
|
||||
_borderType = [aCoder decodeIntForKey:@"NSBorderType"];
|
||||
_transparent = [aCoder decodeBoolForKey:@"NSTransparent"];
|
||||
|
||||
var borderColor = [aCoder decodeObjectForKey:@"NSBorderColor2"],
|
||||
fillColor = [aCoder decodeObjectForKey:@"NSFillColor2"],
|
||||
@@ -70,7 +71,7 @@
|
||||
|
||||
- (CGRect)_nib2CibAdjustment
|
||||
{
|
||||
if ((_boxType === CPBoxPrimary) || (_boxType === CPBoxSecondary))
|
||||
if ((_boxType !== CPBoxSeparator) && ((_boxType === CPBoxPrimary) || (_boxType === CPBoxSecondary)))
|
||||
{
|
||||
// We use a special nib2cib-adjustment-frame for primary/secondary boxes
|
||||
var theme = [Nib2Cib defaultTheme],
|
||||
|
||||
+33
-16
@@ -43,13 +43,13 @@ var NSButtonIsBorderedMask = 0x00800000,
|
||||
// don't really follow much of a pattern.
|
||||
NSButtonImagePositionMask = 0xFF0000,
|
||||
NSButtonImagePositionShift = 16,
|
||||
NSButtonNoImagePositionMask = 0x04,
|
||||
NSButtonImageAbovePositionMask = 0x0C,
|
||||
NSButtonImageBelowPositionMask = 0x1C,
|
||||
NSButtonImageRightPositionMask = 0x2C,
|
||||
NSButtonImageLeftPositionMask = 0x3C,
|
||||
NSButtonImageOnlyPositionMask = 0x44,
|
||||
NSButtonImageOverlapsPositionMask = 0x6C,
|
||||
NSButtonNoImagePositionMask = 0x00,
|
||||
NSButtonImageAbovePositionMask = 0x08,
|
||||
NSButtonImageBelowPositionMask = 0x18,
|
||||
NSButtonImageRightPositionMask = 0x28,
|
||||
NSButtonImageLeftPositionMask = 0x38,
|
||||
NSButtonImageOnlyPositionMask = 0x40,
|
||||
NSButtonImageOverlapsPositionMask = 0x48,
|
||||
|
||||
// You cannot set neither highlightsBy nor showsStateBy in IB,
|
||||
// but you can set button type which implicitly sets the masks.
|
||||
@@ -111,9 +111,25 @@ var NSButtonIsBorderedMask = 0x00800000,
|
||||
_alternateTitle = [cell alternateTitle];
|
||||
|
||||
[self setBordered:[cell isBordered]];
|
||||
_bezelStyle = [cell bezelStyle];
|
||||
|
||||
var fixedHeight;
|
||||
var bezelStyleFromIB = [cell bezelStyle];
|
||||
|
||||
// Xcode IB is not consistent between popup and pulldown buttons :
|
||||
// pulldown bordered buttons (type "push") get bezel type "rounded"
|
||||
// pulldown unbordered buttons (type "bevel") get bezel type "regular square"
|
||||
// popup bordered buttons (type "push") get bezel type "rounded"
|
||||
// popup unbordered buttons (type "bevel") get bezel type "rounded"
|
||||
// In order to fix consistency, we force bezel type to be "regular square" in the last case
|
||||
|
||||
if ([self isKindOfClass:CPPopUpButton] && (![self isBordered]))
|
||||
bezelStyleFromIB = CPRegularSquareBezelStyle;
|
||||
|
||||
[self setBezelStyle:bezelStyleFromIB];
|
||||
|
||||
|
||||
var fixedHeight,
|
||||
theme = [Nib2Cib defaultTheme],
|
||||
isCSSBased = [theme valueForAttributeWithName:@"css-based" forClass:[CPView class]];
|
||||
|
||||
// Fix height
|
||||
switch (_bezelStyle)
|
||||
@@ -122,6 +138,9 @@ var NSButtonIsBorderedMask = 0x00800000,
|
||||
case CPRoundedBezelStyle: // Push IB style
|
||||
case CPTexturedRoundedBezelStyle: // Round Textured IB style
|
||||
case CPHUDBezelStyle:
|
||||
case CPDisclosureBezelStyle:
|
||||
case CPRoundedDisclosureBezelStyle:
|
||||
case CPInlineBezelStyle:
|
||||
|
||||
// approximations:
|
||||
case CPRoundRectBezelStyle: // Round Rect IB style
|
||||
@@ -139,10 +158,8 @@ var NSButtonIsBorderedMask = 0x00800000,
|
||||
break;
|
||||
|
||||
// unsupported
|
||||
case CPRoundedDisclosureBezelStyle:
|
||||
case CPHelpButtonBezelStyle:
|
||||
case CPCircularBezelStyle:
|
||||
case CPDisclosureBezelStyle:
|
||||
CPLog.warn("NSButton [%s]: unsupported bezel style: %d", _title == null ? "<no title>" : '"' + _title + '"', _bezelStyle);
|
||||
_bezelStyle = CPHUDBezelStyle;
|
||||
fixedHeight = YES;
|
||||
@@ -165,9 +182,8 @@ var NSButtonIsBorderedMask = 0x00800000,
|
||||
- If there is just a max height, use that for only for fixed height buttons.
|
||||
- If there is no max height either, don't do any height adjustments.
|
||||
*/
|
||||
var theme = [Nib2Cib defaultTheme],
|
||||
minSize = [theme valueForAttributeWithName:@"min-size" forClass:[self class]],
|
||||
maxSize = [theme valueForAttributeWithName:@"max-size" forClass:[self class]],
|
||||
var minSize = [theme valueForAttributeWithName:@"min-size" inState:[self themeState] forClass:[self class]],
|
||||
maxSize = [theme valueForAttributeWithName:@"max-size" inState:[self themeState] forClass:[self class]],
|
||||
adjustHeight = NO;
|
||||
|
||||
if (minSize.height > 0 && maxSize.height > 0 && minSize.height === maxSize.height)
|
||||
@@ -220,10 +236,11 @@ var NSButtonIsBorderedMask = 0x00800000,
|
||||
var frameAdjustment = [super _nib2CibAdjustment],
|
||||
positionOffsetSizeWidth = 0,
|
||||
positionOffsetOriginX = 0,
|
||||
positionOffsetOriginY = 0;
|
||||
positionOffsetOriginY = 0,
|
||||
directAdjustment = [[Nib2Cib defaultTheme] valueForAttributeWithName:@"direct-nib2cib-adjustment" inState:[self themeState] forClass:[self class]];
|
||||
|
||||
// We want certain control like CPPopupButton to have their own nib2cib-adjustment-frame theme attribute
|
||||
if (![self isBordered] || [self isKindOfClass:[CPPopUpButton class]])
|
||||
if (![self isBordered] || [self isKindOfClass:[CPPopUpButton class]] || directAdjustment)
|
||||
return frameAdjustment;
|
||||
|
||||
// Map Cocoa bezel styles to Cappuccino bezel styles and adjust frame
|
||||
|
||||
@@ -34,6 +34,7 @@ var FILE = require("file"),
|
||||
supportedTemplateImages = {
|
||||
"NSAddTemplate": "CPAddTemplate",
|
||||
"NSRemoveTemplate": "CPRemoveTemplate",
|
||||
"NSActionTemplate": "CPActionTemplate",
|
||||
"NSToolbarShowColors": "CPImageNameColorPanel"
|
||||
};
|
||||
|
||||
@@ -64,6 +65,12 @@ var FILE = require("file"),
|
||||
else
|
||||
[CPException raise:Nib2CibException format:@"The built in image “%@” is not supported.", _resourceName];
|
||||
}
|
||||
else if (/^(.+)@MaterialIcons+$/.test(_resourceName))
|
||||
{
|
||||
// Catch a material icon specified by "icon-name@MaterialIcons"
|
||||
// Just do nothing and leave _resourceName as is.
|
||||
// It will be treated during runtime by _CPCibCustomResource
|
||||
}
|
||||
else
|
||||
{
|
||||
var match = /^(.+)@(.+)$/.exec(_resourceName);
|
||||
|
||||
@@ -41,6 +41,13 @@ var _CPCibCustomViewClassNameKey = @"_CPCibCustomViewClassNameKey";
|
||||
if (self)
|
||||
_className = [aCoder decodeObjectForKey:@"NSClassName"];
|
||||
|
||||
// FIXME: Warning : trick here !
|
||||
// Workaround for the ibtool bug which reverses subviews order when using a custom view
|
||||
// Valid as of ibtool version 14460.31
|
||||
// As nib2cib doesn't deal with DOM, we can simply use reverse()
|
||||
|
||||
_subviews.reverse();
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
+70
-11
@@ -24,6 +24,7 @@
|
||||
|
||||
@import "NSCell.j"
|
||||
|
||||
@class Nib2Cib
|
||||
|
||||
@implementation CPSlider (CPCoding)
|
||||
|
||||
@@ -44,6 +45,9 @@
|
||||
|
||||
_altIncrementValue = [cell altIncrementValue];
|
||||
[self setSliderType:[cell sliderType]];
|
||||
[self setAllowsTickMarkValuesOnly:[cell allowsTickMarkValuesOnly]];
|
||||
[self setTickMarkPosition:[cell tickMarkPosition]];
|
||||
[self setNumberOfTickMarks:[cell numberOfTickMarks]];
|
||||
[self setEnabled:[cell isEnabled]];
|
||||
}
|
||||
|
||||
@@ -63,12 +67,17 @@
|
||||
[self NS_initWithCell:cell];
|
||||
[self _adjustNib2CibSize];
|
||||
|
||||
var frame = [self frame];
|
||||
var directAdjustment = [[Nib2Cib defaultTheme] valueForAttributeWithName:@"direct-nib2cib-adjustment" forClass:[self class]];
|
||||
|
||||
if ([self sliderType] === CPCircularSlider)
|
||||
[self setFrameSize:CGSizeMake(frame.size.width + 2.0, frame.size.height + 2.0)];
|
||||
else
|
||||
[self setFrame:CGRectMake(frame.origin.x + 2, frame.origin.y, frame.size.width - 4, frame.size.height)];
|
||||
if (!directAdjustment)
|
||||
{
|
||||
var frame = [self frame];
|
||||
|
||||
if ([self sliderType] === CPCircularSlider)
|
||||
[self setFrameSize:CGSizeMake(frame.size.width + 2.0, frame.size.height + 2.0)];
|
||||
else
|
||||
[self setFrame:CGRectMake(frame.origin.x + 2, frame.origin.y, frame.size.width - 4, frame.size.height)];
|
||||
}
|
||||
}
|
||||
|
||||
return self;
|
||||
@@ -79,15 +88,62 @@
|
||||
return [CPSlider class];
|
||||
}
|
||||
|
||||
- (CGRect)_nib2CibAdjustment
|
||||
{
|
||||
var directAdjustment = [[Nib2Cib defaultTheme] valueForAttributeWithName:@"direct-nib2cib-adjustment" forClass:[self class]];
|
||||
|
||||
if (!directAdjustment)
|
||||
return [super _nib2CibAdjustment];
|
||||
|
||||
var size = [self frameSize],
|
||||
state;
|
||||
|
||||
if ([self sliderType] === CPCircularSlider)
|
||||
state = CPThemeStateCircular;
|
||||
else if (size.height > size.width)
|
||||
{
|
||||
state = CPThemeStateVertical;
|
||||
state = state.and(([self tickMarkPosition] === CPTickMarkPositionTrailing) ? CPThemeStateBelowRightTickedSlider : CPThemeStateAboveLeftTickedSlider);
|
||||
}
|
||||
else
|
||||
{
|
||||
state = CPThemeStateNormal;
|
||||
state = state.and(([self tickMarkPosition] === CPTickMarkPositionBelow) ? CPThemeStateBelowRightTickedSlider : CPThemeStateAboveLeftTickedSlider);
|
||||
}
|
||||
|
||||
if ([self numberOfTickMarks] > 0)
|
||||
state = state.and(CPThemeStateTickedSlider);
|
||||
|
||||
// Theme has not been loaded yet.
|
||||
// Get attribute value directly from the theme or from the default value of the object otherwise.
|
||||
var frameAdjustment = [[Nib2Cib defaultTheme] valueForAttributeWithName:@"nib2cib-adjustment-frame" inState:state forClass:[self class]];
|
||||
|
||||
if (frameAdjustment)
|
||||
return frameAdjustment;
|
||||
|
||||
if ([self hasThemeAttribute:@"nib2cib-adjustment-frame"])
|
||||
{
|
||||
frameAdjustment = [self valueForThemeAttribute:@"nib2cib-adjustment-frame" inState:state];
|
||||
|
||||
if (frameAdjustment)
|
||||
return frameAdjustment;
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation NSSliderCell : NSCell
|
||||
{
|
||||
double _minValue @accessors(readonly, getter=minValue);
|
||||
double _maxValue @accessors(readonly, getter=maxValue);
|
||||
double _altIncrementValue @accessors(readonly, getter=altIncrementValue);
|
||||
BOOL _vertical @accessors(readonly, getter=isVertical);
|
||||
int _sliderType @accessors(readonly, getter=sliderType);
|
||||
double _minValue @accessors(readonly, getter=minValue);
|
||||
double _maxValue @accessors(readonly, getter=maxValue);
|
||||
double _altIncrementValue @accessors(readonly, getter=altIncrementValue);
|
||||
BOOL _vertical @accessors(readonly, getter=isVertical);
|
||||
int _sliderType @accessors(readonly, getter=sliderType);
|
||||
BOOL _allowsTickMarkValuesOnly @accessors(readonly, getter=allowsTickMarkValuesOnly);
|
||||
int _tickMarkPosition @accessors(readonly, getter=tickMarkPosition);
|
||||
int _numberOfTickMarks @accessors(readonly, getter=numberOfTickMarks);
|
||||
}
|
||||
|
||||
- (id)initWithCoder:(CPCoder)aCoder
|
||||
@@ -103,7 +159,10 @@
|
||||
self._altIncrementValue = [aCoder decodeDoubleForKey:@"NSAltIncValue"];
|
||||
self._isVertical = [aCoder decodeBoolForKey:@"NSVertical"];
|
||||
|
||||
self._sliderType = [aCoder decodeIntForKey:@"NSSliderType"];
|
||||
self._sliderType = [aCoder decodeIntForKey:@"NSSliderType"];
|
||||
self._allowsTickMarkValuesOnly = [aCoder decodeIntForKey:@"NSAllowsTickMarkValuesOnly"];
|
||||
self._tickMarkPosition = [aCoder decodeIntForKey:@"NSTickMarkPosition"];
|
||||
self._numberOfTickMarks = [aCoder decodeIntForKey:@"NSNumberOfTickMarks"];
|
||||
}
|
||||
|
||||
return self;
|
||||
|
||||
@@ -51,7 +51,7 @@
|
||||
|
||||
[self setLineBreakMode:[cell lineBreakMode]];
|
||||
[self setAlignment:[cell alignment]];
|
||||
[self setTextFieldBackgroundColor:[cell backgroundColor]];
|
||||
[self setBackgroundColor:[cell backgroundColor]];
|
||||
|
||||
[self setPlaceholderString:[cell placeholderString]];
|
||||
|
||||
@@ -72,6 +72,38 @@
|
||||
CPLog.debug(">> Formatter: " + [[self formatter] description]);
|
||||
}
|
||||
|
||||
// Labels WITHOUT background use a special adjustment frame.
|
||||
// As we can't just let the theming system choose, we have to
|
||||
// adapt _nib2cibAdjustment
|
||||
- (CGRect)_nib2CibAdjustment
|
||||
{
|
||||
// Theme has not been loaded yet.
|
||||
// Get attribute value directly from the theme or from the default value of the object otherwise.
|
||||
var theme = [Nib2Cib defaultTheme],
|
||||
themeState = [self themeState];
|
||||
|
||||
// Is this a label with a background ?
|
||||
if (!([self hasThemeState:CPThemeStateBezeled] || [self hasThemeState:CPThemeStateBordered]) && [self drawsBackground])
|
||||
|
||||
// Yes, so use normal frame adjustment (that is, consider it's bezeled)
|
||||
themeState = themeState.and(CPThemeStateBezeled);
|
||||
|
||||
var frameAdjustment = [theme valueForAttributeWithName:@"nib2cib-adjustment-frame" inState:themeState forClass:[self class]];
|
||||
|
||||
if (frameAdjustment)
|
||||
return frameAdjustment;
|
||||
|
||||
if ([self hasThemeAttribute:@"nib2cib-adjustment-frame"])
|
||||
{
|
||||
frameAdjustment = [self valueForThemeAttribute:@"nib2cib-adjustment-frame" inState:themeState];
|
||||
|
||||
if (frameAdjustment)
|
||||
return frameAdjustment;
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation NSTextField : CPTextField
|
||||
|
||||
Reference in New Issue
Block a user