Skip to content

Commit

Permalink
Fire button and shield button animations (#476)
Browse files Browse the repository at this point in the history
Task/Issue URL: https://app.asana.com/0/0/1201945397495594/f

Description:
Fire button and shield button animates if mouse cursor is over
  • Loading branch information
tomasstrba authored Mar 25, 2022
1 parent 9d9fd10 commit 55d1cef
Show file tree
Hide file tree
Showing 25 changed files with 348 additions and 37 deletions.
60 changes: 50 additions & 10 deletions DuckDuckGo.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -394,6 +394,14 @@
AA7412B724D1687000D22FE0 /* TabBarScrollView.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA7412B624D1687000D22FE0 /* TabBarScrollView.swift */; };
AA7412BD24D2BEEE00D22FE0 /* MainWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA7412BC24D2BEEE00D22FE0 /* MainWindow.swift */; };
AA75A0AE26F3500C0086B667 /* PrivacyIconViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA75A0AD26F3500C0086B667 /* PrivacyIconViewModel.swift */; };
AA7EB6DF27E7C57D00036718 /* MouseOverAnimationButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA7EB6DE27E7C57D00036718 /* MouseOverAnimationButton.swift */; };
AA7EB6E227E7D05500036718 /* flame-mouse-over.json in Resources */ = {isa = PBXBuildFile; fileRef = AA7EB6E027E7D05500036718 /* flame-mouse-over.json */; };
AA7EB6E327E7D05500036718 /* dark-flame-mouse-over.json in Resources */ = {isa = PBXBuildFile; fileRef = AA7EB6E127E7D05500036718 /* dark-flame-mouse-over.json */; };
AA7EB6E527E7D6DC00036718 /* AnimationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA7EB6E427E7D6DC00036718 /* AnimationView.swift */; };
AA7EB6E727E8809D00036718 /* shield-mouse-over.json in Resources */ = {isa = PBXBuildFile; fileRef = AA7EB6E627E8809D00036718 /* shield-mouse-over.json */; };
AA7EB6E927E880A600036718 /* shield-dot-mouse-over.json in Resources */ = {isa = PBXBuildFile; fileRef = AA7EB6E827E880A600036718 /* shield-dot-mouse-over.json */; };
AA7EB6EB27E880AE00036718 /* dark-shield-mouse-over.json in Resources */ = {isa = PBXBuildFile; fileRef = AA7EB6EA27E880AE00036718 /* dark-shield-mouse-over.json */; };
AA7EB6ED27E880B600036718 /* dark-shield-dot-mouse-over.json in Resources */ = {isa = PBXBuildFile; fileRef = AA7EB6EC27E880B600036718 /* dark-shield-dot-mouse-over.json */; };
AA80EC54256BE3BC007083E7 /* UserText.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA80EC53256BE3BC007083E7 /* UserText.swift */; };
AA80EC67256C4691007083E7 /* BrowserTab.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = AA80EC69256C4691007083E7 /* BrowserTab.storyboard */; };
AA80EC73256C46A2007083E7 /* Suggestion.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = AA80EC75256C46A2007083E7 /* Suggestion.storyboard */; };
Expand Down Expand Up @@ -1125,6 +1133,14 @@
AA7412BC24D2BEEE00D22FE0 /* MainWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainWindow.swift; sourceTree = "<group>"; };
AA75A0AD26F3500C0086B667 /* PrivacyIconViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrivacyIconViewModel.swift; sourceTree = "<group>"; };
AA7DE8E026A9BD000012B490 /* History 2.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "History 2.xcdatamodel"; sourceTree = "<group>"; };
AA7EB6DE27E7C57D00036718 /* MouseOverAnimationButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MouseOverAnimationButton.swift; sourceTree = "<group>"; };
AA7EB6E027E7D05500036718 /* flame-mouse-over.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "flame-mouse-over.json"; sourceTree = "<group>"; };
AA7EB6E127E7D05500036718 /* dark-flame-mouse-over.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "dark-flame-mouse-over.json"; sourceTree = "<group>"; };
AA7EB6E427E7D6DC00036718 /* AnimationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnimationView.swift; sourceTree = "<group>"; };
AA7EB6E627E8809D00036718 /* shield-mouse-over.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "shield-mouse-over.json"; sourceTree = "<group>"; };
AA7EB6E827E880A600036718 /* shield-dot-mouse-over.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "shield-dot-mouse-over.json"; sourceTree = "<group>"; };
AA7EB6EA27E880AE00036718 /* dark-shield-mouse-over.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "dark-shield-mouse-over.json"; sourceTree = "<group>"; };
AA7EB6EC27E880B600036718 /* dark-shield-dot-mouse-over.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "dark-shield-dot-mouse-over.json"; sourceTree = "<group>"; };
AA80EC53256BE3BC007083E7 /* UserText.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserText.swift; sourceTree = "<group>"; };
AA80EC68256C4691007083E7 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/BrowserTab.storyboard; sourceTree = "<group>"; };
AA80EC74256C46A2007083E7 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Suggestion.storyboard; sourceTree = "<group>"; };
Expand Down Expand Up @@ -2173,6 +2189,7 @@
B693954426F04BE90015B914 /* LongPressButton.swift */,
B693953F26F04BE80015B914 /* MouseClickView.swift */,
B693954926F04BEB0015B914 /* MouseOverButton.swift */,
AA7EB6DE27E7C57D00036718 /* MouseOverAnimationButton.swift */,
4B379C2327BDE1B0008A968E /* FlatButton.swift */,
B693953D26F04BE70015B914 /* MouseOverView.swift */,
B693953C26F04BE70015B914 /* NibLoadable.swift */,
Expand Down Expand Up @@ -2728,6 +2745,29 @@
path = View;
sourceTree = "<group>";
};
AA7EB6EE27E880EA00036718 /* Animations */ = {
isa = PBXGroup;
children = (
AA3439732754D55100B241FA /* trackers-1.json */,
AA3439742754D55100B241FA /* trackers-2.json */,
AA3439752754D55100B241FA /* trackers-3.json */,
AA34396A2754D4E200B241FA /* shield.json */,
AA34396B2754D4E300B241FA /* shield-dot.json */,
AA7EB6E027E7D05500036718 /* flame-mouse-over.json */,
AA7EB6E627E8809D00036718 /* shield-mouse-over.json */,
AA7EB6E827E880A600036718 /* shield-dot-mouse-over.json */,
AA3439762754D55100B241FA /* dark-trackers-1.json */,
AA3439722754D55100B241FA /* dark-trackers-2.json */,
AA3439772754D55100B241FA /* dark-trackers-3.json */,
AA34396F2754D4E900B241FA /* dark-shield.json */,
AA34396E2754D4E900B241FA /* dark-shield-dot.json */,
AA7EB6E127E7D05500036718 /* dark-flame-mouse-over.json */,
AA7EB6EA27E880AE00036718 /* dark-shield-mouse-over.json */,
AA7EB6EC27E880B600036718 /* dark-shield-dot-mouse-over.json */,
);
path = Animations;
sourceTree = "<group>";
};
AA80EC52256BE33A007083E7 /* Localizables */ = {
isa = PBXGroup;
children = (
Expand Down Expand Up @@ -2802,6 +2842,7 @@
AA86491624D8339A001BABEE /* View */ = {
isa = PBXGroup;
children = (
AA7EB6EE27E880EA00036718 /* Animations */,
85589E8C27BBBB870038AD11 /* NavigationBar.storyboard */,
AA68C3D22490ED62001B8783 /* NavigationBarViewController.swift */,
14D9B8F924F7E089000D4D13 /* AddressBarViewController.swift */,
Expand All @@ -2810,16 +2851,6 @@
AAC5E4F025D6BF10007F5990 /* AddressBarButton.swift */,
AAA0CC32252F181A0079BC96 /* NavigationButtonMenuDelegate.swift */,
AAA0CC462533833C0079BC96 /* MoreOptionsMenu.swift */,
AA3439732754D55100B241FA /* trackers-1.json */,
AA3439742754D55100B241FA /* trackers-2.json */,
AA3439752754D55100B241FA /* trackers-3.json */,
AA34396A2754D4E200B241FA /* shield.json */,
AA34396B2754D4E300B241FA /* shield-dot.json */,
AA3439762754D55100B241FA /* dark-trackers-1.json */,
AA3439722754D55100B241FA /* dark-trackers-2.json */,
AA3439772754D55100B241FA /* dark-trackers-3.json */,
AA34396F2754D4E900B241FA /* dark-shield.json */,
AA34396E2754D4E900B241FA /* dark-shield-dot.json */,
);
path = View;
sourceTree = "<group>";
Expand Down Expand Up @@ -3173,6 +3204,7 @@
children = (
B6DB3CF826A00E2D00D459B7 /* AVCaptureDevice+SwizzledAuthState.swift */,
AA61C0D12727F59B00E6B681 /* ArrayExtension.swift */,
AA7EB6E427E7D6DC00036718 /* AnimationView.swift */,
B6106B9D26A565DA0013B453 /* BundleExtension.swift */,
4BA1A6C1258B0A1300F6F690 /* ContiguousBytesExtension.swift */,
85AC3AF625D5DBFD00C7D2AA /* DataExtension.swift */,
Expand Down Expand Up @@ -3871,13 +3903,15 @@
9833913127AAA4B500DAF119 /* trackerData.json in Resources */,
4B0511CE262CAA5A00F6079C /* DownloadPreferencesTableCellView.xib in Resources */,
B31055CA27A1BA1D001AC618 /* background.js in Resources */,
AA7EB6ED27E880B600036718 /* dark-shield-dot-mouse-over.json in Resources */,
8511E18425F82B34002F516B /* 01_Fire_really_small.json in Resources */,
85B7184A27677C2D00B4277F /* Onboarding.storyboard in Resources */,
4B0511C3262CAA5A00F6079C /* Preferences.storyboard in Resources */,
EA477680272A21B700419EDA /* clickToLoadConfig.json in Resources */,
B6B1E88226D5DAC30062C350 /* Downloads.storyboard in Resources */,
AA3439712754D4E900B241FA /* dark-shield.json in Resources */,
B31055C827A1BA1D001AC618 /* background-bundle.js in Resources */,
AA7EB6EB27E880AE00036718 /* dark-shield-mouse-over.json in Resources */,
B31055CB27A1BA1D001AC618 /* autoconsent-bundle.js in Resources */,
7B1E819F27C8874900FF0E60 /* ContentOverlay.storyboard in Resources */,
85A0117425AF2EDF00FA6A0C /* FindInPage.storyboard in Resources */,
Expand Down Expand Up @@ -3908,6 +3942,7 @@
4B92929026670D1700AD2C21 /* BookmarkTableCellView.xib in Resources */,
85AC7AD927BD625000FFB69B /* HomePageAssets.xcassets in Resources */,
339A6B5826A044BA00E3DAE8 /* duckduckgo-privacy-dashboard in Resources */,
AA7EB6E727E8809D00036718 /* shield-mouse-over.json in Resources */,
4B379C1B27BD9F88008A968E /* LoginsPreferencesTableCellView.xib in Resources */,
B31055C727A1BA1D001AC618 /* browser-shim.js in Resources */,
4B92928E26670D1700AD2C21 /* BookmarkOutlineViewCell.xib in Resources */,
Expand All @@ -3918,10 +3953,13 @@
AAB7320726DD0C37002FACF9 /* Fire.storyboard in Resources */,
85589E8F27BBBBF10038AD11 /* Main.storyboard in Resources */,
EA18D1CA272F0DC8006DC101 /* social_images in Resources */,
AA7EB6E927E880A600036718 /* shield-dot-mouse-over.json in Resources */,
AA80EC8F256C49BC007083E7 /* Localizable.stringsdict in Resources */,
EAC80DE0271F6C0100BBF02D /* fb-sdk.js in Resources */,
85625994269C8F9600EE44BC /* PasswordManager.storyboard in Resources */,
4B0511C6262CAA5A00F6079C /* DefaultBrowserTableCellView.xib in Resources */,
AA7EB6E327E7D05500036718 /* dark-flame-mouse-over.json in Resources */,
AA7EB6E227E7D05500036718 /* flame-mouse-over.json in Resources */,
4B677433255DBEB800025BD8 /* httpsMobileV2Bloom.bin in Resources */,
AA34397B2754D55100B241FA /* trackers-3.json in Resources */,
026ADE1426C3010C002518EE /* macos-config.json in Resources */,
Expand Down Expand Up @@ -4356,6 +4394,7 @@
4B9292A326670D2A00AD2C21 /* BookmarkManagedObject.swift in Sources */,
4B723E1326B0007A00E14D75 /* CSVLoginExporter.swift in Sources */,
85C48CCC278D808F00D3263E /* NSAttributedStringExtension.swift in Sources */,
AA7EB6E527E7D6DC00036718 /* AnimationView.swift in Sources */,
8562599A269CA0A600EE44BC /* NSRectExtension.swift in Sources */,
4B0511C5262CAA5A00F6079C /* PrivacySecurityPreferencesTableCellView.swift in Sources */,
B6040856274B830F00680351 /* DictionaryExtension.swift in Sources */,
Expand Down Expand Up @@ -4412,6 +4451,7 @@
B693954A26F04BEB0015B914 /* NibLoadable.swift in Sources */,
AA3D531527A1ED9300074EC1 /* FeedbackWindow.swift in Sources */,
85F0FF1327CFAB04001C7C6E /* RecentlyVisitedView.swift in Sources */,
AA7EB6DF27E7C57D00036718 /* MouseOverAnimationButton.swift in Sources */,
AA7412B724D1687000D22FE0 /* TabBarScrollView.swift in Sources */,
4B0511C7262CAA5A00F6079C /* PreferenceTableCellView.swift in Sources */,
4B9292D92667124B00AD2C21 /* BookmarkListTreeControllerDataSource.swift in Sources */,
Expand Down
33 changes: 33 additions & 0 deletions DuckDuckGo/Common/Extensions/AnimationView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
//
// AnimationView.swift
//
// Copyright © 2022 DuckDuckGo. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

import Foundation
import Lottie

extension AnimationView {

convenience init?(named animationName: String, imageProvider: AnimationImageProvider? = nil) {
guard let animation = Animation.named(animationName, animationCache: LottieAnimationCache.shared) else {
return nil
}

self.init(animation: animation, imageProvider: imageProvider)
identifier = NSUserInterfaceItemIdentifier(rawValue: animationName)
}

}
178 changes: 178 additions & 0 deletions DuckDuckGo/Common/View/AppKit/MouseOverAnimationButton.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
//
// MouseOverAnimationButton.swift
//
// Copyright © 2022 DuckDuckGo. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

import Foundation
import Lottie
import Combine

final class MouseOverAnimationButton: AddressBarButton {

// MARK: - Events

override func awakeFromNib() {
super.awakeFromNib()

subscribeToIsMouseOver()
subscribeToEffectiveAppearance()
}

private var isMouseOverCancellable: AnyCancellable?

private func subscribeToIsMouseOver() {
isMouseOverCancellable = $isMouseOver
.dropFirst()
.sink { [weak self] isMouseOver in
if isMouseOver {
self?.animate()
} else {
self?.stopAnimation()
}
}
}

private var effectiveAppearanceCancellable: AnyCancellable?

private func subscribeToEffectiveAppearance() {
effectiveAppearanceCancellable = NSApp.publisher(for: \.effectiveAppearance)
.dropFirst()
.receive(on: DispatchQueue.main)
.sink { [weak self] _ in
self?.updateAnimationView()
}
}

// MARK: - Loading & Updating of Animation Views

struct AnimationNames: Equatable {
let aqua: String
let dark: String
}

var animationNames: AnimationNames? {
didSet {
if oldValue != animationNames {
loadAnimationViews()
updateAnimationView()
}
}
}

struct AnimationViews {
let aqua: AnimationView
let dark: AnimationView
}

private var animationViewCache: AnimationViews?

private func loadAnimationViews() {
guard let animationNames = animationNames,
let aquaAnimationView = AnimationView(named: animationNames.aqua),
let darkAnimationView = AnimationView(named: animationNames.dark) else {
assertionFailure("Missing animation names or animation files in the bundle")
return
}

animationViewCache = AnimationViews(
aqua: aquaAnimationView,
dark: darkAnimationView)
}

private var currentAnimationView: AnimationView?

private func updateAnimationView() {
guard let animationViewCache = animationViewCache else {
return
}

let isAquaMode = NSApp.effectiveAppearance.name == NSAppearance.Name.aqua
let newAnimationView: AnimationView
// Animation view causes problems in tests
if AppDelegate.isRunningTests {
newAnimationView = AnimationView()
} else {
newAnimationView = isAquaMode ? animationViewCache.aqua : animationViewCache.dark
}

guard currentAnimationView?.identifier != newAnimationView.identifier else {
// No need to update
return
}

currentAnimationView?.removeFromSuperview()
currentAnimationView = newAnimationView

newAnimationView.isHidden = true
addAndLayout(newAnimationView)
}

// MARK: - Animating

@Published var isAnimationViewVisible = false

override var image: NSImage? {
get {
return super.image
}

set {
if isAnimationViewVisible {
imageCache = newValue
} else {
super.image = newValue
}
}
}

var imageCache: NSImage?

private func hideImage() {
imageCache = image
super.image = nil
}

private func showImage() {
if let imageCache = imageCache {
NSAppearance.withAppAppearance {
image = imageCache
}
}
}

private func hideAnimation() {
currentAnimationView?.isHidden = true
isAnimationViewVisible = false
}

private func showAnimation() {
currentAnimationView?.isHidden = false
isAnimationViewVisible = true
}

private func animate() {
hideImage()
showAnimation()
currentAnimationView?.play()
}

private func stopAnimation() {
hideAnimation()
showImage()
currentAnimationView?.stop()
}

}
3 changes: 2 additions & 1 deletion DuckDuckGo/Common/View/AppKit/MouseOverButton.swift
Original file line number Diff line number Diff line change
Expand Up @@ -121,12 +121,14 @@ internal class MouseOverButton: NSButton {
@Published private(set) var isMouseOver = false {
didSet {
updateTintColor()
updateLayer()
}
}

var isMouseDown = false {
didSet {
updateTintColor()
updateLayer()
}
}

Expand All @@ -140,7 +142,6 @@ internal class MouseOverButton: NSButton {
self.contentTintColor = self.normalTintColor
}
}
updateLayer()
}

override func updateLayer() {
Expand Down
Loading

0 comments on commit 55d1cef

Please sign in to comment.