import Foundation import UIKit import Display import TelegramPresentationData import ComponentFlow import GlassBackgroundComponent public final class GlassControlPanelComponent: Component { public final class Item: Equatable { public let items: [GlassControlGroupComponent.Item] public let background: GlassControlGroupComponent.Background public init(items: [GlassControlGroupComponent.Item], background: GlassControlGroupComponent.Background) { self.items = items self.background = background } public static func ==(lhs: Item, rhs: Item) -> Bool { if lhs.items != rhs.items { return false } if lhs.background != rhs.background { return false } return true } } public let theme: PresentationTheme public let leftItem: Item? public let rightItem: Item? public let centralItem: Item? public init( theme: PresentationTheme, leftItem: Item?, centralItem: Item?, rightItem: Item? ) { self.theme = theme self.leftItem = leftItem self.centralItem = centralItem self.rightItem = rightItem } public static func ==(lhs: GlassControlPanelComponent, rhs: GlassControlPanelComponent) -> Bool { if lhs.theme !== rhs.theme { return false } if lhs.leftItem != rhs.leftItem { return false } if lhs.centralItem != rhs.centralItem { return false } if lhs.rightItem != rhs.rightItem { return false } return true } public final class View: UIView { private let glassContainerView: GlassBackgroundContainerView private var leftItemComponent: ComponentView? private var centralItemComponent: ComponentView? private var rightItemComponent: ComponentView? private var component: GlassControlPanelComponent? private weak var state: EmptyComponentState? public var leftItemView: GlassControlGroupComponent.View? { return self.leftItemComponent?.view as? GlassControlGroupComponent.View } public var centerItemView: GlassControlGroupComponent.View? { return self.centralItemComponent?.view as? GlassControlGroupComponent.View } public var rightItemView: GlassControlGroupComponent.View? { return self.rightItemComponent?.view as? GlassControlGroupComponent.View } override public init(frame: CGRect) { self.glassContainerView = GlassBackgroundContainerView() super.init(frame: frame) self.addSubview(self.glassContainerView) } required public init(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } func update(component: GlassControlPanelComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { self.component = component self.state = state let alphaTransition: ComponentTransition = transition.animation.isImmediate ? .immediate : .easeInOut(duration: 0.2) let minSpacing: CGFloat = 8.0 var leftItemFrame: CGRect? if let leftItem = component.leftItem { let leftItemComponent: ComponentView var leftItemTransition = transition if let current = self.leftItemComponent { leftItemComponent = current } else { leftItemComponent = ComponentView() self.leftItemComponent = leftItemComponent leftItemTransition = transition.withAnimation(.none) } let leftItemSize = leftItemComponent.update( transition: leftItemTransition, component: AnyComponent(GlassControlGroupComponent( theme: component.theme, background: leftItem.background, items: leftItem.items, minWidth: 40.0 )), environment: {}, containerSize: CGSize(width: availableSize.width, height: availableSize.height) ) let leftItemFrameValue = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: leftItemSize) leftItemFrame = leftItemFrameValue if let leftItemComponentView = leftItemComponent.view { var animateIn = false if leftItemComponentView.superview == nil { animateIn = true self.glassContainerView.contentView.addSubview(leftItemComponentView) ComponentTransition.immediate.setScale(view: leftItemComponentView, scale: 0.001) } leftItemTransition.setPosition(view: leftItemComponentView, position: leftItemFrameValue.center) leftItemTransition.setBounds(view: leftItemComponentView, bounds: CGRect(origin: CGPoint(), size: leftItemFrameValue.size)) if animateIn { alphaTransition.animateAlpha(view: leftItemComponentView, from: 0.0, to: 1.0) transition.setScale(view: leftItemComponentView, scale: 1.0) } } } else if let leftItemComponent = self.leftItemComponent { self.leftItemComponent = nil if let leftItemComponentView = leftItemComponent.view { transition.setScale(view: leftItemComponentView, scale: 0.001) alphaTransition.setAlpha(view: leftItemComponentView, alpha: 0.0, completion: { [weak leftItemComponentView] _ in leftItemComponentView?.removeFromSuperview() }) } } var rightItemFrame: CGRect? if let rightItem = component.rightItem { let rightItemComponent: ComponentView var rightItemTransition = transition if let current = self.rightItemComponent { rightItemComponent = current } else { rightItemComponent = ComponentView() self.rightItemComponent = rightItemComponent rightItemTransition = transition.withAnimation(.none) } let rightItemSize = rightItemComponent.update( transition: rightItemTransition, component: AnyComponent(GlassControlGroupComponent( theme: component.theme, background: rightItem.background, items: rightItem.items, minWidth: 40.0 )), environment: {}, containerSize: CGSize(width: availableSize.width, height: availableSize.height) ) let rightItemFrameValue = CGRect(origin: CGPoint(x: availableSize.width - rightItemSize.width, y: 0.0), size: rightItemSize) rightItemFrame = rightItemFrameValue if let rightItemComponentView = rightItemComponent.view { var animateIn = false if rightItemComponentView.superview == nil { animateIn = true self.glassContainerView.contentView.addSubview(rightItemComponentView) ComponentTransition.immediate.setScale(view: rightItemComponentView, scale: 0.001) } rightItemTransition.setPosition(view: rightItemComponentView, position: rightItemFrameValue.center) rightItemTransition.setBounds(view: rightItemComponentView, bounds: CGRect(origin: CGPoint(), size: rightItemFrameValue.size)) if animateIn { alphaTransition.animateAlpha(view: rightItemComponentView, from: 0.0, to: 1.0) transition.setScale(view: rightItemComponentView, scale: 1.0) } } } else if let rightItemComponent = self.rightItemComponent { self.rightItemComponent = nil if let rightItemComponentView = rightItemComponent.view { transition.setScale(view: rightItemComponentView, scale: 0.001) alphaTransition.setAlpha(view: rightItemComponentView, alpha: 0.0, completion: { [weak rightItemComponentView] _ in rightItemComponentView?.removeFromSuperview() }) } } if let centralItem = component.centralItem { let centralItemComponent: ComponentView var centralItemTransition = transition if let current = self.centralItemComponent { centralItemComponent = current } else { centralItemComponent = ComponentView() self.centralItemComponent = centralItemComponent centralItemTransition = transition.withAnimation(.none) } var maxCentralItemSize = CGSize(width: availableSize.width, height: availableSize.height) var centralRightInset: CGFloat = 0.0 if let rightItemFrame { centralRightInset = availableSize.width - rightItemFrame.minX + minSpacing } var centralLeftInset: CGFloat = 0.0 if let leftItemFrame { centralLeftInset = leftItemFrame.maxX + minSpacing } if centralRightInset <= 48.0 && centralLeftInset <= 48.0 { let maxInset = max(centralRightInset, centralLeftInset) centralLeftInset = maxInset centralRightInset = maxInset } maxCentralItemSize.width = max(1.0, availableSize.width - centralLeftInset - centralRightInset) let centralItemSize = centralItemComponent.update( transition: centralItemTransition, component: AnyComponent(GlassControlGroupComponent( theme: component.theme, background: centralItem.background, items: centralItem.items, minWidth: 165.0 )), environment: {}, containerSize: maxCentralItemSize ) let centralItemFrameValue = CGRect(origin: CGPoint(x: centralLeftInset + floor((availableSize.width - centralLeftInset - centralRightInset - centralItemSize.width) * 0.5), y: 0.0), size: centralItemSize) if let centralItemComponentView = centralItemComponent.view { var animateIn = false if centralItemComponentView.superview == nil { animateIn = true self.glassContainerView.contentView.addSubview(centralItemComponentView) ComponentTransition.immediate.setScale(view: centralItemComponentView, scale: 0.001) } centralItemTransition.setPosition(view: centralItemComponentView, position: centralItemFrameValue.center) centralItemTransition.setBounds(view: centralItemComponentView, bounds: CGRect(origin: CGPoint(), size: centralItemFrameValue.size)) if animateIn { alphaTransition.animateAlpha(view: centralItemComponentView, from: 0.0, to: 1.0) transition.setScale(view: centralItemComponentView, scale: 1.0) } } } else if let centralItemComponent = self.centralItemComponent { self.centralItemComponent = nil if let centralItemComponentView = centralItemComponent.view { transition.setScale(view: centralItemComponentView, scale: 0.001) alphaTransition.setAlpha(view: centralItemComponentView, alpha: 0.0, completion: { [weak centralItemComponentView] _ in centralItemComponentView?.removeFromSuperview() }) } } transition.setFrame(view: self.glassContainerView, frame: CGRect(origin: CGPoint(), size: availableSize)) self.glassContainerView.update(size: availableSize, isDark: component.theme.overallDarkAppearance, transition: transition) return availableSize } } public func makeView() -> View { return View(frame: CGRect()) } public func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition) } }