1/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2/* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3/* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this file, 5 * You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7#include "VibrancyManager.h" 8 9#import <objc/message.h> 10 11#include "nsChildView.h" 12#include "nsCocoaFeatures.h" 13#include "SDKDeclarations.h" 14 15using namespace mozilla; 16 17@interface MOZVibrantView : NSVisualEffectView { 18 VibrancyType mType; 19} 20- (instancetype)initWithFrame:(NSRect)aRect vibrancyType:(VibrancyType)aVibrancyType; 21@end 22 23@interface MOZVibrantLeafView : MOZVibrantView 24@end 25 26static NSAppearance* AppearanceForVibrancyType(VibrancyType aType) { 27 if (@available(macOS 10.14, *)) { 28 switch (aType) { 29 case VibrancyType::TITLEBAR_LIGHT: 30 // This must always be light (regular aqua), regardless of window appearance. 31 return [NSAppearance appearanceNamed:NSAppearanceNameAqua]; 32 case VibrancyType::TITLEBAR_DARK: 33 // This must always be dark (dark aqua), regardless of window appearance. 34 return [NSAppearance appearanceNamed:NSAppearanceNameDarkAqua]; 35 case VibrancyType::TOOLTIP: 36 case VibrancyType::MENU: 37 case VibrancyType::HIGHLIGHTED_MENUITEM: 38 case VibrancyType::SOURCE_LIST: 39 case VibrancyType::SOURCE_LIST_SELECTION: 40 case VibrancyType::ACTIVE_SOURCE_LIST_SELECTION: 41 // Inherit the appearance from the window. If the window is using Dark Mode, the vibrancy 42 // will automatically be dark, too. This is available starting with macOS 10.14. 43 return nil; 44 } 45 } 46 47 // For 10.13 and below, a vibrant appearance name must be used. There is no system dark mode and 48 // no automatic adaptation to the window; all windows are light. 49 switch (aType) { 50 case VibrancyType::TITLEBAR_LIGHT: 51 case VibrancyType::TOOLTIP: 52 case VibrancyType::MENU: 53 case VibrancyType::HIGHLIGHTED_MENUITEM: 54 case VibrancyType::SOURCE_LIST: 55 case VibrancyType::SOURCE_LIST_SELECTION: 56 case VibrancyType::ACTIVE_SOURCE_LIST_SELECTION: 57 return [NSAppearance appearanceNamed:NSAppearanceNameVibrantLight]; 58 case VibrancyType::TITLEBAR_DARK: 59 return [NSAppearance appearanceNamed:NSAppearanceNameVibrantDark]; 60 } 61} 62 63static NSVisualEffectState VisualEffectStateForVibrancyType(VibrancyType aType) { 64 switch (aType) { 65 case VibrancyType::TOOLTIP: 66 case VibrancyType::MENU: 67 case VibrancyType::HIGHLIGHTED_MENUITEM: 68 // Tooltip and menu windows are never "key", so we need to tell the vibrancy effect to look 69 // active regardless of window state. 70 return NSVisualEffectStateActive; 71 default: 72 return NSVisualEffectStateFollowsWindowActiveState; 73 } 74} 75 76static NSVisualEffectMaterial VisualEffectMaterialForVibrancyType(VibrancyType aType, 77 BOOL* aOutIsEmphasized) { 78 switch (aType) { 79 case VibrancyType::TITLEBAR_LIGHT: 80 case VibrancyType::TITLEBAR_DARK: 81 return NSVisualEffectMaterialTitlebar; 82 case VibrancyType::TOOLTIP: 83 if (@available(macOS 10.14, *)) { 84 return (NSVisualEffectMaterial)NSVisualEffectMaterialToolTip; 85 } else { 86 return NSVisualEffectMaterialMenu; 87 } 88 case VibrancyType::MENU: 89 return NSVisualEffectMaterialMenu; 90 case VibrancyType::SOURCE_LIST: 91 return NSVisualEffectMaterialSidebar; 92 case VibrancyType::SOURCE_LIST_SELECTION: 93 return NSVisualEffectMaterialSelection; 94 case VibrancyType::HIGHLIGHTED_MENUITEM: 95 case VibrancyType::ACTIVE_SOURCE_LIST_SELECTION: 96 *aOutIsEmphasized = YES; 97 return NSVisualEffectMaterialSelection; 98 } 99} 100 101static BOOL HasVibrantForeground(VibrancyType aType) { 102 switch (aType) { 103 case VibrancyType::MENU: 104 return YES; 105 default: 106 return NO; 107 } 108} 109 110@implementation MOZVibrantView 111 112- (instancetype)initWithFrame:(NSRect)aRect vibrancyType:(VibrancyType)aType { 113 self = [super initWithFrame:aRect]; 114 mType = aType; 115 116 self.appearance = AppearanceForVibrancyType(mType); 117 self.state = VisualEffectStateForVibrancyType(mType); 118 119 BOOL isEmphasized = NO; 120 self.material = VisualEffectMaterialForVibrancyType(mType, &isEmphasized); 121 self.emphasized = isEmphasized; 122 123 return self; 124} 125 126// Don't override allowsVibrancy here, because this view may have subviews, and 127// returning YES from allowsVibrancy forces on foreground vibrancy for all 128// descendant views, which can have unintended effects. 129 130@end 131 132@implementation MOZVibrantLeafView 133 134- (NSView*)hitTest:(NSPoint)aPoint { 135 // This view must be transparent to mouse events. 136 return nil; 137} 138 139// MOZVibrantLeafView does not have subviews, so we can return YES here without 140// having unintended effects on other contents of the window. 141- (BOOL)allowsVibrancy { 142 return HasVibrantForeground(mType); 143} 144 145@end 146 147bool VibrancyManager::UpdateVibrantRegion(VibrancyType aType, 148 const LayoutDeviceIntRegion& aRegion) { 149 if (aRegion.IsEmpty()) { 150 return mVibrantRegions.Remove(uint32_t(aType)); 151 } 152 auto& vr = *mVibrantRegions.GetOrInsertNew(uint32_t(aType)); 153 return vr.UpdateRegion(aRegion, mCoordinateConverter, mContainerView, ^() { 154 return this->CreateEffectView(aType); 155 }); 156} 157 158LayoutDeviceIntRegion VibrancyManager::GetUnionOfVibrantRegions() const { 159 LayoutDeviceIntRegion result; 160 for (const auto& region : mVibrantRegions.Values()) { 161 result.OrWith(region->Region()); 162 } 163 return result; 164} 165 166/* static */ NSView* VibrancyManager::CreateEffectView(VibrancyType aType, BOOL aIsContainer) { 167 return aIsContainer ? [[MOZVibrantView alloc] initWithFrame:NSZeroRect vibrancyType:aType] 168 : [[MOZVibrantLeafView alloc] initWithFrame:NSZeroRect vibrancyType:aType]; 169} 170