Merge branch 'master' into 010-CPApplicationOSBehavior

This commit is contained in:
Didier Korthoudt
2020-10-10 18:27:45 +02:00
committed by GitHub
56 changed files with 6012 additions and 609 deletions
+319 -74
View File
@@ -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 receivers 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 receivers 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 receivers 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 receivers 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 receivers 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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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)
+1 -6
View File
@@ -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;
+31
View File
@@ -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"
+15 -2
View File
@@ -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];
+37 -24
View File
@@ -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
+4
View File
@@ -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
};
}
+19 -4
View File
@@ -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);
+75 -21
View File
@@ -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
View File
@@ -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
View File
File diff suppressed because it is too large Load Diff
+6 -6
View File
@@ -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
View File
@@ -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
+2
View File
@@ -49,6 +49,8 @@ var CPThemesByName = { },
+ (void)setDefaultTheme:(CPTheme)aTheme
{
CPThemeDefaultTheme = aTheme;
[CPFont initializeSystemFontFromTheme:aTheme];
}
+ (CPTheme)defaultTheme
+1 -1
View File
@@ -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
View File
@@ -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.
+42 -1
View File
@@ -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
+45 -5
View File
@@ -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);
+16 -2
View File
@@ -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)
+1 -1
View File
@@ -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];
}
Binary file not shown.
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 275 KiB

Binary file not shown.
Binary file not shown.
Binary file not shown.
+36
View File
@@ -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)
+18 -11
View File
@@ -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)
+17 -1
View File
@@ -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
+6 -1
View File
@@ -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];
+3 -3
View File
@@ -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;
}
+73 -9
View File
@@ -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
File diff suppressed because one or more lines are too long
+22
View File
@@ -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);
}
+2 -1
View File
@@ -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
View File
@@ -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
+7
View File
@@ -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);
+7
View File
@@ -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
View File
@@ -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;
+33 -1
View File
@@ -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