diff --git a/Display.xcodeproj/project.pbxproj b/Display.xcodeproj/project.pbxproj index ad08084c1c..6ba682acd3 100644 --- a/Display.xcodeproj/project.pbxproj +++ b/Display.xcodeproj/project.pbxproj @@ -112,6 +112,7 @@ D05CC3271B69725400E235A3 /* NavigationBackArrowLight@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = D05CC3261B69725400E235A3 /* NavigationBackArrowLight@2x.png */; }; D05CC3291B69750D00E235A3 /* InteractiveTransitionGestureRecognizer.swift in Sources */ = {isa = PBXBuildFile; fileRef = D05CC3281B69750D00E235A3 /* InteractiveTransitionGestureRecognizer.swift */; }; D06B76DB20592A97006E9EEA /* LayoutSizes.swift in Sources */ = {isa = PBXBuildFile; fileRef = D06B76DA20592A97006E9EEA /* LayoutSizes.swift */; }; + D06D37A220779C82009219B6 /* VolumeControlStatusBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = D06D37A120779C82009219B6 /* VolumeControlStatusBar.swift */; }; D06EE8451B7140FF00837186 /* Font.swift in Sources */ = {isa = PBXBuildFile; fileRef = D06EE8441B7140FF00837186 /* Font.swift */; }; D077B8E91F4637040046D27A /* NavigationBarBadge.swift in Sources */ = {isa = PBXBuildFile; fileRef = D077B8E81F4637040046D27A /* NavigationBarBadge.swift */; }; D081229D1D19AA1C005F7395 /* ContainerViewLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = D081229C1D19AA1C005F7395 /* ContainerViewLayout.swift */; }; @@ -286,6 +287,7 @@ D05CC3261B69725400E235A3 /* NavigationBackArrowLight@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "NavigationBackArrowLight@2x.png"; sourceTree = ""; }; D05CC3281B69750D00E235A3 /* InteractiveTransitionGestureRecognizer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InteractiveTransitionGestureRecognizer.swift; sourceTree = ""; }; D06B76DA20592A97006E9EEA /* LayoutSizes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LayoutSizes.swift; sourceTree = ""; }; + D06D37A120779C82009219B6 /* VolumeControlStatusBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VolumeControlStatusBar.swift; sourceTree = ""; }; D06EE8441B7140FF00837186 /* Font.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Font.swift; sourceTree = ""; }; D077B8E81F4637040046D27A /* NavigationBarBadge.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NavigationBarBadge.swift; sourceTree = ""; }; D081229C1D19AA1C005F7395 /* ContainerViewLayout.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContainerViewLayout.swift; sourceTree = ""; }; @@ -669,6 +671,7 @@ D0B3671F1C94A53A00346D2E /* StatusBarProxyNode.swift */, D03E7DFE1C96F7B400C07816 /* StatusBarManager.swift */, D03B0E6F1D6331FB00955575 /* StatusBarHost.swift */, + D06D37A120779C82009219B6 /* VolumeControlStatusBar.swift */, ); name = "Status Bar"; sourceTree = ""; @@ -984,6 +987,7 @@ D02958001D6F096000360E5E /* ContextMenuContainerNode.swift in Sources */, D05BE4AB1D1F25E3002BD72C /* PresentationContext.swift in Sources */, D0C2DFCA1CC4431D0044FF83 /* ListViewItem.swift in Sources */, + D06D37A220779C82009219B6 /* VolumeControlStatusBar.swift in Sources */, D05CC2A01B69326400E235A3 /* NavigationController.swift in Sources */, D06EE8451B7140FF00837186 /* Font.swift in Sources */, D0CA3F8A2073F7650042D2B6 /* LinkHighlightingNode.swift in Sources */, diff --git a/Display/ContainerViewLayout.swift b/Display/ContainerViewLayout.swift index 50c384972a..51b6ae322d 100644 --- a/Display/ContainerViewLayout.swift +++ b/Display/ContainerViewLayout.swift @@ -32,10 +32,6 @@ public struct LayoutMetrics: Equatable { self.widthClass = .compact self.heightClass = .compact } - - public static func ==(lhs: LayoutMetrics, rhs: LayoutMetrics) -> Bool { - return lhs.widthClass == rhs.widthClass && lhs.heightClass == rhs.heightClass - } } public struct ContainerViewLayout: Equatable { @@ -81,56 +77,4 @@ public struct ContainerViewLayout: Equatable { public func withUpdatedMetrics(_ metrics: LayoutMetrics) -> ContainerViewLayout { return ContainerViewLayout(size: self.size, metrics: metrics, intrinsicInsets: self.intrinsicInsets, safeInsets: self.safeInsets, statusBarHeight: self.statusBarHeight, inputHeight: self.inputHeight, standardInputHeight: self.standardInputHeight, inputHeightIsInteractivellyChanging: self.inputHeightIsInteractivellyChanging) } - - public static func ==(lhs: ContainerViewLayout, rhs: ContainerViewLayout) -> Bool { - if !lhs.size.equalTo(rhs.size) { - return false - } - - if lhs.metrics != rhs.metrics { - return false - } - - if lhs.intrinsicInsets != rhs.intrinsicInsets { - return false - } - - if lhs.safeInsets != rhs.safeInsets { - return false - } - - if let lhsStatusBarHeight = lhs.statusBarHeight { - if let rhsStatusBarHeight = rhs.statusBarHeight { - if !lhsStatusBarHeight.isEqual(to: rhsStatusBarHeight) { - return false - } - } else { - return false - } - } else if let _ = rhs.statusBarHeight { - return false - } - - if let lhsInputHeight = lhs.inputHeight { - if let rhsInputHeight = rhs.inputHeight { - if !lhsInputHeight.isEqual(to: rhsInputHeight) { - return false - } - } else { - return false - } - } else if let _ = rhs.inputHeight { - return false - } - - if !lhs.standardInputHeight.isEqual(to: rhs.standardInputHeight) { - return false - } - - if lhs.inputHeightIsInteractivellyChanging != rhs.inputHeightIsInteractivellyChanging { - return false - } - - return true - } } diff --git a/Display/ContextMenuContainerNode.swift b/Display/ContextMenuContainerNode.swift index 39abfa5ee3..3ba11b0d10 100644 --- a/Display/ContextMenuContainerNode.swift +++ b/Display/ContextMenuContainerNode.swift @@ -7,10 +7,6 @@ private struct CachedMaskParams: Equatable { let arrowOnBottom: Bool } -private func ==(lhs: CachedMaskParams, rhs: CachedMaskParams) -> Bool { - return lhs.size.equalTo(rhs.size) && lhs.relativeArrowPosition.isEqual(to: rhs.relativeArrowPosition) && lhs.arrowOnBottom == rhs.arrowOnBottom -} - private final class ContextMenuContainerMaskView: UIView { override class var layerClass: AnyClass { return CAShapeLayer.self diff --git a/Display/GridNode.swift b/Display/GridNode.swift index e9368db585..290c0f9aa8 100644 --- a/Display/GridNode.swift +++ b/Display/GridNode.swift @@ -53,23 +53,6 @@ public struct GridNodeScrollToItem { public enum GridNodeLayoutType: Equatable { case fixed(itemSize: CGSize, lineSpacing: CGFloat) case balanced(idealHeight: CGFloat) - - public static func ==(lhs: GridNodeLayoutType, rhs: GridNodeLayoutType) -> Bool { - switch lhs { - case let .fixed(itemSize, lineSpacing): - if case .fixed(itemSize, lineSpacing) = rhs { - return true - } else { - return false - } - case let .balanced(idealHeight): - if case .balanced(idealHeight) = rhs { - return true - } else { - return false - } - } - } } public struct GridNodeLayout: Equatable { @@ -86,10 +69,6 @@ public struct GridNodeLayout: Equatable { self.preloadSize = preloadSize self.type = type } - - public static func ==(lhs: GridNodeLayout, rhs: GridNodeLayout) -> Bool { - return lhs.size.equalTo(rhs.size) && lhs.insets == rhs.insets && lhs.preloadSize.isEqual(to: rhs.preloadSize) && lhs.type == rhs.type - } } public struct GridNodeUpdateLayout { @@ -180,13 +159,9 @@ private final class GridNodeItemLayout { public struct GridNodeDisplayedItemRange: Equatable { public let loadedRange: Range? public let visibleRange: Range? - - public static func ==(lhs: GridNodeDisplayedItemRange, rhs: GridNodeDisplayedItemRange) -> Bool { - return lhs.loadedRange == rhs.loadedRange && lhs.visibleRange == rhs.visibleRange - } } -private struct WrappedGridSection: Hashable { +private struct WrappedGridSection: Equatable, Hashable { let section: GridSection init(_ section: GridSection) { diff --git a/Display/ListViewIntermediateState.swift b/Display/ListViewIntermediateState.swift index 966fa3a1ef..c8ed85bdd5 100644 --- a/Display/ListViewIntermediateState.swift +++ b/Display/ListViewIntermediateState.swift @@ -14,29 +14,6 @@ public enum ListViewScrollPosition: Equatable { case top(CGFloat) case bottom(CGFloat) case center(ListViewCenterScrollPositionOverflow) - - public static func ==(lhs: ListViewScrollPosition, rhs: ListViewScrollPosition) -> Bool { - switch lhs { - case let .top(offset): - if case .top(offset) = rhs { - return true - } else { - return false - } - case let .bottom(offset): - if case .bottom(offset) = rhs { - return true - } else { - return false - } - case let .center(overflow): - if case .center(overflow) = rhs { - return true - } else { - return false - } - } - } } public enum ListViewScrollToItemDirectionHint { @@ -147,20 +124,12 @@ public struct ListViewUpdateSizeAndInsets { public struct ListViewItemRange: Equatable { public let firstIndex: Int public let lastIndex: Int - - public static func ==(lhs: ListViewItemRange, rhs: ListViewItemRange) -> Bool { - return lhs.firstIndex == rhs.firstIndex && lhs.lastIndex == rhs.lastIndex - } } public struct ListViewVisibleItemRange: Equatable { public let firstIndex: Int public let firstIndexFullyVisible: Bool public let lastIndex: Int - - public static func ==(lhs: ListViewVisibleItemRange, rhs: ListViewVisibleItemRange) -> Bool { - return lhs.firstIndex == rhs.firstIndex && lhs.firstIndexFullyVisible == rhs.firstIndexFullyVisible && lhs.lastIndex == rhs.lastIndex - } } public struct ListViewDisplayedItemRange: Equatable { @@ -168,10 +137,6 @@ public struct ListViewDisplayedItemRange: Equatable { public let visibleRange: ListViewVisibleItemRange? } -public func ==(lhs: ListViewDisplayedItemRange, rhs: ListViewDisplayedItemRange) -> Bool { - return lhs.loadedRange == rhs.loadedRange && lhs.visibleRange == rhs.visibleRange -} - struct IndexRange { let first: Int let last: Int diff --git a/Display/StatusBarManager.swift b/Display/StatusBarManager.swift index 5546e940e2..a8fcf6f8a6 100644 --- a/Display/StatusBarManager.swift +++ b/Display/StatusBarManager.swift @@ -1,5 +1,6 @@ import Foundation import AsyncDisplayKit +import SwiftSignalKit private struct MappedStatusBar { let style: StatusBarStyle @@ -70,27 +71,79 @@ private func displayHiddenAnimation() -> CAAnimation { class StatusBarManager { private var host: StatusBarHost + private let volumeControlStatusBar: VolumeControlStatusBar + private let volumeControlStatusBarNode: VolumeControlStatusBarNode private var surfaces: [StatusBarSurface] = [] + private var validParams: (withSafeInsets: Bool, forceInCallStatusBarText: String?, forceHiddenBySystemWindows: Bool)? var inCallNavigate: (() -> Void)? - init(host: StatusBarHost) { + private var volumeTimer: SwiftSignalKit.Timer? + + init(host: StatusBarHost, volumeControlStatusBar: VolumeControlStatusBar, volumeControlStatusBarNode: VolumeControlStatusBarNode) { self.host = host + self.volumeControlStatusBar = volumeControlStatusBar + self.volumeControlStatusBarNode = volumeControlStatusBarNode + self.volumeControlStatusBarNode.isHidden = true + + self.volumeControlStatusBar.valueChanged = { [weak self] previous, updated in + if let strongSelf = self { + strongSelf.startVolumeTimer() + strongSelf.volumeControlStatusBarNode.updateValue(from: CGFloat(previous), to: CGFloat(updated)) + } + } + } + + private func startVolumeTimer() { + self.volumeTimer?.invalidate() + let timer = SwiftSignalKit.Timer(timeout: 2.0, repeat: false, completion: { [weak self] in + self?.endVolumeTimer() + }, queue: Queue.mainQueue()) + self.volumeTimer = timer + timer.start() + if self.volumeControlStatusBarNode.isHidden { + self.volumeControlStatusBarNode.isHidden = false + self.volumeControlStatusBarNode.alpha = 1.0 + self.volumeControlStatusBarNode.allowsGroupOpacity = true + self.volumeControlStatusBarNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2, delay: 0.18, completion: { [weak self] _ in + self?.volumeControlStatusBarNode.allowsGroupOpacity = false + }) + } + if let (withSafeInsets, forceInCallStatusBarText, forceHiddenBySystemWindows) = self.validParams { + self.updateSurfaces(self.surfaces, withSafeInsets: withSafeInsets, forceInCallStatusBarText: forceInCallStatusBarText, forceHiddenBySystemWindows: forceHiddenBySystemWindows, animated: false, alphaTransition: .animated(duration: 0.2, curve: .easeInOut)) + } + } + + private func endVolumeTimer() { + self.volumeControlStatusBarNode.alpha = 0.0 + self.volumeControlStatusBarNode.allowsGroupOpacity = true + self.volumeControlStatusBarNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15, completion: { [weak self] completed in + if let strongSelf = self, completed { + strongSelf.volumeControlStatusBarNode.isHidden = true + strongSelf.volumeControlStatusBarNode.allowsGroupOpacity = false + } + }) + self.volumeTimer = nil + if let (withSafeInsets, forceInCallStatusBarText, forceHiddenBySystemWindows) = self.validParams { + self.updateSurfaces(self.surfaces, withSafeInsets: withSafeInsets, forceInCallStatusBarText: forceInCallStatusBarText, forceHiddenBySystemWindows: forceHiddenBySystemWindows, animated: false, alphaTransition: .animated(duration: 0.2, curve: .easeInOut)) + } } func updateState(surfaces: [StatusBarSurface], withSafeInsets: Bool, forceInCallStatusBarText: String?, forceHiddenBySystemWindows: Bool, animated: Bool) { let previousSurfaces = self.surfaces self.surfaces = surfaces - self.updateSurfaces(previousSurfaces, withSafeInsets: withSafeInsets, forceInCallStatusBarText: forceInCallStatusBarText, forceHiddenBySystemWindows: forceHiddenBySystemWindows, animated: animated) + self.updateSurfaces(previousSurfaces, withSafeInsets: withSafeInsets, forceInCallStatusBarText: forceInCallStatusBarText, forceHiddenBySystemWindows: forceHiddenBySystemWindows, animated: animated, alphaTransition: .immediate) } - private func updateSurfaces(_ previousSurfaces: [StatusBarSurface], withSafeInsets: Bool, forceInCallStatusBarText: String?, forceHiddenBySystemWindows: Bool, animated: Bool) { + private func updateSurfaces(_ previousSurfaces: [StatusBarSurface], withSafeInsets: Bool, forceInCallStatusBarText: String?, forceHiddenBySystemWindows: Bool, animated: Bool, alphaTransition: ContainedViewLayoutTransition) { let statusBarFrame = self.host.statusBarFrame guard let statusBarView = self.host.statusBarView else { return } + self.validParams = (withSafeInsets, forceInCallStatusBarText, forceHiddenBySystemWindows) + if self.host.statusBarWindow?.isUserInteractionEnabled != (forceInCallStatusBarText == nil) { self.host.statusBarWindow?.isUserInteractionEnabled = (forceInCallStatusBarText == nil) } @@ -215,6 +268,11 @@ class StatusBarManager { statusBar.updateState(statusBar: statusBarView, withSafeInsets: withSafeInsets, inCallText: forceInCallStatusBarText, animated: animated) } + if self.volumeTimer != nil { + globalStatusBar?.1 = 0.0 + } + self.volumeControlStatusBarNode.isDark = globalStatusBar?.0.systemStyle == UIStatusBarStyle.lightContent + if let globalStatusBar = globalStatusBar, !forceHiddenBySystemWindows { let statusBarStyle: UIStatusBarStyle if forceInCallStatusBarText != nil { @@ -226,7 +284,7 @@ class StatusBarManager { self.host.statusBarStyle = statusBarStyle } if let statusBarWindow = self.host.statusBarWindow { - statusBarView.alpha = globalStatusBar.1 + alphaTransition.updateAlpha(layer: statusBarView.layer, alpha: globalStatusBar.1) var statusBarBounds = statusBarWindow.bounds if !statusBarBounds.origin.y.isEqual(to: globalStatusBar.2) { statusBarBounds.origin.y = globalStatusBar.2 diff --git a/Display/TextNode.swift b/Display/TextNode.swift index cb6291687b..1699232272 100644 --- a/Display/TextNode.swift +++ b/Display/TextNode.swift @@ -28,10 +28,6 @@ public struct TextNodeCutout: Equatable { self.position = position self.size = size } - - public static func ==(lhs: TextNodeCutout, rhs: TextNodeCutout) -> Bool { - return lhs.position == rhs.position && lhs.size == rhs.size - } } public final class TextNodeLayoutArguments { diff --git a/Display/VolumeControlStatusBar.swift b/Display/VolumeControlStatusBar.swift new file mode 100644 index 0000000000..5b55afb5c5 --- /dev/null +++ b/Display/VolumeControlStatusBar.swift @@ -0,0 +1,136 @@ +import Foundation +import UIKit +import AsyncDisplayKit +import MediaPlayer + +private let volumeNotificationKey = "AVSystemController_SystemVolumeDidChangeNotification" +private let volumeParameterKey = "AVSystemController_AudioVolumeNotificationParameter" + +final class VolumeControlStatusBar: UIView { + private let control: MPVolumeView + private var observer: Any? + private var currentValue: Float + + var valueChanged: ((Float, Float) -> Void)? + + override init(frame: CGRect) { + self.control = MPVolumeView(frame: CGRect(origin: CGPoint(), size: CGSize(width: 100.0, height: 20.0))) + self.currentValue = AVAudioSession.sharedInstance().outputVolume + + super.init(frame: frame) + + self.addSubview(self.control) + self.observer = NotificationCenter.default.addObserver(forName: NSNotification.Name(rawValue: volumeNotificationKey), object: nil, queue: OperationQueue.main, using: { [weak self] notification in + if let strongSelf = self { + if let volume = notification.userInfo?[volumeParameterKey] as? Float { + let previous = strongSelf.currentValue + strongSelf.currentValue = volume + strongSelf.valueChanged?(previous, volume) + } + } + }) + } + + required init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + deinit { + if let observer = self.observer { + NotificationCenter.default.removeObserver(observer) + } + } +} + +final class VolumeControlStatusBarNode: ASDisplayNode { + private let backgroundNode: ASImageNode + private let foregroundNode: ASImageNode + private let foregroundClippingNode: ASDisplayNode + + private var validLayout: ContainerViewLayout? + + var isDark: Bool = false { + didSet { + if self.isDark != oldValue { + if self.isDark { + self.backgroundNode.image = generateStretchableFilledCircleImage(diameter: 4.0, color: UIColor(white: 0.6, alpha: 1.0)) + self.foregroundNode.image = generateStretchableFilledCircleImage(diameter: 4.0, color: .white) + } else { + self.backgroundNode.image = generateStretchableFilledCircleImage(diameter: 4.0, color: UIColor(white: 0.6, alpha: 1.0)) + self.foregroundNode.image = generateStretchableFilledCircleImage(diameter: 4.0, color: .black) + } + } + } + } + private var value: CGFloat = 1.0 + + override init() { + self.backgroundNode = ASImageNode() + self.backgroundNode.isLayerBacked = true + self.backgroundNode.displaysAsynchronously = false + self.backgroundNode.displayWithoutProcessing = true + self.backgroundNode.image = generateStretchableFilledCircleImage(diameter: 4.0, color: .gray) + + self.foregroundNode = ASImageNode() + self.foregroundNode.isLayerBacked = true + self.foregroundNode.displaysAsynchronously = false + self.foregroundNode.displayWithoutProcessing = true + self.foregroundNode.image = generateStretchableFilledCircleImage(diameter: 4.0, color: .black) + + self.foregroundClippingNode = ASDisplayNode() + self.foregroundClippingNode.clipsToBounds = true + self.foregroundClippingNode.addSubnode(self.foregroundNode) + + super.init() + + self.isUserInteractionEnabled = false + + self.addSubnode(self.backgroundNode) + self.addSubnode(self.foregroundClippingNode) + } + + func updateLayout(layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) { + self.validLayout = layout + + let barHeight: CGFloat = 4.0 + let barWidth: CGFloat + + let statusBarHeight: CGFloat + let sideInset: CGFloat + if let actual = layout.statusBarHeight { + statusBarHeight = actual + } else { + statusBarHeight = 20.0 + } + if layout.safeInsets.left.isZero && layout.safeInsets.top.isZero && layout.intrinsicInsets.left.isZero && layout.intrinsicInsets.top.isZero { + sideInset = 4.0 + } else { + sideInset = 12.0 + } + + if !layout.intrinsicInsets.bottom.isZero { + barWidth = 92.0 - sideInset * 2.0 + } else { + barWidth = layout.size.width - sideInset * 2.0 + } + + let boundingRect = CGRect(origin: CGPoint(x: sideInset, y: floor((statusBarHeight - barHeight) / 2.0)), size: CGSize(width: barWidth, height: barHeight)) + + transition.updateFrame(node: self.backgroundNode, frame: boundingRect) + transition.updateFrame(node: self.foregroundNode, frame: CGRect(origin: CGPoint(), size: boundingRect.size)) + transition.updateFrame(node: self.foregroundClippingNode, frame: CGRect(origin: boundingRect.origin, size: CGSize(width: self.value * boundingRect.width, height: boundingRect.height))) + } + + func updateValue(from fromValue: CGFloat, to toValue: CGFloat) { + if let layout = self.validLayout { + if self.foregroundClippingNode.layer.animation(forKey: "bounds") == nil { + self.value = fromValue + self.updateLayout(layout: layout, transition: .immediate) + } + self.value = toValue + self.updateLayout(layout: layout, transition: .animated(duration: 0.25, curve: .spring)) + } else { + self.value = toValue + } + } +} diff --git a/Display/WindowContent.swift b/Display/WindowContent.swift index e77c611266..1803920f76 100644 --- a/Display/WindowContent.swift +++ b/Display/WindowContent.swift @@ -23,50 +23,6 @@ private struct WindowLayout: Equatable { let safeInsets: UIEdgeInsets let onScreenNavigationHeight: CGFloat? let upperKeyboardInputPositionBound: CGFloat? - - static func ==(lhs: WindowLayout, rhs: WindowLayout) -> Bool { - if !lhs.size.equalTo(rhs.size) { - return false - } - - if let lhsStatusBarHeight = lhs.statusBarHeight { - if let rhsStatusBarHeight = rhs.statusBarHeight { - if !lhsStatusBarHeight.isEqual(to: rhsStatusBarHeight) { - return false - } - } else { - return false - } - } else if let _ = rhs.statusBarHeight { - return false - } - - if lhs.forceInCallStatusBarText != rhs.forceInCallStatusBarText { - return false - } - - if let lhsInputHeight = lhs.inputHeight, let rhsInputHeight = rhs.inputHeight { - if !lhsInputHeight.isEqual(to: rhsInputHeight) { - return false - } - } else if (lhs.inputHeight != nil) != (rhs.inputHeight != nil) { - return false - } - - if lhs.safeInsets != rhs.safeInsets { - return false - } - - if lhs.onScreenNavigationHeight != rhs.onScreenNavigationHeight { - return false - } - - if lhs.upperKeyboardInputPositionBound != rhs.upperKeyboardInputPositionBound { - return false - } - - return true - } } private struct UpdatingLayout { @@ -337,6 +293,16 @@ private final class KeyboardGestureRecognizerDelegate: NSObject, UIGestureRecogn } } +public final class StatusBarVolumeColors { + public let background: UIColor + public let foreground: UIColor + + public init(background: UIColor, foreground: UIColor) { + self.background = background + self.foreground = foreground + } +} + public class Window1 { public let hostView: WindowHostView @@ -365,6 +331,7 @@ public class Window1 { public var previewThemeAccentColor: UIColor = .blue public var previewThemeDarkBlur: Bool = false + public var statusBarVolumeColors: StatusBarVolumeColors = StatusBarVolumeColors(background: .lightGray, foreground: .black) public private(set) var forceInCallStatusBarText: String? = nil public var inCallNavigate: (() -> Void)? { @@ -380,13 +347,19 @@ public class Window1 { private var keyboardTypeChangeTimer: SwiftSignalKit.Timer? + private let volumeControlStatusBar: VolumeControlStatusBar + private let volumeControlStatusBarNode: VolumeControlStatusBarNode + public init(hostView: WindowHostView, statusBarHost: StatusBarHost?) { self.hostView = hostView + self.volumeControlStatusBar = VolumeControlStatusBar(frame: CGRect(origin: CGPoint(x: 0.0, y: -20.0), size: CGSize(width: 100.0, height: 20.0))) + self.volumeControlStatusBarNode = VolumeControlStatusBarNode() + self.statusBarHost = statusBarHost let statusBarHeight: CGFloat if let statusBarHost = statusBarHost { - self.statusBarManager = StatusBarManager(host: statusBarHost) + self.statusBarManager = StatusBarManager(host: statusBarHost, volumeControlStatusBar: self.volumeControlStatusBar, volumeControlStatusBarNode: self.volumeControlStatusBarNode) statusBarHeight = statusBarHost.statusBarFrame.size.height self.keyboardManager = KeyboardManager(host: statusBarHost) } else { @@ -403,6 +376,7 @@ public class Window1 { } self.windowLayout = WindowLayout(size: boundsSize, metrics: layoutMetricsForScreenSize(boundsSize), statusBarHeight: statusBarHeight, forceInCallStatusBarText: self.forceInCallStatusBarText, inputHeight: 0.0, safeInsets: safeInsetsForScreenSize(boundsSize), onScreenNavigationHeight: onScreenNavigationHeight, upperKeyboardInputPositionBound: nil) + self.updatingLayout = UpdatingLayout(layout: self.windowLayout, transition: .immediate) self.presentationContext = PresentationContext() self.overlayPresentationContext = GlobalOverlayPresentationContext(statusBarHost: statusBarHost) @@ -541,6 +515,9 @@ public class Window1 { } self.windowPanRecognizer = recognizer self.hostView.view.addGestureRecognizer(recognizer) + + self.hostView.view.addSubview(self.volumeControlStatusBar) + self.hostView.view.addSubview(self.volumeControlStatusBarNode.view) } public required init(coder aDecoder: NSCoder) { @@ -653,7 +630,7 @@ public class Window1 { if let coveringView = self.coveringView { self.hostView.view.insertSubview(rootController.view, belowSubview: coveringView) } else { - self.hostView.view.addSubview(rootController.view) + self.hostView.view.insertSubview(rootController.view, belowSubview: self.volumeControlStatusBarNode.view) } } } @@ -676,7 +653,7 @@ public class Window1 { if let coveringView = self.coveringView { self.hostView.view.insertSubview(controller.view, belowSubview: coveringView) } else { - self.hostView.view.addSubview(controller.view) + self.hostView.view.insertSubview(controller.view, belowSubview: self.volumeControlStatusBarNode.view) } } @@ -689,7 +666,7 @@ public class Window1 { if self.coveringView !== oldValue { oldValue?.removeFromSuperview() if let coveringView = self.coveringView { - self.hostView.view.addSubview(coveringView) + self.hostView.view.insertSubview(coveringView, belowSubview: self.volumeControlStatusBarNode.view) if !self.windowLayout.size.width.isZero { coveringView.frame = CGRect(origin: CGPoint(), size: self.windowLayout.size) coveringView.updateLayout(self.windowLayout.size) @@ -813,10 +790,13 @@ public class Window1 { } } + private var isFirstLayout = true + private func commitUpdatingLayout() { if let updatingLayout = self.updatingLayout { self.updatingLayout = nil - if updatingLayout.layout != self.windowLayout { + if updatingLayout.layout != self.windowLayout || self.isFirstLayout { + self.isFirstLayout = false var statusBarHeight: CGFloat? if let statusBarHost = self.statusBarHost { statusBarHeight = statusBarHost.statusBarFrame.size.height @@ -864,6 +844,9 @@ public class Window1 { }) } + self.volumeControlStatusBarNode.frame = CGRect(origin: CGPoint(), size: self.windowLayout.size) + self.volumeControlStatusBarNode.updateLayout(layout: childLayout, transition: updatingLayout.transition) + if let coveringView = self.coveringView { coveringView.frame = CGRect(origin: CGPoint(), size: self.windowLayout.size) coveringView.updateLayout(self.windowLayout.size) diff --git a/DisplayMac/UIKit.swift b/DisplayMac/UIKit.swift index d080662bc6..09f6956cce 100644 --- a/DisplayMac/UIKit.swift +++ b/DisplayMac/UIKit.swift @@ -20,22 +20,6 @@ public struct UIEdgeInsets: Equatable { self.bottom = bottom self.right = right } - - public static func ==(lhs: UIEdgeInsets, rhs: UIEdgeInsets) -> Bool { - if !lhs.top.isEqual(to: rhs.top) { - return false - } - if !lhs.left.isEqual(to: rhs.left) { - return false - } - if !lhs.bottom.isEqual(to: rhs.bottom) { - return false - } - if !lhs.right.isEqual(to: rhs.right) { - return false - } - return true - } } public final class UIColor: NSObject {