Files
cappuccino/AppKit/CPVisualEffectView.j
T
2015-08-13 21:12:04 -07:00

241 lines
6.7 KiB
Plaintext

/*
* CPVisualEffectView.j
* AppKit
*
* Created by Antoine Mercadal.
* Copyright 2015, 280 Cappuccino Project.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@import <Foundation/Foundation.j>
@import "CPAppearance.j"
@import "CPView.j"
@typedef CPVisualEffectMaterial
CPVisualEffectMaterialAppearanceBased = 0;
CPVisualEffectMaterialLight = 1;
CPVisualEffectMaterialDark = 2;
CPVisualEffectMaterialTitlebar = 3;
@typedef CPVisualEffectBlendingMode
CPVisualEffectBlendingModeBehindWindow = 0;
CPVisualEffectBlendingModeWithinWindow = 1;
@typedef CPVisualEffectState
CPVisualEffectStateFollowsWindowActiveState = 0;
CPVisualEffectStateActive = 1;
CPVisualEffectStateInactive = 2;
/*! @ingroup appkit
Very naive implementation of CPVisualEffectView. This view allows
to use vibrancy effect. This is only working with Safari 9+ and the
support in Chrome/ium should come quite soon.
Using this class with a browser that doesn't support backdrop-filter
While still work, but you will not get the blurry effect.
*/
@implementation CPVisualEffectView : CPView
{
CPImage _maskImage @accessors(property=maskImage);
CPVisualEffectBlendingMode _blendingMode @accessors(property=blendingMode);
CPVisualEffectMaterial _material @accessors(property=material);
CPVisualEffectState _state @accessors(property=state);
}
#pragma mark -
#pragma mark Initialization
- (id)initWithFrame:(CGRect)aFrame
{
if (self = [super initWithFrame:aFrame])
{
_material = CPVisualEffectMaterialAppearanceBased;
_blendingMode = CPVisualEffectBlendingModeWithinWindow;
_state = CPVisualEffectStateFollowsWindowActiveState;
_appearance = [CPAppearance appearanceNamed:CPAppearanceNameVibrantDark];
}
return self;
}
#pragma mark -
#pragma mark CPVisualEffectView API
/*! Sets the appearance of the CPVisualEffectView.
Only CPAppearance named CPAppearanceNameVibrantDark or CPAppearanceNameVibrantLight are valid
@param anAppearance the CPAppearance.
*/
- (void)setAppearance:(CPAppearance)anAppearance
{
if (![self _validAppearance:anAppearance])
[CPException raise:CPInvalidArgumentException reason:"Appearance can only be CPAppearanceNameVibrantDark or CPAppearanceNameVibrantLight in CPVisualEffectView, but is " + anAppearance];
[super setAppearance:anAppearance];
[self _applyVibrancyState];
}
/*! Sets the received effect state.
Possible values:
<pre>
CPVisualEffectStateFollowsWindowActiveState (default)
CPVisualEffectStateActive
CPVisualEffectStateInactive
</pre>
*/
- (void)setState:(CPVisualEffectState)aState
{
if (_state == aState)
return;
[self willChangeValueForKey:"state"];
_state = aState;
[self didChangeValueForKey:"state"];
[self _applyVibrancyState];
}
#pragma mark -
#pragma mark Utilities
- (void)_setEffectEnabled:(BOOL)shouldEnable
{
var dark = [[self appearance] isEqual:[CPAppearance appearanceNamed:CPAppearanceNameVibrantDark]],
darkColor = [CPColor colorWithHexString:@"1e1e1e"],
lightColor = [CPColor whiteColor],
prop;
#if PLATFORM(DOM)
prop = CPBrowserStyleProperty("backdrop-filter");
#endif
if (shouldEnable)
{
if (prop)
self._DOMElement.style[prop] = "blur(30px)";
[self setBackgroundColor:[(dark ? darkColor : lightColor) colorWithAlphaComponent:0.6]];
}
else
{
if (prop)
self._DOMElement.style[prop] = nil;
[self setBackgroundColor:(dark ? darkColor : lightColor)];
}
}
- (void)_applyVibrancyState
{
switch (_state)
{
case CPVisualEffectStateFollowsWindowActiveState:
[self _setEffectEnabled:[self hasThemeState:CPThemeStateKeyWindow]];
break;
case CPVisualEffectStateActive:
[self _setEffectEnabled:YES];
break;
case CPVisualEffectStateInactive:
[self _setEffectEnabled:NO];
break;
}
}
- (BOOL)_validAppearance:(CPAppearance)anAppearance
{
return [anAppearance isEqual:[CPAppearance appearanceNamed:CPAppearanceNameVibrantDark]] || [anAppearance isEqual:[CPAppearance appearanceNamed:CPAppearanceNameVibrantLight]];
}
#pragma mark -
#pragma mark Overrides
- (BOOL)setThemeState:(ThemeState)aState
{
if (aState.isa && [aState isKindOfClass:CPArray])
aState = CPThemeState.apply(null, aState);
var ret = [super setThemeState:aState];
[self _applyVibrancyState];
return ret;
}
- (BOOL)unsetThemeState:(ThemeState)aState
{
if (aState.isa && [aState isKindOfClass:CPArray])
aState = CPThemeState.apply(null, aState);
var ret = [super unsetThemeState:aState];
[self _applyVibrancyState];
return ret;
}
- (void)viewDidMoveToSuperview
{
[super viewDidMoveToSuperview];
if (_superview)
[self _applyVibrancyState];
}
- (void)viewDidMoveToWindow
{
[super viewDidMoveToWindow];
if (_window)
[self _applyVibrancyState];
}
#pragma mark -
#pragma mark CPCoding
- (id)initWithCoder:(CPCoder)aCoder
{
if (self = [super initWithCoder:aCoder])
{
_blendingMode = [aCoder decodeIntForKey:@"_blendingMode"] || CPVisualEffectBlendingModeWithinWindow;
_maskImage = [aCoder decodeObjectForKey:@"_maskImage"];
_material = [aCoder decodeIntForKey:@"_material"] || CPVisualEffectMaterialAppearanceBased;
_state = [aCoder decodeIntForKey:@"_state"] || CPVisualEffectStateFollowsWindowActiveState;
}
return self;
}
- (void)encodeWithCoder:(CPCoder)aCoder
{
[super encodeWithCoder:aCoder];
[aCoder encodeObject:_maskImage forKey:@"_maskImage"];
[aCoder encodeInt:_blendingMode forKey:@"_blendingMode"];
[aCoder encodeInt:_material forKey:@"_material"];
[aCoder encodeInt:_state forKey:@"_state"];
}
@end