From 2d79df7e751ffde0b0a59ef9dd9bdc038790ac47 Mon Sep 17 00:00:00 2001 From: Peter Iakovlev Date: Thu, 8 Feb 2018 14:21:11 +0400 Subject: [PATCH] no message --- Display.xcodeproj/project.pbxproj | 34 +++- .../xcschemes/DisplayMac.xcscheme | 82 +++++++++ .../xcschemes/xcschememanagement.plist | 2 +- Display/ActionSheetCheckboxItem.swift | 1 + Display/AlertController.swift | 28 ++- Display/AlertControllerNode.swift | 6 +- Display/BarButtonItemWrapper.swift | 49 ------ Display/CASeeThroughTracingLayer.h | 2 - Display/CASeeThroughTracingLayer.m | 4 - Display/ContextMenuContainerNode.swift | 11 +- Display/Font.swift | 24 +++ Display/ListView.swift | 97 ++--------- Display/ListViewScroller.swift | 4 + Display/ListViewTransactionQueue.swift | 4 + Display/NavigationBar.swift | 101 ++++++----- Display/NavigationButtonNode.swift | 161 +++++++++++++++++- Display/NavigationTransitionCoordinator.swift | 2 +- Display/TextAlertController.swift | 24 +-- Display/TooltipController.swift | 114 +++++++++++++ Display/TooltipControllerNode.swift | 118 +++++++++++++ Display/UINavigationItem+Proxy.h | 3 + Display/UINavigationItem+Proxy.m | 35 ++++ Display/WindowContent.swift | 62 +++++-- Display/WindowCoveringView.swift | 7 + DisplayMac/ASDisplayNode.swift | 31 +++- DisplayMac/CADisplayLink.swift | 89 ++++++++++ DisplayMac/UIKit.swift | 8 +- DisplayMac/UIScrollView.swift | 8 +- DisplayMac/UIView.swift | 35 +++- 29 files changed, 904 insertions(+), 242 deletions(-) create mode 100644 Display.xcodeproj/xcuserdata/peter.xcuserdatad/xcschemes/DisplayMac.xcscheme delete mode 100644 Display/BarButtonItemWrapper.swift create mode 100644 Display/TooltipController.swift create mode 100644 Display/TooltipControllerNode.swift create mode 100644 Display/WindowCoveringView.swift create mode 100644 DisplayMac/CADisplayLink.swift diff --git a/Display.xcodeproj/project.pbxproj b/Display.xcodeproj/project.pbxproj index 6f0aefa93f..aeccfbe154 100644 --- a/Display.xcodeproj/project.pbxproj +++ b/Display.xcodeproj/project.pbxproj @@ -7,6 +7,8 @@ objects = { /* Begin PBXBuildFile section */ + D00701982029CAD6006B9E34 /* TooltipController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D00701972029CAD6006B9E34 /* TooltipController.swift */; }; + D007019A2029CAE2006B9E34 /* TooltipControllerNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D00701992029CAE2006B9E34 /* TooltipControllerNode.swift */; }; D0078A681C92B21400DF6D92 /* StatusBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0078A671C92B21400DF6D92 /* StatusBar.swift */; }; D00C7CD21E3657570080C3D5 /* TextFieldNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D00C7CD11E3657570080C3D5 /* TextFieldNode.swift */; }; D01159BB1F40E96C0039383E /* DisplayMac.h in Headers */ = {isa = PBXBuildFile; fileRef = D01159B91F40E96C0039383E /* DisplayMac.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -62,6 +64,7 @@ D05174B41EAA833200A1BF36 /* CASeeThroughTracingLayer.m in Sources */ = {isa = PBXBuildFile; fileRef = D05174B21EAA833200A1BF36 /* CASeeThroughTracingLayer.m */; }; D053CB601D22B4F200DD41DF /* CATracingLayer.h in Headers */ = {isa = PBXBuildFile; fileRef = D053CB5E1D22B4F200DD41DF /* CATracingLayer.h */; settings = {ATTRIBUTES = (Public, ); }; }; D053CB611D22B4F200DD41DF /* CATracingLayer.m in Sources */ = {isa = PBXBuildFile; fileRef = D053CB5F1D22B4F200DD41DF /* CATracingLayer.m */; }; + D053DAD12018ECF900993D32 /* WindowCoveringView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D053DAD02018ECF900993D32 /* WindowCoveringView.swift */; }; D05BE4AB1D1F25E3002BD72C /* PresentationContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = D05BE4AA1D1F25E3002BD72C /* PresentationContext.swift */; }; D05CC2671B69316F00E235A3 /* Display.h in Headers */ = {isa = PBXBuildFile; fileRef = D05CC2661B69316F00E235A3 /* Display.h */; settings = {ATTRIBUTES = (Public, ); }; }; D05CC26E1B69316F00E235A3 /* Display.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D05CC2631B69316F00E235A3 /* Display.framework */; }; @@ -90,7 +93,6 @@ D05CC3191B695A9600E235A3 /* NavigationBackButtonNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D05CC30D1B695A9500E235A3 /* NavigationBackButtonNode.swift */; }; D05CC31A1B695A9600E235A3 /* NavigationButtonNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D05CC30E1B695A9500E235A3 /* NavigationButtonNode.swift */; }; D05CC31B1B695A9600E235A3 /* NavigationTitleNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D05CC30F1B695A9500E235A3 /* NavigationTitleNode.swift */; }; - D05CC31C1B695A9600E235A3 /* BarButtonItemWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = D05CC3101B695A9600E235A3 /* BarButtonItemWrapper.swift */; }; D05CC31D1B695A9600E235A3 /* UIBarButtonItem+Proxy.m in Sources */ = {isa = PBXBuildFile; fileRef = D05CC3111B695A9600E235A3 /* UIBarButtonItem+Proxy.m */; }; D05CC31E1B695A9600E235A3 /* UIBarButtonItem+Proxy.h in Headers */ = {isa = PBXBuildFile; fileRef = D05CC3121B695A9600E235A3 /* UIBarButtonItem+Proxy.h */; settings = {ATTRIBUTES = (Public, ); }; }; D05CC31F1B695A9600E235A3 /* NavigationControllerProxy.m in Sources */ = {isa = PBXBuildFile; fileRef = D05CC3131B695A9600E235A3 /* NavigationControllerProxy.m */; }; @@ -125,7 +127,6 @@ D0C2DFC61CC4431D0044FF83 /* ASTransformLayerNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C2DFBB1CC4431D0044FF83 /* ASTransformLayerNode.swift */; }; D0C2DFC71CC4431D0044FF83 /* ListViewItemNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C2DFBC1CC4431D0044FF83 /* ListViewItemNode.swift */; }; D0C2DFC81CC4431D0044FF83 /* Spring.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C2DFBD1CC4431D0044FF83 /* Spring.swift */; }; - D0C2DFC91CC4431D0044FF83 /* ListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C2DFBE1CC4431D0044FF83 /* ListView.swift */; }; D0C2DFCA1CC4431D0044FF83 /* ListViewItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C2DFBF1CC4431D0044FF83 /* ListViewItem.swift */; }; D0C2DFCB1CC4431D0044FF83 /* ListViewAnimation.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C2DFC01CC4431D0044FF83 /* ListViewAnimation.swift */; }; D0C2DFCD1CC4431D0044FF83 /* ListViewTransactionQueue.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C2DFC21CC4431D0044FF83 /* ListViewTransactionQueue.swift */; }; @@ -151,8 +152,11 @@ D0E1D6721CBC201E00B04029 /* AsyncDisplayKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D0E1D6711CBC201E00B04029 /* AsyncDisplayKit.framework */; }; D0E35A031DE473B900BC6096 /* HighlightableButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0E35A021DE473B900BC6096 /* HighlightableButton.swift */; }; D0E49C881B83A3580099E553 /* ImageCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0E49C871B83A3580099E553 /* ImageCache.swift */; }; + D0E8175E2014ED7D00B82BBB /* CADisplayLink.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0E8175C2014ED7D00B82BBB /* CADisplayLink.swift */; }; + D0E8175F2014F18F00B82BBB /* ListViewTransactionQueue.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C2DFC21CC4431D0044FF83 /* ListViewTransactionQueue.swift */; }; D0F1132F1D6F3C20008C3597 /* ContextMenuActionNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0F1132E1D6F3C20008C3597 /* ContextMenuActionNode.swift */; }; D0F7AB371DCFF6F8009AD9A1 /* ListViewItemHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0F7AB361DCFF6F8009AD9A1 /* ListViewItemHeader.swift */; }; + D0F8C3932014FB7C00236FC5 /* ListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C2DFBE1CC4431D0044FF83 /* ListView.swift */; }; D0FF9B301E7196F6000C66DB /* KeyboardManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0FF9B2F1E7196F6000C66DB /* KeyboardManager.swift */; }; /* End PBXBuildFile section */ @@ -167,6 +171,8 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ + D00701972029CAD6006B9E34 /* TooltipController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TooltipController.swift; sourceTree = ""; }; + D00701992029CAE2006B9E34 /* TooltipControllerNode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TooltipControllerNode.swift; sourceTree = ""; }; D0078A671C92B21400DF6D92 /* StatusBar.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StatusBar.swift; sourceTree = ""; }; D00C7CD11E3657570080C3D5 /* TextFieldNode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TextFieldNode.swift; sourceTree = ""; }; D01159B71F40E96B0039383E /* DisplayMac.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = DisplayMac.framework; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -211,6 +217,7 @@ D05174B21EAA833200A1BF36 /* CASeeThroughTracingLayer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CASeeThroughTracingLayer.m; sourceTree = ""; }; D053CB5E1D22B4F200DD41DF /* CATracingLayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CATracingLayer.h; sourceTree = ""; }; D053CB5F1D22B4F200DD41DF /* CATracingLayer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CATracingLayer.m; sourceTree = ""; }; + D053DAD02018ECF900993D32 /* WindowCoveringView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WindowCoveringView.swift; sourceTree = ""; }; D05BE4AA1D1F25E3002BD72C /* PresentationContext.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PresentationContext.swift; sourceTree = ""; }; D05CC2631B69316F00E235A3 /* Display.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Display.framework; sourceTree = BUILT_PRODUCTS_DIR; }; D05CC2661B69316F00E235A3 /* Display.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Display.h; sourceTree = ""; }; @@ -242,7 +249,6 @@ D05CC30D1B695A9500E235A3 /* NavigationBackButtonNode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NavigationBackButtonNode.swift; sourceTree = ""; }; D05CC30E1B695A9500E235A3 /* NavigationButtonNode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NavigationButtonNode.swift; sourceTree = ""; }; D05CC30F1B695A9500E235A3 /* NavigationTitleNode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NavigationTitleNode.swift; sourceTree = ""; }; - D05CC3101B695A9600E235A3 /* BarButtonItemWrapper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BarButtonItemWrapper.swift; sourceTree = ""; }; D05CC3111B695A9600E235A3 /* UIBarButtonItem+Proxy.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIBarButtonItem+Proxy.m"; sourceTree = ""; }; D05CC3121B695A9600E235A3 /* UIBarButtonItem+Proxy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIBarButtonItem+Proxy.h"; sourceTree = ""; }; D05CC3131B695A9600E235A3 /* NavigationControllerProxy.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NavigationControllerProxy.m; sourceTree = ""; }; @@ -304,6 +310,7 @@ D0E1D6711CBC201E00B04029 /* AsyncDisplayKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = AsyncDisplayKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; D0E35A021DE473B900BC6096 /* HighlightableButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HighlightableButton.swift; sourceTree = ""; }; D0E49C871B83A3580099E553 /* ImageCache.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImageCache.swift; sourceTree = ""; }; + D0E8175C2014ED7D00B82BBB /* CADisplayLink.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CADisplayLink.swift; sourceTree = ""; }; D0F1132E1D6F3C20008C3597 /* ContextMenuActionNode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContextMenuActionNode.swift; sourceTree = ""; }; D0F7AB361DCFF6F8009AD9A1 /* ListViewItemHeader.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ListViewItemHeader.swift; sourceTree = ""; }; D0FF9B2F1E7196F6000C66DB /* KeyboardManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeyboardManager.swift; sourceTree = ""; }; @@ -338,6 +345,15 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + D00701962029CAC4006B9E34 /* Tooltip */ = { + isa = PBXGroup; + children = ( + D00701972029CAD6006B9E34 /* TooltipController.swift */, + D00701992029CAE2006B9E34 /* TooltipControllerNode.swift */, + ); + name = Tooltip; + sourceTree = ""; + }; D01159B81F40E96C0039383E /* DisplayMac */ = { isa = PBXGroup; children = ( @@ -351,6 +367,7 @@ D01847761FFA78C100075256 /* UIGestureRecognizer.swift */, D01847781FFA7A4E00075256 /* UITouch.swift */, D018477B1FFA7ABF00075256 /* UISlider.swift */, + D0E8175C2014ED7D00B82BBB /* CADisplayLink.swift */, ); path = DisplayMac; sourceTree = ""; @@ -363,6 +380,7 @@ D087BFB41F75181D003FD209 /* ChildWindowHostView.swift */, D0BB4EB91F96DCC60036D9DE /* WindowInputAccessoryHeightProvider.swift */, D0CB788F1F9822F8004AB79B /* WindowPanRecognizer.swift */, + D053DAD02018ECF900993D32 /* WindowCoveringView.swift */, ); name = Window; sourceTree = ""; @@ -579,7 +597,6 @@ D05CC30E1B695A9500E235A3 /* NavigationButtonNode.swift */, D05CC30F1B695A9500E235A3 /* NavigationTitleNode.swift */, D0C12A191F3375B400B3F66D /* NavigationBarTitleTransitionNode.swift */, - D05CC3101B695A9600E235A3 /* BarButtonItemWrapper.swift */, D05CC3281B69750D00E235A3 /* InteractiveTransitionGestureRecognizer.swift */, D0AE3D4C1D25C816001CCE13 /* NavigationBarTransitionState.swift */, D077B8E81F4637040046D27A /* NavigationBarBadge.swift */, @@ -631,6 +648,7 @@ D015F7551D1B142300E269B5 /* Tab Bar */, D015F7561D1B465600E269B5 /* Action Sheet */, D03725BF1D6DF57B007FC290 /* Context Menu */, + D00701962029CAC4006B9E34 /* Tooltip */, D0DA444A1E4DCA36005FDCA7 /* Alert */, ); name = Controllers; @@ -860,6 +878,7 @@ D01847691FFA756600075256 /* ListViewAccessoryItemNode.swift in Sources */, D01847611FFA703B00075256 /* UIKit.swift in Sources */, D01847701FFA773100075256 /* ListView.swift in Sources */, + D0E8175F2014F18F00B82BBB /* ListViewTransactionQueue.swift in Sources */, D01847771FFA78C100075256 /* UIGestureRecognizer.swift in Sources */, D01C06C21FC254F8001561AB /* ASDisplayNode.swift in Sources */, D01847631FFA70FC00075256 /* UIView.swift in Sources */, @@ -877,6 +896,7 @@ D018476F1FFA76FD00075256 /* ListViewAccessoryItem.swift in Sources */, D018477A1FFA7A8800075256 /* ListViewOverscrollBackgroundNode.swift in Sources */, D01847741FFA780400075256 /* UIScrollView.swift in Sources */, + D0E8175E2014ED7D00B82BBB /* CADisplayLink.swift in Sources */, D01847681FFA749F00075256 /* ListViewAnimation.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -891,6 +911,7 @@ D0078A681C92B21400DF6D92 /* StatusBar.swift in Sources */, D05CC2F81B6955D000E235A3 /* UIViewController+Navigation.m in Sources */, D0F1132F1D6F3C20008C3597 /* ContextMenuActionNode.swift in Sources */, + D053DAD12018ECF900993D32 /* WindowCoveringView.swift in Sources */, D02BDB021B6AC703008AFAD2 /* RuntimeUtils.swift in Sources */, D0BB4EBA1F96DCC60036D9DE /* WindowInputAccessoryHeightProvider.swift in Sources */, D05CC31F1B695A9600E235A3 /* NavigationControllerProxy.m in Sources */, @@ -905,6 +926,7 @@ D0BE93191E8ED71100DCC1E6 /* NativeWindowHostView.swift in Sources */, D05CC3251B695B0700E235A3 /* NavigationBarProxy.m in Sources */, D05174B41EAA833200A1BF36 /* CASeeThroughTracingLayer.m in Sources */, + D0F8C3932014FB7C00236FC5 /* ListView.swift in Sources */, D03E7DE61C96B96E00C07816 /* NavigationBarTransitionContainer.swift in Sources */, D0C85DD01D1C082E00124894 /* ActionSheetItemGroupsContainerNode.swift in Sources */, D05CC2F71B6955D000E235A3 /* UIKitUtils.swift in Sources */, @@ -916,9 +938,9 @@ D0C2DFD01CC4431D0044FF83 /* ListViewAccessoryItemNode.swift in Sources */, D0DA44501E4DCBDE005FDCA7 /* AlertContentNode.swift in Sources */, D0D94A171D3814F900740E02 /* UniversalTapRecognizer.swift in Sources */, + D00701982029CAD6006B9E34 /* TooltipController.swift in Sources */, D015F7581D1B467200E269B5 /* ActionSheetController.swift in Sources */, D0DA444C1E4DCA4A005FDCA7 /* AlertController.swift in Sources */, - D0C2DFC91CC4431D0044FF83 /* ListView.swift in Sources */, D03E7DF91C96C5F200C07816 /* NSWeakReference.m in Sources */, D0DC48541BF93D8B00F672FD /* TabBarController.swift in Sources */, D03E7E011C974AB300C07816 /* DisplayLinkDispatcher.swift in Sources */, @@ -933,7 +955,6 @@ D05CC2E71B69555800E235A3 /* CAAnimationUtils.swift in Sources */, D05CC31B1B695A9600E235A3 /* NavigationTitleNode.swift in Sources */, D04C468E1F4C97BE00D30FE1 /* PageControlNode.swift in Sources */, - D05CC31C1B695A9600E235A3 /* BarButtonItemWrapper.swift in Sources */, D0C2DFCF1CC4431D0044FF83 /* ListViewScroller.swift in Sources */, D00C7CD21E3657570080C3D5 /* TextFieldNode.swift in Sources */, D0DC485F1BF949FB00F672FD /* TabBarContollerNode.swift in Sources */, @@ -966,6 +987,7 @@ D02383821DDF798E004018B6 /* LegacyPresentedControllerNode.swift in Sources */, D05CC2FC1B6955D000E235A3 /* UIKitUtils.m in Sources */, D0C2DFC61CC4431D0044FF83 /* ASTransformLayerNode.swift in Sources */, + D007019A2029CAE2006B9E34 /* TooltipControllerNode.swift in Sources */, D05CC3291B69750D00E235A3 /* InteractiveTransitionGestureRecognizer.swift in Sources */, D077B8E91F4637040046D27A /* NavigationBarBadge.swift in Sources */, D0CE67921F7DA11700FFB557 /* ActionSheetTheme.swift in Sources */, diff --git a/Display.xcodeproj/xcuserdata/peter.xcuserdatad/xcschemes/DisplayMac.xcscheme b/Display.xcodeproj/xcuserdata/peter.xcuserdatad/xcschemes/DisplayMac.xcscheme new file mode 100644 index 0000000000..4c933753f7 --- /dev/null +++ b/Display.xcodeproj/xcuserdata/peter.xcuserdatad/xcschemes/DisplayMac.xcscheme @@ -0,0 +1,82 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Display.xcodeproj/xcuserdata/peter.xcuserdatad/xcschemes/xcschememanagement.plist b/Display.xcodeproj/xcuserdata/peter.xcuserdatad/xcschemes/xcschememanagement.plist index 1a8a7d206a..4b2e9b0571 100644 --- a/Display.xcodeproj/xcuserdata/peter.xcuserdatad/xcschemes/xcschememanagement.plist +++ b/Display.xcodeproj/xcuserdata/peter.xcuserdatad/xcschemes/xcschememanagement.plist @@ -12,7 +12,7 @@ DisplayMac.xcscheme orderHint - 26 + 24 DisplayTests.xcscheme diff --git a/Display/ActionSheetCheckboxItem.swift b/Display/ActionSheetCheckboxItem.swift index 26a3cfe2d9..36a5b2e340 100644 --- a/Display/ActionSheetCheckboxItem.swift +++ b/Display/ActionSheetCheckboxItem.swift @@ -54,6 +54,7 @@ public class ActionSheetCheckboxItemNode: ActionSheetItemNode { self.labelNode = ASTextNode() self.labelNode.maximumNumberOfLines = 1 + self.labelNode.truncationMode = .byTruncatingTail self.labelNode.isUserInteractionEnabled = false self.labelNode.displaysAsynchronously = false diff --git a/Display/AlertController.swift b/Display/AlertController.swift index 37bcb5f2d3..c8a1db2987 100644 --- a/Display/AlertController.swift +++ b/Display/AlertController.swift @@ -1,17 +1,41 @@ import Foundation import AsyncDisplayKit +public final class AlertControllerTheme { + public let backgroundColor: UIColor + public let separatorColor: UIColor + public let highlightedItemColor: UIColor + public let primaryColor: UIColor + public let secondaryColor: UIColor + public let accentColor: UIColor + public let destructiveColor: UIColor + + public init(backgroundColor: UIColor, separatorColor: UIColor, highlightedItemColor: UIColor, primaryColor: UIColor, secondaryColor: UIColor, accentColor: UIColor, destructiveColor: UIColor) { + self.backgroundColor = backgroundColor + self.separatorColor = separatorColor + self.highlightedItemColor = highlightedItemColor + self.primaryColor = primaryColor + self.secondaryColor = secondaryColor + self.accentColor = accentColor + self.destructiveColor = destructiveColor + } +} + open class AlertController: ViewController { private var controllerNode: AlertControllerNode { return self.displayNode as! AlertControllerNode } + private let theme: AlertControllerTheme private let contentNode: AlertContentNode - public init(contentNode: AlertContentNode) { + public init(theme: AlertControllerTheme, contentNode: AlertContentNode) { + self.theme = theme self.contentNode = contentNode super.init(navigationBarTheme: nil) + + self.statusBar.statusBarStyle = .Ignore } required public init(coder aDecoder: NSCoder) { @@ -19,7 +43,7 @@ open class AlertController: ViewController { } override open func loadDisplayNode() { - self.displayNode = AlertControllerNode(contentNode: self.contentNode) + self.displayNode = AlertControllerNode(contentNode: self.contentNode, theme: self.theme) self.displayNodeDidLoad() self.controllerNode.dismiss = { [weak self] in diff --git a/Display/AlertControllerNode.swift b/Display/AlertControllerNode.swift index bab1916e40..65ca70aacc 100644 --- a/Display/AlertControllerNode.swift +++ b/Display/AlertControllerNode.swift @@ -9,17 +9,17 @@ final class AlertControllerNode: ASDisplayNode { var dismiss: (() -> Void)? - init(contentNode: AlertContentNode) { + init(contentNode: AlertContentNode, theme: AlertControllerTheme) { self.dimmingNode = ASDisplayNode() self.dimmingNode.backgroundColor = UIColor(white: 0.0, alpha: 0.5) self.containerNode = ASDisplayNode() - self.containerNode.backgroundColor = .white + self.containerNode.backgroundColor = theme.backgroundColor self.containerNode.layer.cornerRadius = 14.0 self.containerNode.layer.masksToBounds = true self.effectNode = ASDisplayNode(viewBlock: { - let view = UIView()//UIVisualEffectView(effect: UIBlurEffect(style: .light)) + let view = UIView() return view }) diff --git a/Display/BarButtonItemWrapper.swift b/Display/BarButtonItemWrapper.swift deleted file mode 100644 index 3e6f1b5695..0000000000 --- a/Display/BarButtonItemWrapper.swift +++ /dev/null @@ -1,49 +0,0 @@ -import UIKit -import AsyncDisplayKit - -internal class BarButtonItemWrapper { - let parentNode: ASDisplayNode - let barButtonItem: UIBarButtonItem - let layoutNeeded: () -> () - - let buttonNode: NavigationButtonNode - - private var setEnabledListenerKey: Int! - private var setTitleListenerKey: Int! - - init(parentNode: ASDisplayNode, barButtonItem: UIBarButtonItem, layoutNeeded: @escaping () -> ()) { - self.parentNode = parentNode - self.barButtonItem = barButtonItem - self.layoutNeeded = layoutNeeded - - self.buttonNode = NavigationButtonNode() - self.buttonNode.pressed = { [weak self] in - self?.barButtonItem.performActionOnTarget() - return - } - self.parentNode.addSubnode(self.buttonNode) - - self.setEnabledListenerKey = barButtonItem.addSetEnabledListener({ [weak self] enabled in - self?.buttonNode.isEnabled = enabled - return - }) - - self.setTitleListenerKey = barButtonItem.addSetTitleListener({ [weak self] title in - self?.buttonNode.text = title ?? "" - if let layoutNeeded = self?.layoutNeeded { - layoutNeeded() - } - return - }) - - self.buttonNode.text = barButtonItem.title ?? "" - self.buttonNode.isEnabled = barButtonItem.isEnabled ?? true - self.buttonNode.bold = (barButtonItem.style ?? UIBarButtonItemStyle.plain) == UIBarButtonItemStyle.done - } - - deinit { - self.barButtonItem.removeSetTitleListener(self.setTitleListenerKey) - self.barButtonItem.removeSetEnabledListener(self.setEnabledListenerKey) - self.buttonNode.removeFromSupernode() - } -} diff --git a/Display/CASeeThroughTracingLayer.h b/Display/CASeeThroughTracingLayer.h index 231170d8b5..8c6ff3de8e 100644 --- a/Display/CASeeThroughTracingLayer.h +++ b/Display/CASeeThroughTracingLayer.h @@ -2,8 +2,6 @@ @interface CASeeThroughTracingLayer : CALayer -@property (nonatomic, copy) void (^updateRelativePosition)(CGPoint); - @end @interface CASeeThroughTracingView : UIView diff --git a/Display/CASeeThroughTracingLayer.m b/Display/CASeeThroughTracingLayer.m index b3c01da78b..79ff8d3631 100644 --- a/Display/CASeeThroughTracingLayer.m +++ b/Display/CASeeThroughTracingLayer.m @@ -44,10 +44,6 @@ [(CASeeThroughTracingLayer *)sublayer _mirrorTransformToSublayers]; } } - - if (_updateRelativePosition) { - _updateRelativePosition(sublayerParentOffset); - } } @end diff --git a/Display/ContextMenuContainerNode.swift b/Display/ContextMenuContainerNode.swift index 02c6b9f7b9..74d7b9e28f 100644 --- a/Display/ContextMenuContainerNode.swift +++ b/Display/ContextMenuContainerNode.swift @@ -45,6 +45,10 @@ final class ContextMenuContainerNode: ASDisplayNode { override func layout() { super.layout() + self.updateLayout(transition: .immediate) + } + + func updateLayout(transition: ContainedViewLayoutTransition) { //self.effectView.frame = self.bounds let maskParams = CachedMaskParams(size: self.bounds.size, relativeArrowPosition: self.relativeArrowPosition?.0 ?? self.bounds.size.width / 2.0, arrowOnBottom: self.relativeArrowPosition?.1 ?? true) @@ -78,7 +82,12 @@ final class ContextMenuContainerNode: ASDisplayNode { path.close() self.cachedMaskParams = maskParams - (self.maskView.layer as? CAShapeLayer)?.path = path.cgPath + if let layer = self.maskView.layer as? CAShapeLayer { + if case let .animated(duration, curve) = transition, let previousPath = layer.path { + layer.animate(from: previousPath, to: path.cgPath, keyPath: "path", timingFunction: curve.timingFunction, duration: duration) + } + layer.path = path.cgPath + } } } } diff --git a/Display/Font.swift b/Display/Font.swift index 89461a969a..ed0a02001d 100644 --- a/Display/Font.swift +++ b/Display/Font.swift @@ -38,6 +38,30 @@ public struct Font { } } + public static func semiboldItalic(_ size: CGFloat) -> UIFont { + if let descriptor = UIFont.systemFont(ofSize: size).fontDescriptor.withSymbolicTraits([.traitBold, .traitItalic]) { + return UIFont(descriptor: descriptor, size: size) + } else { + return UIFont.italicSystemFont(ofSize: size) + } + } + + public static func monospace(_ size: CGFloat) -> UIFont { + return UIFont(name: "Menlo-Regular", size: size - 1.0) ?? UIFont.systemFont(ofSize: size) + } + + public static func semiboldMonospace(_ size: CGFloat) -> UIFont { + return UIFont(name: "Menlo-Bold", size: size - 1.0) ?? UIFont.systemFont(ofSize: size) + } + + public static func italicMonospace(_ size: CGFloat) -> UIFont { + return UIFont(name: "Menlo-Italic", size: size - 1.0) ?? UIFont.systemFont(ofSize: size) + } + + public static func semiboldItalicMonospace(_ size: CGFloat) -> UIFont { + return UIFont(name: "Menlo-BoldItalic", size: size - 1.0) ?? UIFont.systemFont(ofSize: size) + } + public static func italic(_ size: CGFloat) -> UIFont { return UIFont.italicSystemFont(ofSize: size) } diff --git a/Display/ListView.swift b/Display/ListView.swift index 8896f99983..5d12a1c7bf 100644 --- a/Display/ListView.swift +++ b/Display/ListView.swift @@ -6,8 +6,6 @@ import AsyncDisplayKit import SwiftSignalKit #endif -private let usePerformanceTracker = false -private let useDynamicTuning = false private let useBackgroundDeallocation = false private let infiniteScrollSize: CGFloat = 10000.0 @@ -34,13 +32,7 @@ private final class ListViewBackingLayer: CALayer { } } -#if os(iOS) -typealias ListBaseView = UIView -#else -typealias ListBaseView = NSView -#endif - -final class ListViewBackingView: ListBaseView { +final class ListViewBackingView: UIView { weak var target: ListView? override class var layerClass: AnyClass { @@ -56,6 +48,7 @@ final class ListViewBackingView: ListBaseView { override func setNeedsDisplay() { } + #if os(iOS) override func touchesBegan(_ touches: Set, with event: UIEvent?) { self.target?.touchesBegan(touches, with: event) } @@ -80,6 +73,7 @@ final class ListViewBackingView: ListBaseView { } return super.hitTest(point, with: event) } + #endif } private final class ListViewTimerProxy: NSObject { @@ -212,8 +206,6 @@ open class ListView: ASDisplayNode, UIScrollViewDelegate, UIGestureRecognizerDel private let freeResistanceSlider = UISlider() private let scrollingResistanceSlider = UISlider() - //let performanceTracker: FBAnimationPerformanceTracker - private var selectionTouchLocation: CGPoint? private var selectionTouchDelayTimer: Foundation.Timer? private var selectionLongTapDelayTimer: Foundation.Timer? @@ -246,10 +238,6 @@ open class ListView: ASDisplayNode, UIScrollViewDelegate, UIGestureRecognizerDel self.scroller = ListViewScroller() - /*var performanceTrackerConfig = FBAnimationPerformanceTracker.standardConfig() - performanceTrackerConfig.reportStackTraces = true - self.performanceTracker = FBAnimationPerformanceTracker(config: performanceTrackerConfig)*/ - super.init() self.setViewBlock({ () -> UIView in @@ -266,8 +254,6 @@ open class ListView: ASDisplayNode, UIScrollViewDelegate, UIGestureRecognizerDel } } - //self.performanceTracker.delegate = self - self.scroller.alwaysBounceVertical = true self.scroller.contentSize = CGSize(width: 0.0, height: infiniteScrollSize * 2.0) self.scroller.isHidden = true @@ -282,38 +268,12 @@ open class ListView: ASDisplayNode, UIScrollViewDelegate, UIGestureRecognizerDel self.displayLink = CADisplayLink(target: DisplayLinkProxy(target: self), selector: #selector(DisplayLinkProxy.displayLinkEvent)) self.displayLink.add(to: RunLoop.main, forMode: RunLoopMode.commonModes) + #if os(iOS) if #available(iOS 10.0, *) { self.displayLink.preferredFramesPerSecond = 60 } + #endif self.displayLink.isPaused = true - - if useDynamicTuning { - self.frictionSlider.addTarget(self, action: #selector(self.frictionSliderChanged(_:)), for: .valueChanged) - self.springSlider.addTarget(self, action: #selector(self.springSliderChanged(_:)), for: .valueChanged) - self.freeResistanceSlider.addTarget(self, action: #selector(self.freeResistanceSliderChanged(_:)), for: .valueChanged) - self.scrollingResistanceSlider.addTarget(self, action: #selector(self.scrollingResistanceSliderChanged(_:)), for: .valueChanged) - - self.frictionSlider.minimumValue = Float(testSpringFrictionLimits.0) - self.frictionSlider.maximumValue = Float(testSpringFrictionLimits.1) - self.frictionSlider.value = Float(testSpringFriction) - - self.springSlider.minimumValue = Float(testSpringConstantLimits.0) - self.springSlider.maximumValue = Float(testSpringConstantLimits.1) - self.springSlider.value = Float(testSpringConstant) - - self.freeResistanceSlider.minimumValue = Float(testSpringResistanceFreeLimits.0) - self.freeResistanceSlider.maximumValue = Float(testSpringResistanceFreeLimits.1) - self.freeResistanceSlider.value = Float(testSpringFreeResistance) - - self.scrollingResistanceSlider.minimumValue = Float(testSpringResistanceScrollingLimits.0) - self.scrollingResistanceSlider.maximumValue = Float(testSpringResistanceScrollingLimits.1) - self.scrollingResistanceSlider.value = Float(testSpringScrollingResistance) - - self.view.addSubview(self.frictionSlider) - self.view.addSubview(self.springSlider) - self.view.addSubview(self.freeResistanceSlider) - self.view.addSubview(self.scrollingResistanceSlider) - } } deinit { @@ -344,26 +304,6 @@ open class ListView: ASDisplayNode, UIScrollViewDelegate, UIGestureRecognizerDel self.waitingForNodesDisposable.dispose() } - @objc func frictionSliderChanged(_ slider: UISlider) { - testSpringFriction = CGFloat(slider.value) - print("friction: \(testSpringFriction)") - } - - @objc func springSliderChanged(_ slider: UISlider) { - testSpringConstant = CGFloat(slider.value) - print("spring: \(testSpringConstant)") - } - - @objc func freeResistanceSliderChanged(_ slider: UISlider) { - testSpringFreeResistance = CGFloat(slider.value) - print("free resistance: \(testSpringFreeResistance)") - } - - @objc func scrollingResistanceSliderChanged(_ slider: UISlider) { - testSpringScrollingResistance = CGFloat(slider.value) - print("free resistance: \(testSpringScrollingResistance)") - } - private func displayLinkEvent() { self.updateAnimations() } @@ -451,9 +391,6 @@ open class ListView: ASDisplayNode, UIScrollViewDelegate, UIGestureRecognizerDel self.updateHeaderItemsFlashing(animated: true) self.lastContentOffsetTimestamp = 0.0 - /*if usePerformanceTracker { - self.performanceTracker.stop() - }*/ } } @@ -462,10 +399,6 @@ open class ListView: ASDisplayNode, UIScrollViewDelegate, UIGestureRecognizerDel self.isDeceleratingAfterTracking = false self.resetHeaderItemsFlashTimer(start: true) self.updateHeaderItemsFlashing(animated: true) - - /*if usePerformanceTracker { - self.performanceTracker.stop() - }*/ } public func scrollViewDidScroll(_ scrollView: UIScrollView) { @@ -1083,14 +1016,6 @@ open class ListView: ASDisplayNode, UIScrollViewDelegate, UIGestureRecognizerDel self.visibleSize = updateSizeAndInsets.size self.insets = updateSizeAndInsets.insets - if useDynamicTuning { - let size = updateSizeAndInsets.size - self.frictionSlider.frame = CGRect(x: 10.0, y: size.height - insets.bottom - 10.0 - self.frictionSlider.bounds.height, width: size.width - 20.0, height: self.frictionSlider.bounds.height) - self.springSlider.frame = CGRect(x: 10.0, y: self.frictionSlider.frame.minY - self.springSlider.bounds.height, width: size.width - 20.0, height: self.springSlider.bounds.height) - self.freeResistanceSlider.frame = CGRect(x: 10.0, y: self.springSlider.frame.minY - self.freeResistanceSlider.bounds.height, width: size.width - 20.0, height: self.freeResistanceSlider.bounds.height) - self.scrollingResistanceSlider.frame = CGRect(x: 10.0, y: self.freeResistanceSlider.frame.minY - self.scrollingResistanceSlider.bounds.height, width: size.width - 20.0, height: self.scrollingResistanceSlider.bounds.height) - } - let wasIgnoringScrollingEvents = self.ignoreScrollingEvents self.ignoreScrollingEvents = true self.scroller.frame = CGRect(origin: CGPoint(), size: updateSizeAndInsets.size) @@ -1596,12 +1521,6 @@ open class ListView: ASDisplayNode, UIScrollViewDelegate, UIGestureRecognizerDel apply().1() self.itemNodes.insert(node, at: nodeIndex) - if useDynamicTuning { - self.insertSubnode(node, at: 0) - } else { - //self.addSubnode(node) - } - var offsetHeight = node.apparentHeight var takenAnimation = false @@ -2954,6 +2873,7 @@ open class ListView: ASDisplayNode, UIScrollViewDelegate, UIGestureRecognizerDel } } + #if os(iOS) override open func touchesBegan(_ touches: Set, with event: UIEvent?) { let touchesPosition = touches.first!.location(in: self.view) @@ -3043,6 +2963,7 @@ open class ListView: ASDisplayNode, UIScrollViewDelegate, UIGestureRecognizerDel self.updateScroller(transition: .immediate) } + #endif public func clearHighlightAnimated(_ animated: Bool) { if let highlightedItemIndex = self.highlightedItemIndex { @@ -3096,6 +3017,7 @@ open class ListView: ASDisplayNode, UIScrollViewDelegate, UIGestureRecognizerDel return nil } + #if os(iOS) override open func touchesMoved(_ touches: Set, with event: UIEvent?) { if let selectionTouchLocation = self.selectionTouchLocation { let location = touches.first!.location(in: self.view) @@ -3177,6 +3099,7 @@ open class ListView: ASDisplayNode, UIScrollViewDelegate, UIGestureRecognizerDel break } } + #endif public func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool { return true @@ -3189,6 +3112,7 @@ open class ListView: ASDisplayNode, UIScrollViewDelegate, UIGestureRecognizerDel } } + #if os(iOS) fileprivate func internalHitTest(_ point: CGPoint, with event: UIEvent?) -> Bool { if self.limitHitTestToNodes { var foundHit = false @@ -3204,4 +3128,5 @@ open class ListView: ASDisplayNode, UIScrollViewDelegate, UIGestureRecognizerDel } return true } + #endif } diff --git a/Display/ListViewScroller.swift b/Display/ListViewScroller.swift index c853d1fd0c..55ac809eea 100644 --- a/Display/ListViewScroller.swift +++ b/Display/ListViewScroller.swift @@ -7,10 +7,12 @@ class ListViewScroller: UIScrollView, UIGestureRecognizerDelegate { override init(frame: CGRect) { super.init(frame: frame) + #if os(iOS) self.scrollsToTop = false if #available(iOSApplicationExtension 11.0, *) { self.contentInsetAdjustmentBehavior = .never } + #endif } required init?(coder aDecoder: NSCoder) { @@ -21,7 +23,9 @@ class ListViewScroller: UIScrollView, UIGestureRecognizerDelegate { return false } + #if os(iOS) override func touchesShouldCancel(in view: UIView) -> Bool { return true } + #endif } diff --git a/Display/ListViewTransactionQueue.swift b/Display/ListViewTransactionQueue.swift index 0513e7f1e1..689c8dbc81 100644 --- a/Display/ListViewTransactionQueue.swift +++ b/Display/ListViewTransactionQueue.swift @@ -1,5 +1,9 @@ import Foundation +#if os(iOS) import SwiftSignalKit +#else +import SwiftSignalKitMac +#endif public typealias ListViewTransaction = (@escaping () -> Void) -> Void diff --git a/Display/NavigationBar.swift b/Display/NavigationBar.swift index cdb421e869..1fd46152f3 100644 --- a/Display/NavigationBar.swift +++ b/Display/NavigationBar.swift @@ -75,7 +75,7 @@ open class NavigationBar: ASDisplayNode { private let stripeNode: ASDisplayNode private let clippingNode: ASDisplayNode - var contentNode: NavigationBarContentNode? + public private(set) var contentNode: NavigationBarContentNode? private var itemTitleListenerKey: Int? private var itemTitleViewListenerKey: Int? @@ -84,7 +84,7 @@ open class NavigationBar: ASDisplayNode { private var itemLeftButtonSetEnabledListenerKey: Int? private var itemRightButtonListenerKey: Int? - private var itemRightButtonSetEnabledListenerKey: Int? + private var itemRightButtonsListenerKey: Int? private var itemBadgeListenerKey: Int? @@ -116,9 +116,9 @@ open class NavigationBar: ASDisplayNode { self.itemRightButtonListenerKey = nil } - if let itemRightButtonSetEnabledListenerKey = self.itemRightButtonSetEnabledListenerKey { - previousValue.rightBarButtonItem?.removeSetEnabledListener(itemRightButtonSetEnabledListenerKey) - self.itemRightButtonSetEnabledListenerKey = nil + if let itemRightButtonsListenerKey = self.itemRightButtonsListenerKey { + previousValue.removeSetMultipleRightBarButtonItemsListener(itemRightButtonsListenerKey) + self.itemRightButtonsListenerKey = nil } if let itemBadgeListenerKey = self.itemBadgeListenerKey { @@ -165,19 +165,14 @@ open class NavigationBar: ASDisplayNode { self.itemRightButtonListenerKey = item.addSetRightBarButtonItemListener { [weak self] previousItem, currentItem, animated in if let strongSelf = self { - if let itemRightButtonSetEnabledListenerKey = strongSelf.itemRightButtonSetEnabledListenerKey { - previousItem?.removeSetEnabledListener(itemRightButtonSetEnabledListenerKey) - strongSelf.itemRightButtonSetEnabledListenerKey = nil - } - - if let currentItem = currentItem { - strongSelf.itemRightButtonSetEnabledListenerKey = currentItem.addSetEnabledListener { _ in - if let strongSelf = self { - strongSelf.updateRightButton(animated: false) - } - } - } - + strongSelf.updateRightButton(animated: animated) + strongSelf.invalidateCalculatedLayout() + strongSelf.requestLayout() + } + } + + self.itemRightButtonsListenerKey = item.addSetMultipleRightBarButtonItemsListener { [weak self] items, animated in + if let strongSelf = self { strongSelf.updateRightButton(animated: animated) strongSelf.invalidateCalculatedLayout() strongSelf.requestLayout() @@ -260,9 +255,9 @@ open class NavigationBar: ASDisplayNode { self.previousItemListenerKey = previousItem.addSetTitleListener { [weak self] _, _ in if let strongSelf = self, let previousItem = strongSelf.previousItem { if let backBarButtonItem = previousItem.backBarButtonItem { - strongSelf.backButtonNode.text = backBarButtonItem.title ?? "" + strongSelf.backButtonNode.updateManualText(backBarButtonItem.title ?? "") } else { - strongSelf.backButtonNode.text = previousItem.title ?? "" + strongSelf.backButtonNode.updateManualText(previousItem.title ?? "") } strongSelf.invalidateCalculatedLayout() strongSelf.requestLayout() @@ -272,9 +267,9 @@ open class NavigationBar: ASDisplayNode { self.previousItemBackListenerKey = previousItem.addSetBackBarButtonItemListener { [weak self] _, _, _ in if let strongSelf = self, let previousItem = strongSelf.previousItem { if let backBarButtonItem = previousItem.backBarButtonItem { - strongSelf.backButtonNode.text = backBarButtonItem.title ?? "" + strongSelf.backButtonNode.updateManualText(backBarButtonItem.title ?? "") } else { - strongSelf.backButtonNode.text = previousItem.title ?? "" + strongSelf.backButtonNode.updateManualText(previousItem.title ?? "") } strongSelf.invalidateCalculatedLayout() strongSelf.requestLayout() @@ -348,9 +343,8 @@ open class NavigationBar: ASDisplayNode { self.backButtonArrow.removeFromSupernode() self.badgeNode.removeFromSupernode() - self.leftButtonNode.text = leftBarButtonItem.title ?? "" - self.leftButtonNode.bold = leftBarButtonItem.style == .done - self.leftButtonNode.isEnabled = leftBarButtonItem.isEnabled + self.leftButtonNode.updateItems([leftBarButtonItem]) + if self.leftButtonNode.supernode == nil { self.clippingNode.addSubnode(self.leftButtonNode) } @@ -384,7 +378,7 @@ open class NavigationBar: ASDisplayNode { } if let backTitle = backTitle { - self.backButtonNode.text = backTitle + self.backButtonNode.updateManualText(backTitle) if self.backButtonNode.supernode == nil { self.clippingNode.addSubnode(self.backButtonNode) self.clippingNode.addSubnode(self.backButtonArrow) @@ -414,7 +408,14 @@ open class NavigationBar: ASDisplayNode { private func updateRightButton(animated: Bool) { if let item = self.item { - if let rightBarButtonItem = item.rightBarButtonItem { + var items: [UIBarButtonItem] = [] + if let rightBarButtonItems = item.rightBarButtonItems, !rightBarButtonItems.isEmpty { + items = rightBarButtonItems + } else if let rightBarButtonItem = item.rightBarButtonItem { + items = [rightBarButtonItem] + } + + if !items.isEmpty { if animated, self.rightButtonNode.view.superview != nil { if let snapshotView = self.rightButtonNode.view.snapshotContentTree() { snapshotView.frame = self.rightButtonNode.frame @@ -424,11 +425,7 @@ open class NavigationBar: ASDisplayNode { }) } } - self.rightButtonNode.text = rightBarButtonItem.title ?? "" - self.rightButtonNode.image = rightBarButtonItem.image - self.rightButtonNode.bold = rightBarButtonItem.style == .done - self.rightButtonNode.isEnabled = rightBarButtonItem.isEnabled - self.rightButtonNode.node = rightBarButtonItem.customDisplayNode + self.rightButtonNode.updateItems(items) if self.rightButtonNode.supernode == nil { self.clippingNode.addSubnode(self.rightButtonNode) } @@ -565,13 +562,13 @@ open class NavigationBar: ASDisplayNode { self.titleNode.truncationMode = .byTruncatingTail self.titleNode.isOpaque = false - self.backButtonNode.highlightChanged = { [weak self] highlighted in - if let strongSelf = self { + self.backButtonNode.highlightChanged = { [weak self] index, highlighted in + if let strongSelf = self, index == 0 { strongSelf.backButtonArrow.alpha = (highlighted ? 0.4 : 1.0) } } - self.backButtonNode.pressed = { [weak self] in - if let strongSelf = self { + self.backButtonNode.pressed = { [weak self] index in + if let strongSelf = self, index == 0 { if let leftBarButtonItem = strongSelf.item?.leftBarButtonItem, leftBarButtonItem.backButtonAppearance { leftBarButtonItem.performActionOnTarget() } else { @@ -580,15 +577,25 @@ open class NavigationBar: ASDisplayNode { } } - self.leftButtonNode.pressed = { [weak self] in - if let item = self?.item, let leftBarButtonItem = item.leftBarButtonItem { - leftBarButtonItem.performActionOnTarget() + self.leftButtonNode.pressed = { [weak self] index in + if let item = self?.item { + if index == 0 { + if let leftBarButtonItem = item.leftBarButtonItem { + leftBarButtonItem.performActionOnTarget() + } + } } } - self.rightButtonNode.pressed = { [weak self] in - if let item = self?.item, let rightBarButtonItem = item.rightBarButtonItem { - rightBarButtonItem.performActionOnTarget() + self.rightButtonNode.pressed = { [weak self] index in + if let item = self?.item { + if let rightBarButtonItems = item.rightBarButtonItems, !rightBarButtonItems.isEmpty { + if index < rightBarButtonItems.count { + rightBarButtonItems[index].performActionOnTarget() + } + } else if let rightBarButtonItem = item.rightBarButtonItem { + rightBarButtonItem.performActionOnTarget() + } } } } @@ -645,7 +652,7 @@ open class NavigationBar: ASDisplayNode { var leftTitleInset: CGFloat = leftInset + 4.0 var rightTitleInset: CGFloat = rightInset + 4.0 if self.backButtonNode.supernode != nil { - let backButtonSize = self.backButtonNode.measure(CGSize(width: size.width, height: nominalHeight)) + let backButtonSize = self.backButtonNode.updateLayout(constrainedSize: CGSize(width: size.width, height: nominalHeight)) leftTitleInset += backButtonSize.width + backButtonInset + 4.0 + 4.0 let topHitTestSlop = (nominalHeight - backButtonSize.height) * 0.5 @@ -690,7 +697,7 @@ open class NavigationBar: ASDisplayNode { self.badgeNode.alpha = 1.0 } } else if self.leftButtonNode.supernode != nil { - let leftButtonSize = self.leftButtonNode.measure(CGSize(width: size.width, height: nominalHeight)) + let leftButtonSize = self.leftButtonNode.updateLayout(constrainedSize: CGSize(width: size.width, height: nominalHeight)) leftTitleInset += leftButtonSize.width + leftButtonInset + 8.0 + 8.0 self.leftButtonNode.alpha = 1.0 @@ -702,7 +709,7 @@ open class NavigationBar: ASDisplayNode { self.badgeNode.frame = CGRect(origin: backButtonArrowFrame.origin.offsetBy(dx: 7.0, dy: -9.0), size: badgeSize) if self.rightButtonNode.supernode != nil { - let rightButtonSize = self.rightButtonNode.measure(CGSize(width: size.width, height: nominalHeight)) + let rightButtonSize = self.rightButtonNode.updateLayout(constrainedSize: (CGSize(width: size.width, height: nominalHeight))) rightTitleInset += rightButtonSize.width + leftButtonInset + 8.0 + 8.0 self.rightButtonNode.alpha = 1.0 self.rightButtonNode.frame = CGRect(origin: CGPoint(x: size.width - leftButtonInset - rightButtonSize.width, y: contentVerticalOrigin + floor((nominalHeight - rightButtonSize.height) / 2.0)), size: rightButtonSize) @@ -716,7 +723,7 @@ open class NavigationBar: ASDisplayNode { break case .bottom: if let transitionBackButtonNode = self.transitionBackButtonNode { - let transitionBackButtonSize = transitionBackButtonNode.measure(CGSize(width: size.width, height: nominalHeight)) + let transitionBackButtonSize = transitionBackButtonNode.updateLayout(constrainedSize: CGSize(width: size.width, height: nominalHeight)) let initialX: CGFloat = backButtonInset + size.width * 0.3 let finalX: CGFloat = floor((size.width - transitionBackButtonSize.width) / 2.0) @@ -831,7 +838,7 @@ open class NavigationBar: ASDisplayNode { private func makeTransitionBackButtonNode(accentColor: UIColor) -> NavigationButtonNode? { if self.backButtonNode.supernode != nil { let node = NavigationButtonNode() - node.text = self.backButtonNode.text + node.updateManualText(self.backButtonNode.manualText) node.color = accentColor return node } else { diff --git a/Display/NavigationButtonNode.swift b/Display/NavigationButtonNode.swift index 87afb05a70..7bda908127 100644 --- a/Display/NavigationButtonNode.swift +++ b/Display/NavigationButtonNode.swift @@ -1,7 +1,7 @@ import UIKit import AsyncDisplayKit -public class NavigationButtonNode: ASTextNode { +private final class NavigationButtonItemNode: ASTextNode { private func fontForCurrentState() -> UIFont { return self.bold ? UIFont.boldSystemFont(ofSize: 17.0) : UIFont.systemFont(ofSize: 17.0) } @@ -13,6 +13,25 @@ public class NavigationButtonNode: ASTextNode { ] } + private var setEnabledListener: Int? + + var item: UIBarButtonItem? { + didSet { + if self.item !== oldValue { + if let oldValue = oldValue, let setEnabledListener = self.setEnabledListener { + oldValue.removeSetEnabledListener(setEnabledListener) + self.setEnabledListener = nil + } + + if let item = self.item { + self.setEnabledListener = item.addSetEnabledListener { [weak self] value in + self?.isEnabled = value + } + } + } + } + } + private var _text: String? public var text: String { get { @@ -174,13 +193,13 @@ public class NavigationButtonNode: ASTextNode { let alpha: CGFloat = !self.isEnabled ? 1.0 : (highlighted ? 0.4 : 1.0) /*if animated { - UIView.animate(withDuration: 0.3, delay: 0.0, options: UIViewAnimationOptions.beginFromCurrentState, animations: { () -> Void in - self.alpha = alpha - }, completion: nil) - } - else {*/ - self.alpha = alpha - self.highlightChanged(highlighted) + UIView.animate(withDuration: 0.3, delay: 0.0, options: UIViewAnimationOptions.beginFromCurrentState, animations: { () -> Void in + self.alpha = alpha + }, completion: nil) + } + else {*/ + self.alpha = alpha + self.highlightChanged(highlighted) //} } } @@ -192,9 +211,133 @@ public class NavigationButtonNode: ASTextNode { set(value) { if self.isEnabled != value { super.isEnabled = value - + self.attributedText = NSAttributedString(string: text, attributes: self.attributesForCurrentState()) } } } } + + +final class NavigationButtonNode: ASDisplayNode { + private var nodes: [NavigationButtonItemNode] = [] + + public var pressed: (Int) -> () = { _ in } + public var highlightChanged: (Int, Bool) -> () = { _, _ in } + + public var color: UIColor = UIColor(rgb: 0x007ee5) { + didSet { + if !self.color.isEqual(oldValue) { + for node in self.nodes { + node.color = self.color + } + } + } + } + + override init() { + super.init() + } + + var manualText: String { + return self.nodes.first?.text ?? "" + } + + func updateManualText(_ text: String) { + let node: NavigationButtonItemNode + if self.nodes.count > 0 { + node = self.nodes[0] + } else { + node = NavigationButtonItemNode() + node.color = self.color + node.highlightChanged = { [weak node, weak self] value in + if let strongSelf = self, let node = node { + if let index = strongSelf.nodes.index(where: { $0 === node }) { + strongSelf.highlightChanged(index, value) + } + } + } + node.pressed = { [weak self, weak node] in + if let strongSelf = self, let node = node { + if let index = strongSelf.nodes.index(where: { $0 === node }) { + strongSelf.pressed(index) + } + } + } + self.nodes.append(node) + self.addSubnode(node) + } + node.item = nil + node.text = text + node.image = nil + node.bold = false + node.isEnabled = true + node.node = nil + + if 1 < self.nodes.count { + for i in 1 ..< self.nodes.count { + self.nodes[i].removeFromSupernode() + } + self.nodes.removeSubrange(1...) + } + } + + func updateItems(_ items: [UIBarButtonItem]) { + for i in 0 ..< items.count { + let node: NavigationButtonItemNode + if self.nodes.count > i { + node = self.nodes[i] + } else { + node = NavigationButtonItemNode() + node.color = self.color + node.highlightChanged = { [weak node, weak self] value in + if let strongSelf = self, let node = node { + if let index = strongSelf.nodes.index(where: { $0 === node }) { + strongSelf.highlightChanged(index, value) + } + } + } + node.pressed = { [weak self, weak node] in + if let strongSelf = self, let node = node { + if let index = strongSelf.nodes.index(where: { $0 === node }) { + strongSelf.pressed(index) + } + } + } + self.nodes.append(node) + self.addSubnode(node) + } + node.item = items[i] + node.text = items[i].title ?? "" + node.image = items[i].image + node.bold = items[i].style == .done + node.isEnabled = items[i].isEnabled + node.node = items[i].customDisplayNode + } + if items.count < self.nodes.count { + for i in items.count ..< self.nodes.count { + self.nodes[i].removeFromSupernode() + } + self.nodes.removeSubrange(items.count...) + } + } + + func updateLayout(constrainedSize: CGSize) -> CGSize { + var nodeOrigin = CGPoint() + var totalSize = CGSize() + for node in self.nodes { + if !totalSize.width.isZero { + totalSize.width += 16.0 + nodeOrigin.x += 16.0 + } + var nodeSize = node.calculateSizeThatFits(constrainedSize) + nodeSize.width = ceil(nodeSize.width) + nodeSize.height = ceil(nodeSize.height) + totalSize.width += nodeSize.width + totalSize.height = max(totalSize.height, nodeSize.height) + node.frame = CGRect(origin: nodeOrigin, size: nodeSize) + nodeOrigin.x += node.bounds.width + } + return totalSize + } +} diff --git a/Display/NavigationTransitionCoordinator.swift b/Display/NavigationTransitionCoordinator.swift index 1aae757ce6..8749945c1e 100644 --- a/Display/NavigationTransitionCoordinator.swift +++ b/Display/NavigationTransitionCoordinator.swift @@ -56,7 +56,7 @@ class NavigationTransitionCoordinator { self.dimView.backgroundColor = UIColor.black self.shadowView = UIImageView(image: shadowImage) - if let topNavigationBar = topNavigationBar, let bottomNavigationBar = bottomNavigationBar, !topNavigationBar.isHidden, !bottomNavigationBar.isHidden { + if let topNavigationBar = topNavigationBar, let bottomNavigationBar = bottomNavigationBar, !topNavigationBar.isHidden, !bottomNavigationBar.isHidden, topNavigationBar.contentNode == nil, bottomNavigationBar.contentNode == nil { var topFrame = topNavigationBar.view.convert(topNavigationBar.bounds, to: container) var bottomFrame = bottomNavigationBar.view.convert(bottomNavigationBar.bounds, to: container) topFrame.origin.x = 0.0 diff --git a/Display/TextAlertController.swift b/Display/TextAlertController.swift index fce5275127..60ebfe9d12 100644 --- a/Display/TextAlertController.swift +++ b/Display/TextAlertController.swift @@ -24,10 +24,10 @@ private final class TextAlertContentActionNode: HighlightableButtonNode { let action: TextAlertAction - init(action: TextAlertAction) { + init(theme: AlertControllerTheme, action: TextAlertAction) { self.backgroundNode = ASDisplayNode() self.backgroundNode.isLayerBacked = true - self.backgroundNode.backgroundColor = UIColor(rgb: 0xe0e5e6) + self.backgroundNode.backgroundColor = theme.highlightedItemColor self.backgroundNode.alpha = 0.0 self.action = action @@ -36,12 +36,12 @@ private final class TextAlertContentActionNode: HighlightableButtonNode { self.titleNode.maximumNumberOfLines = 2 let font = Font.regular(17.0) - var color = UIColor(rgb: 0x007ee5) + var color = theme.accentColor switch action.type { case .defaultAction, .genericAction: break case .destructiveAction: - color = UIColor(rgb: 0xff3b30) + color = theme.destructiveColor } self.setAttributedTitle(NSAttributedString(string: action.title, font: font, textColor: color, paragraphAlignment: .center), for: []) @@ -86,7 +86,7 @@ final class TextAlertContentNode: AlertContentNode { private let actionNodes: [TextAlertContentActionNode] private let actionVerticalSeparators: [ASDisplayNode] - init(title: NSAttributedString?, text: NSAttributedString, actions: [TextAlertAction]) { + init(theme: AlertControllerTheme, title: NSAttributedString?, text: NSAttributedString, actions: [TextAlertAction]) { if let title = title { let titleNode = ASTextNode() titleNode.attributedText = title @@ -106,10 +106,10 @@ final class TextAlertContentNode: AlertContentNode { self.actionNodesSeparator = ASDisplayNode() self.actionNodesSeparator.isLayerBacked = true - self.actionNodesSeparator.backgroundColor = UIColor(rgb: 0xc9cdd7) + self.actionNodesSeparator.backgroundColor = theme.separatorColor self.actionNodes = actions.map { action -> TextAlertContentActionNode in - return TextAlertContentActionNode(action: action) + return TextAlertContentActionNode(theme: theme, action: action) } var actionVerticalSeparators: [ASDisplayNode] = [] @@ -117,7 +117,7 @@ final class TextAlertContentNode: AlertContentNode { for _ in 0 ..< actions.count - 1 { let separatorNode = ASDisplayNode() separatorNode.isLayerBacked = true - separatorNode.backgroundColor = UIColor(rgb: 0xc9cdd7) + separatorNode.backgroundColor = theme.separatorColor actionVerticalSeparators.append(separatorNode) } } @@ -216,13 +216,13 @@ final class TextAlertContentNode: AlertContentNode { } } -public func textAlertController(title: NSAttributedString?, text: NSAttributedString, actions: [TextAlertAction]) -> AlertController { - return AlertController(contentNode: TextAlertContentNode(title: title, text: text, actions: actions)) +public func textAlertController(theme: AlertControllerTheme, title: NSAttributedString?, text: NSAttributedString, actions: [TextAlertAction]) -> AlertController { + return AlertController(theme: theme, contentNode: TextAlertContentNode(theme: theme, title: title, text: text, actions: actions)) } -public func standardTextAlertController(title: String?, text: String, actions: [TextAlertAction]) -> AlertController { +public func standardTextAlertController(theme: AlertControllerTheme, title: String?, text: String, actions: [TextAlertAction]) -> AlertController { var dismissImpl: (() -> Void)? - let controller = AlertController(contentNode: TextAlertContentNode(title: title != nil ? NSAttributedString(string: title!, font: Font.medium(17.0), textColor: .black, paragraphAlignment: .center) : nil, text: NSAttributedString(string: text, font: title == nil ? Font.semibold(17.0) : Font.regular(13.0), textColor: .black, paragraphAlignment: .center), actions: actions.map { action in + let controller = AlertController(theme: theme, contentNode: TextAlertContentNode(theme: theme, title: title != nil ? NSAttributedString(string: title!, font: Font.medium(17.0), textColor: theme.primaryColor, paragraphAlignment: .center) : nil, text: NSAttributedString(string: text, font: title == nil ? Font.semibold(17.0) : Font.regular(13.0), textColor: theme.primaryColor, paragraphAlignment: .center), actions: actions.map { action in return TextAlertAction(type: action.type, title: action.title, action: { dismissImpl?() action.action() diff --git a/Display/TooltipController.swift b/Display/TooltipController.swift new file mode 100644 index 0000000000..ca828047a3 --- /dev/null +++ b/Display/TooltipController.swift @@ -0,0 +1,114 @@ +import Foundation +import AsyncDisplayKit +import SwiftSignalKit + +public final class TooltipControllerPresentationArguments { + fileprivate let sourceNodeAndRect: () -> (ASDisplayNode, CGRect)? + + public init(sourceNodeAndRect: @escaping () -> (ASDisplayNode, CGRect)?) { + self.sourceNodeAndRect = sourceNodeAndRect + } +} + +public final class TooltipController: ViewController { + private var controllerNode: TooltipControllerNode { + return self.displayNode as! TooltipControllerNode + } + + public var text: String { + didSet { + if self.text != oldValue { + if self.isNodeLoaded { + self.controllerNode.updateText(self.text, transition: .animated(duration: 0.25, curve: .easeInOut)) + if self.timeoutTimer != nil { + self.timeoutTimer?.invalidate() + self.timeoutTimer = nil + self.beginTimeout() + } + } + } + } + } + + private let timeout: Double + private var timeoutTimer: SwiftSignalKit.Timer? + + private var layout: ContainerViewLayout? + + public var dismissed: (() -> Void)? + + public init(text: String, timeout: Double = 1.0) { + self.text = text + self.timeout = timeout + + super.init(navigationBarTheme: nil) + } + + required public init(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + deinit { + self.timeoutTimer?.invalidate() + } + + open override func loadDisplayNode() { + self.displayNode = TooltipControllerNode(text: self.text, dismiss: { [weak self] in + self?.dismissed?() + self?.controllerNode.animateOut { [weak self] in + self?.presentingViewController?.dismiss(animated: false) + } + }) + self.displayNodeDidLoad() + } + + override public func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + + self.controllerNode.animateIn() + self.beginTimeout() + } + + override open func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) { + super.containerLayoutUpdated(layout, transition: transition) + + if self.layout != nil && self.layout! != layout { + self.dismissed?() + self.controllerNode.animateOut { [weak self] in + self?.presentingViewController?.dismiss(animated: false) + } + } else { + self.layout = layout + + if let presentationArguments = self.presentationArguments as? TooltipControllerPresentationArguments, let (sourceNode, sourceRect) = presentationArguments.sourceNodeAndRect() { + self.controllerNode.sourceRect = sourceNode.view.convert(sourceRect, to: nil) + } else { + self.controllerNode.sourceRect = nil + } + + self.controllerNode.containerLayoutUpdated(layout, transition: transition) + } + } + + open override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + + self.controllerNode.animateIn() + self.beginTimeout() + } + + private func beginTimeout() { + if self.timeoutTimer == nil { + let timeoutTimer = SwiftSignalKit.Timer(timeout: self.timeout, repeat: false, completion: { [weak self] in + if let strongSelf = self { + strongSelf.dismissed?() + strongSelf.controllerNode.animateOut { + self?.presentingViewController?.dismiss(animated: false) + } + } + }, queue: Queue.mainQueue()) + self.timeoutTimer = timeoutTimer + timeoutTimer.start() + } + } +} diff --git a/Display/TooltipControllerNode.swift b/Display/TooltipControllerNode.swift new file mode 100644 index 0000000000..a41d943946 --- /dev/null +++ b/Display/TooltipControllerNode.swift @@ -0,0 +1,118 @@ +import Foundation +import UIKit +import AsyncDisplayKit + +final class TooltipControllerNode: ASDisplayNode { + private let dismiss: () -> Void + + private var validLayout: ContainerViewLayout? + + private let containerNode: ContextMenuContainerNode + private let textNode: ASTextNode + + var sourceRect: CGRect? + var arrowOnBottom: Bool = true + + private var dismissedByTouchOutside = false + + init(text: String, dismiss: @escaping () -> Void) { + self.containerNode = ContextMenuContainerNode() + self.containerNode.backgroundColor = UIColor(white: 0.0, alpha: 0.8) + + self.textNode = ASTextNode() + self.textNode.attributedText = NSAttributedString(string: text, font: Font.regular(14.0), textColor: .white, paragraphAlignment: .center) + self.textNode.isLayerBacked = true + self.textNode.displaysAsynchronously = false + + self.dismiss = dismiss + + super.init() + + self.containerNode.addSubnode(self.textNode) + + self.addSubnode(self.containerNode) + } + + func updateText(_ text: String, transition: ContainedViewLayoutTransition) { + if transition.isAnimated, let copyLayer = self.textNode.layer.snapshotContentTree() { + copyLayer.frame = self.textNode.layer.frame + self.textNode.layer.superlayer?.addSublayer(copyLayer) + transition.updateAlpha(layer: copyLayer, alpha: 0.0, completion: { [weak copyLayer] _ in + copyLayer?.removeFromSuperlayer() + }) + self.textNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.12) + } + self.textNode.attributedText = NSAttributedString(string: text, font: Font.regular(14.0), textColor: .white, paragraphAlignment: .center) + if let layout = self.validLayout { + self.containerLayoutUpdated(layout, transition: transition) + } + } + + func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) { + self.validLayout = layout + + let maxActionsWidth = layout.size.width - 20.0 + + var textSize = self.textNode.measure(CGSize(width: maxActionsWidth, height: CGFloat.greatestFiniteMagnitude)) + textSize.width = ceil(textSize.width / 2.0) * 2.0 + textSize.height = ceil(textSize.height / 2.0) * 2.0 + let contentSize = CGSize(width: textSize.width + 12.0, height: textSize.height + 34.0) + + let sourceRect: CGRect = self.sourceRect ?? CGRect(origin: CGPoint(x: layout.size.width / 2.0, y: layout.size.height / 2.0), size: CGSize()) + + let insets = layout.insets(options: [.statusBar, .input]) + + let verticalOrigin: CGFloat + var arrowOnBottom = true + if sourceRect.minY - 54.0 > insets.top { + verticalOrigin = sourceRect.minY - contentSize.height + } else { + verticalOrigin = min(layout.size.height - insets.bottom - contentSize.height, sourceRect.maxY) + arrowOnBottom = false + } + self.arrowOnBottom = arrowOnBottom + + let horizontalOrigin: CGFloat = floor(min(max(8.0, sourceRect.midX - contentSize.width / 2.0), layout.size.width - contentSize.width - 8.0)) + + transition.updateFrame(node: self.containerNode, frame: CGRect(origin: CGPoint(x: horizontalOrigin, y: verticalOrigin), size: contentSize)) + self.containerNode.relativeArrowPosition = (sourceRect.midX - horizontalOrigin, arrowOnBottom) + + self.containerNode.updateLayout(transition: transition) + + let textFrame = CGRect(origin: CGPoint(x: 6.0, y: 17.0), size: textSize) + if transition.isAnimated, textFrame.size != self.textNode.frame.size { + transition.animatePositionAdditive(node: self.textNode, offset: CGPoint(x: textFrame.minX - self.textNode.frame.minX, y: 0.0)) + } + self.textNode.frame = textFrame + } + + func animateIn() { + self.containerNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.1) + } + + func animateOut(completion: @escaping () -> Void) { + self.containerNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false, completion: { _ in + completion() + }) + } + + override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { + if let event = event { + var eventIsPresses = false + if #available(iOSApplicationExtension 9.0, *) { + eventIsPresses = event.type == .presses + } + if event.type == .touches || eventIsPresses { + if self.containerNode.frame.contains(point) { + if !self.dismissedByTouchOutside { + self.dismissedByTouchOutside = true + self.dismiss() + } + } + return nil + } + } + return super.hitTest(point, with: event) + } +} + diff --git a/Display/UINavigationItem+Proxy.h b/Display/UINavigationItem+Proxy.h index 08a1ef6da6..5d607cfb47 100644 --- a/Display/UINavigationItem+Proxy.h +++ b/Display/UINavigationItem+Proxy.h @@ -4,6 +4,7 @@ typedef void (^UINavigationItemSetTitleListener)(NSString * _Nullable, bool); typedef void (^UINavigationItemSetTitleViewListener)(UIView * _Nullable); typedef void (^UINavigationItemSetImageListener)(UIImage * _Nullable); typedef void (^UINavigationItemSetBarButtonItemListener)(UIBarButtonItem * _Nullable, UIBarButtonItem * _Nullable, BOOL); +typedef void (^UINavigationItemSetMutipleBarButtonItemsListener)(NSArray * _Nullable, BOOL); typedef void (^UITabBarItemSetBadgeListener)(NSString * _Nullable); @interface UINavigationItem (Proxy) @@ -20,6 +21,8 @@ typedef void (^UITabBarItemSetBadgeListener)(NSString * _Nullable); - (void)removeSetLeftBarButtonItemListener:(NSInteger)key; - (NSInteger)addSetRightBarButtonItemListener:(UINavigationItemSetBarButtonItemListener _Nonnull)listener; - (void)removeSetRightBarButtonItemListener:(NSInteger)key; +- (NSInteger)addSetMultipleRightBarButtonItemsListener:(UINavigationItemSetMutipleBarButtonItemsListener _Nonnull)listener; +- (void)removeSetMultipleRightBarButtonItemsListener:(NSInteger)key; - (NSInteger)addSetBackBarButtonItemListener:(UINavigationItemSetBarButtonItemListener _Nonnull)listener; - (void)removeSetBackBarButtonItemListener:(NSInteger)key; - (NSInteger)addSetBadgeListener:(UITabBarItemSetBadgeListener _Nonnull)listener; diff --git a/Display/UINavigationItem+Proxy.m b/Display/UINavigationItem+Proxy.m index 98871e7042..2682e8adfb 100644 --- a/Display/UINavigationItem+Proxy.m +++ b/Display/UINavigationItem+Proxy.m @@ -12,6 +12,7 @@ static const void *setSelectedImageListenerBagKey = &setSelectedImageListenerBag static const void *setTitleViewListenerBagKey = &setTitleViewListenerBagKey; static const void *setLeftBarButtonItemListenerBagKey = &setLeftBarButtonItemListenerBagKey; static const void *setRightBarButtonItemListenerBagKey = &setRightBarButtonItemListenerBagKey; +static const void *setMultipleRightBarButtonItemsListenerKey = &setMultipleRightBarButtonItemsListenerKey; static const void *setBackBarButtonItemListenerBagKey = &setBackBarButtonItemListenerBagKey; static const void *setBadgeListenerBagKey = &setBadgeListenerBagKey; static const void *badgeKey = &badgeKey; @@ -29,6 +30,8 @@ static const void *badgeKey = &badgeKey; [RuntimeUtils swizzleInstanceMethodOfClass:[UINavigationItem class] currentSelector:@selector(setLeftBarButtonItem:animated:) newSelector:@selector(_ac91f40f_setLeftBarButtonItem:animated:)]; [RuntimeUtils swizzleInstanceMethodOfClass:[UINavigationItem class] currentSelector:@selector(setRightBarButtonItem:) newSelector:@selector(_ac91f40f_setRightBarButtonItem:)]; [RuntimeUtils swizzleInstanceMethodOfClass:[UINavigationItem class] currentSelector:@selector(setRightBarButtonItem:animated:) newSelector:@selector(_ac91f40f_setRightBarButtonItem:animated:)]; + [RuntimeUtils swizzleInstanceMethodOfClass:[UINavigationItem class] currentSelector:@selector(setRightBarButtonItems:) newSelector:@selector(_ac91f40f_setRightBarButtonItems:)]; + [RuntimeUtils swizzleInstanceMethodOfClass:[UINavigationItem class] currentSelector:@selector(setRightBarButtonItems:animated:) newSelector:@selector(_ac91f40f_setRightBarButtonItems:animated:)]; [RuntimeUtils swizzleInstanceMethodOfClass:[UINavigationItem class] currentSelector:@selector(setBackBarButtonItem:) newSelector:@selector(_ac91f40f_setBackBarButtonItem:)]; }); } @@ -114,6 +117,24 @@ static const void *badgeKey = &badgeKey; } } +- (void)_ac91f40f_setRightBarButtonItems:(NSArray *)rightBarButtonItems { + [self setRightBarButtonItems:rightBarButtonItems animated:false]; +} + +- (void)_ac91f40f_setRightBarButtonItems:(NSArray *)rightBarButtonItems animated:(BOOL)animated +{ + [self _ac91f40f_setRightBarButtonItems:rightBarButtonItems animated:animated]; + + UINavigationItem *targetItem = [self associatedObjectForKey:targetItemKey]; + if (targetItem != nil) { + [targetItem setRightBarButtonItems:rightBarButtonItems animated:animated]; + } else { + [(NSBag *)[self associatedObjectForKey:setMultipleRightBarButtonItemsListenerKey] enumerateItems:^(UINavigationItemSetMutipleBarButtonItemsListener listener) { + listener(rightBarButtonItems, animated); + }]; + } +} + - (void)_ac91f40f_setBackBarButtonItem:(UIBarButtonItem *)backBarButtonItem { UIBarButtonItem *previousItem = self.rightBarButtonItem; @@ -218,6 +239,20 @@ static const void *badgeKey = &badgeKey; [(NSBag *)[self associatedObjectForKey:setRightBarButtonItemListenerBagKey] removeItem:key]; } +- (NSInteger)addSetMultipleRightBarButtonItemsListener:(UINavigationItemSetMutipleBarButtonItemsListener _Nonnull)listener { + NSBag *bag = [self associatedObjectForKey:setMultipleRightBarButtonItemsListenerKey]; + if (bag == nil) + { + bag = [[NSBag alloc] init]; + [self setAssociatedObject:bag forKey:setMultipleRightBarButtonItemsListenerKey]; + } + return [bag addItem:[listener copy]]; +} + +- (void)removeSetMultipleRightBarButtonItemsListener:(NSInteger)key { + [(NSBag *)[self associatedObjectForKey:setMultipleRightBarButtonItemsListenerKey] removeItem:key]; +} + - (NSInteger)addSetBackBarButtonItemListener:(UINavigationItemSetBarButtonItemListener)listener { NSBag *bag = [self associatedObjectForKey:setBackBarButtonItemListenerBagKey]; if (bag == nil) diff --git a/Display/WindowContent.swift b/Display/WindowContent.swift index d74be8d3a8..b2b1f9cdee 100644 --- a/Display/WindowContent.swift +++ b/Display/WindowContent.swift @@ -45,15 +45,11 @@ private struct WindowLayout: Equatable { return false } - if let lhsInputHeight = lhs.inputHeight { - if let rhsInputHeight = rhs.inputHeight { - if !lhsInputHeight.isEqual(to: rhsInputHeight) { - return false - } - } else { + if let lhsInputHeight = lhs.inputHeight, let rhsInputHeight = rhs.inputHeight { + if !lhsInputHeight.isEqual(to: rhsInputHeight) { return false } - } else if let _ = rhs.inputHeight { + } else if (lhs.inputHeight != nil) != (rhs.inputHeight != nil) { return false } @@ -345,6 +341,7 @@ public class Window1 { } } + private var windowPanRecognizer: WindowPanRecognizer? private let keyboardGestureRecognizerDelegate = KeyboardGestureRecognizerDelegate() private var keyboardGestureBeginLocation: CGPoint? private var keyboardGestureAccessoryHeight: CGFloat? @@ -431,7 +428,20 @@ public class Window1 { self.keyboardFrameChangeObserver = NotificationCenter.default.addObserver(forName: NSNotification.Name.UIKeyboardWillChangeFrame, object: nil, queue: nil, using: { [weak self] notification in if let strongSelf = self { let keyboardFrame: CGRect = (notification.userInfo?[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue ?? CGRect() - let keyboardHeight = max(0.0, UIScreen.main.bounds.size.height - keyboardFrame.minY) + + let screenHeight: CGFloat + + if true || !UIScreen.main.bounds.width.isEqual(to: strongSelf.windowLayout.size.width) { + if keyboardFrame.width.isEqual(to: UIScreen.main.bounds.width) { + screenHeight = UIScreen.main.bounds.height + } else { + screenHeight = UIScreen.main.bounds.width + } + } else { + screenHeight = UIScreen.main.bounds.height + } + + let keyboardHeight = max(0.0, screenHeight - keyboardFrame.minY) var duration: Double = (notification.userInfo?[UIKeyboardAnimationDurationUserInfoKey] as? NSNumber)?.doubleValue ?? 0.0 if duration > Double.ulpOfOne { duration = 0.5 @@ -491,6 +501,7 @@ public class Window1 { recognizer.ended = { [weak self] point, velocity in self?.panGestureEnded(location: point, velocity: velocity) } + self.windowPanRecognizer = recognizer self.hostView.view.addGestureRecognizer(recognizer) } @@ -536,6 +547,9 @@ public class Window1 { } public func cancelInteractiveKeyboardGestures() { + self.windowPanRecognizer?.isEnabled = false + self.windowPanRecognizer?.isEnabled = true + if self.windowLayout.upperKeyboardInputPositionBound != nil { self.updateLayout { $0.update(upperKeyboardInputPositionBound: nil, transition: .animated(duration: 0.25, curve: .spring), overrideTransition: false) @@ -594,7 +608,11 @@ public class Window1 { rootController.containerLayoutUpdated(containedLayoutForWindowLayout(self.windowLayout), transition: .immediate) } - self.hostView.view.addSubview(rootController.view) + if let coveringView = self.coveringView { + self.hostView.view.insertSubview(rootController.view, belowSubview: coveringView) + } else { + self.hostView.view.addSubview(rootController.view) + } } } } @@ -613,13 +631,32 @@ public class Window1 { for controller in self._topLevelOverlayControllers { controller.containerLayoutUpdated(containedLayoutForWindowLayout(self.windowLayout), transition: .immediate) - self.hostView.view.addSubview(controller.view) + if let coveringView = self.coveringView { + self.hostView.view.insertSubview(controller.view, belowSubview: coveringView) + } else { + self.hostView.view.addSubview(controller.view) + } } self.presentationContext.topLevelSubview = self._topLevelOverlayControllers.first?.view } } + public var coveringView: WindowCoveringView? { + didSet { + if self.coveringView !== oldValue { + oldValue?.removeFromSuperview() + if let coveringView = self.coveringView { + self.hostView.view.addSubview(coveringView) + if !self.windowLayout.size.width.isZero { + coveringView.frame = CGRect(origin: CGPoint(), size: self.windowLayout.size) + coveringView.updateLayout(self.windowLayout.size) + } + } + } + } + } + private func layoutSubviews() { var hasPreview = false var updatedHasPreview = false @@ -783,6 +820,11 @@ public class Window1 { } }) } + + if let coveringView = self.coveringView { + coveringView.frame = CGRect(origin: CGPoint(), size: self.windowLayout.size) + coveringView.updateLayout(self.windowLayout.size) + } } } } diff --git a/Display/WindowCoveringView.swift b/Display/WindowCoveringView.swift new file mode 100644 index 0000000000..6ba76a89c6 --- /dev/null +++ b/Display/WindowCoveringView.swift @@ -0,0 +1,7 @@ +import Foundation +import UIKit + +open class WindowCoveringView: UIView { + open func updateLayout(_ size: CGSize) { + } +} diff --git a/DisplayMac/ASDisplayNode.swift b/DisplayMac/ASDisplayNode.swift index ed43abb43e..f6052f61ca 100644 --- a/DisplayMac/ASDisplayNode.swift +++ b/DisplayMac/ASDisplayNode.swift @@ -9,6 +9,9 @@ open class ASDisplayNode: NSObject { preconditionFailure() } + weak var supernode: ASDisplayNode? + private(set) var subnodes: [ASDisplayNode] = [] + open var frame: CGRect { get { return self.layer.frame @@ -55,6 +58,14 @@ open class ASDisplayNode: NSObject { var isLayerBacked: Bool = false + var clipsToBounds: Bool { + get { + return self.layer.masksToBounds + } set(value) { + self.layer.masksToBounds = value + } + } + override init() { super.init() } @@ -74,11 +85,27 @@ open class ASDisplayNode: NSObject { } - open func insertSubnode(belowSubnode: ASDisplayNode) { + open func insertSubnode(_ subnode: ASDisplayNode, belowSubnode: ASDisplayNode) { } - open func insertSubnode(aboveSubnode: ASDisplayNode) { + open func insertSubnode(_ subnode: ASDisplayNode, aboveSubnode: ASDisplayNode) { + + } + + open func insertSubnode(_ subnode: ASDisplayNode, at: Int) { + + } + + open func removeFromSupernode() { + + } + + func recursivelyEnsureDisplaySynchronously(_ synchronously: Bool) { } } + +func ASPerformMainThreadDeallocation(_ ref: inout AnyObject?) { + +} diff --git a/DisplayMac/CADisplayLink.swift b/DisplayMac/CADisplayLink.swift new file mode 100644 index 0000000000..36a084b4c8 --- /dev/null +++ b/DisplayMac/CADisplayLink.swift @@ -0,0 +1,89 @@ +import Foundation +import CoreVideo +import SwiftSignalKitMac + +private final class CADisplayLinkContext { + weak var impl: CADisplayLink? + + init(_ impl: CADisplayLink) { + self.impl = impl + } +} + +private final class CADisplayLinkContexts { + private var nextId: Int32 = 0 + var contexts: [Int32: CADisplayLinkContext] = [:] + + func add(_ impl: CADisplayLink) -> Int32 { + let id = self.nextId + self.nextId += 1 + self.contexts[id] = CADisplayLinkContext(impl) + return id + } + + func remove(_ id: Int32) { + self.contexts.removeValue(forKey: id) + } + + func get(id: Int32) -> CADisplayLink? { + return self.contexts[id]?.impl + } +} + +private let contexts = Atomic(value: CADisplayLinkContexts()) + +public final class CADisplayLink { + private var id: Int32? + private var displayLink: CVDisplayLink? + + public var isPaused: Bool = true { + didSet { + if self.isPaused != oldValue { + + } + } + } + + private let target: Any? + private let action: Selector? + + init(target: Any?, selector: Selector?) { + self.target = target + self.action = selector + + let id = contexts.with { contexts in + return contexts.add(self) + } + self.id = id + CVDisplayLinkCreateWithActiveCGDisplays(&self.displayLink) + if let displayLink = self.displayLink { + CVDisplayLinkSetOutputCallback(displayLink, { _, _, _, _, _, ref in + let id: Int32 = Int32(unsafeBitCast(ref, to: intptr_t.self)) + if let impl = (contexts.with { contexts in + return contexts.get(id: id) + }) { + impl.performAction() + } + return kCVReturnSuccess + }, UnsafeMutableRawPointer(bitPattern: Int(id))) + } + } + + deinit { + if let id = self.id { + contexts.with { contexts in + contexts.remove(id) + } + } + } + + public func invalidate() { + + } + + private func performAction() { + if let target = self.target, let action = self.action { + let _ = (target as? AnyObject)?.perform(action) + } + } +} diff --git a/DisplayMac/UIKit.swift b/DisplayMac/UIKit.swift index 54c06ae71c..d080662bc6 100644 --- a/DisplayMac/UIKit.swift +++ b/DisplayMac/UIKit.swift @@ -2,10 +2,10 @@ import Foundation import QuartzCore public struct UIEdgeInsets: Equatable { - public let top: CGFloat - public let left: CGFloat - public let bottom: CGFloat - public let right: CGFloat + public var top: CGFloat + public var left: CGFloat + public var bottom: CGFloat + public var right: CGFloat public init() { self.top = 0.0 diff --git a/DisplayMac/UIScrollView.swift b/DisplayMac/UIScrollView.swift index d3c4276004..a1ba22d1a3 100644 --- a/DisplayMac/UIScrollView.swift +++ b/DisplayMac/UIScrollView.swift @@ -19,6 +19,10 @@ open class UIScrollView: UIView { } } - public var alwaysBoundsVertical: Bool = false - public var alwaysBoundsHorizontal: Bool = false + public var alwaysBounceVertical: Bool = false + public var alwaysBounceHorizontal: Bool = false + + public func setContentOffset(_ contentOffset: CGPoint, animated: Bool) { + self.contentOffset = contentOffset + } } diff --git a/DisplayMac/UIView.swift b/DisplayMac/UIView.swift index 5fa17be3da..3a323cd80c 100644 --- a/DisplayMac/UIView.swift +++ b/DisplayMac/UIView.swift @@ -28,7 +28,19 @@ open class UIView: NSObject { } } - init(frame: CGRect) { + open var isHidden: Bool { + get { + return self.layer.isHidden + } set(value) { + self.layer.isHidden = value + } + } + + open class var layerClass: AnyClass { + return CALayer.self + } + + public init(frame: CGRect) { self.layer = CALayer() self.layer.frame = frame @@ -46,4 +58,25 @@ open class UIView: NSObject { public func bringSubview(toFront: UIView) { } + + public func addSubview(_ subview: UIView) { + + } + + public func removeFromSuperview() { + + } + + open func setNeedsLayout() { + } + + open func layoutSubviews() { + } + + open func setNeedsDisplay() { + } + + open func snapshotView(afterScreenUpdates: Bool) -> UIView? { + return nil + } }