mirror of
https://github.com/cappuccino/cappuccino.git
synced 2026-08-25 04:57:03 +00:00
New: Added dynamic attributes set for CSS based themes (#2701)
This commit is contained in:
@@ -26,6 +26,7 @@
|
||||
@import "_CPMenuManager.j"
|
||||
|
||||
@class _CPMenuView
|
||||
@class CPMenuItem
|
||||
|
||||
var _CPMenuWindowPool = [],
|
||||
_CPMenuWindowPoolCapacity = 5,
|
||||
@@ -419,6 +420,21 @@ _CPMenuWindowAttachedMenuBackgroundStyle = 2;
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark -
|
||||
|
||||
@implementation _CPMenuWindow (CSSTheming)
|
||||
|
||||
- (void)_setThemeIncludingDescendants:(CPTheme)aTheme
|
||||
{
|
||||
[[self contentView] _setThemeIncludingDescendants:aTheme];
|
||||
[_menuView _setThemeIncludingDescendants:aTheme];
|
||||
[_menuView tile];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark -
|
||||
|
||||
/*
|
||||
@ignore
|
||||
*/
|
||||
@@ -552,6 +568,7 @@ _CPMenuWindowAttachedMenuBackgroundStyle = 2;
|
||||
|
||||
[view setFrameOrigin:CGPointMake(0.0, y)];
|
||||
|
||||
[view _setThemeIncludingDescendants:[CPTheme defaultTheme]];
|
||||
[self addSubview:view];
|
||||
|
||||
var size = [view minSize],
|
||||
@@ -582,3 +599,18 @@ _CPMenuWindowAttachedMenuBackgroundStyle = 2;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark -
|
||||
|
||||
@implementation _CPMenuView (CSSTheming)
|
||||
|
||||
- (void)_setThemeIncludingDescendants:(CPTheme)aTheme
|
||||
{
|
||||
[self setTheme:aTheme];
|
||||
[_menuItemViews makeObjectsPerformSelector:@selector(_setThemeIncludingDescendants:) withObject:aTheme];
|
||||
|
||||
for (var i = 0, items = [[self menu] itemArray], count = [items count]; i < count; i++)
|
||||
[[items[i] _menuItemView] _setThemeIncludingDescendants:aTheme];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -897,6 +897,20 @@ CPControlKeyMask
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark -
|
||||
|
||||
@implementation CPMenuItem (CSSTheming)
|
||||
|
||||
- (void)_setThemeIncludingDescendants:(CPTheme)aTheme
|
||||
{
|
||||
[_view _setThemeIncludingDescendants:aTheme];
|
||||
[_menuItemView _setThemeIncludingDescendants:aTheme];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark -
|
||||
|
||||
var CPMenuItemIsSeparatorKey = @"CPMenuItemIsSeparatorKey",
|
||||
|
||||
CPMenuItemTitleKey = @"CPMenuItemTitleKey",
|
||||
|
||||
@@ -352,6 +352,22 @@
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark -
|
||||
|
||||
@implementation _CPMenuItemStandardView (CSSTheming)
|
||||
|
||||
#pragma mark Override
|
||||
|
||||
- (void)_setThemeIncludingDescendants:(CPTheme)aTheme
|
||||
{
|
||||
[self setTheme:aTheme];
|
||||
[[self subviews] makeObjectsPerformSelector:@selector(_setThemeIncludingDescendants:) withObject:aTheme];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark -
|
||||
|
||||
@implementation _CPMenuItemSubmenuIndicatorView : CPView
|
||||
{
|
||||
CPColor _color;
|
||||
|
||||
@@ -252,6 +252,31 @@
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark -
|
||||
|
||||
@implementation _CPMenuItemView (CSSTheming)
|
||||
|
||||
#pragma mark Override
|
||||
|
||||
- (void)_setThemeIncludingDescendants:(CPTheme)aTheme
|
||||
{
|
||||
[self setTheme:aTheme];
|
||||
[_view _setThemeIncludingDescendants:aTheme];
|
||||
|
||||
// Items must also perform this (without this, only the selected item does)
|
||||
|
||||
[_imageAndTextView _setThemeIncludingDescendants:aTheme];
|
||||
[_submenuView _setThemeIncludingDescendants:aTheme];
|
||||
[[_menuItem view] _setThemeIncludingDescendants:aTheme];
|
||||
|
||||
if ([_view respondsToSelector:@selector(update)])
|
||||
[_view update];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark -
|
||||
|
||||
@implementation _CPMenuItemArrowView : CPView
|
||||
{
|
||||
CPColor _color;
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
@class _CPThemeAttribute
|
||||
@class CPImage
|
||||
@class CPColor
|
||||
@class CPApplication
|
||||
|
||||
var CPThemesByName = { },
|
||||
CPThemeDefaultTheme = nil,
|
||||
@@ -339,9 +340,143 @@ var CPThemeNameKey = @"CPThemeNameKey",
|
||||
//
|
||||
// The method -(void)setCSSResourcesPath is meant to be used only by CPThemeBlend during theme loading in order to
|
||||
// replace the special path "%%" in CSS components (like in url(%%packed.png) ) with the path to the theme blend resources folder.
|
||||
//
|
||||
// DYNAMIC ATTRIBUTES SET:
|
||||
//
|
||||
// If the default theme is CSS based and exposes some dynamic attributes (like Aristo3 does for color schema), you can switch "live"
|
||||
// to another set of values by calling [CPTheme updateDefaultThemeDynamicAttributesSetWithSet:aDynamicSet] where aDynamicSet is a
|
||||
// dictionary with keys being the names of exposed dynamic attributes (see the themeDescriptor.j of the CSS based theme you use)
|
||||
// and values, accordingly to the type of the corresponding default values provided by the theme (CPColor, a CSS color string, ...).
|
||||
// You can provide key-value pairs for a subset of the exposed dynamic attributes. If no key-value pair is specified for a dynamic
|
||||
// attribute, the theme default value will be used.
|
||||
//
|
||||
// To restore default theme values, simply pass nil as dynamic set : [CPTheme updateDefaultThemeDynamicAttributesSetWithSet:nil]
|
||||
//
|
||||
// During theme loading, if your application responds to -(CPDictionary)themeDynamicAttributesSet, the returned values set is directly
|
||||
// applied to the default set. This avoids displaying the theme with its default values and then redisplaying it with your values.
|
||||
// The -(CPDictionary)themeDynamicAttributesSet must be declared in a CPApplication category (and not in your application delegate) as
|
||||
// it will be called during application initialisation, before the delegate has been defined.
|
||||
//
|
||||
// To use default theme values, simply return nil.
|
||||
//
|
||||
//
|
||||
// TYPICAL USAGE:
|
||||
//
|
||||
// One can imagine that a user can choose between some predefined color schemas. You can store this user preference in standard user defaults:
|
||||
//
|
||||
// [[CPUserDefaults standardUserDefaults] setObject:ConstantRepresentingAColorSchema forKey:@"userTheme"];
|
||||
// [[CPUserDefaults standardUserDefaults] synchronize];
|
||||
//
|
||||
// As you want to use the choosen color schema at run time, you've implemented -(CPDictionary)themeDynamicAttributesSet (see below), so updating
|
||||
// the UI as soon as the user has made his choice is easy :
|
||||
//
|
||||
// [CPTheme updateDefaultThemeDynamicAttributesSetWithSet:[[CPApplication sharedApplication] themeDynamicAttributesSet]];
|
||||
//
|
||||
// If the user chooses the theme default color schema, you can then simply remove the user default:
|
||||
//
|
||||
// [[CPUserDefaults standardUserDefaults] removeObjectForKey:@"userTheme"];
|
||||
// [[CPUserDefaults standardUserDefaults] synchronize];
|
||||
//
|
||||
// Your CPApplication category could then be something like:
|
||||
//
|
||||
// @implementation CPApplication (themeColorSchema)
|
||||
//
|
||||
// - (CPDictionary)themeDynamicAttributesSet
|
||||
// {
|
||||
// var userTheme = [[CPUserDefaults standardUserDefaults] objectForKey:@"userTheme"];
|
||||
//
|
||||
// if (!userTheme)
|
||||
|
||||
// // Return nil to have the theme default color schema
|
||||
// return nil;
|
||||
//
|
||||
// switch (userTheme)
|
||||
// {
|
||||
// case ConstantRepresentingAColorSchema:
|
||||
// return @{
|
||||
// @"ExposedDynamicAttribute1": [CPColor colorWithRed:245.0/255.0 green:164.0/255.0 blue:9.0/255.0 alpha:0.85],
|
||||
// @"ExposedDynamicAttribute2": [CPColor colorWithRed:245.0/255.0 green:164.0/255.0 blue:9.0/255.0 alpha:0.85],
|
||||
// @"ExposedDynamicAttribute3": @"rgb(245,164,9)",
|
||||
// @"ExposedDynamicAttribute4": @"url(./Resources/mypacked.png)"
|
||||
// };
|
||||
// break;
|
||||
//
|
||||
// case AnotherConstantRepresentingAColorSchema:
|
||||
// return @{
|
||||
// @"ExposedDynamicAttribute1": [CPColor colorWithRed:39.0/255.0 green:216.0/255.0 blue:93.0/255.0 alpha:0.85],
|
||||
// @"ExposedDynamicAttribute3": @"rgb(39,216,93)"
|
||||
// };
|
||||
// break;
|
||||
//
|
||||
// ...
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// @end
|
||||
|
||||
var _savedThemesByName = { };
|
||||
|
||||
@implementation CPTheme (CSSTheming)
|
||||
|
||||
- (void)_initializeDynamicAttributesAndResourcesPathWithBundle:(CPBundle)aBundle
|
||||
{
|
||||
// First, save a template of the theme so dynamic attributes and images placeholders are preserved
|
||||
_savedThemesByName[_name] = [CPKeyedArchiver archivedDataWithRootObject:self];
|
||||
|
||||
// Then, compute the dynamic attributes set that will be applied to the theme
|
||||
// Does the application delegate provide a dynamic attributes set ?
|
||||
var dynamicSet = ([[CPApplication sharedApplication] respondsToSelector:@selector(themeDynamicAttributesSet)]) ? [[CPApplication sharedApplication] themeDynamicAttributesSet] : nil;
|
||||
|
||||
// Take the one defined in the theme
|
||||
var defaultDynamicSet = [self valueForAttributeWithName:@"dynamic-set" forClass:[CPView class]];
|
||||
|
||||
// Update the default dynamic attributes set with the one provided by the application
|
||||
[dynamicSet enumerateKeysAndObjectsUsingBlock:function(aKey, anObject, stop)
|
||||
{
|
||||
[defaultDynamicSet setObject:anObject forKey:aKey];
|
||||
}];
|
||||
|
||||
// Apply the resulting set
|
||||
[self applyDynamicSet:defaultDynamicSet];
|
||||
|
||||
// Then fix resources path in CSS directives
|
||||
[self setCSSResourcesPath:[aBundle resourcePath]];
|
||||
}
|
||||
|
||||
+ (void)updateDefaultThemeDynamicAttributesSetWithSet:(CPDictionary)dynamicSet
|
||||
{
|
||||
// First, get a copy of the theme template
|
||||
var newTheme = [CPKeyedUnarchiver unarchiveObjectWithData:_savedThemesByName[[CPThemeDefaultTheme name]]];
|
||||
|
||||
// Take the dynamic set defined in the theme
|
||||
var defaultDynamicSet = [newTheme valueForAttributeWithName:@"dynamic-set" forClass:[CPView class]];
|
||||
|
||||
// Update the default dynamic attributes set with the one provided by the application
|
||||
[dynamicSet enumerateKeysAndObjectsUsingBlock:function(aKey, anObject, stop)
|
||||
{
|
||||
[defaultDynamicSet setObject:anObject forKey:aKey];
|
||||
}];
|
||||
|
||||
// Apply the resulting set to the theme
|
||||
[newTheme applyDynamicSet:defaultDynamicSet];
|
||||
|
||||
// Then, fix resources path in CSS directives
|
||||
[newTheme setCSSResourcesPath:[[[[CPApplication sharedApplication] themeBlend] bundle] resourcePath]];
|
||||
|
||||
// Now apply the new theme variation
|
||||
[CPTheme setDefaultTheme:newTheme];
|
||||
|
||||
var windows = [[CPApplication sharedApplication] windows];
|
||||
|
||||
for (var i = 0, count = windows.length; i < count; i++)
|
||||
{
|
||||
[windows[i] _setThemeIncludingDescendants:newTheme];
|
||||
}
|
||||
|
||||
// Update the menu bar
|
||||
[CPMenu updateMenuBarAttributesWithTheme:newTheme];
|
||||
}
|
||||
|
||||
- (void)setCSSResourcesPath:(CPString)pathToResources
|
||||
{
|
||||
[_attributes enumerateKeysAndObjectsUsingBlock:function(aKey, anObject, stop)
|
||||
@@ -372,6 +507,46 @@ var CPThemeNameKey = @"CPThemeNameKey",
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)applyDynamicSet:(CPDictionary)dynamicSet
|
||||
{
|
||||
[_attributes enumerateKeysAndObjectsUsingBlock:function(aKey, anObject, stop)
|
||||
{
|
||||
[anObject enumerateKeysAndObjectsUsingBlock:function(aKey2, anObject2, stop2)
|
||||
{
|
||||
var anObject2Values = [anObject2 values];
|
||||
|
||||
[anObject2Values enumerateKeysAndObjectsUsingBlock:function(aKey3, anObject3, stop3)
|
||||
{
|
||||
if (anObject3.isa)
|
||||
{
|
||||
if (([anObject3 isKindOfClass:CPImage] || [anObject3 isKindOfClass:CPColor]) && [anObject3 cssDictionary])
|
||||
{
|
||||
// We have a CSS defined image or color
|
||||
[self _applyDynamicSet:dynamicSet toCSSDictionary:[anObject3 cssDictionary]];
|
||||
[self _applyDynamicSet:dynamicSet toCSSDictionary:[anObject3 cssBeforeDictionary]];
|
||||
[self _applyDynamicSet:dynamicSet toCSSDictionary:[anObject3 cssAfterDictionary]];
|
||||
}
|
||||
else if ([anObject3 isKindOfClass:CPString])
|
||||
{
|
||||
// We have a direct string value
|
||||
if ([dynamicSet containsKey:anObject3])
|
||||
[anObject2Values setObject:[dynamicSet objectForKey:anObject3] forKey:aKey3];
|
||||
}
|
||||
}
|
||||
}];
|
||||
}];
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)_applyDynamicSet:(CPDictionary)dynamicSet toCSSDictionary:(CPDictionary)aDictionary
|
||||
{
|
||||
[aDictionary enumerateKeysAndObjectsUsingBlock:function(aKey, anObject, stop)
|
||||
{
|
||||
if ([dynamicSet containsKey:anObject])
|
||||
[aDictionary setObject:[dynamicSet objectForKey:anObject] forKey:aKey];
|
||||
}];
|
||||
}
|
||||
|
||||
- (BOOL)isCSSBased
|
||||
{
|
||||
return !![self valueForAttributeWithName:@"css-based" forClass:[CPView class]];
|
||||
|
||||
@@ -103,7 +103,7 @@
|
||||
aTheme = [CPTheme themeNamed:aThemeName];
|
||||
|
||||
if ([aTheme isCSSBased])
|
||||
[aTheme setCSSResourcesPath:[aBundle resourcePath]];
|
||||
[aTheme _initializeDynamicAttributesAndResourcesPathWithBundle:aBundle];
|
||||
}
|
||||
|
||||
[_loadDelegate blendDidFinishLoading:self];
|
||||
|
||||
@@ -333,6 +333,7 @@ var CPViewHighDPIDrawingEnabled = YES;
|
||||
{
|
||||
return @{
|
||||
@"css-based": NO,
|
||||
@"dynamic-set": [CPNull null],
|
||||
@"nib2cib-adjustment-frame": CGRectMakeZero()
|
||||
};
|
||||
}
|
||||
|
||||
@@ -4389,6 +4389,19 @@ var interpolate = function(fromValue, toValue, progress)
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark -
|
||||
|
||||
@implementation CPWindow (CSSTheming)
|
||||
|
||||
- (void)_setThemeIncludingDescendants:(CPTheme)aTheme
|
||||
{
|
||||
[[self contentView] _setThemeIncludingDescendants:aTheme];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark -
|
||||
|
||||
function _CPWindowFullPlatformWindowSessionMake(aWindowView, aContentRect, hasShadow, aLevel)
|
||||
{
|
||||
return { windowView:aWindowView, contentRect:aContentRect, hasShadow:hasShadow, level:aLevel };
|
||||
|
||||
Reference in New Issue
Block a user