From 0055396d4de750b24ad138266166d7343518bcf7 Mon Sep 17 00:00:00 2001 From: Isaac <> Date: Wed, 26 Nov 2025 23:05:30 +0800 Subject: [PATCH] Update --- .claude/settings.local.json | 8 +- MODULE.bazel.lock | 6 +- Telegram/BUILD | 2 + build-system/Make/Make.py | 25 +- submodules/AdUI/Sources/AdInfoScreen.swift | 2 +- .../AuthorizationSequenceController.swift | 2 +- .../Sources/BrowserBookmarksScreen.swift | 2 +- .../Sources/CallListController.swift | 6 +- .../Sources/ChatListController.swift | 22 +- .../Sources/ChatTitleActivityNode.swift | 5 +- .../Sources/ViewControllerComponent.swift | 2 +- submodules/ContactListUI/BUILD | 1 + .../Sources/ContactsController.swift | 27 +- .../Sources/ContactsControllerNode.swift | 21 +- .../Sources/InviteContactsController.swift | 2 +- .../Display/Source/ImmediateTextNode.swift | 6 +- .../Navigation/NavigationModalFrame.swift | 2 +- .../Source/NavigationBackgroundView.swift | 332 +++ submodules/Display/Source/NavigationBar.swift | 1829 +---------------- .../Display/Source/NavigationBarBadge.swift | 4 +- .../Source/NavigationBarTransitionState.swift | 21 - .../NavigationTransitionCoordinator.swift | 16 +- .../Display/Source/SparseContainerView.swift | 47 + .../Display/Source/ViewController.swift | 6 +- .../Sources/FeaturedStickersScreen.swift | 2 +- .../GalleryUI/Sources/GalleryController.swift | 8 +- .../Sources/HashtagSearchController.swift | 2 +- .../Sources/ItemListController.swift | 5 +- .../Sources/LocationPickerController.swift | 2 +- .../Sources/LocationViewController.swift | 2 +- .../Sources/MediaPickerScreen.swift | 2 +- .../Sources/MediaPickerTitleView.swift | 6 +- .../SetupTwoStepVerificationController.swift | 4 +- .../TwoFactorAuthDataInputScreen.swift | 2 +- .../Sources/TwoFactorAuthSplashScreen.swift | 2 +- .../QrCodeUI/Sources/QrCodeScanScreen.swift | 2 +- .../LocalizationListController.swift | 2 +- .../Exceptions/NotificationExceptions.swift | 2 +- .../PrivacyIntroController.swift | 2 +- .../Themes/ThemePreviewController.swift | 2 +- .../StatisticsUI/Sources/BackButton.swift | 2 +- .../Sources/PermissionController.swift | 6 +- .../Sources/ComponentsThemes.swift | 33 +- .../PresentationResourcesRootController.swift | 4 +- submodules/TelegramUI/BUILD | 1 + .../Sources/BackButtonComponent.swift | 2 +- .../Sources/Components/BackButtonView.swift | 2 +- .../Sources/ChatAvatarNavigationNode.swift | 10 +- .../Sources/ChatRecentActionsController.swift | 2 +- .../Sources/ChatListHeaderComponent.swift | 13 +- .../TelegramUI/Components/ChatTitleView/BUILD | 1 + .../ChatTitleView/Sources/ChatTitleView.swift | 184 +- .../Sources/GlassBackgroundComponent.swift | 61 +- .../Components/NavigationBarImpl/BUILD | 24 + .../Sources/NavigationBarImpl.swift | 1123 ++++++++++ .../Sources}/NavigationButtonNode.swift | 317 ++- .../PeerInfoHeaderNavigationButton.swift | 2 +- .../Sources/PeerInfoHeaderNode.swift | 14 +- .../Sources/PeerInfoScreen.swift | 120 +- .../Sources/PeerSelectionController.swift | 2 +- .../Sources/LanguageSelectionScreen.swift | 2 +- .../Sources/BackButton.swift | 2 +- .../Sources/ThemeAccentColorController.swift | 2 +- .../Sources/TimezoneSelectionScreen.swift | 2 +- .../Sources/WallpaperGalleryController.swift | 2 +- .../Sources/ThemeColorsGridController.swift | 2 +- .../Sources/ThemeGridController.swift | 2 +- .../TelegramUI/Sources/AppDelegate.swift | 5 + .../TelegramUI/Sources/ChatController.swift | 24 +- .../Sources/ChatControllerNode.swift | 52 +- .../ChatInterfaceTitlePanelNodes.swift | 4 + .../Sources/ChatSearchResultsController.swift | 2 +- .../Sources/ComposeController.swift | 2 +- .../ContactMultiselectionController.swift | 2 +- .../Sources/ContactSelectionController.swift | 2 +- .../Sources/ExperimentalUISettings.swift | 6 - submodules/WallpaperBackgroundNode/BUILD | 1 + .../Sources/WallpaperBackgroundNode.swift | 76 +- .../Sources/WebSearchGalleryController.swift | 2 +- .../WebUI/Sources/WebAppController.swift | 3 +- 80 files changed, 2312 insertions(+), 2249 deletions(-) create mode 100644 submodules/Display/Source/NavigationBackgroundView.swift delete mode 100644 submodules/Display/Source/NavigationBarTransitionState.swift create mode 100644 submodules/Display/Source/SparseContainerView.swift create mode 100644 submodules/TelegramUI/Components/NavigationBarImpl/BUILD create mode 100644 submodules/TelegramUI/Components/NavigationBarImpl/Sources/NavigationBarImpl.swift rename submodules/{Display/Source => TelegramUI/Components/NavigationBarImpl/Sources}/NavigationButtonNode.swift (64%) diff --git a/.claude/settings.local.json b/.claude/settings.local.json index 3d54595b0f..3a27f2450e 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -5,8 +5,12 @@ "Bash(.build/debug/makeproject:*)", "Bash(mkdir:*)", "Bash(find:*)", - "Bash(grep:*)" + "Bash(grep:*)", + "Skill(superpowers:brainstorming)", + "Bash(cat:*)", + "Bash(python3:*)", + "Skill(superpowers:writing-plans)" ], "deny": [] } -} \ No newline at end of file +} diff --git a/MODULE.bazel.lock b/MODULE.bazel.lock index a158c62a2c..ff6647515f 100644 --- a/MODULE.bazel.lock +++ b/MODULE.bazel.lock @@ -98,8 +98,8 @@ "https://bcr.bazel.build/modules/rules_java/7.2.0/MODULE.bazel": "06c0334c9be61e6cef2c8c84a7800cef502063269a5af25ceb100b192453d4ab", "https://bcr.bazel.build/modules/rules_java/7.3.2/MODULE.bazel": "50dece891cfdf1741ea230d001aa9c14398062f2b7c066470accace78e412bc2", "https://bcr.bazel.build/modules/rules_java/7.6.1/MODULE.bazel": "2f14b7e8a1aa2f67ae92bc69d1ec0fa8d9f827c4e17ff5e5f02e91caa3b2d0fe", - "https://bcr.bazel.build/modules/rules_java/8.12.0/MODULE.bazel": "8e6590b961f2defdfc2811c089c75716cb2f06c8a4edeb9a8d85eaa64ee2a761", - "https://bcr.bazel.build/modules/rules_java/8.12.0/source.json": "cbd5d55d9d38d4008a7d00bee5b5a5a4b6031fcd4a56515c9accbcd42c7be2ba", + "https://bcr.bazel.build/modules/rules_java/8.14.0/MODULE.bazel": "717717ed40cc69994596a45aec6ea78135ea434b8402fb91b009b9151dd65615", + "https://bcr.bazel.build/modules/rules_java/8.14.0/source.json": "8a88c4ca9e8759da53cddc88123880565c520503321e2566b4e33d0287a3d4bc", "https://bcr.bazel.build/modules/rules_java/8.3.2/MODULE.bazel": "7336d5511ad5af0b8615fdc7477535a2e4e723a357b6713af439fe8cf0195017", "https://bcr.bazel.build/modules/rules_java/8.5.1/MODULE.bazel": "d8a9e38cc5228881f7055a6079f6f7821a073df3744d441978e7a43e20226939", "https://bcr.bazel.build/modules/rules_jvm_external/4.4.2/MODULE.bazel": "a56b85e418c83eb1839819f0b515c431010160383306d13ec21959ac412d2fe7", @@ -156,7 +156,7 @@ "moduleExtensions": { "@@rules_kotlin+//src/main/starlark/core/repositories:bzlmod_setup.bzl%rules_kotlin_extensions": { "general": { - "bzlTransitiveDigest": "hUTp2w+RUVdL7ma5esCXZJAFnX7vLbVfLd7FwnQI6bU=", + "bzlTransitiveDigest": "OlvsB0HsvxbR8ZN+J9Vf00X/+WVz/Y/5Xrq2LgcVfdo=", "usagesDigest": "QI2z8ZUR+mqtbwsf2fLqYdJAkPOHdOV+tF2yVAUgRzw=", "recordedFileInputs": {}, "recordedDirentsInputs": {}, diff --git a/Telegram/BUILD b/Telegram/BUILD index a7e40651fc..0f6377e34b 100644 --- a/Telegram/BUILD +++ b/Telegram/BUILD @@ -335,6 +335,7 @@ filegroup( objc_library( name = "Main", + module_name = "Main", srcs = [ "Telegram-iOS/main.m" ], @@ -342,6 +343,7 @@ objc_library( swift_library( name = "Lib", + module_name = "Lib", srcs = glob([ "Telegram-iOS/Application.swift", ]), diff --git a/build-system/Make/Make.py b/build-system/Make/Make.py index 1898ad5cb4..b64eeb5ccd 100644 --- a/build-system/Make/Make.py +++ b/build-system/Make/Make.py @@ -369,17 +369,15 @@ class BazelCommandLine: print(subprocess.list2cmdline(combined_arguments)) call_executable(combined_arguments) - def get_spm_aspect_invocation(self): + def invoke_spm_build(self): combined_arguments = [ self.build_environment.bazel_path ] combined_arguments += self.get_startup_bazel_arguments() combined_arguments += ['build'] - if self.custom_target is not None: - combined_arguments += [self.custom_target] - else: - combined_arguments += ['Telegram/Telegram'] + # Build the generate_spm target directly to get the dependency tree JSON + combined_arguments += ['//Telegram:spm_build_root'] if self.continue_on_error: combined_arguments += ['--keep_going'] @@ -409,8 +407,6 @@ class BazelCommandLine: combined_arguments += self.configuration_args - combined_arguments += ['--aspects', '//build-system/bazel-utils:spm.bzl%spm_text_aspect'] - print(subprocess.list2cmdline(combined_arguments)) call_executable(combined_arguments) @@ -719,7 +715,7 @@ def query(bazel, arguments): bazel_command_line.invoke_query(query_args) -def get_spm_aspect_invocation(bazel, arguments): +def build_spm(bazel, arguments): bazel_command_line = BazelCommandLine( bazel=bazel, override_bazel_version=arguments.overrideBazelVersion, @@ -741,13 +737,12 @@ def get_spm_aspect_invocation(bazel, arguments): bazel_command_line.set_configuration(arguments.configuration) bazel_command_line.set_build_number(arguments.buildNumber) - bazel_command_line.set_custom_target(arguments.target) bazel_command_line.set_continue_on_error(False) bazel_command_line.set_show_actions(False) bazel_command_line.set_enable_sandbox(False) bazel_command_line.set_split_swiftmodules(False) - bazel_command_line.get_spm_aspect_invocation() + bazel_command_line.invoke_spm_build() def add_codesigning_common_arguments(current_parser: argparse.ArgumentParser): configuration_group = current_parser.add_mutually_exclusive_group(required=True) @@ -1188,13 +1183,7 @@ if __name__ == '__main__': metavar='query_string' ) - spm_parser = subparsers.add_parser('spm', help='Generate SPM package') - spm_parser.add_argument( - '--target', - type=str, - help='A custom bazel target name to build.', - metavar='target_name' - ) + spm_parser = subparsers.add_parser('spm', help='Generate SPM package (outputs bazel-bin/Telegram/spm_build_root_modules.json)') spm_parser.add_argument( '--buildNumber', required=False, @@ -1351,7 +1340,7 @@ if __name__ == '__main__': elif args.commandName == 'query': query(bazel=bazel_path, arguments=args) elif args.commandName == 'spm': - get_spm_aspect_invocation(bazel=bazel_path, arguments=args) + build_spm(bazel=bazel_path, arguments=args) else: raise Exception('Unknown command') except KeyboardInterrupt: diff --git a/submodules/AdUI/Sources/AdInfoScreen.swift b/submodules/AdUI/Sources/AdInfoScreen.swift index 9a3309f9b8..7cecf8224b 100644 --- a/submodules/AdUI/Sources/AdInfoScreen.swift +++ b/submodules/AdUI/Sources/AdInfoScreen.swift @@ -73,7 +73,7 @@ public final class AdInfoScreen: ViewController { self.titleNode = ImmediateTextNode() self.titleNode.maximumNumberOfLines = 1 - self.titleNode.attributedText = NSAttributedString(string: self.presentationData.strings.SponsoredMessageInfoScreen_Title, font: NavigationBar.titleFont, textColor: self.presentationData.theme.rootController.navigationBar.primaryTextColor) + self.titleNode.attributedText = NSAttributedString(string: self.presentationData.strings.SponsoredMessageInfoScreen_Title, font: Font.with(size: 17.0, design: .regular, weight: .semibold, traits: [.monospacedNumbers]), textColor: self.presentationData.theme.rootController.navigationBar.primaryTextColor) self.scrollNode = ASScrollNode() self.scrollNode.view.showsVerticalScrollIndicator = true diff --git a/submodules/AuthorizationUI/Sources/AuthorizationSequenceController.swift b/submodules/AuthorizationUI/Sources/AuthorizationSequenceController.swift index 9c9c03a5d3..51fb4c667e 100644 --- a/submodules/AuthorizationUI/Sources/AuthorizationSequenceController.swift +++ b/submodules/AuthorizationUI/Sources/AuthorizationSequenceController.swift @@ -33,7 +33,7 @@ private enum InnerState: Equatable { public final class AuthorizationSequenceController: NavigationController, ASAuthorizationControllerDelegate, ASAuthorizationControllerPresentationContextProviding { static func navigationBarTheme(_ theme: PresentationTheme) -> NavigationBarTheme { - return NavigationBarTheme(buttonColor: theme.intro.accentTextColor, disabledButtonColor: theme.intro.disabledTextColor, primaryTextColor: theme.intro.primaryTextColor, backgroundColor: .clear, opaqueBackgroundColor: .clear, enableBackgroundBlur: false, separatorColor: .clear, badgeBackgroundColor: theme.rootController.navigationBar.badgeBackgroundColor, badgeStrokeColor: theme.rootController.navigationBar.badgeStrokeColor, badgeTextColor: theme.rootController.navigationBar.badgeTextColor) + return NavigationBarTheme(overallDarkAppearance: theme.overallDarkAppearance, buttonColor: theme.intro.accentTextColor, disabledButtonColor: theme.intro.disabledTextColor, primaryTextColor: theme.intro.primaryTextColor, backgroundColor: .clear, opaqueBackgroundColor: .clear, enableBackgroundBlur: false, separatorColor: .clear, badgeBackgroundColor: theme.rootController.navigationBar.badgeBackgroundColor, badgeStrokeColor: theme.rootController.navigationBar.badgeStrokeColor, badgeTextColor: theme.rootController.navigationBar.badgeTextColor) } private let sharedContext: SharedAccountContext diff --git a/submodules/BrowserUI/Sources/BrowserBookmarksScreen.swift b/submodules/BrowserUI/Sources/BrowserBookmarksScreen.swift index 274ce3b596..b1ef15533d 100644 --- a/submodules/BrowserUI/Sources/BrowserBookmarksScreen.swift +++ b/submodules/BrowserUI/Sources/BrowserBookmarksScreen.swift @@ -441,7 +441,7 @@ public final class BrowserBookmarksScreen: ViewController { private func updateThemeAndStrings() { self.statusBar.statusBarStyle = self.presentationData.theme.rootController.statusBarStyle.style - self.navigationBar?.updatePresentationData(NavigationBarPresentationData(presentationData: self.presentationData)) + self.navigationBar?.updatePresentationData(NavigationBarPresentationData(presentationData: self.presentationData), transition: .immediate) self.searchContentNode?.updateThemeAndPlaceholder(theme: self.presentationData.theme, placeholder: self.presentationData.strings.Common_Search) } diff --git a/submodules/CallListUI/Sources/CallListController.swift b/submodules/CallListUI/Sources/CallListController.swift index 06bfd50c46..c759b95527 100644 --- a/submodules/CallListUI/Sources/CallListController.swift +++ b/submodules/CallListUI/Sources/CallListController.swift @@ -205,7 +205,7 @@ public final class CallListController: TelegramBaseController { } self.statusBar.statusBarStyle = self.presentationData.theme.rootController.statusBarStyle.style - self.navigationBar?.updatePresentationData(NavigationBarPresentationData(presentationData: self.presentationData)) + self.navigationBar?.updatePresentationData(NavigationBarPresentationData(presentationData: self.presentationData), transition: .immediate) if self.isNodeLoaded { self.controllerNode.updateThemeAndStrings(presentationData: self.presentationData) @@ -780,6 +780,10 @@ public final class CallListController: TelegramBaseController { let controller = ContextController(presentationData: self.presentationData, source: .reference(CallListTabBarContextReferenceContentSource(controller: self, sourceView: sourceView)), items: .single(ContextController.Items(content: .list(items))), recognizer: nil, gesture: gesture) self.context.sharedContext.mainWindow?.presentInGlobalOverlay(controller) } + + override public func tabBarActivateSearch() { + self.beginCallImpl() + } } private final class CallListTabBarContextReferenceContentSource: ContextReferenceContentSource { diff --git a/submodules/ChatListUI/Sources/ChatListController.swift b/submodules/ChatListUI/Sources/ChatListController.swift index 078854b0e6..cb011f8b21 100644 --- a/submodules/ChatListUI/Sources/ChatListController.swift +++ b/submodules/ChatListUI/Sources/ChatListController.swift @@ -438,23 +438,6 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController }).strict() if !previewing { - /* - self.searchContentNode = NavigationBarSearchContentNode(theme: self.presentationData.theme, placeholder: placeholder, compactPlaceholder: compactPlaceholder, activate: { [weak self] in - self?.chatListDisplayNode.mainContainerNode.currentItemNode.cancelTracking() - self?.activateSearch(filter: isForum ? .topics : .chats) - }) - self.searchContentNode?.updateExpansionProgress(0.0) - self.navigationBar?.setContentNode(self.searchContentNode, animated: false)*/ - - let tabsIsEmpty: Bool - if let (resolvedItems, displayTabsAtBottom, _) = self.tabContainerData { - tabsIsEmpty = resolvedItems.count <= 1 || displayTabsAtBottom - } else { - tabsIsEmpty = true - } - - self.navigationBar?.secondaryContentHeight = !tabsIsEmpty ? NavigationBar.defaultSecondaryContentHeight : 0.0 - enum State: Equatable { case empty(hasDownloads: Bool) case downloading(progress: Double) @@ -937,7 +920,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController } self.statusBar.statusBarStyle = self.presentationData.theme.rootController.statusBarStyle.style - self.navigationBar?.updatePresentationData(NavigationBarPresentationData(presentationData: self.presentationData)) + self.navigationBar?.updatePresentationData(NavigationBarPresentationData(presentationData: self.presentationData), transition: .immediate) if let layout = self.validLayout { self.tabContainerNode.update(size: CGSize(width: layout.size.width, height: 46.0), sideInset: layout.safeInsets.left, filters: self.tabContainerData?.0 ?? [], selectedFilter: self.chatListDisplayNode.effectiveContainerNode.currentItemFilter, isReordering: self.chatListDisplayNode.isReorderingFilters || (self.chatListDisplayNode.effectiveContainerNode.currentItemNode.currentState.editing && !self.chatListDisplayNode.didBeginSelectingChatsWhileEditing), isEditing: self.chatListDisplayNode.effectiveContainerNode.currentItemNode.currentState.editing, canReorderAllChats: self.isPremium, filtersLimit: self.tabContainerData?.2, transitionFraction: self.chatListDisplayNode.effectiveContainerNode.transitionFraction, presentationData: self.presentationData, transition: .immediate) @@ -3522,8 +3505,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController tabContainerOffset += 44.0 + 20.0 } - let navigationBarHeight: CGFloat = 0.0//self.navigationBar?.frame.maxY ?? 0.0 - //let secondaryContentHeight = self.navigationBar?.secondaryContentHeight ?? 0.0 + let navigationBarHeight: CGFloat = 0.0 transition.updateFrame(node: self.tabContainerNode, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: layout.size.width, height: 46.0))) diff --git a/submodules/ChatTitleActivityNode/Sources/ChatTitleActivityNode.swift b/submodules/ChatTitleActivityNode/Sources/ChatTitleActivityNode.swift index b9013f66ff..86efa6d049 100644 --- a/submodules/ChatTitleActivityNode/Sources/ChatTitleActivityNode.swift +++ b/submodules/ChatTitleActivityNode/Sources/ChatTitleActivityNode.swift @@ -130,6 +130,9 @@ public class ChatTitleActivityNode: ASDisplayNode { } public func updateLayout(_ constrainedSize: CGSize, offset: CGFloat = 0.0, alignment: NSTextAlignment) -> CGSize { - return CGSize(width: 0.0, height: self.contentNode?.updateLayout(constrainedSize, offset: offset, alignment: alignment).height ?? 0.0) + guard let contentSize = self.contentNode?.updateLayout(constrainedSize, offset: offset, alignment: alignment) else { + return CGSize() + } + return contentSize } } diff --git a/submodules/Components/ViewControllerComponent/Sources/ViewControllerComponent.swift b/submodules/Components/ViewControllerComponent/Sources/ViewControllerComponent.swift index 25d70a78e8..805506728a 100644 --- a/submodules/Components/ViewControllerComponent/Sources/ViewControllerComponent.swift +++ b/submodules/Components/ViewControllerComponent/Sources/ViewControllerComponent.swift @@ -361,7 +361,7 @@ open class ViewControllerComponentContainer: ViewController { navigationBarPresentationData = NavigationBarPresentationData(presentationData: presentationData) } if let navigationBarPresentationData { - strongSelf.navigationBar?.updatePresentationData(navigationBarPresentationData) + strongSelf.navigationBar?.updatePresentationData(navigationBarPresentationData, transition: .immediate) } if let layout = strongSelf.validLayout { diff --git a/submodules/ContactListUI/BUILD b/submodules/ContactListUI/BUILD index 99168662b1..1c8ad8b5d1 100644 --- a/submodules/ContactListUI/BUILD +++ b/submodules/ContactListUI/BUILD @@ -48,6 +48,7 @@ swift_library( "//submodules/ContextUI", "//submodules/TelegramUI/Components/EdgeEffect", "//submodules/TelegramUI/Components/SearchInputPanelComponent", + "//submodules/SearchBarNode", ], visibility = [ "//visibility:public", diff --git a/submodules/ContactListUI/Sources/ContactsController.swift b/submodules/ContactListUI/Sources/ContactsController.swift index e75897f583..5c4006c513 100644 --- a/submodules/ContactListUI/Sources/ContactsController.swift +++ b/submodules/ContactListUI/Sources/ContactsController.swift @@ -24,6 +24,7 @@ import ChatListHeaderComponent import TelegramIntents import UndoUI import ShareController +import SearchBarNode private final class HeaderContextReferenceContentSource: ContextReferenceContentSource { private let controller: ViewController @@ -141,7 +142,6 @@ public class ContactsController: ViewController { self.sortButton = SortHeaderButton(presentationData: self.presentationData) - //super.init(navigationBarPresentationData: NavigationBarPresentationData(presentationData: self.presentationData)) super.init(navigationBarPresentationData: nil) self.tabBarItemContextActionType = .always @@ -244,7 +244,6 @@ public class ContactsController: ViewController { private func updateThemeAndStrings() { self.sortButton.update(theme: self.presentationData.theme, strings: self.presentationData.strings) self.statusBar.statusBarStyle = self.presentationData.theme.rootController.statusBarStyle.style - self.navigationBar?.updatePresentationData(NavigationBarPresentationData(presentationData: self.presentationData)) self.title = self.presentationData.strings.Contacts_Title self.tabBarItem.title = self.presentationData.strings.Contacts_Title @@ -282,8 +281,6 @@ public class ContactsController: ViewController { |> take(1) |> map { _ -> Bool in true }) - self.contactsNode.navigationBar = self.navigationBar - let openPeer: (ContactListPeer, Bool) -> Void = { [weak self] peer, fromSearch in if let strongSelf = self { switch peer { @@ -583,17 +580,19 @@ public class ContactsController: ViewController { } private func activateSearch() { - if let searchContentNode = self.searchContentNode() { - self.contactsNode.activateSearch(placeholderNode: searchContentNode.placeholderNode) + self.contactsNode.activateSearch(placeholderNode: self.searchContentNode()?.placeholderNode) + self.updateTabBarSearchState(ViewController.TabBarSearchState(isActive: true), transition: .animated(duration: 0.5, curve: .spring)) + if let searchBarNode = self.currentTabBarSearchNode?() as? SearchBarNode { + self.contactsNode.searchDisplayController?.setSearchBar(searchBarNode) + searchBarNode.activate() } self.requestLayout(transition: .animated(duration: 0.5, curve: .spring)) } private func deactivateSearch(animated: Bool) { - if let searchContentNode = self.searchContentNode() { - self.contactsNode.deactivateSearch(placeholderNode: searchContentNode.placeholderNode, animated: animated) - self.requestLayout(transition: .animated(duration: 0.5, curve: .spring)) - } + self.contactsNode.deactivateSearch(placeholderNode: self.searchContentNode()?.placeholderNode, animated: animated) + self.updateTabBarSearchState(ViewController.TabBarSearchState(isActive: false), transition: .animated(duration: 0.5, curve: .spring)) + self.requestLayout(transition: .animated(duration: 0.5, curve: .spring)) } func presentSortMenu(sourceView: UIView, gesture: ContextGesture?) { @@ -798,6 +797,14 @@ public class ContactsController: ViewController { let controller = ContextController(presentationData: self.presentationData, source: .reference(ContactsTabBarContextReferenceContentSource(controller: self, sourceView: sourceView)), items: .single(ContextController.Items(content: .list(items))), recognizer: nil, gesture: gesture) self.context.sharedContext.mainWindow?.presentInGlobalOverlay(controller) } + + override public func tabBarActivateSearch() { + self.activateSearch() + } + + override public func tabBarDeactivateSearch() { + self.deactivateSearch(animated: true) + } } private final class ContactsTabBarContextReferenceContentSource: ContextReferenceContentSource { diff --git a/submodules/ContactListUI/Sources/ContactsControllerNode.swift b/submodules/ContactListUI/Sources/ContactsControllerNode.swift index bd1e967a3b..dea9f4322c 100644 --- a/submodules/ContactListUI/Sources/ContactsControllerNode.swift +++ b/submodules/ContactListUI/Sources/ContactsControllerNode.swift @@ -58,7 +58,6 @@ final class ContactsControllerNode: ASDisplayNode, ASGestureRecognizerDelegate { private var containerLayout: (ContainerViewLayout, CGFloat)? - var navigationBar: NavigationBar? let navigationBarView = ComponentView() var requestDeactivateSearch: (() -> Void)? @@ -361,7 +360,7 @@ final class ContactsControllerNode: ASDisplayNode, ASGestureRecognizerDelegate { strings: self.presentationData.strings, statusBarHeight: layout.statusBarHeight ?? 0.0, sideInset: layout.safeInsets.left, - search: ChatListNavigationBar.Search(isEnabled: true), + search: nil,//ChatListNavigationBar.Search(isEnabled: true), isSearchActive: self.isSearchDisplayControllerActive, primaryContent: primaryContent, secondaryContent: nil, @@ -477,7 +476,7 @@ final class ContactsControllerNode: ASDisplayNode, ASGestureRecognizerDelegate { } } - func activateSearch(placeholderNode: SearchBarPlaceholderNode) { + func activateSearch(placeholderNode: SearchBarPlaceholderNode?) { guard let (containerLayout, navigationBarHeight) = self.containerLayout, self.searchDisplayController == nil else { return } @@ -503,7 +502,7 @@ final class ContactsControllerNode: ASDisplayNode, ASGestureRecognizerDelegate { if let requestDeactivateSearch = self?.requestDeactivateSearch { requestDeactivateSearch() } - }) + }, searchBarIsExternal: placeholderNode == nil) self.searchDisplayController?.containerLayoutUpdated(containerLayout, navigationBarHeight: navigationBarHeight, transition: .immediate) self.searchDisplayController?.activate(insertSubnode: { [weak self] subnode, isSearchBar in @@ -519,16 +518,22 @@ final class ContactsControllerNode: ASDisplayNode, ASGestureRecognizerDelegate { }, placeholder: placeholderNode) } - func deactivateSearch(placeholderNode: SearchBarPlaceholderNode, animated: Bool) { + func deactivateSearch(placeholderNode: SearchBarPlaceholderNode?, animated: Bool) { self.isSearchDisplayControllerActive = false if let searchDisplayController = self.searchDisplayController { - let previousFrame = placeholderNode.frame - placeholderNode.frame = previousFrame.offsetBy(dx: 0.0, dy: 54.0) + var previousFrame: CGRect? + if let placeholderNode { + let previousFrameValue = placeholderNode.frame + previousFrame = previousFrameValue + placeholderNode.frame = previousFrameValue.offsetBy(dx: 0.0, dy: 54.0) + } searchDisplayController.deactivate(placeholder: placeholderNode, animated: animated) self.searchDisplayController = nil - placeholderNode.frame = previousFrame + if let placeholderNode, let previousFrame { + placeholderNode.frame = previousFrame + } } } } diff --git a/submodules/ContactListUI/Sources/InviteContactsController.swift b/submodules/ContactListUI/Sources/InviteContactsController.swift index 00c58c2de9..f492f99735 100644 --- a/submodules/ContactListUI/Sources/InviteContactsController.swift +++ b/submodules/ContactListUI/Sources/InviteContactsController.swift @@ -86,7 +86,7 @@ public class InviteContactsController: ViewController, MFMessageComposeViewContr private func updateThemeAndStrings() { self.statusBar.statusBarStyle = self.presentationData.theme.rootController.statusBarStyle.style - self.navigationBar?.updatePresentationData(NavigationBarPresentationData(presentationData: self.presentationData)) + self.navigationBar?.updatePresentationData(NavigationBarPresentationData(presentationData: self.presentationData), transition: .immediate) self.searchContentNode?.updateThemeAndPlaceholder(theme: self.presentationData.theme, placeholder: self.presentationData.strings.Common_Search) self.title = self.presentationData.strings.Contacts_InviteFriends self.navigationItem.backBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.Common_Back, style: .plain, target: nil, action: nil) diff --git a/submodules/Display/Source/ImmediateTextNode.swift b/submodules/Display/Source/ImmediateTextNode.swift index 8851e6c209..61a6d4e22f 100644 --- a/submodules/Display/Source/ImmediateTextNode.swift +++ b/submodules/Display/Source/ImmediateTextNode.swift @@ -13,7 +13,7 @@ public struct ImmediateTextNodeLayoutInfo { } } -public class ImmediateTextNode: TextNode { +open class ImmediateTextNode: TextNode { public var attributedText: NSAttributedString? public var textAlignment: NSTextAlignment = .natural public var verticalAlignment: TextVerticalAlignment = .top @@ -60,7 +60,7 @@ public class ImmediateTextNode: TextNode { public var trailingLineWidth: CGFloat? - var constrainedSize: CGSize? + public var constrainedSize: CGSize? public var highlightAttributeAction: (([NSAttributedString.Key: Any]) -> NSAttributedString.Key?)? { didSet { @@ -94,7 +94,7 @@ public class ImmediateTextNode: TextNode { return node } - public func updateLayout(_ constrainedSize: CGSize) -> CGSize { + open func updateLayout(_ constrainedSize: CGSize) -> CGSize { self.constrainedSize = constrainedSize let makeLayout = TextNode.asyncLayout(self) diff --git a/submodules/Display/Source/Navigation/NavigationModalFrame.swift b/submodules/Display/Source/Navigation/NavigationModalFrame.swift index 2120a419fe..91c05d6632 100644 --- a/submodules/Display/Source/Navigation/NavigationModalFrame.swift +++ b/submodules/Display/Source/Navigation/NavigationModalFrame.swift @@ -109,7 +109,7 @@ public final class NavigationModalFrame: ASDisplayNode { let contentScale = (layout.size.width - sideInset * 2.0) / layout.size.width let bottomInset: CGFloat = layout.size.height - contentScale * layout.size.height - topInset - let cornerRadius: CGFloat = 28.0 + let cornerRadius: CGFloat = 38.0 let initialCornerRadius: CGFloat if !layout.safeInsets.top.isZero { initialCornerRadius = layout.deviceMetrics.screenCornerRadius diff --git a/submodules/Display/Source/NavigationBackgroundView.swift b/submodules/Display/Source/NavigationBackgroundView.swift new file mode 100644 index 0000000000..2fff1fe113 --- /dev/null +++ b/submodules/Display/Source/NavigationBackgroundView.swift @@ -0,0 +1,332 @@ +import Foundation +import UIKit +import AsyncDisplayKit + +private var sharedIsReduceTransparencyEnabled = UIAccessibility.isReduceTransparencyEnabled + +public final class NavigationBackgroundNode: ASDisplayNode { + private var _color: UIColor + + public var color: UIColor { + return self._color + } + + private var enableBlur: Bool + private var enableSaturation: Bool + private var customBlurRadius: CGFloat? + + public var effectView: UIVisualEffectView? + private let backgroundNode: ASDisplayNode + + public var backgroundView: UIView { + return self.backgroundNode.view + } + + private var validLayout: (CGSize, CGFloat)? + + public var backgroundCornerRadius: CGFloat { + if let (_, cornerRadius) = self.validLayout { + return cornerRadius + } else { + return 0.0 + } + } + + public init(color: UIColor, enableBlur: Bool = true, enableSaturation: Bool = true, customBlurRadius: CGFloat? = nil) { + self._color = .clear + self.enableBlur = enableBlur + self.enableSaturation = enableSaturation + self.customBlurRadius = customBlurRadius + + self.backgroundNode = ASDisplayNode() + + super.init() + + self.addSubnode(self.backgroundNode) + + self.updateColor(color: color, transition: .immediate) + } + + + public override func didLoad() { + super.didLoad() + + if self.scheduledUpdate { + self.scheduledUpdate = false + self.updateBackgroundBlur(forceKeepBlur: false) + } + } + + private var scheduledUpdate = false + + private func updateBackgroundBlur(forceKeepBlur: Bool) { + guard self.isNodeLoaded else { + self.scheduledUpdate = true + return + } + if self.enableBlur && !sharedIsReduceTransparencyEnabled && ((self._color.alpha > .ulpOfOne && self._color.alpha < 0.95) || forceKeepBlur) { + if self.effectView == nil { + let effectView = UIVisualEffectView(effect: UIBlurEffect(style: .light)) + + for subview in effectView.subviews { + if subview.description.contains("VisualEffectSubview") { + subview.isHidden = true + } + } + + if let sublayer = effectView.layer.sublayers?[0], let filters = sublayer.filters { + sublayer.backgroundColor = nil + sublayer.isOpaque = false + var allowedKeys: [String] = [ + "gaussianBlur" + ] + if self.enableSaturation { + allowedKeys.append("colorSaturate") + } + sublayer.filters = filters.filter { filter in + guard let filter = filter as? NSObject else { + return true + } + let filterName = String(describing: filter) + if !allowedKeys.contains(filterName) { + return false + } + if let customBlurRadius = self.customBlurRadius, filterName == "gaussianBlur" { + filter.setValue(customBlurRadius as NSNumber, forKey: "inputRadius") + } + return true + } + } + + if let (size, cornerRadius) = self.validLayout { + effectView.frame = CGRect(origin: CGPoint(), size: size) + ContainedViewLayoutTransition.immediate.updateCornerRadius(layer: effectView.layer, cornerRadius: cornerRadius) + effectView.clipsToBounds = !cornerRadius.isZero + } + self.effectView = effectView + self.view.insertSubview(effectView, at: 0) + } + } else if let effectView = self.effectView { + self.effectView = nil + effectView.removeFromSuperview() + } + } + + public func updateColor(color: UIColor, enableBlur: Bool? = nil, enableSaturation: Bool? = nil, forceKeepBlur: Bool = false, transition: ContainedViewLayoutTransition) { + let effectiveEnableBlur = enableBlur ?? self.enableBlur + let effectiveEnableSaturation = enableSaturation ?? self.enableSaturation + + if self._color.isEqual(color) && self.enableBlur == effectiveEnableBlur && self.enableSaturation == effectiveEnableSaturation { + return + } + self._color = color + self.enableBlur = effectiveEnableBlur + self.enableSaturation = effectiveEnableSaturation + + if sharedIsReduceTransparencyEnabled { + transition.updateBackgroundColor(node: self.backgroundNode, color: self._color.withAlphaComponent(1.0)) + } else { + transition.updateBackgroundColor(node: self.backgroundNode, color: self._color) + } + + self.updateBackgroundBlur(forceKeepBlur: forceKeepBlur) + } + + public func update(size: CGSize, cornerRadius: CGFloat = 0.0, transition: ContainedViewLayoutTransition, beginWithCurrentState: Bool = true) { + self.validLayout = (size, cornerRadius) + + let contentFrame = CGRect(origin: CGPoint(), size: size) + transition.updateFrame(node: self.backgroundNode, frame: contentFrame, beginWithCurrentState: true) + if let effectView = self.effectView, effectView.frame != contentFrame { + transition.updateFrame(layer: effectView.layer, frame: contentFrame, beginWithCurrentState: true) + if let sublayers = effectView.layer.sublayers { + for sublayer in sublayers { + transition.updateFrame(layer: sublayer, frame: contentFrame, beginWithCurrentState: true) + } + } + } + + transition.updateCornerRadius(node: self.backgroundNode, cornerRadius: cornerRadius) + if let effectView = self.effectView { + transition.updateCornerRadius(layer: effectView.layer, cornerRadius: cornerRadius) + effectView.clipsToBounds = !cornerRadius.isZero + } + } + + public func update(size: CGSize, cornerRadius: CGFloat = 0.0, animator: ControlledTransitionAnimator) { + self.validLayout = (size, cornerRadius) + + let contentFrame = CGRect(origin: CGPoint(), size: size) + animator.updateFrame(layer: self.backgroundNode.layer, frame: contentFrame, completion: nil) + if let effectView = self.effectView, effectView.frame != contentFrame { + animator.updateFrame(layer: effectView.layer, frame: contentFrame, completion: nil) + if let sublayers = effectView.layer.sublayers { + for sublayer in sublayers { + animator.updateFrame(layer: sublayer, frame: contentFrame, completion: nil) + } + } + } + + animator.updateCornerRadius(layer: self.backgroundNode.layer, cornerRadius: cornerRadius, completion: nil) + if let effectView = self.effectView { + animator.updateCornerRadius(layer: effectView.layer, cornerRadius: cornerRadius, completion: nil) + effectView.clipsToBounds = !cornerRadius.isZero + } + } +} + +open class BlurredBackgroundView: UIView { + private var _color: UIColor? + + private var enableBlur: Bool + private var customBlurRadius: CGFloat? + + public private(set) var effectView: UIVisualEffectView? + private let backgroundView: UIView + + private var validLayout: (CGSize, CGFloat)? + + public var backgroundCornerRadius: CGFloat { + if let (_, cornerRadius) = self.validLayout { + return cornerRadius + } else { + return 0.0 + } + } + + public init(color: UIColor?, enableBlur: Bool = true, customBlurRadius: CGFloat? = nil) { + self._color = nil + self.enableBlur = enableBlur + self.customBlurRadius = customBlurRadius + + self.backgroundView = UIView() + + super.init(frame: CGRect()) + + self.addSubview(self.backgroundView) + + if let color = color { + self.updateColor(color: color, transition: .immediate) + } + } + + required public init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + private func updateBackgroundBlur(forceKeepBlur: Bool) { + if let color = self._color, self.enableBlur && !sharedIsReduceTransparencyEnabled && ((color.alpha > .ulpOfOne && color.alpha < 0.95) || forceKeepBlur) { + if self.effectView == nil { + let effectView = UIVisualEffectView(effect: UIBlurEffect(style: .light)) + + for subview in effectView.subviews { + if subview.description.contains("VisualEffectSubview") { + subview.isHidden = true + } + } + + if let sublayer = effectView.layer.sublayers?[0], let filters = sublayer.filters { + sublayer.backgroundColor = nil + sublayer.isOpaque = false + //sublayer.setValue(true as NSNumber, forKey: "allowsInPlaceFiltering") + let allowedKeys: [String] = [ + "colorSaturate", + "gaussianBlur" + ] + sublayer.filters = filters.filter { filter in + guard let filter = filter as? NSObject else { + return true + } + let filterName = String(describing: filter) + if !allowedKeys.contains(filterName) { + return false + } + if let customBlurRadius = self.customBlurRadius, filterName == "gaussianBlur" { + filter.setValue(customBlurRadius as NSNumber, forKey: "inputRadius") + } + return true + } + } + + if let (size, cornerRadius) = self.validLayout { + effectView.frame = CGRect(origin: CGPoint(), size: size) + ContainedViewLayoutTransition.immediate.updateCornerRadius(layer: effectView.layer, cornerRadius: cornerRadius) + effectView.clipsToBounds = !cornerRadius.isZero + } + self.effectView = effectView + self.insertSubview(effectView, at: 0) + } + } else if let effectView = self.effectView { + self.effectView = nil + effectView.removeFromSuperview() + } + } + + public func updateColor(color: UIColor, enableBlur: Bool? = nil, forceKeepBlur: Bool = false, transition: ContainedViewLayoutTransition) { + let effectiveEnableBlur = enableBlur ?? self.enableBlur + + if self._color == color && self.enableBlur == effectiveEnableBlur { + return + } + self._color = color + self.enableBlur = effectiveEnableBlur + + if sharedIsReduceTransparencyEnabled { + transition.updateBackgroundColor(layer: self.backgroundView.layer, color: color.withAlphaComponent(1.0)) + } else { + transition.updateBackgroundColor(layer: self.backgroundView.layer, color: color) + } + + self.updateBackgroundBlur(forceKeepBlur: forceKeepBlur) + } + + public func update(size: CGSize, cornerRadius: CGFloat = 0.0, maskedCorners: CACornerMask = [.layerMaxXMaxYCorner, .layerMaxXMinYCorner, .layerMinXMaxYCorner, .layerMinXMinYCorner], transition: ContainedViewLayoutTransition) { + self.validLayout = (size, cornerRadius) + + let contentFrame = CGRect(origin: CGPoint(), size: size) + transition.updateFrame(view: self.backgroundView, frame: contentFrame, beginWithCurrentState: true) + if let effectView = self.effectView, effectView.frame != contentFrame { + transition.updateFrame(layer: effectView.layer, frame: contentFrame, beginWithCurrentState: true) + if let sublayers = effectView.layer.sublayers { + for sublayer in sublayers { + transition.updateFrame(layer: sublayer, frame: contentFrame, beginWithCurrentState: true) + } + } + } + + if #available(iOS 11.0, *) { + self.backgroundView.layer.maskedCorners = maskedCorners + } + + transition.updateCornerRadius(layer: self.backgroundView.layer, cornerRadius: cornerRadius) + if let effectView = self.effectView { + transition.updateCornerRadius(layer: effectView.layer, cornerRadius: cornerRadius) + effectView.clipsToBounds = !cornerRadius.isZero + + if #available(iOS 11.0, *) { + effectView.layer.maskedCorners = maskedCorners + } + } + } + + public func update(size: CGSize, cornerRadius: CGFloat = 0.0, animator: ControlledTransitionAnimator) { + self.validLayout = (size, cornerRadius) + + let contentFrame = CGRect(origin: CGPoint(), size: size) + animator.updateFrame(layer: self.backgroundView.layer, frame: contentFrame, completion: nil) + if let effectView = self.effectView, effectView.frame != contentFrame { + animator.updateFrame(layer: effectView.layer, frame: contentFrame, completion: nil) + if let sublayers = effectView.layer.sublayers { + for sublayer in sublayers { + animator.updateFrame(layer: sublayer, frame: contentFrame, completion: nil) + } + } + } + + animator.updateCornerRadius(layer: self.backgroundView.layer, cornerRadius: cornerRadius, completion: nil) + if let effectView = self.effectView { + animator.updateCornerRadius(layer: effectView.layer, cornerRadius: cornerRadius, completion: nil) + effectView.clipsToBounds = !cornerRadius.isZero + } + } +} diff --git a/submodules/Display/Source/NavigationBar.swift b/submodules/Display/Source/NavigationBar.swift index 03f4f71898..99c8035ac8 100644 --- a/submodules/Display/Source/NavigationBar.swift +++ b/submodules/Display/Source/NavigationBar.swift @@ -4,50 +4,6 @@ import SwiftSignalKit private var backArrowImageCache: [Int32: UIImage] = [:] -open class SparseNode: ASDisplayNode { - override open func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { - if self.alpha.isZero { - return nil - } - for view in self.view.subviews.reversed() { - if let result = view.hitTest(self.view.convert(point, to: view), with: event), result.isUserInteractionEnabled { - return result - } - } - - if !self.bounds.inset(by: self.hitTestSlop).contains(point) { - return nil - } - - let result = super.hitTest(point, with: event) - if result != self.view { - return result - } else { - return nil - } - } -} - -open class SparseContainerView: UIView { - override public func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { - if self.alpha.isZero { - return nil - } - for view in self.subviews.reversed() { - if let result = view.hitTest(self.convert(point, to: view), with: event), result.isUserInteractionEnabled { - return result - } - } - - let result = super.hitTest(point, with: event) - if result != self { - return result - } else { - return nil - } - } -} - public final class NavigationBarTheme { public static func generateBackArrowImage(color: UIColor) -> UIImage? { return generateImage(CGSize(width: 13.0, height: 22.0), rotatedContext: { size, context in @@ -60,6 +16,7 @@ public final class NavigationBarTheme { }) } + public let overallDarkAppearance: Bool public let buttonColor: UIColor public let disabledButtonColor: UIColor public let primaryTextColor: UIColor @@ -70,8 +27,11 @@ public final class NavigationBarTheme { public let badgeBackgroundColor: UIColor public let badgeStrokeColor: UIColor public let badgeTextColor: UIColor + public let edgeEffectColor: UIColor? + public let style: NavigationBar.Style - public init(buttonColor: UIColor, disabledButtonColor: UIColor, primaryTextColor: UIColor, backgroundColor: UIColor, opaqueBackgroundColor: UIColor? = nil, enableBackgroundBlur: Bool, separatorColor: UIColor, badgeBackgroundColor: UIColor, badgeStrokeColor: UIColor, badgeTextColor: UIColor) { + public init(overallDarkAppearance: Bool, buttonColor: UIColor, disabledButtonColor: UIColor, primaryTextColor: UIColor, backgroundColor: UIColor, opaqueBackgroundColor: UIColor? = nil, enableBackgroundBlur: Bool, separatorColor: UIColor, badgeBackgroundColor: UIColor, badgeStrokeColor: UIColor, badgeTextColor: UIColor, edgeEffectColor: UIColor? = nil, style: NavigationBar.Style = .legacy) { + self.overallDarkAppearance = overallDarkAppearance self.buttonColor = buttonColor self.disabledButtonColor = disabledButtonColor self.primaryTextColor = primaryTextColor @@ -82,14 +42,16 @@ public final class NavigationBarTheme { self.badgeBackgroundColor = badgeBackgroundColor self.badgeStrokeColor = badgeStrokeColor self.badgeTextColor = badgeTextColor + self.edgeEffectColor = edgeEffectColor + self.style = style } public func withUpdatedBackgroundColor(_ color: UIColor) -> NavigationBarTheme { - return NavigationBarTheme(buttonColor: self.buttonColor, disabledButtonColor: self.disabledButtonColor, primaryTextColor: self.primaryTextColor, backgroundColor: color, opaqueBackgroundColor: self.opaqueBackgroundColor, enableBackgroundBlur: false, separatorColor: self.separatorColor, badgeBackgroundColor: self.badgeBackgroundColor, badgeStrokeColor: self.badgeStrokeColor, badgeTextColor: self.badgeTextColor) + return NavigationBarTheme(overallDarkAppearance: self.overallDarkAppearance, buttonColor: self.buttonColor, disabledButtonColor: self.disabledButtonColor, primaryTextColor: self.primaryTextColor, backgroundColor: color, opaqueBackgroundColor: self.opaqueBackgroundColor, enableBackgroundBlur: false, separatorColor: self.separatorColor, badgeBackgroundColor: self.badgeBackgroundColor, badgeStrokeColor: self.badgeStrokeColor, badgeTextColor: self.badgeTextColor, edgeEffectColor: self.edgeEffectColor, style: self.style) } public func withUpdatedSeparatorColor(_ color: UIColor) -> NavigationBarTheme { - return NavigationBarTheme(buttonColor: self.buttonColor, disabledButtonColor: self.disabledButtonColor, primaryTextColor: self.primaryTextColor, backgroundColor: self.backgroundColor, opaqueBackgroundColor: self.opaqueBackgroundColor, enableBackgroundBlur: self.enableBackgroundBlur, separatorColor: color, badgeBackgroundColor: self.badgeBackgroundColor, badgeStrokeColor: self.badgeStrokeColor, badgeTextColor: self.badgeTextColor) + return NavigationBarTheme(overallDarkAppearance: self.overallDarkAppearance, buttonColor: self.buttonColor, disabledButtonColor: self.disabledButtonColor, primaryTextColor: self.primaryTextColor, backgroundColor: self.backgroundColor, opaqueBackgroundColor: self.opaqueBackgroundColor, enableBackgroundBlur: self.enableBackgroundBlur, separatorColor: color, badgeBackgroundColor: self.badgeBackgroundColor, badgeStrokeColor: self.badgeStrokeColor, badgeTextColor: self.badgeTextColor, edgeEffectColor: self.edgeEffectColor, style: self.style) } } @@ -135,1700 +97,97 @@ public enum NavigationPreviousAction: Equatable { } } -private var sharedIsReduceTransparencyEnabled = UIAccessibility.isReduceTransparencyEnabled - -public final class NavigationBackgroundNode: ASDisplayNode { - private var _color: UIColor - - public var color: UIColor { - return self._color - } - - private var enableBlur: Bool - private var enableSaturation: Bool - private var customBlurRadius: CGFloat? - - public var effectView: UIVisualEffectView? - private let backgroundNode: ASDisplayNode - - public var backgroundView: UIView { - return self.backgroundNode.view - } - - private var validLayout: (CGSize, CGFloat)? - - public var backgroundCornerRadius: CGFloat { - if let (_, cornerRadius) = self.validLayout { - return cornerRadius - } else { - return 0.0 - } - } - - public init(color: UIColor, enableBlur: Bool = true, enableSaturation: Bool = true, customBlurRadius: CGFloat? = nil) { - self._color = .clear - self.enableBlur = enableBlur - self.enableSaturation = enableSaturation - self.customBlurRadius = customBlurRadius - - self.backgroundNode = ASDisplayNode() - - super.init() - - self.addSubnode(self.backgroundNode) - - self.updateColor(color: color, transition: .immediate) - } - - - public override func didLoad() { - super.didLoad() - - if self.scheduledUpdate { - self.scheduledUpdate = false - self.updateBackgroundBlur(forceKeepBlur: false) - } - } - - private var scheduledUpdate = false - - private func updateBackgroundBlur(forceKeepBlur: Bool) { - guard self.isNodeLoaded else { - self.scheduledUpdate = true - return - } - if self.enableBlur && !sharedIsReduceTransparencyEnabled && ((self._color.alpha > .ulpOfOne && self._color.alpha < 0.95) || forceKeepBlur) { - if self.effectView == nil { - let effectView = UIVisualEffectView(effect: UIBlurEffect(style: .light)) - - for subview in effectView.subviews { - if subview.description.contains("VisualEffectSubview") { - subview.isHidden = true - } - } - - if let sublayer = effectView.layer.sublayers?[0], let filters = sublayer.filters { - sublayer.backgroundColor = nil - sublayer.isOpaque = false - var allowedKeys: [String] = [ - "gaussianBlur" - ] - if self.enableSaturation { - allowedKeys.append("colorSaturate") - } - sublayer.filters = filters.filter { filter in - guard let filter = filter as? NSObject else { - return true - } - let filterName = String(describing: filter) - if !allowedKeys.contains(filterName) { - return false - } - if let customBlurRadius = self.customBlurRadius, filterName == "gaussianBlur" { - filter.setValue(customBlurRadius as NSNumber, forKey: "inputRadius") - } - return true - } - } - - if let (size, cornerRadius) = self.validLayout { - effectView.frame = CGRect(origin: CGPoint(), size: size) - ContainedViewLayoutTransition.immediate.updateCornerRadius(layer: effectView.layer, cornerRadius: cornerRadius) - effectView.clipsToBounds = !cornerRadius.isZero - } - self.effectView = effectView - self.view.insertSubview(effectView, at: 0) - } - } else if let effectView = self.effectView { - self.effectView = nil - effectView.removeFromSuperview() - } - } - - public func updateColor(color: UIColor, enableBlur: Bool? = nil, enableSaturation: Bool? = nil, forceKeepBlur: Bool = false, transition: ContainedViewLayoutTransition) { - let effectiveEnableBlur = enableBlur ?? self.enableBlur - let effectiveEnableSaturation = enableSaturation ?? self.enableSaturation - - if self._color.isEqual(color) && self.enableBlur == effectiveEnableBlur && self.enableSaturation == effectiveEnableSaturation { - return - } - self._color = color - self.enableBlur = effectiveEnableBlur - self.enableSaturation = effectiveEnableSaturation - - if sharedIsReduceTransparencyEnabled { - transition.updateBackgroundColor(node: self.backgroundNode, color: self._color.withAlphaComponent(1.0)) - } else { - transition.updateBackgroundColor(node: self.backgroundNode, color: self._color) - } - - self.updateBackgroundBlur(forceKeepBlur: forceKeepBlur) - } - - public func update(size: CGSize, cornerRadius: CGFloat = 0.0, transition: ContainedViewLayoutTransition, beginWithCurrentState: Bool = true) { - self.validLayout = (size, cornerRadius) - - let contentFrame = CGRect(origin: CGPoint(), size: size) - transition.updateFrame(node: self.backgroundNode, frame: contentFrame, beginWithCurrentState: true) - if let effectView = self.effectView, effectView.frame != contentFrame { - transition.updateFrame(layer: effectView.layer, frame: contentFrame, beginWithCurrentState: true) - if let sublayers = effectView.layer.sublayers { - for sublayer in sublayers { - transition.updateFrame(layer: sublayer, frame: contentFrame, beginWithCurrentState: true) - } - } - } - - transition.updateCornerRadius(node: self.backgroundNode, cornerRadius: cornerRadius) - if let effectView = self.effectView { - transition.updateCornerRadius(layer: effectView.layer, cornerRadius: cornerRadius) - effectView.clipsToBounds = !cornerRadius.isZero - } - } - - public func update(size: CGSize, cornerRadius: CGFloat = 0.0, animator: ControlledTransitionAnimator) { - self.validLayout = (size, cornerRadius) - - let contentFrame = CGRect(origin: CGPoint(), size: size) - animator.updateFrame(layer: self.backgroundNode.layer, frame: contentFrame, completion: nil) - if let effectView = self.effectView, effectView.frame != contentFrame { - animator.updateFrame(layer: effectView.layer, frame: contentFrame, completion: nil) - if let sublayers = effectView.layer.sublayers { - for sublayer in sublayers { - animator.updateFrame(layer: sublayer, frame: contentFrame, completion: nil) - } - } - } - - animator.updateCornerRadius(layer: self.backgroundNode.layer, cornerRadius: cornerRadius, completion: nil) - if let effectView = self.effectView { - animator.updateCornerRadius(layer: effectView.layer, cornerRadius: cornerRadius, completion: nil) - effectView.clipsToBounds = !cornerRadius.isZero - } - } +public enum NavigationBarStyle { + case legacy + case glass } -open class BlurredBackgroundView: UIView { - private var _color: UIColor? - - private var enableBlur: Bool - private var customBlurRadius: CGFloat? - - public private(set) var effectView: UIVisualEffectView? - private let backgroundView: UIView - - private var validLayout: (CGSize, CGFloat)? +public func navigationBarBackArrowImage(color: UIColor) -> UIImage? { + var red: CGFloat = 0.0 + var green: CGFloat = 0.0 + var blue: CGFloat = 0.0 + var alpha: CGFloat = 0.0 + color.getRed(&red, green: &green, blue: &blue, alpha: &alpha) - public var backgroundCornerRadius: CGFloat { - if let (_, cornerRadius) = self.validLayout { - return cornerRadius - } else { - return 0.0 - } - } - - public init(color: UIColor?, enableBlur: Bool = true, customBlurRadius: CGFloat? = nil) { - self._color = nil - self.enableBlur = enableBlur - self.customBlurRadius = customBlurRadius - - self.backgroundView = UIView() - - super.init(frame: CGRect()) - - self.addSubview(self.backgroundView) - - if let color = color { - self.updateColor(color: color, transition: .immediate) - } - } - - required public init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - private func updateBackgroundBlur(forceKeepBlur: Bool) { - if let color = self._color, self.enableBlur && !sharedIsReduceTransparencyEnabled && ((color.alpha > .ulpOfOne && color.alpha < 0.95) || forceKeepBlur) { - if self.effectView == nil { - let effectView = UIVisualEffectView(effect: UIBlurEffect(style: .light)) - - for subview in effectView.subviews { - if subview.description.contains("VisualEffectSubview") { - subview.isHidden = true - } - } - - if let sublayer = effectView.layer.sublayers?[0], let filters = sublayer.filters { - sublayer.backgroundColor = nil - sublayer.isOpaque = false - //sublayer.setValue(true as NSNumber, forKey: "allowsInPlaceFiltering") - let allowedKeys: [String] = [ - "colorSaturate", - "gaussianBlur" - ] - sublayer.filters = filters.filter { filter in - guard let filter = filter as? NSObject else { - return true - } - let filterName = String(describing: filter) - if !allowedKeys.contains(filterName) { - return false - } - if let customBlurRadius = self.customBlurRadius, filterName == "gaussianBlur" { - filter.setValue(customBlurRadius as NSNumber, forKey: "inputRadius") - } - return true - } - } - - if let (size, cornerRadius) = self.validLayout { - effectView.frame = CGRect(origin: CGPoint(), size: size) - ContainedViewLayoutTransition.immediate.updateCornerRadius(layer: effectView.layer, cornerRadius: cornerRadius) - effectView.clipsToBounds = !cornerRadius.isZero - } - self.effectView = effectView - self.insertSubview(effectView, at: 0) - } - } else if let effectView = self.effectView { - self.effectView = nil - effectView.removeFromSuperview() - } - } - - public func updateColor(color: UIColor, enableBlur: Bool? = nil, forceKeepBlur: Bool = false, transition: ContainedViewLayoutTransition) { - let effectiveEnableBlur = enableBlur ?? self.enableBlur - - if self._color == color && self.enableBlur == effectiveEnableBlur { - return - } - self._color = color - self.enableBlur = effectiveEnableBlur - - if sharedIsReduceTransparencyEnabled { - transition.updateBackgroundColor(layer: self.backgroundView.layer, color: color.withAlphaComponent(1.0)) - } else { - transition.updateBackgroundColor(layer: self.backgroundView.layer, color: color) - } - - self.updateBackgroundBlur(forceKeepBlur: forceKeepBlur) - } - - public func update(size: CGSize, cornerRadius: CGFloat = 0.0, maskedCorners: CACornerMask = [.layerMaxXMaxYCorner, .layerMaxXMinYCorner, .layerMinXMaxYCorner, .layerMinXMinYCorner], transition: ContainedViewLayoutTransition) { - self.validLayout = (size, cornerRadius) - - let contentFrame = CGRect(origin: CGPoint(), size: size) - transition.updateFrame(view: self.backgroundView, frame: contentFrame, beginWithCurrentState: true) - if let effectView = self.effectView, effectView.frame != contentFrame { - transition.updateFrame(layer: effectView.layer, frame: contentFrame, beginWithCurrentState: true) - if let sublayers = effectView.layer.sublayers { - for sublayer in sublayers { - transition.updateFrame(layer: sublayer, frame: contentFrame, beginWithCurrentState: true) - } - } - } - - if #available(iOS 11.0, *) { - self.backgroundView.layer.maskedCorners = maskedCorners - } - - transition.updateCornerRadius(layer: self.backgroundView.layer, cornerRadius: cornerRadius) - if let effectView = self.effectView { - transition.updateCornerRadius(layer: effectView.layer, cornerRadius: cornerRadius) - effectView.clipsToBounds = !cornerRadius.isZero - - if #available(iOS 11.0, *) { - effectView.layer.maskedCorners = maskedCorners - } - } - } - - public func update(size: CGSize, cornerRadius: CGFloat = 0.0, animator: ControlledTransitionAnimator) { - self.validLayout = (size, cornerRadius) - - let contentFrame = CGRect(origin: CGPoint(), size: size) - animator.updateFrame(layer: self.backgroundView.layer, frame: contentFrame, completion: nil) - if let effectView = self.effectView, effectView.frame != contentFrame { - animator.updateFrame(layer: effectView.layer, frame: contentFrame, completion: nil) - if let sublayers = effectView.layer.sublayers { - for sublayer in sublayers { - animator.updateFrame(layer: sublayer, frame: contentFrame, completion: nil) - } - } - } - - animator.updateCornerRadius(layer: self.backgroundView.layer, cornerRadius: cornerRadius, completion: nil) - if let effectView = self.effectView { - animator.updateCornerRadius(layer: effectView.layer, cornerRadius: cornerRadius, completion: nil) - effectView.clipsToBounds = !cornerRadius.isZero - } - } -} - -public protocol NavigationBarHeaderView: UIView { -} - -private final class NavigationBackgroundCutoutView: UIView { - private let topLayer: SimpleLayer - private let rightLayer: SimpleLayer - - weak var targetNode: ASDisplayNode? - - override init(frame: CGRect) { - self.topLayer = SimpleLayer() - self.topLayer.backgroundColor = UIColor.white.cgColor - - self.rightLayer = SimpleLayer() - self.rightLayer.backgroundColor = UIColor.white.cgColor - - super.init(frame: frame) - - self.layer.addSublayer(self.topLayer) - self.layer.addSublayer(self.rightLayer) - } - - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - func update(size: CGSize, cutout: CGSize?, transition: ContainedViewLayoutTransition) { - let cutout = cutout ?? CGSize() - - let topFrame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: size.width, height: cutout.height)) - let rightFrame = CGRect(origin: CGPoint(x: cutout.width, y: 0.0), size: CGSize(width: max(0.0, size.width - cutout.width), height: size.height)) - - if self.topLayer.frame != topFrame || self.rightLayer.frame != rightFrame { - if cutout.width != 0.0 && cutout.height != 0.0 { - self.targetNode?.view.mask = self - //self.targetNode?.view.addSubview(self) - } - - transition.updateFrame(layer: self.topLayer, frame: topFrame) - transition.updateFrame(layer: self.rightLayer, frame: rightFrame, completion: { [weak self] completed in - guard let self, completed else { - return - } - - if !(cutout.width != 0.0 && cutout.height != 0.0) { - self.targetNode?.view.mask = nil - //self.removeFromSuperview() - } - }) - } - } -} - -open class NavigationBar: ASDisplayNode { - public static var defaultSecondaryContentHeight: CGFloat { - return 38.0 - } - - public static func backArrowImage(color: UIColor) -> UIImage? { - var red: CGFloat = 0.0 - var green: CGFloat = 0.0 - var blue: CGFloat = 0.0 - var alpha: CGFloat = 0.0 - color.getRed(&red, green: &green, blue: &blue, alpha: &alpha) - - let key = (Int32(alpha * 255.0) << 24) | (Int32(red * 255.0) << 16) | (Int32(green * 255.0) << 8) | Int32(blue * 255.0) - if let image = backArrowImageCache[key] { + let key = (Int32(alpha * 255.0) << 24) | (Int32(red * 255.0) << 16) | (Int32(green * 255.0) << 8) | Int32(blue * 255.0) + if let image = backArrowImageCache[key] { + return image + } else { + if let image = NavigationBarTheme.generateBackArrowImage(color: color) { + backArrowImageCache[key] = image return image - } else { - if let image = NavigationBarTheme.generateBackArrowImage(color: color) { - backArrowImageCache[key] = image - return image - } else { - return nil - } - } - } - - public static let thinBackArrowImage = generateTintedImage(image: UIImage(bundleImageName: "Navigation/BackArrow"), color: .white)?.withRenderingMode(.alwaysTemplate) - - public static let titleFont = Font.with(size: 17.0, design: .regular, weight: .semibold, traits: [.monospacedNumbers]) - - var presentationData: NavigationBarPresentationData - - private var validLayout: (size: CGSize, defaultHeight: CGFloat, additionalTopHeight: CGFloat, additionalContentHeight: CGFloat, additionalBackgroundHeight: CGFloat, additionalCutout: CGSize?, leftInset: CGFloat, rightInset: CGFloat, appearsHidden: Bool, isLandscape: Bool)? - private var requestedLayout: Bool = false - var requestContainerLayout: (ContainedViewLayoutTransition) -> Void = { _ in } - - public var backPressed: () -> () = { } - - public var userInfo: Any? - public var makeCustomTransitionNode: ((NavigationBar, Bool) -> CustomNavigationTransitionNode?)? - public var allowsCustomTransition: (() -> Bool)? - - public var customSetContentNode: ((NavigationBarContentNode?, Bool) -> Void)? - - private var collapsed: Bool { - get { - return self.frame.size.height.isLess(than: 44.0) - } - } - - public let stripeNode: ASDisplayNode - public let clippingNode: SparseNode - private let buttonsContainerNode: ASDisplayNode - - public private(set) var contentNode: NavigationBarContentNode? - public private(set) var secondaryContentNode: ASDisplayNode? - public var secondaryContentNodeDisplayFraction: CGFloat = 1.0 - - private var itemTitleListenerKey: Int? - private var itemTitleViewListenerKey: Int? - - private var itemLeftButtonListenerKey: Int? - private var itemLeftButtonSetEnabledListenerKey: Int? - - private var itemRightButtonListenerKey: Int? - private var itemRightButtonsListenerKey: Int? - - private var itemBadgeListenerKey: Int? - - private var hintAnimateTitleNodeOnNextLayout: Bool = false - - private var _item: UINavigationItem? - public var item: UINavigationItem? { - get { - return self._item - } set(value) { - if let previousValue = self._item { - if let itemTitleListenerKey = self.itemTitleListenerKey { - previousValue.removeSetTitleListener(itemTitleListenerKey) - self.itemTitleListenerKey = nil - } - - if let itemLeftButtonListenerKey = self.itemLeftButtonListenerKey { - previousValue.removeSetLeftBarButtonItemListener(itemLeftButtonListenerKey) - self.itemLeftButtonListenerKey = nil - } - - if let itemLeftButtonSetEnabledListenerKey = self.itemLeftButtonSetEnabledListenerKey { - previousValue.leftBarButtonItem?.removeSetEnabledListener(itemLeftButtonSetEnabledListenerKey) - self.itemLeftButtonSetEnabledListenerKey = nil - } - - if let itemRightButtonListenerKey = self.itemRightButtonListenerKey { - previousValue.removeSetRightBarButtonItemListener(itemRightButtonListenerKey) - self.itemRightButtonListenerKey = nil - } - - if let itemRightButtonsListenerKey = self.itemRightButtonsListenerKey { - previousValue.removeSetMultipleRightBarButtonItemsListener(itemRightButtonsListenerKey) - self.itemRightButtonsListenerKey = nil - } - - if let itemBadgeListenerKey = self.itemBadgeListenerKey { - previousValue.removeSetBadgeListener(itemBadgeListenerKey) - self.itemBadgeListenerKey = nil - } - } - self._item = value - - self.leftButtonNode.removeFromSupernode() - self.rightButtonNode.removeFromSupernode() - - if let item = value { - self.title = item.title - self.itemTitleListenerKey = item.addSetTitleListener { [weak self] text, animated in - if let strongSelf = self { - let animateIn = animated && (strongSelf.title?.isEmpty ?? true) - strongSelf.title = text - if animateIn { - strongSelf.titleNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25) - } - } - } - - self.titleView = item.titleView - self.itemTitleViewListenerKey = item.addSetTitleViewListener { [weak self] titleView in - if let strongSelf = self { - strongSelf.titleView = titleView - } - } - - self.itemLeftButtonListenerKey = item.addSetLeftBarButtonItemListener { [weak self] previousItem, _, animated in - if let strongSelf = self { - if let itemLeftButtonSetEnabledListenerKey = strongSelf.itemLeftButtonSetEnabledListenerKey { - previousItem?.removeSetEnabledListener(itemLeftButtonSetEnabledListenerKey) - strongSelf.itemLeftButtonSetEnabledListenerKey = nil - } - - strongSelf.updateLeftButton(animated: animated) - strongSelf.invalidateCalculatedLayout() - strongSelf.requestLayout() - } - } - - self.itemRightButtonListenerKey = item.addSetRightBarButtonItemListener { [weak self] previousItem, currentItem, animated in - if let strongSelf = self { - 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() - } - } - - self.itemBadgeListenerKey = item.addSetBadgeListener { [weak self] text in - if let strongSelf = self { - strongSelf.updateBadgeText(text: text) - } - } - self.updateBadgeText(text: item.badge) - - self.updateLeftButton(animated: false) - self.updateRightButton(animated: false) - } else { - self.title = nil - self.updateLeftButton(animated: false) - self.updateRightButton(animated: false) - } - self.invalidateCalculatedLayout() - self.requestLayout() - } - } - - public var customBackButtonText: String? - - private var title: String? { - didSet { - if let title = self.title { - self.titleNode.attributedText = NSAttributedString(string: title, font: NavigationBar.titleFont, textColor: self.presentationData.theme.primaryTextColor) - self.titleNode.accessibilityLabel = title - if self.titleNode.supernode == nil { - self.buttonsContainerNode.addSubnode(self.titleNode) - } - } else { - self.titleNode.removeFromSupernode() - } - - self.updateAccessibilityElements() - self.invalidateCalculatedLayout() - self.requestLayout() - } - } - - public private(set) var titleView: UIView? { - didSet { - if let oldValue = oldValue { - oldValue.removeFromSuperview() - } - - if let titleView = self.titleView { - self.buttonsContainerNode.view.addSubview(titleView) - } - - self.invalidateCalculatedLayout() - self.requestLayout() - } - } - - public var customHeaderContentView: NavigationBarHeaderView? { - didSet { - if self.customHeaderContentView !== oldValue { - self.customHeaderContentView?.removeFromSuperview() - - if let customHeaderContentView = self.customHeaderContentView { - self.buttonsContainerNode.view.addSubview(customHeaderContentView) - self.backButtonNode.isHidden = true - self.backButtonArrow.isHidden = true - } else { - self.backButtonNode.isHidden = false - self.backButtonArrow.isHidden = false - } - } - } - } - - public var layoutSuspended: Bool = false - - private let titleNode: ImmediateTextNode - - var previousItemListenerKey: Int? - var previousItemBackListenerKey: Int? - - private func updateAccessibilityElements() { - } - - override open var accessibilityElements: [Any]? { - get { - var accessibilityElements: [Any] = [] - if self.backButtonNode.supernode != nil { - addAccessibilityChildren(of: self.backButtonNode, container: self, to: &accessibilityElements) - } - if self.leftButtonNode.supernode != nil { - addAccessibilityChildren(of: self.leftButtonNode, container: self, to: &accessibilityElements) - } - if self.titleNode.supernode != nil { - addAccessibilityChildren(of: self.titleNode, container: self, to: &accessibilityElements) - accessibilityElements.append(self.titleNode) - } - if let titleView = self.titleView, titleView.superview != nil { - titleView.accessibilityFrame = UIAccessibility.convertToScreenCoordinates(titleView.bounds, in: titleView) - accessibilityElements.append(titleView) - } - if self.rightButtonNode.supernode != nil { - addAccessibilityChildren(of: self.rightButtonNode, container: self, to: &accessibilityElements) - } - if let customHeaderContentView = self.customHeaderContentView, customHeaderContentView.superview != nil { - customHeaderContentView.accessibilityFrame = UIAccessibility.convertToScreenCoordinates(customHeaderContentView.bounds, in: customHeaderContentView) - accessibilityElements.append(customHeaderContentView) - } - if let contentNode = self.contentNode { - addAccessibilityChildren(of: contentNode, container: self, to: &accessibilityElements) - } - if let secondaryContentNode = self.secondaryContentNode { - addAccessibilityChildren(of: secondaryContentNode, container: self, to: &accessibilityElements) - } - return accessibilityElements - } set(value) { - } - } - - override open func didLoad() { - super.didLoad() - - self.updateAccessibilityElements() - } - - public var enableAutomaticBackButton: Bool = true - - var _previousItem: NavigationPreviousAction? - public internal(set) var previousItem: NavigationPreviousAction? { - get { - if !self.enableAutomaticBackButton { - return nil - } - return self._previousItem - } set(value) { - if !self.enableAutomaticBackButton { - self._previousItem = nil - return - } - if self._previousItem != value { - if let previousValue = self._previousItem, case let .item(itemValue) = previousValue { - if let previousItemListenerKey = self.previousItemListenerKey { - itemValue.removeSetTitleListener(previousItemListenerKey) - self.previousItemListenerKey = nil - } - if let previousItemBackListenerKey = self.previousItemBackListenerKey { - itemValue.removeSetBackBarButtonItemListener(previousItemBackListenerKey) - self.previousItemBackListenerKey = nil - } - } - self._previousItem = value - - if let previousItem = value { - switch previousItem { - case let .item(itemValue): - self.previousItemListenerKey = itemValue.addSetTitleListener { [weak self] _, _ in - if let strongSelf = self, let previousItem = strongSelf.previousItem, case let .item(itemValue) = previousItem { - if let customBackButtonText = strongSelf.customBackButtonText { - strongSelf.backButtonNode.updateManualText(customBackButtonText, isBack: true) - } else if let backBarButtonItem = itemValue.backBarButtonItem { - strongSelf.backButtonNode.updateManualText(backBarButtonItem.title ?? "", isBack: true) - } else { - strongSelf.backButtonNode.updateManualText(itemValue.title ?? "", isBack: true) - } - strongSelf.invalidateCalculatedLayout() - strongSelf.requestLayout() - } - } - - self.previousItemBackListenerKey = itemValue.addSetBackBarButtonItemListener { [weak self] _, _, _ in - if let strongSelf = self, let previousItem = strongSelf.previousItem, case let .item(itemValue) = previousItem { - if let customBackButtonText = strongSelf.customBackButtonText { - strongSelf.backButtonNode.updateManualText(customBackButtonText, isBack: true) - } else if let backBarButtonItem = itemValue.backBarButtonItem { - strongSelf.backButtonNode.updateManualText(backBarButtonItem.title ?? "", isBack: true) - } else { - strongSelf.backButtonNode.updateManualText(itemValue.title ?? "", isBack: true) - } - strongSelf.invalidateCalculatedLayout() - strongSelf.requestLayout() - } - } - case .close: - break - } - } - self.updateLeftButton(animated: false) - - self.invalidateCalculatedLayout() - self.requestLayout() - } - } - } - - private func updateBadgeText(text: String?) { - let actualText = text ?? "" - if self.badgeNode.text != actualText { - self.badgeNode.text = actualText - self.badgeNode.isHidden = actualText.isEmpty - self.backButtonNode.manualAlpha = self.badgeNode.isHidden ? 1.0 : 0.0 - - self.invalidateCalculatedLayout() - self.requestLayout() - } - } - - private func updateLeftButton(animated: Bool) { - if let item = self.item { - var needsLeftButton = false - if let leftBarButtonItem = item.leftBarButtonItem, !leftBarButtonItem.backButtonAppearance { - needsLeftButton = true - } else if let previousItem = self.previousItem, case .close = previousItem { - needsLeftButton = true - } - - if needsLeftButton { - if animated { - if self.backButtonNode.view.superview != nil { - if let snapshotView = self.backButtonNode.view.snapshotContentTree() { - snapshotView.frame = self.backButtonNode.frame - self.backButtonNode.view.superview?.insertSubview(snapshotView, aboveSubview: self.backButtonNode.view) - snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15, removeOnCompletion: false, completion: { [weak snapshotView] _ in - snapshotView?.removeFromSuperview() - }) - } - } - - if self.backButtonArrow.view.superview != nil { - if let snapshotView = self.backButtonArrow.view.snapshotContentTree() { - snapshotView.frame = self.backButtonArrow.frame - self.backButtonArrow.view.superview?.insertSubview(snapshotView, aboveSubview: self.backButtonArrow.view) - snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15, removeOnCompletion: false, completion: { [weak snapshotView] _ in - snapshotView?.removeFromSuperview() - }) - } - } - - if self.badgeNode.view.superview != nil { - if let snapshotView = self.badgeNode.view.snapshotContentTree() { - snapshotView.frame = self.badgeNode.frame - self.badgeNode.view.superview?.insertSubview(snapshotView, aboveSubview: self.badgeNode.view) - snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15, removeOnCompletion: false, completion: { [weak snapshotView] _ in - snapshotView?.removeFromSuperview() - }) - } - } - } - - self.backButtonNode.removeFromSupernode() - self.backButtonArrow.removeFromSupernode() - self.badgeNode.removeFromSupernode() - - if let leftBarButtonItem = item.leftBarButtonItem { - self.leftButtonNode.updateItems([], animated: animated) - self.leftButtonNode.updateItems([leftBarButtonItem], animated: animated) - } else { - self.leftButtonNode.updateItems([], animated: animated) - self.leftButtonNode.updateItems([UIBarButtonItem(title: self.presentationData.strings.close, style: .plain, target: nil, action: nil)], animated: animated) - } - - if self.leftButtonNode.supernode == nil { - self.buttonsContainerNode.addSubnode(self.leftButtonNode) - } - - if animated { - self.leftButtonNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25) - } - } else { - if animated, self.leftButtonNode.view.superview != nil { - if let snapshotView = self.leftButtonNode.view.snapshotContentTree() { - snapshotView.frame = self.leftButtonNode.frame - self.leftButtonNode.view.superview?.insertSubview(snapshotView, aboveSubview: self.leftButtonNode.view) - snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15, removeOnCompletion: false, completion: { [weak snapshotView] _ in - snapshotView?.removeFromSuperview() - }) - } - } - self.leftButtonNode.removeFromSupernode() - - var backTitle: String? - if let customBackButtonText = self.customBackButtonText { - backTitle = customBackButtonText - } else if let leftBarButtonItem = item.leftBarButtonItem, leftBarButtonItem.backButtonAppearance { - backTitle = leftBarButtonItem.title - } else if let previousItem = self.previousItem { - switch previousItem { - case let .item(itemValue): - if let backBarButtonItem = itemValue.backBarButtonItem { - backTitle = backBarButtonItem.title ?? self.presentationData.strings.back - } else { - backTitle = itemValue.title ?? self.presentationData.strings.back - } - case .close: - backTitle = nil - } - } - - if let backTitle = backTitle { - self.backButtonNode.updateManualText(backTitle, isBack: true) - if self.backButtonNode.supernode == nil { - self.buttonsContainerNode.addSubnode(self.backButtonNode) - self.buttonsContainerNode.addSubnode(self.backButtonArrow) - self.buttonsContainerNode.addSubnode(self.badgeNode) - } - - if animated { - self.backButtonNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25) - self.backButtonArrow.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25) - self.badgeNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25) - } - } else { - self.backButtonNode.removeFromSupernode() - } - } - } else { - self.leftButtonNode.removeFromSupernode() - self.backButtonNode.removeFromSupernode() - self.backButtonArrow.removeFromSupernode() - self.badgeNode.removeFromSupernode() - } - - self.updateAccessibilityElements() - } - - private func updateRightButton(animated: Bool) { - if let item = self.item { - var items: [UIBarButtonItem] = [] - if let rightBarButtonItems = item.rightBarButtonItems, !rightBarButtonItems.isEmpty { - items = rightBarButtonItems - } else if let rightBarButtonItem = item.rightBarButtonItem { - items = [rightBarButtonItem] - } - - self.rightButtonNodeUpdated = true - - if !items.isEmpty { - self.rightButtonNode.updateItems([], animated: animated) - self.rightButtonNode.updateItems(items, animated: animated) - if self.rightButtonNode.supernode == nil { - self.buttonsContainerNode.addSubnode(self.rightButtonNode) - } - } else { - if animated, self.rightButtonNode.view.superview != nil { - if let snapshotView = self.rightButtonNode.view.snapshotContentTree() { - snapshotView.frame = self.rightButtonNode.frame - self.rightButtonNode.view.superview?.insertSubview(snapshotView, aboveSubview: self.rightButtonNode.view) - snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15, removeOnCompletion: false, completion: { [weak snapshotView] _ in - snapshotView?.removeFromSuperview() - }) - } - } - self.rightButtonNode.removeFromSupernode() - } - } else { - if animated, self.rightButtonNode.view.superview != nil { - if let snapshotView = self.rightButtonNode.view.snapshotContentTree() { - snapshotView.frame = self.rightButtonNode.frame - self.rightButtonNode.view.superview?.insertSubview(snapshotView, aboveSubview: self.rightButtonNode.view) - snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15, removeOnCompletion: false, completion: { [weak snapshotView] _ in - snapshotView?.removeFromSuperview() - }) - } - } - self.rightButtonNode.removeFromSupernode() - } - - self.updateAccessibilityElements() - } - - public let backgroundNode: NavigationBackgroundNode - public let backButtonNode: NavigationButtonNode - public let badgeNode: NavigationBarBadgeNode - public let backButtonArrow: ASImageNode - public let leftButtonNode: NavigationButtonNode - public let rightButtonNode: NavigationButtonNode - private var rightButtonNodeUpdated: Bool = false - public let additionalContentNode: SparseNode - - private let navigationBackgroundCutoutView: NavigationBackgroundCutoutView - - public func reattachAdditionalContentNode() { - if self.additionalContentNode.supernode !== self { - self.insertSubnode(self.additionalContentNode, aboveSubnode: self.clippingNode) - } - } - - private var _transitionState: NavigationBarTransitionState? - var transitionState: NavigationBarTransitionState? { - get { - return self._transitionState - } set(value) { - let updateNodes = self._transitionState?.navigationBar !== value?.navigationBar - - self._transitionState = value - - if updateNodes { - if let transitionTitleNode = self.transitionTitleNode { - transitionTitleNode.removeFromSupernode() - self.transitionTitleNode = nil - } - - if let transitionBackButtonNode = self.transitionBackButtonNode { - transitionBackButtonNode.removeFromSupernode() - self.transitionBackButtonNode = nil - } - - if let transitionBackArrowNode = self.transitionBackArrowNode { - transitionBackArrowNode.removeFromSupernode() - self.transitionBackArrowNode = nil - } - - if let transitionBadgeNode = self.transitionBadgeNode { - transitionBadgeNode.removeFromSupernode() - self.transitionBadgeNode = nil - } - - if let value = value { - switch value.role { - case .top: - if let transitionTitleNode = value.navigationBar?.makeTransitionTitleNode(foregroundColor: self.presentationData.theme.primaryTextColor) { - self.transitionTitleNode = transitionTitleNode - if self.leftButtonNode.supernode != nil { - self.buttonsContainerNode.insertSubnode(transitionTitleNode, belowSubnode: self.leftButtonNode) - } else if self.backButtonNode.supernode != nil { - self.buttonsContainerNode.insertSubnode(transitionTitleNode, belowSubnode: self.backButtonNode) - } else { - self.buttonsContainerNode.addSubnode(transitionTitleNode) - } - } - case .bottom: - if let transitionBackButtonNode = value.navigationBar?.makeTransitionBackButtonNode(accentColor: self.presentationData.theme.buttonColor) { - self.transitionBackButtonNode = transitionBackButtonNode - self.buttonsContainerNode.addSubnode(transitionBackButtonNode) - } - if let transitionBackArrowNode = value.navigationBar?.makeTransitionBackArrowNode(accentColor: self.presentationData.theme.buttonColor) { - self.transitionBackArrowNode = transitionBackArrowNode - self.buttonsContainerNode.addSubnode(transitionBackArrowNode) - } - if let transitionBadgeNode = value.navigationBar?.makeTransitionBadgeNode() { - self.transitionBadgeNode = transitionBadgeNode - self.buttonsContainerNode.addSubnode(transitionBadgeNode) - } - } - } - } - - self.requestedLayout = true - self.layout() - } - } - - private var transitionTitleNode: ASDisplayNode? - private var transitionBackButtonNode: NavigationButtonNode? - private var transitionBackArrowNode: ASDisplayNode? - private var transitionBadgeNode: ASDisplayNode? - - public var secondaryContentHeight: CGFloat - - public init(presentationData: NavigationBarPresentationData) { - self.presentationData = presentationData - self.stripeNode = ASDisplayNode() - - self.titleNode = ImmediateTextNode() - self.titleNode.isAccessibilityElement = true - self.titleNode.accessibilityTraits = .header - - self.backButtonNode = NavigationButtonNode() - self.backButtonNode.hitTestSlop = UIEdgeInsets(top: 0.0, left: -20.0, bottom: 0.0, right: 0.0) - - self.badgeNode = NavigationBarBadgeNode(fillColor: self.presentationData.theme.buttonColor, strokeColor: self.presentationData.theme.buttonColor, textColor: self.presentationData.theme.badgeTextColor) - self.badgeNode.isUserInteractionEnabled = false - self.badgeNode.isHidden = true - self.backButtonArrow = ASImageNode() - self.backButtonArrow.displayWithoutProcessing = true - self.backButtonArrow.displaysAsynchronously = false - self.backButtonArrow.isUserInteractionEnabled = false - self.leftButtonNode = NavigationButtonNode() - self.rightButtonNode = NavigationButtonNode() - self.rightButtonNode.hitTestSlop = UIEdgeInsets(top: -4.0, left: -4.0, bottom: -4.0, right: -10.0) - - self.clippingNode = SparseNode() - self.clippingNode.clipsToBounds = true - - self.buttonsContainerNode = ASDisplayNode() - self.buttonsContainerNode.clipsToBounds = true - - self.backButtonNode.color = self.presentationData.theme.buttonColor - self.backButtonNode.disabledColor = self.presentationData.theme.disabledButtonColor - self.leftButtonNode.color = self.presentationData.theme.buttonColor - self.leftButtonNode.disabledColor = self.presentationData.theme.disabledButtonColor - self.rightButtonNode.color = self.presentationData.theme.buttonColor - self.rightButtonNode.disabledColor = self.presentationData.theme.disabledButtonColor - self.rightButtonNode.rippleColor = self.presentationData.theme.primaryTextColor.withAlphaComponent(0.05) - self.backButtonArrow.image = NavigationBar.backArrowImage(color: self.presentationData.theme.buttonColor) - if let title = self.title { - self.titleNode.attributedText = NSAttributedString(string: title, font: NavigationBar.titleFont, textColor: self.presentationData.theme.primaryTextColor) - self.titleNode.accessibilityLabel = title - } - self.stripeNode.backgroundColor = self.presentationData.theme.separatorColor - - self.backgroundNode = NavigationBackgroundNode(color: self.presentationData.theme.backgroundColor, enableBlur: self.presentationData.theme.enableBackgroundBlur) - self.additionalContentNode = SparseNode() - - self.secondaryContentHeight = NavigationBar.defaultSecondaryContentHeight - - self.navigationBackgroundCutoutView = NavigationBackgroundCutoutView(frame: CGRect()) - self.navigationBackgroundCutoutView.targetNode = self.backgroundNode - - super.init() - - self.addSubnode(self.backgroundNode) - self.addSubnode(self.buttonsContainerNode) - self.addSubnode(self.clippingNode) - self.addSubnode(self.additionalContentNode) - - self.backgroundColor = nil - self.isOpaque = false - - self.stripeNode.isLayerBacked = true - self.stripeNode.displaysAsynchronously = false - self.addSubnode(self.stripeNode) - - self.titleNode.displaysAsynchronously = false - self.titleNode.maximumNumberOfLines = 1 - self.titleNode.truncationType = .end - self.titleNode.isOpaque = false - - self.backButtonNode.highlightChanged = { [weak self] index, highlighted in - if let strongSelf = self, index == 0 { - strongSelf.backButtonArrow.alpha = (highlighted ? 0.4 : 1.0) - strongSelf.badgeNode.alpha = (highlighted ? 0.4 : 1.0) - } - } - self.backButtonNode.pressed = { [weak self] index in - if let strongSelf = self, index == 0 { - if let leftBarButtonItem = strongSelf.item?.leftBarButtonItem, leftBarButtonItem.backButtonAppearance { - leftBarButtonItem.performActionOnTarget() - } else { - strongSelf.backPressed() - } - } - } - - self.leftButtonNode.pressed = { [weak self] index in - if let item = self?.item { - if index == 0 { - if let leftBarButtonItem = item.leftBarButtonItem { - leftBarButtonItem.performActionOnTarget() - } else if let previousItem = self?.previousItem, case .close = previousItem { - self?.backPressed() - } - } - } - } - - 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() - } - } - } - } - - public var isBackgroundVisible: Bool { - return self.backgroundNode.alpha == 1.0 - } - - public func updateBackgroundAlpha(_ alpha: CGFloat, transition: ContainedViewLayoutTransition) { - let alpha = max(0.0, min(1.0, alpha)) - transition.updateAlpha(node: self.backgroundNode, alpha: alpha, delay: 0.15) - transition.updateAlpha(node: self.stripeNode, alpha: alpha, delay: 0.15) - } - - public func updatePresentationData(_ presentationData: NavigationBarPresentationData, transition: ContainedViewLayoutTransition = .immediate) { - if presentationData.theme !== self.presentationData.theme || presentationData.strings !== self.presentationData.strings { - self.presentationData = presentationData - - self.backgroundNode.updateColor(color: self.presentationData.theme.backgroundColor, transition: transition) - - self.backButtonNode.color = self.presentationData.theme.buttonColor - self.backButtonNode.disabledColor = self.presentationData.theme.disabledButtonColor - self.leftButtonNode.color = self.presentationData.theme.buttonColor - self.leftButtonNode.disabledColor = self.presentationData.theme.disabledButtonColor - self.rightButtonNode.color = self.presentationData.theme.buttonColor - self.rightButtonNode.disabledColor = self.presentationData.theme.disabledButtonColor - self.rightButtonNode.rippleColor = self.presentationData.theme.primaryTextColor.withAlphaComponent(0.05) - self.backButtonArrow.image = NavigationBar.backArrowImage(color: self.presentationData.theme.buttonColor) - if let title = self.title { - self.titleNode.attributedText = NSAttributedString(string: title, font: NavigationBar.titleFont, textColor: self.presentationData.theme.primaryTextColor) - self.titleNode.accessibilityLabel = title - } - self.stripeNode.backgroundColor = self.presentationData.theme.separatorColor - - self.badgeNode.updateTheme(fillColor: self.presentationData.theme.buttonColor, strokeColor: self.presentationData.theme.buttonColor, textColor: self.presentationData.theme.badgeTextColor) - - self.updateLeftButton(animated: false) - self.requestLayout() - } - } - - private func requestLayout() { - self.requestedLayout = true - self.setNeedsLayout() - } - - override open func layout() { - super.layout() - - if let validLayout = self.validLayout, self.requestedLayout { - self.requestedLayout = false - self.updateLayout(size: validLayout.size, defaultHeight: validLayout.defaultHeight, additionalTopHeight: validLayout.additionalTopHeight, additionalContentHeight: validLayout.additionalContentHeight, additionalBackgroundHeight: validLayout.additionalBackgroundHeight, additionalCutout: validLayout.additionalCutout, leftInset: validLayout.leftInset, rightInset: validLayout.rightInset, appearsHidden: validLayout.appearsHidden, isLandscape: validLayout.isLandscape, transition: .immediate) - } - } - - func updateLayout(size: CGSize, defaultHeight: CGFloat, additionalTopHeight: CGFloat, additionalContentHeight: CGFloat, additionalBackgroundHeight: CGFloat, additionalCutout: CGSize?, leftInset: CGFloat, rightInset: CGFloat, appearsHidden: Bool, isLandscape: Bool, transition: ContainedViewLayoutTransition) { - if self.layoutSuspended { - return - } - - self.validLayout = (size, defaultHeight, additionalTopHeight, additionalContentHeight, additionalBackgroundHeight, additionalCutout, leftInset, rightInset, appearsHidden, isLandscape) - - let backgroundFrame = CGRect(origin: CGPoint(), size: CGSize(width: size.width, height: size.height + additionalBackgroundHeight)) - if self.backgroundNode.frame != backgroundFrame { - transition.updateFrame(node: self.backgroundNode, frame: backgroundFrame) - self.backgroundNode.update(size: backgroundFrame.size, transition: transition) - - transition.updateFrame(view: self.navigationBackgroundCutoutView, frame: CGRect(origin: CGPoint(), size: backgroundFrame.size)) - } - self.navigationBackgroundCutoutView.update(size: backgroundFrame.size, cutout: additionalCutout, transition: transition) - - let apparentAdditionalHeight: CGFloat = self.secondaryContentNode != nil ? (self.secondaryContentHeight * self.secondaryContentNodeDisplayFraction) : 0.0 - - let leftButtonInset: CGFloat = leftInset + 16.0 - let backButtonInset: CGFloat = leftInset + 27.0 - - transition.updateFrame(node: self.clippingNode, frame: CGRect(origin: CGPoint(), size: size)) - transition.updateFrame(node: self.additionalContentNode, frame: CGRect(origin: CGPoint(), size: CGSize(width: size.width, height: size.height + additionalBackgroundHeight))) - transition.updateFrame(node: self.buttonsContainerNode, frame: CGRect(origin: CGPoint(), size: size)) - var expansionHeight: CGFloat = 0.0 - if let contentNode = self.contentNode { - var contentNodeFrame: CGRect - switch contentNode.mode { - case .replacement: - expansionHeight = contentNode.height - defaultHeight - contentNodeFrame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: size.width, height: size.height - additionalContentHeight)) - case .expansion: - expansionHeight = contentNode.height - - let additionalExpansionHeight: CGFloat = self.secondaryContentNode != nil && appearsHidden ? (self.secondaryContentHeight * self.secondaryContentNodeDisplayFraction) : 0.0 - contentNodeFrame = CGRect(origin: CGPoint(x: 0.0, y: size.height - (appearsHidden ? 0.0 : additionalContentHeight) - expansionHeight - apparentAdditionalHeight - additionalExpansionHeight), size: CGSize(width: size.width, height: expansionHeight)) - if appearsHidden { - if self.secondaryContentNode != nil { - contentNodeFrame.origin.y += self.secondaryContentHeight * self.secondaryContentNodeDisplayFraction - } - } - } - transition.updateFrame(node: contentNode, frame: contentNodeFrame) - contentNode.updateLayout(size: contentNodeFrame.size, leftInset: leftInset, rightInset: rightInset, transition: transition) - } - - transition.updateFrame(node: self.stripeNode, frame: CGRect(x: (additionalCutout?.width ?? 0.0), y: size.height + additionalBackgroundHeight, width: size.width - (additionalCutout?.width ?? 0.0), height: UIScreenPixel)) - - let nominalHeight: CGFloat = defaultHeight - let contentVerticalOrigin = additionalTopHeight - - var leftTitleInset: CGFloat = leftInset + 1.0 - var rightTitleInset: CGFloat = rightInset + 1.0 - if self.backButtonNode.supernode != nil { - let backButtonSize = self.backButtonNode.updateLayout(constrainedSize: CGSize(width: size.width, height: nominalHeight), isLandscape: isLandscape, isLeftAligned: true) - leftTitleInset = backButtonSize.width + backButtonInset + 1.0 - - let topHitTestSlop = (nominalHeight - backButtonSize.height) * 0.5 - self.backButtonNode.hitTestSlop = UIEdgeInsets(top: -topHitTestSlop, left: -27.0, bottom: -topHitTestSlop, right: -8.0) - - if let transitionState = self.transitionState { - let progress = transitionState.progress - - switch transitionState.role { - case .top: - let initialX: CGFloat = backButtonInset - let finalX: CGFloat = floor((size.width - backButtonSize.width) / 2.0) - size.width - - let backButtonFrame = CGRect(origin: CGPoint(x: initialX * (1.0 - progress) + finalX * progress, y: contentVerticalOrigin + floor((nominalHeight - backButtonSize.height) / 2.0)), size: backButtonSize) - if self.backButtonNode.frame != backButtonFrame { - self.backButtonNode.frame = backButtonFrame - } - let backButtonAlpha = self.backButtonNode.alpha - if self.backButtonNode.alpha != backButtonAlpha { - self.backButtonNode.alpha = backButtonAlpha - } - - if let transitionTitleNode = self.transitionTitleNode { - let transitionTitleSize = transitionTitleNode.measure(CGSize(width: size.width, height: nominalHeight)) - - let initialX: CGFloat = backButtonInset + floor((backButtonSize.width - transitionTitleSize.width) / 2.0) - let finalX: CGFloat = floor((size.width - transitionTitleSize.width) / 2.0) - size.width - - transitionTitleNode.frame = CGRect(origin: CGPoint(x: initialX * (1.0 - progress) + finalX * progress, y: contentVerticalOrigin + floor((nominalHeight - transitionTitleSize.height) / 2.0)), size: transitionTitleSize) - transitionTitleNode.alpha = progress * progress - } - - self.backButtonArrow.frame = CGRect(origin: CGPoint(x: leftInset + 8.0 - progress * size.width, y: contentVerticalOrigin + floor((nominalHeight - 22.0) / 2.0)), size: CGSize(width: 13.0, height: 22.0)) - self.backButtonArrow.alpha = max(0.0, 1.0 - progress * 1.3) - self.badgeNode.alpha = max(0.0, 1.0 - progress * 1.3) - case .bottom: - self.backButtonNode.alpha = 1.0 - self.backButtonNode.frame = CGRect(origin: CGPoint(x: backButtonInset, y: contentVerticalOrigin + floor((nominalHeight - backButtonSize.height) / 2.0)), size: backButtonSize) - self.backButtonArrow.alpha = 1.0 - self.backButtonArrow.frame = CGRect(origin: CGPoint(x: leftInset + 8.0, y: contentVerticalOrigin + floor((nominalHeight - 22.0) / 2.0)), size: CGSize(width: 13.0, height: 22.0)) - self.badgeNode.alpha = 1.0 - } - } else { - self.backButtonNode.alpha = 1.0 - transition.updateFrame(node: self.backButtonNode, frame: CGRect(origin: CGPoint(x: backButtonInset, y: contentVerticalOrigin + floor((nominalHeight - backButtonSize.height) / 2.0)), size: backButtonSize)) - - self.backButtonArrow.alpha = 1.0 - transition.updateFrame(node: self.backButtonArrow, frame: CGRect(origin: CGPoint(x: leftInset + 8.0, y: contentVerticalOrigin + floor((nominalHeight - 22.0) / 2.0)), size: CGSize(width: 13.0, height: 22.0))) - self.badgeNode.alpha = 1.0 - } - } else if self.leftButtonNode.supernode != nil { - let leftButtonSize = self.leftButtonNode.updateLayout(constrainedSize: CGSize(width: size.width, height: nominalHeight), isLandscape: isLandscape, isLeftAligned: true) - leftTitleInset = leftButtonSize.width + leftButtonInset + 1.0 - - var transition = transition - if self.leftButtonNode.frame.width.isZero { - transition = .immediate - } - - self.leftButtonNode.alpha = 1.0 - transition.updateFrame(node: self.leftButtonNode, frame: CGRect(origin: CGPoint(x: leftButtonInset, y: contentVerticalOrigin + floor((nominalHeight - leftButtonSize.height) / 2.0)), size: leftButtonSize)) - } - - let badgeSize = self.badgeNode.measure(CGSize(width: 200.0, height: 100.0)) - let backButtonArrowFrame = self.backButtonArrow.frame - transition.updateFrame(node: self.badgeNode, frame: CGRect(origin: backButtonArrowFrame.origin.offsetBy(dx: 16.0, dy: 2.0), size: badgeSize)) - - if self.rightButtonNode.supernode != nil { - let rightButtonSize = self.rightButtonNode.updateLayout(constrainedSize: (CGSize(width: size.width, height: nominalHeight)), isLandscape: isLandscape, isLeftAligned: false) - rightTitleInset = rightButtonSize.width + leftButtonInset + 1.0 - self.rightButtonNode.alpha = 1.0 - - var transition = transition - if self.rightButtonNode.frame.width.isZero || self.rightButtonNodeUpdated { - transition = .immediate - } - - transition.updateFrame(node: self.rightButtonNode, frame: CGRect(origin: CGPoint(x: size.width - leftButtonInset - rightButtonSize.width, y: contentVerticalOrigin + floor((nominalHeight - rightButtonSize.height) / 2.0)), size: rightButtonSize)) - } - self.rightButtonNodeUpdated = false - - if let transitionState = self.transitionState { - let progress = transitionState.progress - - switch transitionState.role { - case .top: - break - case .bottom: - if let transitionBackButtonNode = self.transitionBackButtonNode { - let transitionBackButtonSize = transitionBackButtonNode.updateLayout(constrainedSize: CGSize(width: size.width, height: nominalHeight), isLandscape: isLandscape, isLeftAligned: true) - let initialX: CGFloat = backButtonInset + size.width * 0.3 - let finalX: CGFloat = floor((size.width - transitionBackButtonSize.width) / 2.0) - - transitionBackButtonNode.frame = CGRect(origin: CGPoint(x: initialX * (1.0 - progress) + finalX * progress, y: contentVerticalOrigin + floor((nominalHeight - transitionBackButtonSize.height) / 2.0)), size: transitionBackButtonSize) - transitionBackButtonNode.alpha = (1.0 - progress) * (1.0 - progress) - } - - if let transitionBackArrowNode = self.transitionBackArrowNode { - let initialX: CGFloat = leftInset + 8.0 + size.width * 0.3 - let finalX: CGFloat = leftInset + 8.0 - - transitionBackArrowNode.frame = CGRect(origin: CGPoint(x: initialX * (1.0 - progress) + finalX * progress, y: contentVerticalOrigin + floor((nominalHeight - 22.0) / 2.0)), size: CGSize(width: 13.0, height: 22.0)) - transitionBackArrowNode.alpha = max(0.0, 1.0 - progress * 1.3) - - if let transitionBadgeNode = self.transitionBadgeNode { - transitionBadgeNode.frame = CGRect(origin: transitionBackArrowNode.frame.origin.offsetBy(dx: 16.0, dy: 2.0), size: transitionBadgeNode.bounds.size) - transitionBadgeNode.alpha = transitionBackArrowNode.alpha - } - } - } - } - - leftTitleInset = floor(leftTitleInset) - if Int(leftTitleInset) % 2 != 0 { - leftTitleInset -= 1.0 - } - - if let customHeaderContentView = self.customHeaderContentView { - let headerSize = CGSize(width: size.width, height: nominalHeight) - var customHeaderFrame = CGRect(origin: CGPoint(x: 0.0, y: contentVerticalOrigin), size: headerSize) - if appearsHidden { - customHeaderFrame.origin.y = -customHeaderFrame.height - } - transition.updateFrame(view: customHeaderContentView, frame: customHeaderFrame) - } - - if self.titleNode.supernode != nil { - let titleSize = self.titleNode.updateLayout(CGSize(width: max(1.0, size.width - max(leftTitleInset, rightTitleInset) * 2.0), height: nominalHeight)) - - if let transitionState = self.transitionState, let otherNavigationBar = transitionState.navigationBar { - let progress = transitionState.progress - - switch transitionState.role { - case .top: - let initialX = floor((size.width - titleSize.width) / 2.0) - let finalX: CGFloat = leftButtonInset - - self.titleNode.frame = CGRect(origin: CGPoint(x: initialX * (1.0 - progress) + finalX * progress, y: contentVerticalOrigin + floorToScreenPixels((nominalHeight - titleSize.height) / 2.0)), size: titleSize) - self.titleNode.alpha = (1.0 - progress) * (1.0 - progress) - case .bottom: - var initialX: CGFloat = backButtonInset - if otherNavigationBar.backButtonNode.supernode != nil { - initialX += floor((otherNavigationBar.backButtonNode.frame.size.width - titleSize.width) / 2.0) - } - initialX += size.width * 0.3 - let finalX: CGFloat = floor((size.width - titleSize.width) / 2.0) - - self.titleNode.frame = CGRect(origin: CGPoint(x: initialX * (1.0 - progress) + finalX * progress, y: contentVerticalOrigin + floorToScreenPixels((nominalHeight - titleSize.height) / 2.0)), size: titleSize) - self.titleNode.alpha = progress * progress - } - } else { - var transition = transition - if self.titleNode.frame.width.isZero { - transition = .immediate - } - self.titleNode.alpha = 1.0 - - var titleOffset: CGFloat = 0.0 - if self.presentationData.theme.backgroundColor == .clear && self.presentationData.theme.separatorColor == .clear { - titleOffset += 3.0 - } - transition.updateFrame(node: self.titleNode, frame: CGRect(origin: CGPoint(x: floor((size.width - titleSize.width) / 2.0), y: contentVerticalOrigin + titleOffset + floorToScreenPixels((nominalHeight - titleSize.height) / 2.0)), size: titleSize)) - } - } - - if let titleView = self.titleView { - let titleSize = CGSize(width: max(1.0, size.width - max(leftTitleInset, rightTitleInset) * 2.0), height: nominalHeight) - let titleFrame = CGRect(origin: CGPoint(x: floor((size.width - titleSize.width) / 2.0), y: contentVerticalOrigin + floorToScreenPixels((nominalHeight - titleSize.height) / 2.0)), size: titleSize) - var titleViewTransition = transition - if titleView.frame.isEmpty { - titleViewTransition = .immediate - titleView.frame = titleFrame - } - - titleViewTransition.updateFrame(view: titleView, frame: titleFrame) - - if let titleView = titleView as? NavigationBarTitleView { - let titleWidth = size.width - (leftTitleInset > 0.0 ? leftTitleInset : rightTitleInset) - (rightTitleInset > 0.0 ? rightTitleInset : leftTitleInset) - - let _ = titleView.updateLayout(size: titleFrame.size, clearBounds: CGRect(origin: CGPoint(x: leftTitleInset - titleFrame.minX, y: 0.0), size: CGSize(width: titleWidth, height: titleFrame.height)), transition: titleViewTransition) - } - - if let transitionState = self.transitionState, let otherNavigationBar = transitionState.navigationBar { - let progress = transitionState.progress - - switch transitionState.role { - case .top: - let initialX = floor((size.width - titleSize.width) / 2.0) - let finalX: CGFloat = leftButtonInset - - titleView.frame = CGRect(origin: CGPoint(x: initialX * (1.0 - progress) + finalX * progress, y: contentVerticalOrigin + floorToScreenPixels((nominalHeight - titleSize.height) / 2.0)), size: titleSize) - titleView.alpha = (1.0 - progress) * (1.0 - progress) - case .bottom: - var initialX: CGFloat = backButtonInset - if otherNavigationBar.backButtonNode.supernode != nil { - initialX += floor((otherNavigationBar.backButtonNode.frame.size.width - titleSize.width) / 2.0) - } - initialX += size.width * 0.3 - let finalX: CGFloat = floor((size.width - titleSize.width) / 2.0) - - titleView.frame = CGRect(origin: CGPoint(x: initialX * (1.0 - progress) + finalX * progress, y: contentVerticalOrigin + floorToScreenPixels((nominalHeight - titleSize.height) / 2.0)), size: titleSize) - titleView.alpha = progress * progress - } - } else { - if self.hintAnimateTitleNodeOnNextLayout { - self.hintAnimateTitleNodeOnNextLayout = false - if let titleView = titleView as? NavigationBarTitleView { - titleView.animateLayoutTransition() - } - } - titleView.alpha = 1.0 - transition.updateFrame(view: titleView, frame: titleFrame) - } - } - } - - public func makeTransitionTitleNode(foregroundColor: UIColor) -> ASDisplayNode? { - if let titleView = self.titleView { - if let transitionView = titleView as? NavigationBarTitleTransitionNode { - return transitionView.makeTransitionMirrorNode() - } else { - return nil - } - } else if let title = self.title { - let node = ImmediateTextNode() - node.attributedText = NSAttributedString(string: title, font: NavigationBar.titleFont, textColor: foregroundColor) - return node } else { return nil } } - - public func makeTransitionBackButtonNode(accentColor: UIColor) -> NavigationButtonNode? { - if self.backButtonNode.supernode != nil { - let node = NavigationButtonNode() - node.manualAlpha = self.backButtonNode.manualAlpha - node.updateManualText(self.backButtonNode.manualText) - node.color = accentColor - if let validLayout = self.validLayout { - let _ = node.updateLayout(constrainedSize: CGSize(width: validLayout.size.width, height: validLayout.defaultHeight), isLandscape: validLayout.isLandscape, isLeftAligned: true) - node.frame = self.backButtonNode.frame - } - return node - } else { - return nil - } - } - - public func makeTransitionBackButtonView(accentColor: UIColor) -> UIView? { - if self.backButtonNode.supernode != nil { - let node = NavigationButtonNode() - node.manualAlpha = self.backButtonNode.manualAlpha - node.updateManualText(self.backButtonNode.manualText) - node.color = accentColor - if let validLayout = self.validLayout { - let _ = node.updateLayout(constrainedSize: CGSize(width: validLayout.size.width, height: validLayout.defaultHeight), isLandscape: validLayout.isLandscape, isLeftAligned: true) - node.frame = self.backButtonNode.frame - } - return node.view - } else { - return nil - } - } - - public func makeTransitionRightButtonNode(accentColor: UIColor) -> NavigationButtonNode? { - if self.rightButtonNode.supernode != nil { - let node = NavigationButtonNode() - var items: [UIBarButtonItem] = [] - if let item = self.item { - if let rightBarButtonItems = item.rightBarButtonItems, !rightBarButtonItems.isEmpty { - items = rightBarButtonItems - } else if let rightBarButtonItem = item.rightBarButtonItem { - items = [rightBarButtonItem] - } - } - node.updateItems(items, animated: false) - node.color = accentColor - if let validLayout = self.validLayout { - let _ = node.updateLayout(constrainedSize: CGSize(width: validLayout.size.width, height: validLayout.defaultHeight), isLandscape: validLayout.isLandscape, isLeftAligned: false) - node.frame = self.backButtonNode.frame - } - return node - } else { - return nil - } - } - - public func makeTransitionBackArrowNode(accentColor: UIColor) -> ASDisplayNode? { - if self.backButtonArrow.supernode != nil { - let node = ASImageNode() - node.image = NavigationBar.backArrowImage(color: accentColor) - node.frame = self.backButtonArrow.frame - node.displayWithoutProcessing = true - node.displaysAsynchronously = false - return node - } else { - return nil - } - } - - public func makeTransitionBackArrowView(accentColor: UIColor) -> UIView? { - if self.backButtonArrow.supernode != nil { - let view = UIImageView() - view.image = NavigationBar.backArrowImage(color: accentColor) - view.frame = self.backButtonArrow.frame - return view - } else { - return nil - } - } - - public func makeTransitionBadgeNode() -> ASDisplayNode? { - if self.badgeNode.supernode != nil && !self.badgeNode.isHidden { - let node = NavigationBarBadgeNode(fillColor: self.presentationData.theme.buttonColor, strokeColor: self.presentationData.theme.buttonColor, textColor: self.presentationData.theme.badgeTextColor) - node.text = self.badgeNode.text - let nodeSize = node.measure(CGSize(width: 200.0, height: 100.0)) - node.frame = CGRect(origin: CGPoint(), size: nodeSize) - return node - } else { - return nil - } - } - - public var intrinsicCanTransitionInline: Bool = true - public var shouldTransitionInline: (() -> Bool)? - - public var passthroughTouches = true - - public var canTransitionInline: Bool { - if let contentNode = self.contentNode, case .replacement = contentNode.mode { - return false - } else { - if let shouldTransitionInline = self.shouldTransitionInline { - if !shouldTransitionInline() { - return false - } - } - return self.intrinsicCanTransitionInline - } - } - - public func contentHeight(defaultHeight: CGFloat) -> CGFloat { - var result: CGFloat = 0.0 - if let contentNode = self.contentNode { - switch contentNode.mode { - case .expansion: - result += defaultHeight + contentNode.height - case .replacement: - result += contentNode.height - } - } else { - result += defaultHeight - } - - if let _ = self.secondaryContentNode { - result += self.secondaryContentHeight * self.secondaryContentNodeDisplayFraction - } - - return result - } - - public func setContentNode(_ contentNode: NavigationBarContentNode?, animated: Bool) { - if let customSetContentNode = self.customSetContentNode { - customSetContentNode(contentNode, animated) - return - } - - if self.contentNode !== contentNode { - if let previous = self.contentNode { - if animated { - previous.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak self, weak previous] _ in - if let strongSelf = self, let previous = previous { - if previous !== strongSelf.contentNode { - previous.removeFromSupernode() - } - } - }) - } else { - previous.removeFromSupernode() - } - } - self.contentNode = contentNode - self.contentNode?.requestContainerLayout = { [weak self] transition in - self?.requestContainerLayout(transition) - } - if let contentNode = contentNode { - contentNode.clipsToBounds = true - contentNode.layer.removeAnimation(forKey: "opacity") - self.insertSubnode(contentNode, belowSubnode: self.stripeNode) - if animated { - contentNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) - } - - if case .replacement = contentNode.mode, !self.buttonsContainerNode.alpha.isZero { - self.buttonsContainerNode.alpha = 0.0 - if animated { - self.buttonsContainerNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2) - } - } - - if !self.bounds.size.width.isZero { - self.requestedLayout = true - self.layout() - } else { - self.requestLayout() - } - } else if self.buttonsContainerNode.alpha.isZero { - self.buttonsContainerNode.alpha = 1.0 - if animated { - self.buttonsContainerNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) - } - } - } - } - - public func setSecondaryContentNode(_ secondaryContentNode: ASDisplayNode?, animated: Bool = false) { - if self.secondaryContentNode !== secondaryContentNode { - if let previous = self.secondaryContentNode, previous.supernode === self.clippingNode { - if animated { - previous.layer.animateAlpha(from: previous.alpha, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak previous] finished in - if finished { - previous?.removeFromSupernode() - previous?.layer.removeAllAnimations() - } - }) - } else { - previous.removeFromSupernode() - } - } - self.secondaryContentNode = secondaryContentNode - if let secondaryContentNode = secondaryContentNode { - self.clippingNode.addSubnode(secondaryContentNode) - - if animated { - secondaryContentNode.layer.animateAlpha(from: 0.0, to: secondaryContentNode.alpha, duration: 0.3) - } - } - } - } - - public func executeBack() -> Bool { - if self.backButtonNode.isInHierarchy { - self.backButtonNode.pressed(0) - } else if self.leftButtonNode.isInHierarchy { - self.leftButtonNode.pressed(0) - } else { - self.backButtonNode.pressed(0) - } - return true - } - - public func setHidden(_ hidden: Bool, animated: Bool) { - if let contentNode = self.contentNode, case .replacement = contentNode.mode { - } else { - let targetAlpha: CGFloat = hidden ? 0.0 : 1.0 - let previousAlpha = self.buttonsContainerNode.alpha - if previousAlpha != targetAlpha { - self.buttonsContainerNode.alpha = targetAlpha - if animated { - self.buttonsContainerNode.layer.animateAlpha(from: previousAlpha, to: targetAlpha, duration: 0.2) - } - } - } - } - - override open func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { - if let result = self.additionalContentNode.view.hitTest(self.view.convert(point, to: self.additionalContentNode.view), with: event) { - return result - } - - if self.frame.minY > -10.0, let customHeaderContentView = self.customHeaderContentView, let result = customHeaderContentView.hitTest(self.view.convert(point, to: customHeaderContentView), with: event) { - return result - } - - guard let result = super.hitTest(point, with: event) else { - return nil - } - - if self.passthroughTouches && (result == self.view || result == self.buttonsContainerNode.view) { - return nil - } - - return result - } } + +public protocol NavigationButtonCustomDisplayNode { + var isHighlightable: Bool { get } +} + +public protocol NavigationButtonNode: ASDisplayNode { + func updateManualAlpha(alpha: CGFloat, transition: ContainedViewLayoutTransition) + var mainContentNode: ASDisplayNode? { get } + var contentsColor: UIColor? { get set } +} + +public protocol NavigationBar: ASDisplayNode { + typealias Style = NavigationBarStyle + + var backPressed: () -> Void { get set } + + var userInfo: Any? { get set } + var makeCustomTransitionNode: ((NavigationBar, Bool) -> CustomNavigationTransitionNode?)? { get set } + var allowsCustomTransition: (() -> Bool)? { get set } + + var stripeNode: ASDisplayNode { get } + var clippingNode: SparseNode { get } + + var contentNode: NavigationBarContentNode? { get } + var secondaryContentNode: ASDisplayNode? { get } + var secondaryContentNodeDisplayFraction: CGFloat { get set } + + var item: UINavigationItem? { get set } + var customBackButtonText: String? { get } + var titleView: UIView? { get } + var layoutSuspended: Bool { get set } + + var enableAutomaticBackButton: Bool { get set } + var previousItem: NavigationPreviousAction? { get set } + + var backgroundNode: NavigationBackgroundNode { get } + var backButtonNode: NavigationButtonNode { get } + var badgeNode: NavigationBarBadgeNode { get } + var backButtonArrow: ASImageNode { get } + var leftButtonNode: NavigationButtonNode { get } + var rightButtonNode: NavigationButtonNode { get } + var additionalContentNode: SparseNode { get } + + func reattachAdditionalContentNode() + + var secondaryContentHeight: CGFloat { get set } + + var isBackgroundVisible: Bool { get } + + func updateBackgroundAlpha(_ alpha: CGFloat, transition: ContainedViewLayoutTransition) + func updatePresentationData(_ presentationData: NavigationBarPresentationData, transition: ContainedViewLayoutTransition) + + var intrinsicCanTransitionInline: Bool { get set } + + var passthroughTouches: Bool { get set } + + var canTransitionInline: Bool { get } + + func contentHeight(defaultHeight: CGFloat) -> CGFloat + func setContentNode(_ contentNode: NavigationBarContentNode?, animated: Bool) + func setSecondaryContentNode(_ secondaryContentNode: ASDisplayNode?, animated: Bool) + func executeBack() -> Bool + func setHidden(_ hidden: Bool, animated: Bool) + + var requestContainerLayout: (ContainedViewLayoutTransition) -> Void { get set } + + func updateLayout(size: CGSize, defaultHeight: CGFloat, additionalTopHeight: CGFloat, additionalContentHeight: CGFloat, additionalBackgroundHeight: CGFloat, additionalCutout: CGSize?, leftInset: CGFloat, rightInset: CGFloat, appearsHidden: Bool, isLandscape: Bool, transition: ContainedViewLayoutTransition) +} + +public var defaultNavigationBarImpl: ((NavigationBarPresentationData) -> NavigationBar)? diff --git a/submodules/Display/Source/NavigationBarBadge.swift b/submodules/Display/Source/NavigationBarBadge.swift index f1236dccc3..734eda034d 100644 --- a/submodules/Display/Source/NavigationBarBadge.swift +++ b/submodules/Display/Source/NavigationBarBadge.swift @@ -12,7 +12,7 @@ public final class NavigationBarBadgeNode: ASDisplayNode { private let font: UIFont = Font.regular(13.0) - var text: String = "" { + public var text: String = "" { didSet { self.textNode.attributedText = NSAttributedString(string: self.text, font: self.font, textColor: self.textColor) self.invalidateCalculatedLayout() @@ -39,7 +39,7 @@ public final class NavigationBarBadgeNode: ASDisplayNode { self.addSubnode(self.textNode) } - func updateTheme(fillColor: UIColor, strokeColor: UIColor, textColor: UIColor) { + public func updateTheme(fillColor: UIColor, strokeColor: UIColor, textColor: UIColor) { self.fillColor = fillColor self.strokeColor = strokeColor self.textColor = textColor diff --git a/submodules/Display/Source/NavigationBarTransitionState.swift b/submodules/Display/Source/NavigationBarTransitionState.swift deleted file mode 100644 index dce45512df..0000000000 --- a/submodules/Display/Source/NavigationBarTransitionState.swift +++ /dev/null @@ -1,21 +0,0 @@ -import Foundation -import UIKit - -enum NavigationBarTransitionRole { - case top - case bottom -} - -final class NavigationBarTransitionState { - weak var navigationBar: NavigationBar? - let transition: NavigationTransition - let role: NavigationBarTransitionRole - let progress: CGFloat - - init(navigationBar: NavigationBar, transition: NavigationTransition, role: NavigationBarTransitionRole, progress: CGFloat) { - self.navigationBar = navigationBar - self.transition = transition - self.role = role - self.progress = progress - } -} diff --git a/submodules/Display/Source/NavigationTransitionCoordinator.swift b/submodules/Display/Source/NavigationTransitionCoordinator.swift index c616774639..707bd537d3 100644 --- a/submodules/Display/Source/NavigationTransitionCoordinator.swift +++ b/submodules/Display/Source/NavigationTransitionCoordinator.swift @@ -201,10 +201,9 @@ final class NavigationTransitionCoordinator { position = progress } - transition.animateView { - topNavigationBar.transitionState = NavigationBarTransitionState(navigationBar: bottomNavigationBar, transition: self.transition, role: .top, progress: position) - bottomNavigationBar.transitionState = NavigationBarTransitionState(navigationBar: topNavigationBar, transition: self.transition, role: .bottom, progress: position) - } + let _ = position + let _ = topNavigationBar + let _ = bottomNavigationBar } } @@ -218,15 +217,16 @@ final class NavigationTransitionCoordinator { position = progress } - topNavigationBar.transitionState = NavigationBarTransitionState(navigationBar: bottomNavigationBar, transition: self.transition, role: .top, progress: position) - bottomNavigationBar.transitionState = NavigationBarTransitionState(navigationBar: topNavigationBar, transition: self.transition, role: .bottom, progress: position) + let _ = position + let _ = topNavigationBar + let _ = bottomNavigationBar } } func endNavigationBarTransition() { if let topNavigationBar = self.topNavigationBar, let bottomNavigationBar = self.bottomNavigationBar, self.inlineNavigationBarTransition { - topNavigationBar.transitionState = nil - bottomNavigationBar.transitionState = nil + let _ = topNavigationBar + let _ = bottomNavigationBar } } diff --git a/submodules/Display/Source/SparseContainerView.swift b/submodules/Display/Source/SparseContainerView.swift new file mode 100644 index 0000000000..e9f2997735 --- /dev/null +++ b/submodules/Display/Source/SparseContainerView.swift @@ -0,0 +1,47 @@ +import Foundation +import UIKit +import AsyncDisplayKit + +open class SparseNode: ASDisplayNode { + override open func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { + if self.alpha.isZero { + return nil + } + for view in self.view.subviews.reversed() { + if let result = view.hitTest(self.view.convert(point, to: view), with: event), result.isUserInteractionEnabled { + return result + } + } + + if !self.bounds.inset(by: self.hitTestSlop).contains(point) { + return nil + } + + let result = super.hitTest(point, with: event) + if result != self.view { + return result + } else { + return nil + } + } +} + +open class SparseContainerView: UIView { + override public func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { + if self.alpha.isZero { + return nil + } + for view in self.subviews.reversed() { + if let result = view.hitTest(self.convert(point, to: view), with: event), result.isUserInteractionEnabled { + return result + } + } + + let result = super.hitTest(point, with: event) + if result != self { + return result + } else { + return nil + } + } +} diff --git a/submodules/Display/Source/ViewController.swift b/submodules/Display/Source/ViewController.swift index f81403f2dd..9a5d7c3d02 100644 --- a/submodules/Display/Source/ViewController.swift +++ b/submodules/Display/Source/ViewController.swift @@ -387,7 +387,7 @@ public protocol CustomViewControllerNavigationDataSummary: AnyObject { public init(navigationBarPresentationData: NavigationBarPresentationData?) { self.statusBar = StatusBar() if let navigationBarPresentationData = navigationBarPresentationData { - self.navigationBar = NavigationBar(presentationData: navigationBarPresentationData) + self.navigationBar = defaultNavigationBarImpl!(navigationBarPresentationData) } else { self.navigationBar = nil } @@ -554,14 +554,14 @@ public protocol CustomViewControllerNavigationDataSummary: AnyObject { UIView.transition(with: navigationBar.view, duration: 0.3, options: [.transitionCrossDissolve], animations: { }, completion: nil) } - self.navigationBar?.updatePresentationData(presentationData) + self.navigationBar?.updatePresentationData(presentationData, transition: .immediate) if let parent = self.parent as? TabBarController { if parent.currentController === self { if animated, let navigationBar = parent.navigationBar { UIView.transition(with: navigationBar.view, duration: 0.3, options: [.transitionCrossDissolve], animations: { }, completion: nil) } - parent.navigationBar?.updatePresentationData(presentationData) + parent.navigationBar?.updatePresentationData(presentationData, transition: .immediate) } } } diff --git a/submodules/FeaturedStickersScreen/Sources/FeaturedStickersScreen.swift b/submodules/FeaturedStickersScreen/Sources/FeaturedStickersScreen.swift index 25d2f27a6d..31e1c17497 100644 --- a/submodules/FeaturedStickersScreen/Sources/FeaturedStickersScreen.swift +++ b/submodules/FeaturedStickersScreen/Sources/FeaturedStickersScreen.swift @@ -919,7 +919,7 @@ public final class FeaturedStickersScreen: ViewController { private func updatePresentationData() { self.statusBar.statusBarStyle = self.presentationData.theme.rootController.statusBarStyle.style - self.navigationBar?.updatePresentationData(NavigationBarPresentationData(presentationData: self.presentationData)) + self.navigationBar?.updatePresentationData(NavigationBarPresentationData(presentationData: self.presentationData), transition: .immediate) self.searchNavigationNode?.updatePresentationData(theme: self.presentationData.theme, strings: self.presentationData.strings) diff --git a/submodules/GalleryUI/Sources/GalleryController.swift b/submodules/GalleryUI/Sources/GalleryController.swift index d8af3d8fa9..e36181f679 100644 --- a/submodules/GalleryUI/Sources/GalleryController.swift +++ b/submodules/GalleryUI/Sources/GalleryController.swift @@ -576,8 +576,8 @@ private func galleryEntriesForMessageHistoryEntries(_ entries: [MessageHistoryEn } public class GalleryController: ViewController, StandalonePresentableController, KeyShortcutResponder, GalleryControllerProtocol { - public static let darkNavigationTheme = NavigationBarTheme(buttonColor: .white, disabledButtonColor: UIColor(rgb: 0x525252), primaryTextColor: .white, backgroundColor: UIColor(white: 0.0, alpha: 0.6), enableBackgroundBlur: false, separatorColor: UIColor(white: 0.0, alpha: 0.8), badgeBackgroundColor: .clear, badgeStrokeColor: .clear, badgeTextColor: .clear) - public static let lightNavigationTheme = NavigationBarTheme(buttonColor: UIColor(rgb: 0x0088ff), disabledButtonColor: UIColor(rgb: 0xd0d0d0), primaryTextColor: .black, backgroundColor: UIColor(red: 0.968626451, green: 0.968626451, blue: 0.968626451, alpha: 1.0), enableBackgroundBlur: false, separatorColor: UIColor(red: 0.6953125, green: 0.6953125, blue: 0.6953125, alpha: 1.0), badgeBackgroundColor: .clear, badgeStrokeColor: .clear, badgeTextColor: .clear) + public static let darkNavigationTheme = NavigationBarTheme(overallDarkAppearance: true, buttonColor: .white, disabledButtonColor: UIColor(rgb: 0x525252), primaryTextColor: .white, backgroundColor: UIColor(white: 0.0, alpha: 0.6), enableBackgroundBlur: false, separatorColor: UIColor(white: 0.0, alpha: 0.8), badgeBackgroundColor: .clear, badgeStrokeColor: .clear, badgeTextColor: .clear) + public static let lightNavigationTheme = NavigationBarTheme(overallDarkAppearance: false, buttonColor: UIColor(rgb: 0x0088ff), disabledButtonColor: UIColor(rgb: 0xd0d0d0), primaryTextColor: .black, backgroundColor: UIColor(red: 0.968626451, green: 0.968626451, blue: 0.968626451, alpha: 1.0), enableBackgroundBlur: false, separatorColor: UIColor(red: 0.6953125, green: 0.6953125, blue: 0.6953125, alpha: 1.0), badgeBackgroundColor: .clear, badgeStrokeColor: .clear, badgeTextColor: .clear) private var galleryNode: GalleryControllerNode { return self.displayNode as! GalleryControllerNode @@ -942,12 +942,12 @@ public class GalleryController: ViewController, StandalonePresentableController, switch style { case .dark: strongSelf.statusBar.statusBarStyle = .White - strongSelf.navigationBar?.updatePresentationData(NavigationBarPresentationData(theme: GalleryController.darkNavigationTheme, strings: NavigationBarStrings(presentationStrings: strongSelf.presentationData.strings))) + strongSelf.navigationBar?.updatePresentationData(NavigationBarPresentationData(theme: GalleryController.darkNavigationTheme, strings: NavigationBarStrings(presentationStrings: strongSelf.presentationData.strings)), transition: .immediate) strongSelf.galleryNode.backgroundNode.backgroundColor = UIColor.black strongSelf.galleryNode.isBackgroundExtendedOverNavigationBar = true case .light: strongSelf.statusBar.statusBarStyle = .Black - strongSelf.navigationBar?.updatePresentationData(NavigationBarPresentationData(theme: GalleryController.darkNavigationTheme, strings: NavigationBarStrings(presentationStrings: strongSelf.presentationData.strings))) + strongSelf.navigationBar?.updatePresentationData(NavigationBarPresentationData(theme: GalleryController.darkNavigationTheme, strings: NavigationBarStrings(presentationStrings: strongSelf.presentationData.strings)), transition: .immediate) strongSelf.galleryNode.backgroundNode.backgroundColor = UIColor(rgb: 0xbdbdc2) strongSelf.galleryNode.isBackgroundExtendedOverNavigationBar = false } diff --git a/submodules/HashtagSearchUI/Sources/HashtagSearchController.swift b/submodules/HashtagSearchUI/Sources/HashtagSearchController.swift index 811950283c..c1710530ea 100644 --- a/submodules/HashtagSearchUI/Sources/HashtagSearchController.swift +++ b/submodules/HashtagSearchUI/Sources/HashtagSearchController.swift @@ -87,7 +87,7 @@ public final class HashtagSearchController: TelegramBaseController { if previousTheme !== presentationData.theme || previousStrings !== presentationData.strings { self.statusBar.statusBarStyle = self.presentationData.theme.rootController.statusBarStyle.style - self.navigationBar?.updatePresentationData(NavigationBarPresentationData(presentationData: self.presentationData)) + self.navigationBar?.updatePresentationData(NavigationBarPresentationData(presentationData: self.presentationData), transition: .immediate) self.controllerNode.updatePresentationData(self.presentationData) } } diff --git a/submodules/ItemListUI/Sources/ItemListController.swift b/submodules/ItemListUI/Sources/ItemListController.swift index 9149f3f635..4da1eb95f7 100644 --- a/submodules/ItemListUI/Sources/ItemListController.swift +++ b/submodules/ItemListUI/Sources/ItemListController.swift @@ -281,11 +281,12 @@ open class ItemListController: ViewController, KeyShortcutResponder, Presentable self.presentationData = presentationData self.hideNavigationBarBackground = hideNavigationBarBackground - super.init(navigationBarPresentationData: NavigationBarPresentationData(theme: NavigationBarTheme(rootControllerTheme: presentationData.theme, hideBackground: hideNavigationBarBackground, hideSeparator: hideNavigationBarBackground), strings: NavigationBarStrings(presentationStrings: presentationData.strings))) + super.init(navigationBarPresentationData: NavigationBarPresentationData(theme: NavigationBarTheme(rootControllerTheme: presentationData.theme, hideBackground: hideNavigationBarBackground, hideSeparator: hideNavigationBarBackground, style: .glass), strings: NavigationBarStrings(presentationStrings: presentationData.strings))) self.isOpaqueWhenInOverlay = true self.blocksBackgroundWhenInOverlay = true self.automaticallyControlPresentationContextLayout = false + self._hasGlassStyle = true self.statusBar.statusBarStyle = presentationData.theme.rootController.statusBarStyle.style @@ -518,7 +519,7 @@ open class ItemListController: ViewController, KeyShortcutResponder, Presentable if strongSelf.presentationData != controllerState.presentationData { strongSelf.presentationData = controllerState.presentationData - strongSelf.navigationBar?.updatePresentationData(NavigationBarPresentationData(theme: NavigationBarTheme(rootControllerTheme: strongSelf.presentationData.theme, hideBackground: strongSelf.hideNavigationBarBackground, hideSeparator: strongSelf.hideNavigationBarBackground), strings: NavigationBarStrings(presentationStrings: strongSelf.presentationData.strings))) + strongSelf.navigationBar?.updatePresentationData(NavigationBarPresentationData(theme: NavigationBarTheme(rootControllerTheme: strongSelf.presentationData.theme, hideBackground: strongSelf.hideNavigationBarBackground, hideSeparator: strongSelf.hideNavigationBarBackground, edgeEffectColor: state.0.style == .blocks ? strongSelf.presentationData.theme.list.blocksBackgroundColor : strongSelf.presentationData.theme.list.plainBackgroundColor, style: .glass), strings: NavigationBarStrings(presentationStrings: strongSelf.presentationData.strings)), transition: .immediate) strongSelf.statusBar.updateStatusBarStyle(strongSelf.presentationData.theme.rootController.statusBarStyle.style, animated: true) strongSelf.segmentedTitleView?.theme = controllerState.presentationData.theme diff --git a/submodules/LocationUI/Sources/LocationPickerController.swift b/submodules/LocationUI/Sources/LocationPickerController.swift index 434b96002e..434db5f010 100644 --- a/submodules/LocationUI/Sources/LocationPickerController.swift +++ b/submodules/LocationUI/Sources/LocationPickerController.swift @@ -129,7 +129,7 @@ public final class LocationPickerController: ViewController, AttachmentContainab let navigationBarPresentationData = NavigationBarPresentationData(theme: NavigationBarTheme(rootControllerTheme: strongSelf.presentationData.theme, hideBackground: style == .glass, hideSeparator: true), strings: NavigationBarStrings(presentationStrings: strongSelf.presentationData.strings)) - strongSelf.navigationBar?.updatePresentationData(navigationBarPresentationData) + strongSelf.navigationBar?.updatePresentationData(navigationBarPresentationData, transition: .immediate) strongSelf.searchNavigationContentNode?.updatePresentationData(strongSelf.presentationData) strongSelf.updateBarButtons() diff --git a/submodules/LocationUI/Sources/LocationViewController.swift b/submodules/LocationUI/Sources/LocationViewController.swift index 173b93567c..1a19c949e0 100644 --- a/submodules/LocationUI/Sources/LocationViewController.swift +++ b/submodules/LocationUI/Sources/LocationViewController.swift @@ -109,7 +109,7 @@ public final class LocationViewController: ViewController { } strongSelf.presentationData = presentationData - strongSelf.navigationBar?.updatePresentationData(NavigationBarPresentationData(theme: NavigationBarTheme(rootControllerTheme: strongSelf.presentationData.theme).withUpdatedSeparatorColor(.clear), strings: NavigationBarStrings(presentationStrings: strongSelf.presentationData.strings))) + strongSelf.navigationBar?.updatePresentationData(NavigationBarPresentationData(theme: NavigationBarTheme(rootControllerTheme: strongSelf.presentationData.theme).withUpdatedSeparatorColor(.clear), strings: NavigationBarStrings(presentationStrings: strongSelf.presentationData.strings)), transition: .immediate) strongSelf.updateRightBarButton() diff --git a/submodules/MediaPickerUI/Sources/MediaPickerScreen.swift b/submodules/MediaPickerUI/Sources/MediaPickerScreen.swift index b59c8fe046..3e68202eaf 100644 --- a/submodules/MediaPickerUI/Sources/MediaPickerScreen.swift +++ b/submodules/MediaPickerUI/Sources/MediaPickerScreen.swift @@ -2673,7 +2673,7 @@ public final class MediaPickerScreenImpl: ViewController, MediaPickerScreen, Att } else { navigationBarPresentationData = NavigationBarPresentationData(presentationData: self.presentationData) } - self.navigationBar?.updatePresentationData(navigationBarPresentationData) + self.navigationBar?.updatePresentationData(navigationBarPresentationData, transition: .immediate) self.titleView.theme = self.presentationData.theme self.cancelButtonNode.theme = self.presentationData.theme self.moreButtonNode.theme = self.presentationData.theme diff --git a/submodules/MediaPickerUI/Sources/MediaPickerTitleView.swift b/submodules/MediaPickerUI/Sources/MediaPickerTitleView.swift index aaaba5dce4..49cf142fb7 100644 --- a/submodules/MediaPickerUI/Sources/MediaPickerTitleView.swift +++ b/submodules/MediaPickerUI/Sources/MediaPickerTitleView.swift @@ -18,7 +18,7 @@ final class MediaPickerTitleView: UIView { public var theme: PresentationTheme { didSet { - self.titleNode.attributedText = NSAttributedString(string: self.title, font: NavigationBar.titleFont, textColor: self.isDark ? .white : self.theme.rootController.navigationBar.primaryTextColor) + self.titleNode.attributedText = NSAttributedString(string: self.title, font: Font.with(size: 17.0, design: .regular, weight: .semibold, traits: [.monospacedNumbers]), textColor: self.isDark ? .white : self.theme.rootController.navigationBar.primaryTextColor) self.subtitleNode.attributedText = NSAttributedString(string: self.subtitle, font: Font.regular(12.0), textColor: self.isDark ? .white.withAlphaComponent(0.5) : self.theme.rootController.navigationBar.secondaryTextColor) self.segmentedControlNode.updateTheme(SegmentedControlTheme(theme: self.theme)) if self.glass { @@ -31,7 +31,7 @@ final class MediaPickerTitleView: UIView { public var isDark: Bool = false { didSet { if self.isDark != oldValue { - self.titleNode.attributedText = NSAttributedString(string: self.title, font: NavigationBar.titleFont, textColor: self.isDark ? .white : self.theme.rootController.navigationBar.primaryTextColor) + self.titleNode.attributedText = NSAttributedString(string: self.title, font: Font.with(size: 17.0, design: .regular, weight: .semibold, traits: [.monospacedNumbers]), textColor: self.isDark ? .white : self.theme.rootController.navigationBar.primaryTextColor) self.subtitleNode.attributedText = NSAttributedString(string: self.subtitle, font: Font.regular(12.0), textColor: self.isDark ? .white.withAlphaComponent(0.5) : self.theme.rootController.navigationBar.secondaryTextColor) if self.glass { self.arrowNode.image = generateTintedImage(image: UIImage(bundleImageName: "Navigation/TitleExpand"), color: self.isDark ? UIColor.white.withAlphaComponent(0.5) : self.theme.rootController.navigationBar.primaryTextColor.withAlphaComponent(0.4)) @@ -44,7 +44,7 @@ final class MediaPickerTitleView: UIView { public var title: String = "" { didSet { if self.title != oldValue { - self.titleNode.attributedText = NSAttributedString(string: self.title, font: NavigationBar.titleFont, textColor: self.isDark ? .white : self.theme.rootController.navigationBar.primaryTextColor) + self.titleNode.attributedText = NSAttributedString(string: self.title, font: Font.with(size: 17.0, design: .regular, weight: .semibold, traits: [.monospacedNumbers]), textColor: self.isDark ? .white : self.theme.rootController.navigationBar.primaryTextColor) self.setNeedsLayout() } } diff --git a/submodules/PasswordSetupUI/Sources/SetupTwoStepVerificationController.swift b/submodules/PasswordSetupUI/Sources/SetupTwoStepVerificationController.swift index 9427aa0c22..e6c938946d 100644 --- a/submodules/PasswordSetupUI/Sources/SetupTwoStepVerificationController.swift +++ b/submodules/PasswordSetupUI/Sources/SetupTwoStepVerificationController.swift @@ -39,7 +39,7 @@ public class SetupTwoStepVerificationController: ViewController { self.presentationData = self.context.sharedContext.currentPresentationData.with { $0 } - super.init(navigationBarPresentationData: NavigationBarPresentationData(theme: NavigationBarTheme(buttonColor: self.presentationData.theme.rootController.navigationBar.accentTextColor, disabledButtonColor: self.presentationData.theme.rootController.navigationBar.disabledButtonColor, primaryTextColor: self.presentationData.theme.rootController.navigationBar.primaryTextColor, backgroundColor: .clear, enableBackgroundBlur: false, separatorColor: .clear, badgeBackgroundColor: .clear, badgeStrokeColor: .clear, badgeTextColor: .clear), strings: NavigationBarStrings(presentationStrings: self.presentationData.strings))) + super.init(navigationBarPresentationData: NavigationBarPresentationData(theme: NavigationBarTheme(overallDarkAppearance: self.presentationData.theme.overallDarkAppearance, buttonColor: self.presentationData.theme.rootController.navigationBar.accentTextColor, disabledButtonColor: self.presentationData.theme.rootController.navigationBar.disabledButtonColor, primaryTextColor: self.presentationData.theme.rootController.navigationBar.primaryTextColor, backgroundColor: .clear, enableBackgroundBlur: false, separatorColor: .clear, badgeBackgroundColor: .clear, badgeStrokeColor: .clear, badgeTextColor: .clear), strings: NavigationBarStrings(presentationStrings: self.presentationData.strings))) self.statusBar.statusBarStyle = self.presentationData.theme.rootController.statusBarStyle.style @@ -89,7 +89,7 @@ public class SetupTwoStepVerificationController: ViewController { private func updateThemeAndStrings() { self.statusBar.statusBarStyle = self.presentationData.theme.rootController.statusBarStyle.style - self.navigationBar?.updatePresentationData(NavigationBarPresentationData(presentationData: self.presentationData)) + self.navigationBar?.updatePresentationData(NavigationBarPresentationData(presentationData: self.presentationData), transition: .immediate) self.navigationItem.backBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.Common_Back, style: .plain, target: nil, action: nil) self.controllerNode.updatePresentationData(self.presentationData) } diff --git a/submodules/PasswordSetupUI/Sources/TwoFactorAuthDataInputScreen.swift b/submodules/PasswordSetupUI/Sources/TwoFactorAuthDataInputScreen.swift index eaa8449741..aa2e38e670 100644 --- a/submodules/PasswordSetupUI/Sources/TwoFactorAuthDataInputScreen.swift +++ b/submodules/PasswordSetupUI/Sources/TwoFactorAuthDataInputScreen.swift @@ -71,7 +71,7 @@ public final class TwoFactorDataInputScreen: ViewController { self.presentationData = self.sharedContext.currentPresentationData.with { $0 } let defaultTheme = NavigationBarTheme(rootControllerTheme: self.presentationData.theme) - let navigationBarTheme = NavigationBarTheme(buttonColor: defaultTheme.buttonColor, disabledButtonColor: defaultTheme.disabledButtonColor, primaryTextColor: defaultTheme.primaryTextColor, backgroundColor: .clear, enableBackgroundBlur: false, separatorColor: .clear, badgeBackgroundColor: defaultTheme.badgeBackgroundColor, badgeStrokeColor: defaultTheme.badgeStrokeColor, badgeTextColor: defaultTheme.badgeTextColor) + let navigationBarTheme = NavigationBarTheme(overallDarkAppearance: defaultTheme.overallDarkAppearance, buttonColor: defaultTheme.buttonColor, disabledButtonColor: defaultTheme.disabledButtonColor, primaryTextColor: defaultTheme.primaryTextColor, backgroundColor: .clear, enableBackgroundBlur: false, separatorColor: .clear, badgeBackgroundColor: defaultTheme.badgeBackgroundColor, badgeStrokeColor: defaultTheme.badgeStrokeColor, badgeTextColor: defaultTheme.badgeTextColor) super.init(navigationBarPresentationData: NavigationBarPresentationData(theme: navigationBarTheme, strings: NavigationBarStrings(back: self.presentationData.strings.Common_Back, close: self.presentationData.strings.Common_Close))) diff --git a/submodules/PasswordSetupUI/Sources/TwoFactorAuthSplashScreen.swift b/submodules/PasswordSetupUI/Sources/TwoFactorAuthSplashScreen.swift index 66e73dbaff..6b0209ed8c 100644 --- a/submodules/PasswordSetupUI/Sources/TwoFactorAuthSplashScreen.swift +++ b/submodules/PasswordSetupUI/Sources/TwoFactorAuthSplashScreen.swift @@ -57,7 +57,7 @@ public final class TwoFactorAuthSplashScreen: ViewController { self.presentationData = self.sharedContext.currentPresentationData.with { $0 } let defaultTheme = NavigationBarTheme(rootControllerTheme: self.presentationData.theme) - let navigationBarTheme = NavigationBarTheme(buttonColor: defaultTheme.buttonColor, disabledButtonColor: defaultTheme.disabledButtonColor, primaryTextColor: defaultTheme.primaryTextColor, backgroundColor: .clear, enableBackgroundBlur: false, separatorColor: .clear, badgeBackgroundColor: defaultTheme.badgeBackgroundColor, badgeStrokeColor: defaultTheme.badgeStrokeColor, badgeTextColor: defaultTheme.badgeTextColor) + let navigationBarTheme = NavigationBarTheme(overallDarkAppearance: defaultTheme.overallDarkAppearance, buttonColor: defaultTheme.buttonColor, disabledButtonColor: defaultTheme.disabledButtonColor, primaryTextColor: defaultTheme.primaryTextColor, backgroundColor: .clear, enableBackgroundBlur: false, separatorColor: .clear, badgeBackgroundColor: defaultTheme.badgeBackgroundColor, badgeStrokeColor: defaultTheme.badgeStrokeColor, badgeTextColor: defaultTheme.badgeTextColor) super.init(navigationBarPresentationData: NavigationBarPresentationData(theme: navigationBarTheme, strings: NavigationBarStrings(back: self.presentationData.strings.Common_Back, close: self.presentationData.strings.Common_Close))) diff --git a/submodules/QrCodeUI/Sources/QrCodeScanScreen.swift b/submodules/QrCodeUI/Sources/QrCodeScanScreen.swift index 3649da5e39..ceab808e4a 100644 --- a/submodules/QrCodeUI/Sources/QrCodeScanScreen.swift +++ b/submodules/QrCodeUI/Sources/QrCodeScanScreen.swift @@ -77,7 +77,7 @@ public final class QrCodeScanScreen: ViewController { self.presentationData = context.sharedContext.currentPresentationData.with { $0 } - let navigationBarTheme = NavigationBarTheme(buttonColor: .white, disabledButtonColor: .white, primaryTextColor: .white, backgroundColor: .clear, enableBackgroundBlur: false, separatorColor: .clear, badgeBackgroundColor: .clear, badgeStrokeColor: .clear, badgeTextColor: .clear) + let navigationBarTheme = NavigationBarTheme(overallDarkAppearance: self.presentationData.theme.overallDarkAppearance, buttonColor: .white, disabledButtonColor: .white, primaryTextColor: .white, backgroundColor: .clear, enableBackgroundBlur: false, separatorColor: .clear, badgeBackgroundColor: .clear, badgeStrokeColor: .clear, badgeTextColor: .clear) super.init(navigationBarPresentationData: NavigationBarPresentationData(theme: navigationBarTheme, strings: NavigationBarStrings(back: self.presentationData.strings.Common_Back, close: self.presentationData.strings.Common_Close))) diff --git a/submodules/SettingsUI/Sources/Language Selection/LocalizationListController.swift b/submodules/SettingsUI/Sources/Language Selection/LocalizationListController.swift index 8417ebce3b..fe53db16ad 100644 --- a/submodules/SettingsUI/Sources/Language Selection/LocalizationListController.swift +++ b/submodules/SettingsUI/Sources/Language Selection/LocalizationListController.swift @@ -86,7 +86,7 @@ public class LocalizationListController: ViewController { private func updateThemeAndStrings() { self.statusBar.statusBarStyle = self.presentationData.theme.rootController.statusBarStyle.style - self.navigationBar?.updatePresentationData(NavigationBarPresentationData(presentationData: self.presentationData)) + self.navigationBar?.updatePresentationData(NavigationBarPresentationData(presentationData: self.presentationData), transition: .immediate) self.searchContentNode?.updateThemeAndPlaceholder(theme: self.presentationData.theme, placeholder: self.presentationData.strings.Common_Search) self.title = self.presentationData.strings.Settings_AppLanguage self.navigationItem.backBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.Common_Back, style: .plain, target: nil, action: nil) diff --git a/submodules/SettingsUI/Sources/Notifications/Exceptions/NotificationExceptions.swift b/submodules/SettingsUI/Sources/Notifications/Exceptions/NotificationExceptions.swift index 7cdd409463..dc30eee8f3 100644 --- a/submodules/SettingsUI/Sources/Notifications/Exceptions/NotificationExceptions.swift +++ b/submodules/SettingsUI/Sources/Notifications/Exceptions/NotificationExceptions.swift @@ -90,7 +90,7 @@ public class NotificationExceptionsController: ViewController { private func updateThemeAndStrings() { self.statusBar.statusBarStyle = self.presentationData.theme.rootController.statusBarStyle.style - self.navigationBar?.updatePresentationData(NavigationBarPresentationData(presentationData: self.presentationData)) + self.navigationBar?.updatePresentationData(NavigationBarPresentationData(presentationData: self.presentationData), transition: .immediate) self.searchContentNode?.updateThemeAndPlaceholder(theme: self.presentationData.theme, placeholder: self.presentationData.strings.Common_Search) self.title = self.presentationData.strings.Notifications_ExceptionsTitle self.navigationItem.backBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.Common_Back, style: .plain, target: nil, action: nil) diff --git a/submodules/SettingsUI/Sources/Privacy and Security/PrivacyIntroController.swift b/submodules/SettingsUI/Sources/Privacy and Security/PrivacyIntroController.swift index 73a50a6592..70454ac895 100644 --- a/submodules/SettingsUI/Sources/Privacy and Security/PrivacyIntroController.swift +++ b/submodules/SettingsUI/Sources/Privacy and Security/PrivacyIntroController.swift @@ -161,7 +161,7 @@ public final class PrivacyIntroController: ViewController { private func updateThemeAndStrings() { self.statusBar.statusBarStyle = self.presentationData.theme.rootController.statusBarStyle.style - self.navigationBar?.updatePresentationData(NavigationBarPresentationData(presentationData: self.presentationData)) + self.navigationBar?.updatePresentationData(NavigationBarPresentationData(presentationData: self.presentationData), transition: .immediate) self.navigationItem.backBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.Common_Back, style: .plain, target: nil, action: nil) self.controllerNode.updatePresentationData(self.presentationData) } diff --git a/submodules/SettingsUI/Sources/Themes/ThemePreviewController.swift b/submodules/SettingsUI/Sources/Themes/ThemePreviewController.swift index 2713ca2a34..26504db15a 100644 --- a/submodules/SettingsUI/Sources/Themes/ThemePreviewController.swift +++ b/submodules/SettingsUI/Sources/Themes/ThemePreviewController.swift @@ -157,7 +157,7 @@ public final class ThemePreviewController: ViewController { let titleView = CounterControllerTitleView(theme: strongSelf.previewTheme) titleView.title = CounterControllerTitle(title: themeName, counter: hasInstallsCount ? strongSelf.presentationData.strings.Theme_UsersCount(max(1, theme.installCount ?? 0)) : "") strongSelf.navigationItem.titleView = titleView - strongSelf.navigationBar?.updatePresentationData(NavigationBarPresentationData(presentationTheme: presentationTheme, presentationStrings: strongSelf.presentationData.strings)) + strongSelf.navigationBar?.updatePresentationData(NavigationBarPresentationData(presentationTheme: presentationTheme, presentationStrings: strongSelf.presentationData.strings), transition: .immediate) } }) diff --git a/submodules/StatisticsUI/Sources/BackButton.swift b/submodules/StatisticsUI/Sources/BackButton.swift index 271cae3d65..ff04a480c3 100644 --- a/submodules/StatisticsUI/Sources/BackButton.swift +++ b/submodules/StatisticsUI/Sources/BackButton.swift @@ -149,7 +149,7 @@ final class PeerInfoHeaderNavigationButton: HighlightableButtonNode { case .back: text = presentationData.strings.Common_Back accessibilityText = presentationData.strings.Common_Back - icon = NavigationBar.backArrowImage(color: .white) + icon = navigationBarBackArrowImage(color: .white) case .edit: text = presentationData.strings.Common_Edit accessibilityText = text diff --git a/submodules/TelegramPermissionsUI/Sources/PermissionController.swift b/submodules/TelegramPermissionsUI/Sources/PermissionController.swift index 9589dcc71d..5d9e099c44 100644 --- a/submodules/TelegramPermissionsUI/Sources/PermissionController.swift +++ b/submodules/TelegramPermissionsUI/Sources/PermissionController.swift @@ -38,7 +38,7 @@ public final class PermissionController: ViewController { let navigationBarPresentationData: NavigationBarPresentationData if splashScreen { - navigationBarPresentationData = NavigationBarPresentationData(theme: NavigationBarTheme(buttonColor: self.presentationData.theme.rootController.navigationBar.accentTextColor, disabledButtonColor: self.presentationData.theme.rootController.navigationBar.disabledButtonColor, primaryTextColor: self.presentationData.theme.rootController.navigationBar.primaryTextColor, backgroundColor: .clear, enableBackgroundBlur: false, separatorColor: .clear, badgeBackgroundColor: .clear, badgeStrokeColor: .clear, badgeTextColor: .clear), strings: NavigationBarStrings(presentationStrings: self.presentationData.strings)) + navigationBarPresentationData = NavigationBarPresentationData(theme: NavigationBarTheme(overallDarkAppearance: self.presentationData.theme.overallDarkAppearance, buttonColor: self.presentationData.theme.rootController.navigationBar.accentTextColor, disabledButtonColor: self.presentationData.theme.rootController.navigationBar.disabledButtonColor, primaryTextColor: self.presentationData.theme.rootController.navigationBar.primaryTextColor, backgroundColor: .clear, enableBackgroundBlur: false, separatorColor: .clear, badgeBackgroundColor: .clear, badgeStrokeColor: .clear, badgeTextColor: .clear), strings: NavigationBarStrings(presentationStrings: self.presentationData.strings)) } else { navigationBarPresentationData = NavigationBarPresentationData(presentationData: self.presentationData) } @@ -88,12 +88,12 @@ public final class PermissionController: ViewController { let navigationBarPresentationData: NavigationBarPresentationData if self.splashScreen { - navigationBarPresentationData = NavigationBarPresentationData(theme: NavigationBarTheme(buttonColor: self.presentationData.theme.rootController.navigationBar.accentTextColor, disabledButtonColor: self.presentationData.theme.rootController.navigationBar.disabledButtonColor, primaryTextColor: self.presentationData.theme.rootController.navigationBar.primaryTextColor, backgroundColor: .clear, enableBackgroundBlur: false, separatorColor: .clear, badgeBackgroundColor: .clear, badgeStrokeColor: .clear, badgeTextColor: .clear), strings: NavigationBarStrings(presentationStrings: self.presentationData.strings)) + navigationBarPresentationData = NavigationBarPresentationData(theme: NavigationBarTheme(overallDarkAppearance: self.presentationData.theme.overallDarkAppearance, buttonColor: self.presentationData.theme.rootController.navigationBar.accentTextColor, disabledButtonColor: self.presentationData.theme.rootController.navigationBar.disabledButtonColor, primaryTextColor: self.presentationData.theme.rootController.navigationBar.primaryTextColor, backgroundColor: .clear, enableBackgroundBlur: false, separatorColor: .clear, badgeBackgroundColor: .clear, badgeStrokeColor: .clear, badgeTextColor: .clear), strings: NavigationBarStrings(presentationStrings: self.presentationData.strings)) } else { navigationBarPresentationData = NavigationBarPresentationData(presentationData: self.presentationData) } - self.navigationBar?.updatePresentationData(navigationBarPresentationData) + self.navigationBar?.updatePresentationData(navigationBarPresentationData, transition: .immediate) self.navigationItem.backBarButtonItem = UIBarButtonItem(title: nil, style: .plain, target: nil, action: nil) if self.navigationItem.rightBarButtonItem != nil { self.navigationItem.rightBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.Permissions_Skip, style: .plain, target: self, action: #selector(PermissionController.nextPressed)) diff --git a/submodules/TelegramPresentationData/Sources/ComponentsThemes.swift b/submodules/TelegramPresentationData/Sources/ComponentsThemes.swift index 3b76534e4e..5cc4393c78 100644 --- a/submodules/TelegramPresentationData/Sources/ComponentsThemes.swift +++ b/submodules/TelegramPresentationData/Sources/ComponentsThemes.swift @@ -46,9 +46,26 @@ public extension ToolbarTheme { } public extension NavigationBarTheme { - convenience init(rootControllerTheme: PresentationTheme, enableBackgroundBlur: Bool = true, hideBackground: Bool = false, hideBadge: Bool = false, hideSeparator: Bool = false) { + convenience init(rootControllerTheme: PresentationTheme, enableBackgroundBlur: Bool = true, hideBackground: Bool = false, hideBadge: Bool = false, hideSeparator: Bool = false, edgeEffectColor: UIColor? = nil, style: NavigationBar.Style = .legacy) { let theme = rootControllerTheme.rootController.navigationBar - self.init(buttonColor: theme.buttonColor, disabledButtonColor: theme.disabledButtonColor, primaryTextColor: theme.primaryTextColor, backgroundColor: hideBackground ? .clear : theme.blurredBackgroundColor, opaqueBackgroundColor: hideBackground ? .clear : theme.opaqueBackgroundColor, enableBackgroundBlur: enableBackgroundBlur, separatorColor: hideBackground || hideSeparator ? .clear : theme.separatorColor, badgeBackgroundColor: hideBadge ? .clear : theme.badgeBackgroundColor, badgeStrokeColor: hideBadge ? .clear : theme.badgeStrokeColor, badgeTextColor: hideBadge ? .clear : theme.badgeTextColor) + + let buttonColor: UIColor + let disabledButtonColor: UIColor + let badgeBackgroundColor: UIColor + let badgeTextColor: UIColor + if case .glass = style { + buttonColor = rootControllerTheme.chat.inputPanel.panelControlColor + disabledButtonColor = buttonColor.withMultipliedAlpha(0.5) + badgeBackgroundColor = rootControllerTheme.chat.inputPanel.panelControlColor + badgeTextColor = rootControllerTheme.list.itemCheckColors.foregroundColor + } else { + buttonColor = theme.buttonColor + disabledButtonColor = theme.disabledButtonColor + badgeBackgroundColor = theme.badgeBackgroundColor + badgeTextColor = theme.badgeTextColor + } + + self.init(overallDarkAppearance: rootControllerTheme.overallDarkAppearance, buttonColor: buttonColor, disabledButtonColor: disabledButtonColor, primaryTextColor: theme.primaryTextColor, backgroundColor: hideBackground ? .clear : theme.blurredBackgroundColor, opaqueBackgroundColor: hideBackground ? .clear : theme.opaqueBackgroundColor, enableBackgroundBlur: enableBackgroundBlur, separatorColor: hideBackground || hideSeparator ? .clear : theme.separatorColor, badgeBackgroundColor: hideBadge ? .clear : badgeBackgroundColor, badgeStrokeColor: .clear, badgeTextColor: hideBadge ? .clear : badgeTextColor, edgeEffectColor: edgeEffectColor, style: style) } } @@ -59,16 +76,16 @@ public extension NavigationBarStrings { } public extension NavigationBarPresentationData { - convenience init(presentationData: PresentationData) { - self.init(theme: NavigationBarTheme(rootControllerTheme: presentationData.theme), strings: NavigationBarStrings(presentationStrings: presentationData.strings)) + convenience init(presentationData: PresentationData, style: NavigationBar.Style = .legacy) { + self.init(theme: NavigationBarTheme(rootControllerTheme: presentationData.theme, style: style), strings: NavigationBarStrings(presentationStrings: presentationData.strings)) } - convenience init(presentationData: PresentationData, hideBackground: Bool, hideBadge: Bool, hideSeparator: Bool = false) { - self.init(theme: NavigationBarTheme(rootControllerTheme: presentationData.theme, hideBackground: hideBackground, hideBadge: hideBadge, hideSeparator: hideSeparator), strings: NavigationBarStrings(presentationStrings: presentationData.strings)) + convenience init(presentationData: PresentationData, hideBackground: Bool, hideBadge: Bool, hideSeparator: Bool = false, style: NavigationBar.Style = .legacy) { + self.init(theme: NavigationBarTheme(rootControllerTheme: presentationData.theme, hideBackground: hideBackground, hideBadge: hideBadge, hideSeparator: hideSeparator, style: style), strings: NavigationBarStrings(presentationStrings: presentationData.strings)) } - convenience init(presentationTheme: PresentationTheme, presentationStrings: PresentationStrings) { - self.init(theme: NavigationBarTheme(rootControllerTheme: presentationTheme), strings: NavigationBarStrings(presentationStrings: presentationStrings)) + convenience init(presentationTheme: PresentationTheme, presentationStrings: PresentationStrings, style: NavigationBar.Style = .legacy) { + self.init(theme: NavigationBarTheme(rootControllerTheme: presentationTheme, style: style), strings: NavigationBarStrings(presentationStrings: presentationStrings)) } } diff --git a/submodules/TelegramPresentationData/Sources/Resources/PresentationResourcesRootController.swift b/submodules/TelegramPresentationData/Sources/Resources/PresentationResourcesRootController.swift index d486af773d..4ae03565ee 100644 --- a/submodules/TelegramPresentationData/Sources/Resources/PresentationResourcesRootController.swift +++ b/submodules/TelegramPresentationData/Sources/Resources/PresentationResourcesRootController.swift @@ -70,7 +70,7 @@ public struct PresentationResourcesRootController { public static func navigationCompactSearchIcon(_ theme: PresentationTheme) -> UIImage? { return theme.image(PresentationResourceKey.navigationCompactSearchIcon.rawValue, { theme in - return generateTintedImage(image: UIImage(bundleImageName: "Chat List/SearchIcon"), color: theme.rootController.navigationBar.accentTextColor) + return generateTintedImage(image: UIImage(bundleImageName: "Chat List/SearchIcon"), color: theme.chat.inputPanel.panelControlColor) }) } @@ -82,7 +82,7 @@ public struct PresentationResourcesRootController { public static func navigationCompactTagsSearchIcon(_ theme: PresentationTheme) -> UIImage? { return theme.image(PresentationResourceKey.navigationCompactTagsSearchIcon.rawValue, { theme in - return generateTintedImage(image: UIImage(bundleImageName: "Chat/NavigationSearchTagsIcon"), color: theme.rootController.navigationBar.accentTextColor) + return generateTintedImage(image: UIImage(bundleImageName: "Chat/NavigationSearchTagsIcon"), color: theme.chat.inputPanel.panelControlColor) }) } diff --git a/submodules/TelegramUI/BUILD b/submodules/TelegramUI/BUILD index 83a0f17408..f9b4f29136 100644 --- a/submodules/TelegramUI/BUILD +++ b/submodules/TelegramUI/BUILD @@ -497,6 +497,7 @@ swift_library( "//submodules/TelegramUI/Components/AttachmentFileController", "//submodules/TelegramUI/Components/Contacts/NewContactScreen", "//submodules/TelegramUI/Components/Chat/ChatSendAsContextMenu", + "//submodules/TelegramUI/Components/NavigationBarImpl", ] + select({ "@build_bazel_rules_apple//apple:ios_arm64": appcenter_targets, "//build-system:ios_sim_arm64": [], diff --git a/submodules/TelegramUI/Components/BackButtonComponent/Sources/BackButtonComponent.swift b/submodules/TelegramUI/Components/BackButtonComponent/Sources/BackButtonComponent.swift index d31c4d1ea3..01f09a6674 100644 --- a/submodules/TelegramUI/Components/BackButtonComponent/Sources/BackButtonComponent.swift +++ b/submodules/TelegramUI/Components/BackButtonComponent/Sources/BackButtonComponent.swift @@ -88,7 +88,7 @@ public final class BackButtonComponent: Component { self.component = component if self.arrowView.image == nil { - self.arrowView.image = NavigationBar.backArrowImage(color: .white)?.withRenderingMode(.alwaysTemplate) + self.arrowView.image = navigationBarBackArrowImage(color: .white)?.withRenderingMode(.alwaysTemplate) } self.arrowView.tintColor = component.color diff --git a/submodules/TelegramUI/Components/Calls/CallScreen/Sources/Components/BackButtonView.swift b/submodules/TelegramUI/Components/Calls/CallScreen/Sources/Components/BackButtonView.swift index 696d0a5727..a8a355ba3d 100644 --- a/submodules/TelegramUI/Components/Calls/CallScreen/Sources/Components/BackButtonView.swift +++ b/submodules/TelegramUI/Components/Calls/CallScreen/Sources/Components/BackButtonView.swift @@ -29,7 +29,7 @@ final class BackButtonView: HighlightableButton { var pressAction: (() -> Void)? override init(frame: CGRect) { - self.iconView = UIImageView(image: NavigationBar.backArrowImage(color: .white)) + self.iconView = UIImageView(image: navigationBarBackArrowImage(color: .white)) self.iconView.isUserInteractionEnabled = false self.textView = TextView() diff --git a/submodules/TelegramUI/Components/Chat/ChatAvatarNavigationNode/Sources/ChatAvatarNavigationNode.swift b/submodules/TelegramUI/Components/Chat/ChatAvatarNavigationNode/Sources/ChatAvatarNavigationNode.swift index 671acfb779..4687fad8fe 100644 --- a/submodules/TelegramUI/Components/Chat/ChatAvatarNavigationNode/Sources/ChatAvatarNavigationNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatAvatarNavigationNode/Sources/ChatAvatarNavigationNode.swift @@ -75,12 +75,8 @@ public final class ChatAvatarNavigationNode: ASDisplayNode { strongSelf.contextAction?(strongSelf.containerNode, gesture) } - self.containerNode.frame = CGRect(origin: CGPoint(), size: CGSize(width: 37.0, height: 37.0)).offsetBy(dx: 10.0, dy: 1.0) - self.avatarNode.frame = self.containerNode.bounds - - #if DEBUG - //self.hasUnseenStories = true - #endif + self.containerNode.frame = CGRect(origin: CGPoint(), size: CGSize(width: 44.0, height: 44.0)) + self.avatarNode.frame = self.containerNode.bounds.insetBy(dx: 3.0, dy: 3.0) } deinit { @@ -265,7 +261,7 @@ public final class ChatAvatarNavigationNode: ASDisplayNode { } override public func calculateSizeThatFits(_ constrainedSize: CGSize) -> CGSize { - return CGSize(width: 37.0, height: 37.0) + return CGSize(width: 44.0, height: 44.0) } public func onLayout() { diff --git a/submodules/TelegramUI/Components/Chat/ChatRecentActionsController/Sources/ChatRecentActionsController.swift b/submodules/TelegramUI/Components/Chat/ChatRecentActionsController/Sources/ChatRecentActionsController.swift index 459319a3d5..ff8c1cd0ba 100644 --- a/submodules/TelegramUI/Components/Chat/ChatRecentActionsController/Sources/ChatRecentActionsController.swift +++ b/submodules/TelegramUI/Components/Chat/ChatRecentActionsController/Sources/ChatRecentActionsController.swift @@ -263,7 +263,7 @@ public final class ChatRecentActionsController: TelegramBaseController { self.navigationItem.setRightBarButton(rightButton.buttonItem, animated: false) self.statusBar.statusBarStyle = self.presentationData.theme.rootController.statusBarStyle.style - self.navigationBar?.updatePresentationData(NavigationBarPresentationData(presentationData: self.presentationData)) + self.navigationBar?.updatePresentationData(NavigationBarPresentationData(presentationData: self.presentationData), transition: .immediate) self.controllerNode.updatePresentationData(self.presentationData) } diff --git a/submodules/TelegramUI/Components/ChatListHeaderComponent/Sources/ChatListHeaderComponent.swift b/submodules/TelegramUI/Components/ChatListHeaderComponent/Sources/ChatListHeaderComponent.swift index 571c5cfe77..11c8d8b3b0 100644 --- a/submodules/TelegramUI/Components/ChatListHeaderComponent/Sources/ChatListHeaderComponent.swift +++ b/submodules/TelegramUI/Components/ChatListHeaderComponent/Sources/ChatListHeaderComponent.swift @@ -715,7 +715,7 @@ public final class ChatListHeaderComponent: Component { } } - public final class View: UIView, NavigationBarHeaderView { + public final class View: UIView { private var component: ChatListHeaderComponent? private weak var state: EmptyComponentState? @@ -770,17 +770,6 @@ public final class ChatListHeaderComponent: Component { return self.effectiveContentView?.titleContentView?.view } - public func makeTransitionBackArrowView(accentColor: UIColor) -> UIView? { - if let backArrowView = self.backArrowView { - let view = UIImageView() - view.image = NavigationBar.backArrowImage(color: accentColor) - view.frame = backArrowView.convert(backArrowView.bounds, to: self) - return view - } else { - return nil - } - } - public func storyPeerListView() -> StoryPeerListComponent.View? { return self.storyPeerList?.view as? StoryPeerListComponent.View } diff --git a/submodules/TelegramUI/Components/ChatTitleView/BUILD b/submodules/TelegramUI/Components/ChatTitleView/BUILD index 26fb6712c8..a136c8871c 100644 --- a/submodules/TelegramUI/Components/ChatTitleView/BUILD +++ b/submodules/TelegramUI/Components/ChatTitleView/BUILD @@ -31,6 +31,7 @@ swift_library( "//submodules/TelegramUI/Components/AnimationCache:AnimationCache", "//submodules/TelegramUI/Components/MultiAnimationRenderer:MultiAnimationRenderer", "//submodules/Components/ComponentDisplayAdapters:ComponentDisplayAdapters", + "//submodules/TelegramUI/Components/GlassBackgroundComponent", ], visibility = [ "//visibility:public", diff --git a/submodules/TelegramUI/Components/ChatTitleView/Sources/ChatTitleView.swift b/submodules/TelegramUI/Components/ChatTitleView/Sources/ChatTitleView.swift index d1226299bd..fa1c80e8fb 100644 --- a/submodules/TelegramUI/Components/ChatTitleView/Sources/ChatTitleView.swift +++ b/submodules/TelegramUI/Components/ChatTitleView/Sources/ChatTitleView.swift @@ -22,6 +22,7 @@ import EmojiStatusComponent import AnimationCache import MultiAnimationRenderer import ComponentDisplayAdapters +import GlassBackgroundComponent private let titleFont = Font.with(size: 17.0, design: .regular, weight: .semibold, traits: [.monospacedNumbers]) private let subtitleFont = Font.regular(13.0) @@ -170,7 +171,6 @@ public final class ChatTitleView: UIView, NavigationBarTitleView { private let context: AccountContext private var theme: PresentationTheme - private var hasEmbeddedTitleContent: Bool = false private var strings: PresentationStrings private var dateTimeFormat: PresentationDateTimeFormat private var nameDisplayOrder: PresentationPersonNameOrder @@ -178,6 +178,7 @@ public final class ChatTitleView: UIView, NavigationBarTitleView { private let animationRenderer: MultiAnimationRenderer private let contentContainer: ASDisplayNode + private let backgroundView: GlassBackgroundView public let titleContainerView: PortalSourceView public let titleTextNode: ImmediateAnimatedCountLabelNode public let titleLeftIconNode: ASImageNode @@ -239,7 +240,7 @@ public final class ChatTitleView: UIView, NavigationBarTitleView { public var titleContent: ChatTitleContent? { didSet { if let titleContent = self.titleContent { - let titleTheme = self.hasEmbeddedTitleContent ? defaultDarkPresentationTheme : self.theme + let titleTheme = self.theme var segments: [AnimatedCountLabelNode.Segment] = [] var titleLeftIcon: ChatTitleIcon = .none @@ -487,7 +488,7 @@ public final class ChatTitleView: UIView, NavigationBarTitleView { } } - let titleTheme = self.hasEmbeddedTitleContent ? defaultDarkPresentationTheme : self.theme + let titleTheme = self.theme var state = ChatTitleActivityNodeState.none switch self.networkState { @@ -739,6 +740,8 @@ public final class ChatTitleView: UIView, NavigationBarTitleView { self.contentContainer = ASDisplayNode() + self.backgroundView = GlassBackgroundView() + self.titleContainerView = PortalSourceView() self.titleTextNode = ImmediateAnimatedCountLabelNode() @@ -770,6 +773,7 @@ public final class ChatTitleView: UIView, NavigationBarTitleView { self.accessibilityTraits = .header self.addSubnode(self.contentContainer) + self.contentContainer.view.addSubview(self.backgroundView) self.titleContainerView.addSubnode(self.titleTextNode) self.contentContainer.view.addSubview(self.titleContainerView) self.contentContainer.addSubnode(self.activityNode) @@ -818,10 +822,9 @@ public final class ChatTitleView: UIView, NavigationBarTitleView { } } - public func updateThemeAndStrings(theme: PresentationTheme, strings: PresentationStrings, hasEmbeddedTitleContent: Bool) { - if self.theme !== theme || self.strings !== strings || self.hasEmbeddedTitleContent != hasEmbeddedTitleContent { + public func updateThemeAndStrings(theme: PresentationTheme, strings: PresentationStrings) { + if self.theme !== theme || self.strings !== strings { self.theme = theme - self.hasEmbeddedTitleContent = hasEmbeddedTitleContent self.strings = strings let titleContent = self.titleContent @@ -986,107 +989,86 @@ public final class ChatTitleView: UIView, NavigationBarTitleView { } let statusSpacing: CGFloat = 3.0 - let titleSideInset: CGFloat = 6.0 + let titleSideInset: CGFloat = 12.0 + 8.0 var titleFrame: CGRect - if size.height > 40.0 { - var titleInsets: UIEdgeInsets = .zero - if case .emojiStatus = self.titleVerifiedIcon, verifiedIconWidth > 0.0 { - titleInsets.left = verifiedIconWidth - } - - var titleSize = self.titleTextNode.updateLayout(size: CGSize(width: clearBounds.width - leftIconWidth - credibilityIconWidth - verifiedIconWidth - statusIconWidth - rightIconWidth - titleSideInset * 2.0, height: size.height), insets: titleInsets, animated: titleTransition.isAnimated) - titleSize.width += credibilityIconWidth - titleSize.width += verifiedIconWidth - if statusIconWidth > 0.0 { - titleSize.width += statusIconWidth - if credibilityIconWidth > 0.0 { - titleSize.width += statusSpacing - } - } - - let activitySize = self.activityNode.updateLayout(CGSize(width: clearBounds.size.width - titleSideInset * 2.0, height: clearBounds.size.height), alignment: .center) - let titleInfoSpacing: CGFloat = 0.0 - - if activitySize.height.isZero { - titleFrame = CGRect(origin: CGPoint(x: floor((clearBounds.width - titleSize.width) / 2.0), y: floor((size.height - titleSize.height) / 2.0)), size: titleSize) - if titleFrame.size.width < size.width { - titleFrame.origin.x = -clearBounds.minX + floor((size.width - titleFrame.width) / 2.0) - } - titleTransition.updateFrameAdditive(view: self.titleContainerView, frame: titleFrame) - titleTransition.updateFrameAdditive(node: self.titleTextNode, frame: CGRect(origin: CGPoint(), size: titleFrame.size)) - } else { - let combinedHeight = titleSize.height + activitySize.height + titleInfoSpacing - - titleFrame = CGRect(origin: CGPoint(x: floor((clearBounds.width - titleSize.width) / 2.0), y: floor((size.height - combinedHeight) / 2.0)), size: titleSize) - if titleFrame.size.width < size.width { - titleFrame.origin.x = -clearBounds.minX + floor((size.width - titleFrame.width) / 2.0) - } - titleFrame.origin.x = max(titleFrame.origin.x, clearBounds.minX + leftIconWidth) - titleTransition.updateFrameAdditive(view: self.titleContainerView, frame: titleFrame) - titleTransition.updateFrameAdditive(node: self.titleTextNode, frame: CGRect(origin: CGPoint(), size: titleFrame.size)) - - var activityFrame = CGRect(origin: CGPoint(x: floor((clearBounds.width - activitySize.width) / 2.0), y: floor((size.height - combinedHeight) / 2.0) + titleSize.height + titleInfoSpacing), size: activitySize) - if activitySize.width < size.width { - activityFrame.origin.x = -clearBounds.minX + floor((size.width - activityFrame.width) / 2.0) - } - titleTransition.updateFrameAdditiveToCenter(node: self.activityNode, frame: activityFrame) - } - - if let image = self.titleLeftIconNode.image { - titleTransition.updateFrame(node: self.titleLeftIconNode, frame: CGRect(origin: CGPoint(x: -image.size.width - 3.0 - UIScreenPixel, y: 4.0), size: image.size)) - } - - var nextIconX: CGFloat = titleFrame.width - - titleTransition.updateFrame(view: self.titleVerifiedIconView, frame: CGRect(origin: CGPoint(x: 0.0, y: floor((titleFrame.height - titleVerifiedSize.height) / 2.0)), size: titleVerifiedSize)) - - self.titleCredibilityIconView.frame = CGRect(origin: CGPoint(x: nextIconX - titleCredibilitySize.width, y: floor((titleFrame.height - titleCredibilitySize.height) / 2.0)), size: titleCredibilitySize) - nextIconX -= titleCredibilitySize.width - if credibilityIconWidth > 0.0 { - nextIconX -= statusSpacing - } - - self.titleStatusIconView.frame = CGRect(origin: CGPoint(x: nextIconX - titleStatusSize.width, y: floor((titleFrame.height - titleStatusSize.height) / 2.0)), size: titleStatusSize) - nextIconX -= titleStatusSize.width - if let image = self.titleRightIconNode.image { - self.titleRightIconNode.frame = CGRect(origin: CGPoint(x: titleFrame.width + 3.0 + UIScreenPixel, y: 6.0), size: image.size) - } - } else { - let titleSize = self.titleTextNode.updateLayout(size: CGSize(width: floor(clearBounds.width / 2.0 - leftIconWidth - credibilityIconWidth - verifiedIconWidth - statusIconWidth - rightIconWidth - titleSideInset * 2.0), height: size.height), animated: titleTransition.isAnimated) - let activitySize = self.activityNode.updateLayout(CGSize(width: floor(clearBounds.width / 2.0), height: size.height), alignment: .center) - - let titleInfoSpacing: CGFloat = 8.0 - let combinedWidth = titleSize.width + leftIconWidth + credibilityIconWidth + verifiedIconWidth + statusIconWidth + rightIconWidth + activitySize.width + titleInfoSpacing - - titleFrame = CGRect(origin: CGPoint(x: leftIconWidth + floor((clearBounds.width - combinedWidth) / 2.0), y: floor((size.height - titleSize.height) / 2.0)), size: titleSize) - - titleTransition.updateFrameAdditiveToCenter(view: self.titleContainerView, frame: titleFrame) - titleTransition.updateFrameAdditiveToCenter(node: self.titleTextNode, frame: CGRect(origin: CGPoint(), size: titleFrame.size)) - - titleTransition.updateFrameAdditiveToCenter(node: self.activityNode, frame: CGRect(origin: CGPoint(x: floor((clearBounds.width - combinedWidth) / 2.0 + titleSize.width + leftIconWidth + credibilityIconWidth + verifiedIconWidth + statusIconWidth + rightIconWidth + titleInfoSpacing), y: floor((size.height - activitySize.height) / 2.0)), size: activitySize)) - - if let image = self.titleLeftIconNode.image { - self.titleLeftIconNode.frame = CGRect(origin: CGPoint(x: titleFrame.minX, y: titleFrame.minY + 4.0), size: image.size) - } - - var nextIconX: CGFloat = titleFrame.maxX - - self.titleVerifiedIconView.frame = CGRect(origin: CGPoint(x: 0.0, y: floor((titleFrame.height - titleVerifiedSize.height) / 2.0)), size: titleVerifiedSize) - - self.titleCredibilityIconView.frame = CGRect(origin: CGPoint(x: nextIconX - titleCredibilitySize.width, y: floor((titleFrame.height - titleCredibilitySize.height) / 2.0)), size: titleCredibilitySize) - nextIconX -= titleCredibilitySize.width - - titleTransition.updateFrame(view: self.titleStatusIconView, frame: CGRect(origin: CGPoint(x: nextIconX - titleStatusSize.width, y: floor((titleFrame.height - titleStatusSize.height) / 2.0)), size: titleStatusSize)) - nextIconX -= titleStatusSize.width - - if let image = self.titleRightIconNode.image { - titleTransition.updateFrame(node: self.titleRightIconNode, frame: CGRect(origin: CGPoint(x: titleFrame.maxX - image.size.width, y: titleFrame.minY + 6.0), size: image.size)) + var titleInsets: UIEdgeInsets = .zero + if case .emojiStatus = self.titleVerifiedIcon, verifiedIconWidth > 0.0 { + titleInsets.left = verifiedIconWidth + } + + var titleSize = self.titleTextNode.updateLayout(size: CGSize(width: clearBounds.width - leftIconWidth - credibilityIconWidth - verifiedIconWidth - statusIconWidth - rightIconWidth - titleSideInset * 2.0, height: size.height), insets: titleInsets, animated: titleTransition.isAnimated) + titleSize.width += credibilityIconWidth + titleSize.width += verifiedIconWidth + if statusIconWidth > 0.0 { + titleSize.width += statusIconWidth + if credibilityIconWidth > 0.0 { + titleSize.width += statusSpacing } } + let activitySize = self.activityNode.updateLayout(CGSize(width: clearBounds.size.width - titleSideInset * 2.0, height: clearBounds.size.height), alignment: .center) + let titleInfoSpacing: CGFloat = 0.0 + + var activityFrame = CGRect() + + if activitySize.height.isZero { + titleFrame = CGRect(origin: CGPoint(x: floor((clearBounds.width - titleSize.width) / 2.0), y: floor((size.height - titleSize.height) / 2.0)), size: titleSize) + if titleFrame.size.width < size.width { + titleFrame.origin.x = -clearBounds.minX + floor((size.width - titleFrame.width) / 2.0) + } + titleTransition.updateFrameAdditive(view: self.titleContainerView, frame: titleFrame) + titleTransition.updateFrameAdditive(node: self.titleTextNode, frame: CGRect(origin: CGPoint(), size: titleFrame.size)) + } else { + let combinedHeight = titleSize.height + activitySize.height + titleInfoSpacing + + let contentWidth = max(titleSize.width + rightIconWidth, activitySize.width) + var contentX = floor((clearBounds.width - contentWidth) / 2.0) + contentX = max(contentX, clearBounds.minX + 20.0) + + titleFrame = CGRect(origin: CGPoint(x: contentX + floor((contentWidth - titleSize.width) / 2.0), y: floor((size.height - combinedHeight) / 2.0)), size: titleSize) + + titleFrame.origin.x = max(titleFrame.origin.x, clearBounds.minX + leftIconWidth) + titleTransition.updateFrameAdditive(view: self.titleContainerView, frame: titleFrame) + titleTransition.updateFrameAdditive(node: self.titleTextNode, frame: CGRect(origin: CGPoint(), size: titleFrame.size)) + + activityFrame = CGRect(origin: CGPoint(x: titleFrame.minX + floor((titleFrame.width - activitySize.width) / 2.0), y: floor((size.height - combinedHeight) / 2.0) + titleSize.height + titleInfoSpacing), size: activitySize) + titleTransition.updateFrameAdditiveToCenter(node: self.activityNode, frame: activityFrame.offsetBy(dx: activitySize.width * 0.5, dy: 0.0)) + } + + if let image = self.titleLeftIconNode.image { + titleTransition.updateFrame(node: self.titleLeftIconNode, frame: CGRect(origin: CGPoint(x: -image.size.width - 3.0 - UIScreenPixel, y: 4.0), size: image.size)) + } + + var nextIconX: CGFloat = titleFrame.width + + titleTransition.updateFrame(view: self.titleVerifiedIconView, frame: CGRect(origin: CGPoint(x: 0.0, y: floor((titleFrame.height - titleVerifiedSize.height) / 2.0)), size: titleVerifiedSize)) + + self.titleCredibilityIconView.frame = CGRect(origin: CGPoint(x: nextIconX - titleCredibilitySize.width, y: floor((titleFrame.height - titleCredibilitySize.height) / 2.0)), size: titleCredibilitySize) + nextIconX -= titleCredibilitySize.width + if credibilityIconWidth > 0.0 { + nextIconX -= statusSpacing + } + + self.titleStatusIconView.frame = CGRect(origin: CGPoint(x: nextIconX - titleStatusSize.width, y: floor((titleFrame.height - titleStatusSize.height) / 2.0)), size: titleStatusSize) + nextIconX -= titleStatusSize.width + + if let image = self.titleRightIconNode.image { + self.titleRightIconNode.frame = CGRect(origin: CGPoint(x: titleFrame.width + 3.0 + UIScreenPixel, y: 6.0), size: image.size) + } + self.pointerInteraction = PointerInteraction(view: self, style: .rectangle(CGSize(width: titleFrame.width + 16.0, height: 40.0))) + var backgroundFrame = CGRect(origin: CGPoint(x: titleFrame.minX, y: 0.0), size: CGSize(width: titleFrame.width, height: 44.0)) + if !activityFrame.isEmpty { + backgroundFrame.origin.x = min(backgroundFrame.minX, activityFrame.minX) + backgroundFrame.size.width = max(backgroundFrame.maxX, activityFrame.maxX) - backgroundFrame.minX + } + backgroundFrame = backgroundFrame.insetBy(dx: -12.0, dy: 0.0) + let componentTransition = ComponentTransition(transition) + componentTransition.setFrame(view: self.backgroundView, frame: backgroundFrame) + self.backgroundView.update(size: backgroundFrame.size, cornerRadius: backgroundFrame.height * 0.5, isDark: self.theme.overallDarkAppearance, tintColor: .init(kind: .panel, color: UIColor(white: self.theme.overallDarkAppearance ? 0.0 : 1.0, alpha: 0.6)), isInteractive: false, transition: componentTransition) + return titleFrame } @@ -1264,7 +1246,7 @@ public final class ChatTitleComponent: Component { if contentView.titleContent != component.content { contentView.titleContent = component.content } - contentView.updateThemeAndStrings(theme: component.theme, strings: component.strings, hasEmbeddedTitleContent: false) + contentView.updateThemeAndStrings(theme: component.theme, strings: component.strings) let _ = contentView.updateLayout(size: availableSize, clearBounds: CGRect(origin: CGPoint(), size: availableSize), transition: transition.containedViewLayoutTransition) transition.setFrame(view: contentView, frame: CGRect(origin: CGPoint(), size: availableSize)) diff --git a/submodules/TelegramUI/Components/GlassBackgroundComponent/Sources/GlassBackgroundComponent.swift b/submodules/TelegramUI/Components/GlassBackgroundComponent/Sources/GlassBackgroundComponent.swift index 4c1852d959..f1511c4927 100644 --- a/submodules/TelegramUI/Components/GlassBackgroundComponent/Sources/GlassBackgroundComponent.swift +++ b/submodules/TelegramUI/Components/GlassBackgroundComponent/Sources/GlassBackgroundComponent.swift @@ -292,12 +292,14 @@ public class GlassBackgroundView: UIView { public let isDark: Bool public let tintColor: TintColor public let isInteractive: Bool + public let isVisible: Bool - init(shape: Shape, isDark: Bool, tintColor: TintColor, isInteractive: Bool) { + init(shape: Shape, isDark: Bool, tintColor: TintColor, isInteractive: Bool, isVisible: Bool) { self.shape = shape self.isDark = isDark self.tintColor = tintColor self.isInteractive = isInteractive + self.isVisible = isVisible } } @@ -402,11 +404,11 @@ public class GlassBackgroundView: UIView { return nil } - public func update(size: CGSize, cornerRadius: CGFloat, isDark: Bool, tintColor: TintColor, isInteractive: Bool = false, transition: ComponentTransition) { - self.update(size: size, shape: .roundedRect(cornerRadius: cornerRadius), isDark: isDark, tintColor: tintColor, isInteractive: isInteractive, transition: transition) + public func update(size: CGSize, cornerRadius: CGFloat, isDark: Bool, tintColor: TintColor, isInteractive: Bool = false, isVisible: Bool = true, transition: ComponentTransition) { + self.update(size: size, shape: .roundedRect(cornerRadius: cornerRadius), isDark: isDark, tintColor: tintColor, isInteractive: isInteractive, isVisible: isVisible, transition: transition) } - public func update(size: CGSize, shape: Shape, isDark: Bool, tintColor: TintColor, isInteractive: Bool = false, transition: ComponentTransition) { + public func update(size: CGSize, shape: Shape, isDark: Bool, tintColor: TintColor, isInteractive: Bool = false, isVisible: Bool = true, transition: ComponentTransition) { if let nativeView = self.nativeView, let nativeViewClippingContext = self.nativeViewClippingContext, (nativeView.bounds.size != size || nativeViewClippingContext.shape != shape) { nativeViewClippingContext.update(shape: shape, size: size, transition: transition) @@ -468,7 +470,7 @@ public class GlassBackgroundView: UIView { innerBackgroundView.removeFromSuperview() } - let params = Params(shape: shape, isDark: isDark, tintColor: tintColor, isInteractive: isInteractive) + let params = Params(shape: shape, isDark: isDark, tintColor: tintColor, isInteractive: isInteractive, isVisible: isVisible) if self.params != params { self.params = params @@ -498,21 +500,44 @@ public class GlassBackgroundView: UIView { } else { if let nativeParamsView = self.nativeParamsView, let nativeView = self.nativeView { if #available(iOS 26.0, *) { - let glassEffect = UIGlassEffect(style: .regular) - switch tintColor.kind { - case .panel: - glassEffect.tintColor = UIColor(white: isDark ? 0.0 : 1.0, alpha: 0.1) - case .custom: - glassEffect.tintColor = tintColor.color - } - glassEffect.isInteractive = params.isInteractive + var glassEffect: UIGlassEffect? - if transition.animation.isImmediate { - nativeView.effect = glassEffect - } else { - UIView.animate(withDuration: 0.2, animations: { + if isVisible { + let glassEffectValue = UIGlassEffect(style: .regular) + switch tintColor.kind { + case .panel: + glassEffectValue.tintColor = UIColor(white: isDark ? 0.0 : 1.0, alpha: 0.1) + case .custom: + glassEffectValue.tintColor = tintColor.color + } + glassEffectValue.isInteractive = params.isInteractive + glassEffect = glassEffectValue + } + + if glassEffect == nil && nativeView.effect != nil { + if transition.animation.isImmediate { + if #available(iOS 26.2, *) { + } else { + nativeView.effect = UIBlurEffect(style: .regular) + } nativeView.effect = glassEffect - }) + } else { + UIView.animate(withDuration: 0.2, animations: { + if #available(iOS 26.2, *) { + } else { + nativeView.effect = UIBlurEffect(style: .regular) + } + nativeView.effect = glassEffect + }) + } + } else { + if transition.animation.isImmediate { + nativeView.effect = glassEffect + } else { + UIView.animate(withDuration: 0.2, animations: { + nativeView.effect = glassEffect + }) + } } if isDark { diff --git a/submodules/TelegramUI/Components/NavigationBarImpl/BUILD b/submodules/TelegramUI/Components/NavigationBarImpl/BUILD new file mode 100644 index 0000000000..722bffd862 --- /dev/null +++ b/submodules/TelegramUI/Components/NavigationBarImpl/BUILD @@ -0,0 +1,24 @@ +load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library") + +swift_library( + name = "NavigationBarImpl", + module_name = "NavigationBarImpl", + srcs = glob([ + "Sources/**/*.swift", + ]), + copts = [ + "-warnings-as-errors", + ], + deps = [ + "//submodules/AsyncDisplayKit", + "//submodules/Display", + "//submodules/ComponentFlow", + "//submodules/TelegramUI/Components/GlassBackgroundComponent", + "//submodules/TelegramUI/Components/EdgeEffect", + "//submodules/Components/ComponentDisplayAdapters", + "//submodules/Components/MultilineTextComponent", + ], + visibility = [ + "//visibility:public", + ], +) diff --git a/submodules/TelegramUI/Components/NavigationBarImpl/Sources/NavigationBarImpl.swift b/submodules/TelegramUI/Components/NavigationBarImpl/Sources/NavigationBarImpl.swift new file mode 100644 index 0000000000..cb43f1f192 --- /dev/null +++ b/submodules/TelegramUI/Components/NavigationBarImpl/Sources/NavigationBarImpl.swift @@ -0,0 +1,1123 @@ +import Foundation +import UIKit +import Display +import ComponentFlow +import GlassBackgroundComponent +import AsyncDisplayKit +import EdgeEffect +import ComponentDisplayAdapters + +public final class NavigationBarImpl: ASDisplayNode, NavigationBar { + public static var defaultSecondaryContentHeight: CGFloat { + return 38.0 + } + + public static let thinBackArrowImage = generateTintedImage(image: UIImage(bundleImageName: "Navigation/BackArrow"), color: .white)?.withRenderingMode(.alwaysTemplate) + + public static let titleFont = Font.with(size: 17.0, design: .regular, weight: .semibold, traits: [.monospacedNumbers]) + + var presentationData: NavigationBarPresentationData + + private var validLayout: (size: CGSize, defaultHeight: CGFloat, additionalTopHeight: CGFloat, additionalContentHeight: CGFloat, additionalBackgroundHeight: CGFloat, additionalCutout: CGSize?, leftInset: CGFloat, rightInset: CGFloat, appearsHidden: Bool, isLandscape: Bool)? + private var requestedLayout: Bool = false + public var requestContainerLayout: (ContainedViewLayoutTransition) -> Void = { _ in } + + public var backPressed: () -> () = { } + + public var userInfo: Any? + public var makeCustomTransitionNode: ((NavigationBar, Bool) -> CustomNavigationTransitionNode?)? + public var allowsCustomTransition: (() -> Bool)? + + private var collapsed: Bool { + get { + return self.frame.size.height.isLess(than: 44.0) + } + } + + public let stripeNode: ASDisplayNode + public let clippingNode: SparseNode + private let buttonsContainerNode: ASDisplayNode + + public private(set) var contentNode: NavigationBarContentNode? + public private(set) var secondaryContentNode: ASDisplayNode? + public var secondaryContentNodeDisplayFraction: CGFloat = 1.0 + + private var itemTitleListenerKey: Int? + private var itemTitleViewListenerKey: Int? + + private var itemLeftButtonListenerKey: Int? + private var itemLeftButtonSetEnabledListenerKey: Int? + + private var itemRightButtonListenerKey: Int? + private var itemRightButtonsListenerKey: Int? + + private var itemBadgeListenerKey: Int? + + private var hintAnimateTitleNodeOnNextLayout: Bool = false + + private var _item: UINavigationItem? + public var item: UINavigationItem? { + get { + return self._item + } set(value) { + if let previousValue = self._item { + if let itemTitleListenerKey = self.itemTitleListenerKey { + previousValue.removeSetTitleListener(itemTitleListenerKey) + self.itemTitleListenerKey = nil + } + + if let itemLeftButtonListenerKey = self.itemLeftButtonListenerKey { + previousValue.removeSetLeftBarButtonItemListener(itemLeftButtonListenerKey) + self.itemLeftButtonListenerKey = nil + } + + if let itemLeftButtonSetEnabledListenerKey = self.itemLeftButtonSetEnabledListenerKey { + previousValue.leftBarButtonItem?.removeSetEnabledListener(itemLeftButtonSetEnabledListenerKey) + self.itemLeftButtonSetEnabledListenerKey = nil + } + + if let itemRightButtonListenerKey = self.itemRightButtonListenerKey { + previousValue.removeSetRightBarButtonItemListener(itemRightButtonListenerKey) + self.itemRightButtonListenerKey = nil + } + + if let itemRightButtonsListenerKey = self.itemRightButtonsListenerKey { + previousValue.removeSetMultipleRightBarButtonItemsListener(itemRightButtonsListenerKey) + self.itemRightButtonsListenerKey = nil + } + + if let itemBadgeListenerKey = self.itemBadgeListenerKey { + previousValue.removeSetBadgeListener(itemBadgeListenerKey) + self.itemBadgeListenerKey = nil + } + } + self._item = value + + self.leftButtonNodeImpl.view.removeFromSuperview() + self.rightButtonNodeImpl.view.removeFromSuperview() + + if let item = value { + self.title = item.title + self.itemTitleListenerKey = item.addSetTitleListener { [weak self] text, animated in + if let strongSelf = self { + let animateIn = animated && (strongSelf.title?.isEmpty ?? true) + strongSelf.title = text + if animateIn { + strongSelf.titleNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25) + } + } + } + + self.titleView = item.titleView + self.itemTitleViewListenerKey = item.addSetTitleViewListener { [weak self] titleView in + if let strongSelf = self { + strongSelf.titleView = titleView + } + } + + self.itemLeftButtonListenerKey = item.addSetLeftBarButtonItemListener { [weak self] previousItem, _, animated in + if let strongSelf = self { + if let itemLeftButtonSetEnabledListenerKey = strongSelf.itemLeftButtonSetEnabledListenerKey { + previousItem?.removeSetEnabledListener(itemLeftButtonSetEnabledListenerKey) + strongSelf.itemLeftButtonSetEnabledListenerKey = nil + } + + strongSelf.updateLeftButton(animated: animated) + strongSelf.invalidateCalculatedLayout() + strongSelf.requestLayout() + } + } + + self.itemRightButtonListenerKey = item.addSetRightBarButtonItemListener { [weak self] previousItem, currentItem, animated in + if let strongSelf = self { + 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() + } + } + + self.itemBadgeListenerKey = item.addSetBadgeListener { [weak self] text in + if let strongSelf = self { + strongSelf.updateBadgeText(text: text) + } + } + self.updateBadgeText(text: item.badge) + + self.updateLeftButton(animated: false) + self.updateRightButton(animated: false) + } else { + self.title = nil + self.updateLeftButton(animated: false) + self.updateRightButton(animated: false) + } + self.invalidateCalculatedLayout() + self.requestLayout() + } + } + + public var customBackButtonText: String? + + private var title: String? { + didSet { + if let title = self.title { + self.titleNode.attributedText = NSAttributedString(string: title, font: NavigationBarImpl.titleFont, textColor: self.presentationData.theme.primaryTextColor) + self.titleNode.accessibilityLabel = title + if self.titleNode.supernode == nil { + self.buttonsContainerNode.addSubnode(self.titleNode) + } + } else { + self.titleNode.removeFromSupernode() + } + + self.updateAccessibilityElements() + self.invalidateCalculatedLayout() + self.requestLayout() + } + } + + public private(set) var titleView: UIView? { + didSet { + if let oldValue = oldValue { + oldValue.removeFromSuperview() + } + + if let titleView = self.titleView { + if let backgroundContainer = self.backgroundContainer { + backgroundContainer.contentView.addSubview(titleView) + } else { + self.buttonsContainerNode.view.addSubview(titleView) + } + } + + self.invalidateCalculatedLayout() + self.requestLayout() + } + } + + public var layoutSuspended: Bool = false + + private let titleNode: ImmediateTextNode + + var previousItemListenerKey: Int? + var previousItemBackListenerKey: Int? + + private func updateAccessibilityElements() { + } + + override public var accessibilityElements: [Any]? { + get { + var accessibilityElements: [Any] = [] + if self.backButtonNodeImpl.view.superview != nil { + addAccessibilityChildren(of: self.backButtonNodeImpl, container: self, to: &accessibilityElements) + } + if self.leftButtonNodeImpl.view.superview != nil { + addAccessibilityChildren(of: self.leftButtonNodeImpl, container: self, to: &accessibilityElements) + } + if self.titleNode.view.superview != nil { + addAccessibilityChildren(of: self.titleNode, container: self, to: &accessibilityElements) + accessibilityElements.append(self.titleNode) + } + if let titleView = self.titleView, titleView.superview != nil { + titleView.accessibilityFrame = UIAccessibility.convertToScreenCoordinates(titleView.bounds, in: titleView) + accessibilityElements.append(titleView) + } + if self.rightButtonNodeImpl.supernode != nil { + addAccessibilityChildren(of: self.rightButtonNodeImpl, container: self, to: &accessibilityElements) + } + if let contentNode = self.contentNode { + addAccessibilityChildren(of: contentNode, container: self, to: &accessibilityElements) + } + if let secondaryContentNode = self.secondaryContentNode { + addAccessibilityChildren(of: secondaryContentNode, container: self, to: &accessibilityElements) + } + return accessibilityElements + } set(value) { + } + } + + override public func didLoad() { + super.didLoad() + + self.updateAccessibilityElements() + } + + public var enableAutomaticBackButton: Bool = true + + var _previousItem: NavigationPreviousAction? + public var previousItem: NavigationPreviousAction? { + get { + if !self.enableAutomaticBackButton { + return nil + } + return self._previousItem + } set(value) { + if !self.enableAutomaticBackButton { + self._previousItem = nil + return + } + if self._previousItem != value { + if let previousValue = self._previousItem, case let .item(itemValue) = previousValue { + if let previousItemListenerKey = self.previousItemListenerKey { + itemValue.removeSetTitleListener(previousItemListenerKey) + self.previousItemListenerKey = nil + } + if let previousItemBackListenerKey = self.previousItemBackListenerKey { + itemValue.removeSetBackBarButtonItemListener(previousItemBackListenerKey) + self.previousItemBackListenerKey = nil + } + } + self._previousItem = value + + if let previousItem = value { + switch previousItem { + case let .item(itemValue): + self.previousItemListenerKey = itemValue.addSetTitleListener { [weak self] _, _ in + if let strongSelf = self, let previousItem = strongSelf.previousItem, case let .item(itemValue) = previousItem { + if case .glass = strongSelf.presentationData.theme.style { + strongSelf.backButtonNodeImpl.updateManualText("", isBack: true) + } else if let customBackButtonText = strongSelf.customBackButtonText { + strongSelf.backButtonNodeImpl.updateManualText(customBackButtonText, isBack: true) + } else if let backBarButtonItem = itemValue.backBarButtonItem { + strongSelf.backButtonNodeImpl.updateManualText(backBarButtonItem.title ?? "", isBack: true) + } else { + strongSelf.backButtonNodeImpl.updateManualText(itemValue.title ?? "", isBack: true) + } + strongSelf.invalidateCalculatedLayout() + strongSelf.requestLayout() + } + } + + self.previousItemBackListenerKey = itemValue.addSetBackBarButtonItemListener { [weak self] _, _, _ in + if let strongSelf = self, let previousItem = strongSelf.previousItem, case let .item(itemValue) = previousItem { + if case .glass = strongSelf.presentationData.theme.style { + strongSelf.backButtonNodeImpl.updateManualText("", isBack: true) + } else if let customBackButtonText = strongSelf.customBackButtonText { + strongSelf.backButtonNodeImpl.updateManualText(customBackButtonText, isBack: true) + } else if let backBarButtonItem = itemValue.backBarButtonItem { + strongSelf.backButtonNodeImpl.updateManualText(backBarButtonItem.title ?? "", isBack: true) + } else { + strongSelf.backButtonNodeImpl.updateManualText(itemValue.title ?? "", isBack: true) + } + strongSelf.invalidateCalculatedLayout() + strongSelf.requestLayout() + } + } + case .close: + break + } + } + self.updateLeftButton(animated: false) + + self.invalidateCalculatedLayout() + self.requestLayout() + } + } + } + + private func updateBadgeText(text: String?) { + let actualText = text ?? "" + if self.badgeNode.text != actualText { + self.badgeNode.text = actualText + self.badgeNode.isHidden = actualText.isEmpty + self.backButtonNodeImpl.manualAlpha = self.badgeNode.isHidden ? 1.0 : 0.0 + + self.invalidateCalculatedLayout() + self.requestLayout() + } + } + + private func updateLeftButton(animated: Bool) { + if let item = self.item { + var needsLeftButton = false + if let leftBarButtonItem = item.leftBarButtonItem, !leftBarButtonItem.backButtonAppearance { + needsLeftButton = true + } else if let previousItem = self.previousItem, case .close = previousItem { + needsLeftButton = true + } + + if needsLeftButton { + if animated { + if self.backButtonNodeImpl.view.superview != nil { + if let snapshotView = self.backButtonNodeImpl.view.snapshotContentTree() { + snapshotView.frame = self.backButtonNodeImpl.frame + self.backButtonNodeImpl.view.superview?.insertSubview(snapshotView, aboveSubview: self.backButtonNodeImpl.view) + snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15, removeOnCompletion: false, completion: { [weak snapshotView] _ in + snapshotView?.removeFromSuperview() + }) + } + } + + if self.backButtonArrow.view.superview != nil { + if let snapshotView = self.backButtonArrow.view.snapshotContentTree() { + snapshotView.frame = self.backButtonArrow.frame + self.backButtonArrow.view.superview?.insertSubview(snapshotView, aboveSubview: self.backButtonArrow.view) + snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15, removeOnCompletion: false, completion: { [weak snapshotView] _ in + snapshotView?.removeFromSuperview() + }) + } + } + + if self.badgeNode.view.superview != nil { + if let snapshotView = self.badgeNode.view.snapshotContentTree() { + snapshotView.frame = self.badgeNode.frame + self.badgeNode.view.superview?.insertSubview(snapshotView, aboveSubview: self.badgeNode.view) + snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15, removeOnCompletion: false, completion: { [weak snapshotView] _ in + snapshotView?.removeFromSuperview() + }) + } + } + } + + self.backButtonNodeImpl.view.removeFromSuperview() + self.backButtonArrow.view.removeFromSuperview() + self.badgeNode.view.removeFromSuperview() + + if let leftBarButtonItem = item.leftBarButtonItem { + self.leftButtonNodeImpl.updateItems([], animated: animated) + self.leftButtonNodeImpl.updateItems([leftBarButtonItem], animated: animated) + } else { + self.leftButtonNodeImpl.updateItems([], animated: animated) + self.leftButtonNodeImpl.updateItems([UIBarButtonItem(title: self.presentationData.strings.close, style: .plain, target: nil, action: nil)], animated: animated) + } + + if self.leftButtonNodeImpl.supernode == nil { + if let leftButtonsBackgroundView = self.leftButtonsBackgroundView { + leftButtonsBackgroundView.contentView.addSubview(self.leftButtonNodeImpl.view) + } else { + self.buttonsContainerNode.view.addSubview(self.leftButtonNodeImpl.view) + } + } + + if animated { + self.leftButtonNodeImpl.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25) + } + } else { + if animated, self.leftButtonNodeImpl.view.superview != nil { + if let snapshotView = self.leftButtonNodeImpl.view.snapshotContentTree() { + snapshotView.frame = self.leftButtonNodeImpl.frame + self.leftButtonNodeImpl.view.superview?.insertSubview(snapshotView, aboveSubview: self.leftButtonNodeImpl.view) + snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15, removeOnCompletion: false, completion: { [weak snapshotView] _ in + snapshotView?.removeFromSuperview() + }) + } + } + self.leftButtonNodeImpl.view.removeFromSuperview() + + var backTitle: String? + if case .glass = self.presentationData.theme.style { + backTitle = "" + } else if let customBackButtonText = self.customBackButtonText { + backTitle = customBackButtonText + } else if let leftBarButtonItem = item.leftBarButtonItem, leftBarButtonItem.backButtonAppearance { + backTitle = leftBarButtonItem.title + } else if let previousItem = self.previousItem { + switch previousItem { + case let .item(itemValue): + if let backBarButtonItem = itemValue.backBarButtonItem { + backTitle = backBarButtonItem.title ?? self.presentationData.strings.back + } else { + backTitle = itemValue.title ?? self.presentationData.strings.back + } + case .close: + backTitle = nil + } + } + + if let backTitle { + self.backButtonNodeImpl.updateManualText(backTitle, isBack: true) + if self.backButtonNodeImpl.supernode == nil { + if let leftButtonsBackgroundView = self.leftButtonsBackgroundView { + leftButtonsBackgroundView.contentView.addSubview(self.backButtonNodeImpl.view) + leftButtonsBackgroundView.contentView.addSubview(self.backButtonArrow.view) + leftButtonsBackgroundView.contentView.addSubview(self.badgeNode.view) + } else { + self.buttonsContainerNode.view.addSubview(self.backButtonNodeImpl.view) + self.buttonsContainerNode.view.addSubview(self.backButtonArrow.view) + self.buttonsContainerNode.view.addSubview(self.badgeNode.view) + } + } + + if animated { + self.backButtonNodeImpl.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25) + self.backButtonArrow.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25) + self.badgeNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25) + } + } else { + self.backButtonNodeImpl.view.removeFromSuperview() + } + } + } else { + self.leftButtonNodeImpl.view.removeFromSuperview() + self.backButtonNodeImpl.view.removeFromSuperview() + self.backButtonArrow.view.removeFromSuperview() + self.badgeNode.view.removeFromSuperview() + } + + self.updateAccessibilityElements() + } + + private func updateRightButton(animated: Bool) { + if let item = self.item { + var items: [UIBarButtonItem] = [] + if let rightBarButtonItems = item.rightBarButtonItems, !rightBarButtonItems.isEmpty { + items = rightBarButtonItems + } else if let rightBarButtonItem = item.rightBarButtonItem { + items = [rightBarButtonItem] + } + + self.rightButtonNodeUpdated = true + + if !items.isEmpty { + self.rightButtonNodeImpl.updateItems([], animated: animated) + self.rightButtonNodeImpl.updateItems(items, animated: animated) + if self.rightButtonNodeImpl.view.superview == nil { + if let rightButtonsBackgroundView = self.rightButtonsBackgroundView { + rightButtonsBackgroundView.contentView.addSubview(self.rightButtonNodeImpl.view) + } else { + self.buttonsContainerNode.view.addSubview(self.rightButtonNodeImpl.view) + } + } + } else { + if animated, self.rightButtonNodeImpl.view.superview != nil { + if let snapshotView = self.rightButtonNodeImpl.view.snapshotContentTree() { + snapshotView.frame = self.rightButtonNodeImpl.frame + self.rightButtonNodeImpl.view.superview?.insertSubview(snapshotView, aboveSubview: self.rightButtonNodeImpl.view) + snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15, removeOnCompletion: false, completion: { [weak snapshotView] _ in + snapshotView?.removeFromSuperview() + }) + } + } + self.rightButtonNodeImpl.view.removeFromSuperview() + } + } else { + if animated, self.rightButtonNodeImpl.view.superview != nil { + if let snapshotView = self.rightButtonNodeImpl.view.snapshotContentTree() { + snapshotView.frame = self.rightButtonNodeImpl.frame + self.rightButtonNodeImpl.view.superview?.insertSubview(snapshotView, aboveSubview: self.rightButtonNodeImpl.view) + snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15, removeOnCompletion: false, completion: { [weak snapshotView] _ in + snapshotView?.removeFromSuperview() + }) + } + } + self.rightButtonNodeImpl.view.removeFromSuperview() + } + + self.updateAccessibilityElements() + } + + public let backgroundNode: NavigationBackgroundNode + + private var leftButtonsBackgroundView: GlassBackgroundView? + private var rightButtonsBackgroundView: GlassBackgroundView? + + private let backButtonNodeImpl: NavigationButtonNodeImpl + public var backButtonNode: NavigationButtonNode { + return self.backButtonNodeImpl + } + public let badgeNode: NavigationBarBadgeNode + public let backButtonArrow: ASImageNode + private let leftButtonNodeImpl: NavigationButtonNodeImpl + public var leftButtonNode: NavigationButtonNode { + return self.leftButtonNodeImpl + } + private let rightButtonNodeImpl: NavigationButtonNodeImpl + public var rightButtonNode: NavigationButtonNode { + return self.rightButtonNodeImpl + } + private var rightButtonNodeUpdated: Bool = false + public let additionalContentNode: SparseNode + + public func reattachAdditionalContentNode() { + if self.additionalContentNode.supernode !== self { + self.insertSubnode(self.additionalContentNode, aboveSubnode: self.clippingNode) + } + } + + public var secondaryContentHeight: CGFloat + + private var edgeEffectView: EdgeEffectView? + private var backgroundContainer: GlassBackgroundContainerView? + + public init(presentationData: NavigationBarPresentationData) { + self.presentationData = presentationData + self.stripeNode = ASDisplayNode() + + self.titleNode = ImmediateTextNode() + self.titleNode.isAccessibilityElement = true + self.titleNode.accessibilityTraits = .header + + self.backButtonNodeImpl = NavigationButtonNodeImpl(isGlass: presentationData.theme.style == .glass) + if case .glass = presentationData.theme.style { + } else { + self.backButtonNodeImpl.hitTestSlop = UIEdgeInsets(top: 0.0, left: -20.0, bottom: 0.0, right: 0.0) + } + + self.badgeNode = NavigationBarBadgeNode(fillColor: self.presentationData.theme.buttonColor, strokeColor: self.presentationData.theme.buttonColor, textColor: self.presentationData.theme.badgeTextColor) + self.badgeNode.isUserInteractionEnabled = false + self.badgeNode.isHidden = true + self.backButtonArrow = ASImageNode() + self.backButtonArrow.displayWithoutProcessing = true + self.backButtonArrow.displaysAsynchronously = false + self.backButtonArrow.isUserInteractionEnabled = false + self.leftButtonNodeImpl = NavigationButtonNodeImpl(isGlass: presentationData.theme.style == .glass) + self.rightButtonNodeImpl = NavigationButtonNodeImpl(isGlass: presentationData.theme.style == .glass) + if case .glass = presentationData.theme.style { + } else { + self.rightButtonNodeImpl.hitTestSlop = UIEdgeInsets(top: -4.0, left: -4.0, bottom: -4.0, right: -10.0) + } + + self.clippingNode = SparseNode() + self.clippingNode.clipsToBounds = true + + self.buttonsContainerNode = SparseNode() + self.buttonsContainerNode.clipsToBounds = true + + self.backButtonNodeImpl.color = self.presentationData.theme.buttonColor + self.backButtonNodeImpl.disabledColor = self.presentationData.theme.disabledButtonColor + self.leftButtonNodeImpl.color = self.presentationData.theme.buttonColor + self.leftButtonNodeImpl.disabledColor = self.presentationData.theme.disabledButtonColor + self.rightButtonNodeImpl.color = self.presentationData.theme.buttonColor + self.rightButtonNodeImpl.disabledColor = self.presentationData.theme.disabledButtonColor + self.backButtonArrow.image = presentationData.theme.style == .glass ? generateTintedImage(image: glassBackArrowImage, color: self.presentationData.theme.buttonColor) : navigationBarBackArrowImage(color: self.presentationData.theme.buttonColor) + if let title = self.title { + self.titleNode.attributedText = NSAttributedString(string: title, font: NavigationBarImpl.titleFont, textColor: self.presentationData.theme.primaryTextColor) + self.titleNode.accessibilityLabel = title + } + self.stripeNode.backgroundColor = self.presentationData.theme.separatorColor + + self.backgroundNode = NavigationBackgroundNode(color: self.presentationData.theme.backgroundColor, enableBlur: self.presentationData.theme.enableBackgroundBlur) + self.additionalContentNode = SparseNode() + + self.secondaryContentHeight = NavigationBarImpl.defaultSecondaryContentHeight + + super.init() + + if case .glass = presentationData.theme.style { + let edgeEffectView = EdgeEffectView() + self.edgeEffectView = edgeEffectView + self.view.addSubview(edgeEffectView) + + let backgroundContainer = GlassBackgroundContainerView() + self.backgroundContainer = backgroundContainer + self.view.addSubview(backgroundContainer) + + let leftButtonsBackgroundView = GlassBackgroundView() + self.leftButtonsBackgroundView = leftButtonsBackgroundView + backgroundContainer.contentView.addSubview(leftButtonsBackgroundView) + + let rightButtonsBackgroundView = GlassBackgroundView() + self.rightButtonsBackgroundView = rightButtonsBackgroundView + backgroundContainer.contentView.addSubview(rightButtonsBackgroundView) + } else { + self.addSubnode(self.backgroundNode) + } + self.addSubnode(self.buttonsContainerNode) + self.addSubnode(self.clippingNode) + self.addSubnode(self.additionalContentNode) + + self.stripeNode.isLayerBacked = true + self.stripeNode.displaysAsynchronously = false + if case .legacy = presentationData.theme.style { + self.addSubnode(self.stripeNode) + } + + self.backgroundColor = nil + self.isOpaque = false + + self.titleNode.displaysAsynchronously = false + self.titleNode.maximumNumberOfLines = 1 + self.titleNode.truncationType = .end + self.titleNode.isOpaque = false + + self.backButtonNodeImpl.highlightChanged = { [weak self] index, highlighted in + if let strongSelf = self, index == 0 { + strongSelf.backButtonArrow.alpha = (highlighted ? 0.4 : 1.0) + strongSelf.badgeNode.alpha = (highlighted ? 0.4 : 1.0) + } + } + self.backButtonNodeImpl.pressed = { [weak self] index in + if let strongSelf = self, index == 0 { + if let leftBarButtonItem = strongSelf.item?.leftBarButtonItem, leftBarButtonItem.backButtonAppearance { + leftBarButtonItem.performActionOnTarget() + } else { + strongSelf.backPressed() + } + } + } + + self.leftButtonNodeImpl.pressed = { [weak self] index in + if let item = self?.item { + if index == 0 { + if let leftBarButtonItem = item.leftBarButtonItem { + leftBarButtonItem.performActionOnTarget() + } else if let previousItem = self?.previousItem, case .close = previousItem { + self?.backPressed() + } + } + } + } + + self.rightButtonNodeImpl.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() + } + } + } + } + + public var isBackgroundVisible: Bool { + return self.backgroundNode.alpha == 1.0 + } + + public func updateBackgroundAlpha(_ alpha: CGFloat, transition: ContainedViewLayoutTransition) { + let alpha = max(0.0, min(1.0, alpha)) + transition.updateAlpha(node: self.backgroundNode, alpha: alpha, delay: 0.15) + transition.updateAlpha(node: self.stripeNode, alpha: alpha, delay: 0.15) + } + + public func updatePresentationData(_ presentationData: NavigationBarPresentationData, transition: ContainedViewLayoutTransition = .immediate) { + if presentationData.theme !== self.presentationData.theme || presentationData.strings !== self.presentationData.strings { + self.presentationData = presentationData + + self.backgroundNode.updateColor(color: self.presentationData.theme.backgroundColor, transition: transition) + + self.backButtonNodeImpl.color = self.presentationData.theme.buttonColor + self.backButtonNodeImpl.disabledColor = self.presentationData.theme.disabledButtonColor + self.leftButtonNodeImpl.color = self.presentationData.theme.buttonColor + self.leftButtonNodeImpl.disabledColor = self.presentationData.theme.disabledButtonColor + self.rightButtonNodeImpl.color = self.presentationData.theme.buttonColor + self.rightButtonNodeImpl.disabledColor = self.presentationData.theme.disabledButtonColor + self.backButtonArrow.image = self.presentationData.theme.style == .glass ? generateTintedImage(image: glassBackArrowImage, color: self.presentationData.theme.buttonColor) : navigationBarBackArrowImage(color: self.presentationData.theme.buttonColor) + if let title = self.title { + self.titleNode.attributedText = NSAttributedString(string: title, font: NavigationBarImpl.titleFont, textColor: self.presentationData.theme.primaryTextColor) + self.titleNode.accessibilityLabel = title + } + self.stripeNode.backgroundColor = self.presentationData.theme.separatorColor + + self.badgeNode.updateTheme(fillColor: self.presentationData.theme.buttonColor, strokeColor: self.presentationData.theme.buttonColor, textColor: self.presentationData.theme.badgeTextColor) + + self.updateLeftButton(animated: false) + self.requestLayout() + } + } + + private func requestLayout() { + self.requestedLayout = true + self.setNeedsLayout() + } + + override public func layout() { + super.layout() + + if let validLayout = self.validLayout, self.requestedLayout { + self.requestedLayout = false + self.updateLayout(size: validLayout.size, defaultHeight: validLayout.defaultHeight, additionalTopHeight: validLayout.additionalTopHeight, additionalContentHeight: validLayout.additionalContentHeight, additionalBackgroundHeight: validLayout.additionalBackgroundHeight, additionalCutout: validLayout.additionalCutout, leftInset: validLayout.leftInset, rightInset: validLayout.rightInset, appearsHidden: validLayout.appearsHidden, isLandscape: validLayout.isLandscape, transition: .immediate) + } + } + + public func updateLayout(size: CGSize, defaultHeight: CGFloat, additionalTopHeight: CGFloat, additionalContentHeight: CGFloat, additionalBackgroundHeight: CGFloat, additionalCutout: CGSize?, leftInset: CGFloat, rightInset: CGFloat, appearsHidden: Bool, isLandscape: Bool, transition: ContainedViewLayoutTransition) { + if self.layoutSuspended { + return + } + + self.validLayout = (size, defaultHeight, additionalTopHeight, additionalContentHeight, additionalBackgroundHeight, additionalCutout, leftInset, rightInset, appearsHidden, isLandscape) + + let backgroundFrame = CGRect(origin: CGPoint(), size: CGSize(width: size.width, height: size.height + additionalBackgroundHeight)) + if self.backgroundNode.frame != backgroundFrame { + transition.updateFrame(node: self.backgroundNode, frame: backgroundFrame) + self.backgroundNode.update(size: backgroundFrame.size, transition: transition) + } + + if let backgroundContainer = self.backgroundContainer { + transition.updateFrame(view: backgroundContainer, frame: backgroundFrame) + backgroundContainer.update(size: backgroundFrame.size, isDark: self.presentationData.theme.overallDarkAppearance, transition: ComponentTransition(transition)) + } + + if let edgeEffectView = self.edgeEffectView { + if let edgeEffectColor = self.presentationData.theme.edgeEffectColor, edgeEffectColor.alpha == 0.0 { + edgeEffectView.isHidden = true + } else { + edgeEffectView.isHidden = false + let edgeEffectFrame = CGRect(origin: CGPoint(), size: CGSize(width: size.width, height: size.height + additionalBackgroundHeight + 20.0)) + transition.updateFrame(view: edgeEffectView, frame: edgeEffectFrame) + edgeEffectView.update(content: self.presentationData.theme.edgeEffectColor ?? .white, blur: true, rect: CGRect(origin: CGPoint(), size: edgeEffectFrame.size), edge: .top, edgeSize: 40.0, transition: ComponentTransition(transition)) + } + } + + let apparentAdditionalHeight: CGFloat = self.secondaryContentNode != nil ? (self.secondaryContentHeight * self.secondaryContentNodeDisplayFraction) : 0.0 + + let leftButtonInset: CGFloat = leftInset + 16.0 + let backButtonInset: CGFloat = leftInset + 27.0 + + transition.updateFrame(node: self.clippingNode, frame: CGRect(origin: CGPoint(), size: size)) + transition.updateFrame(node: self.additionalContentNode, frame: CGRect(origin: CGPoint(), size: CGSize(width: size.width, height: size.height + additionalBackgroundHeight))) + transition.updateFrame(node: self.buttonsContainerNode, frame: CGRect(origin: CGPoint(), size: size)) + var expansionHeight: CGFloat = 0.0 + if let contentNode = self.contentNode { + var contentNodeFrame: CGRect + switch contentNode.mode { + case .replacement: + expansionHeight = contentNode.height - defaultHeight + contentNodeFrame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: size.width, height: size.height - additionalContentHeight)) + case .expansion: + expansionHeight = contentNode.height + + let additionalExpansionHeight: CGFloat = self.secondaryContentNode != nil && appearsHidden ? (self.secondaryContentHeight * self.secondaryContentNodeDisplayFraction) : 0.0 + contentNodeFrame = CGRect(origin: CGPoint(x: 0.0, y: size.height - (appearsHidden ? 0.0 : additionalContentHeight) - expansionHeight - apparentAdditionalHeight - additionalExpansionHeight), size: CGSize(width: size.width, height: expansionHeight)) + if appearsHidden { + if self.secondaryContentNode != nil { + contentNodeFrame.origin.y += self.secondaryContentHeight * self.secondaryContentNodeDisplayFraction + } + } + } + transition.updateFrame(node: contentNode, frame: contentNodeFrame) + contentNode.updateLayout(size: contentNodeFrame.size, leftInset: leftInset, rightInset: rightInset, transition: transition) + } + + transition.updateFrame(node: self.stripeNode, frame: CGRect(x: (additionalCutout?.width ?? 0.0), y: size.height + additionalBackgroundHeight, width: size.width - (additionalCutout?.width ?? 0.0), height: UIScreenPixel)) + + let nominalHeight: CGFloat = defaultHeight + let contentVerticalOrigin = additionalTopHeight + + var leftTitleInset: CGFloat = leftInset + 1.0 + var rightTitleInset: CGFloat = rightInset + 1.0 + + var leftButtonsWidth: CGFloat = 0.0 + if self.backButtonNodeImpl.view.superview != nil { + let backButtonSize = self.backButtonNodeImpl.updateLayout(constrainedSize: CGSize(width: size.width, height: nominalHeight), isLandscape: isLandscape, isLeftAligned: true) + leftTitleInset = backButtonSize.width + backButtonInset + 1.0 + + if case .glass = self.presentationData.theme.style { + } else { + let topHitTestSlop = (nominalHeight - backButtonSize.height) * 0.5 + self.backButtonNodeImpl.hitTestSlop = UIEdgeInsets(top: -topHitTestSlop, left: -27.0, bottom: -topHitTestSlop, right: -8.0) + } + + do { + self.backButtonNodeImpl.alpha = 1.0 + if case .glass = self.presentationData.theme.style { + } else { + transition.updateFrame(node: self.backButtonNodeImpl, frame: CGRect(origin: CGPoint(x: backButtonInset, y: contentVerticalOrigin + floor((nominalHeight - backButtonSize.height) / 2.0)), size: backButtonSize)) + } + + self.backButtonArrow.alpha = 1.0 + + let backButtonArrowFrame: CGRect + if case .glass = self.presentationData.theme.style { + let backButtonArrowSize = CGSize(width: 44.0, height: 44.0) + backButtonArrowFrame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: backButtonArrowSize) + } else { + let backButtonArrowSize = CGSize(width: 13.0, height: 22.0) + backButtonArrowFrame = CGRect(origin: CGPoint(x: leftInset + 8.0, y: contentVerticalOrigin + floor((nominalHeight - backButtonArrowSize.height) / 2.0)), size: backButtonArrowSize) + } + leftButtonsWidth += backButtonArrowFrame.width + transition.updateFrame(node: self.backButtonArrow, frame: backButtonArrowFrame) + self.badgeNode.alpha = 1.0 + } + } else if self.leftButtonNodeImpl.view.superview != nil { + let leftButtonSize = self.leftButtonNodeImpl.updateLayout(constrainedSize: CGSize(width: size.width, height: nominalHeight), isLandscape: isLandscape, isLeftAligned: true) + leftTitleInset = leftButtonSize.width + leftButtonInset + 1.0 + + var transition = transition + if self.leftButtonNodeImpl.frame.width.isZero { + transition = .immediate + } + + self.leftButtonNodeImpl.alpha = 1.0 + if case .glass = self.presentationData.theme.style { + transition.updateFrame(node: self.leftButtonNodeImpl, frame: CGRect(origin: CGPoint(x: leftButtonsWidth, y: floor((44.0 - leftButtonSize.height) / 2.0)), size: leftButtonSize)) + } else { + transition.updateFrame(node: self.leftButtonNodeImpl, frame: CGRect(origin: CGPoint(x: leftButtonInset, y: contentVerticalOrigin + floor((nominalHeight - leftButtonSize.height) / 2.0)), size: leftButtonSize)) + } + + if !self.leftButtonNodeImpl.isEmpty { + leftButtonsWidth += leftButtonSize.width + } + } + + let badgeSize = self.badgeNode.measure(CGSize(width: 200.0, height: 100.0)) + let backButtonArrowFrame = self.backButtonArrow.frame + if case .glass = self.presentationData.theme.style, self.badgeNode.view.superview != nil, !self.badgeNode.isHidden { + transition.updateFrame(node: self.badgeNode, frame: CGRect(origin: CGPoint(x: leftButtonsWidth - 14.0, y: floor((44.0 - badgeSize.height) * 0.5)), size: badgeSize)) + leftButtonsWidth += badgeSize.width - 3.0 + } else { + transition.updateFrame(node: self.badgeNode, frame: CGRect(origin: backButtonArrowFrame.origin.offsetBy(dx: 16.0, dy: 2.0), size: badgeSize)) + } + + var rightButtonsWidth: CGFloat = 0.0 + if self.rightButtonNodeImpl.view.superview != nil { + let rightButtonSize = self.rightButtonNodeImpl.updateLayout(constrainedSize: (CGSize(width: size.width, height: nominalHeight)), isLandscape: isLandscape, isLeftAligned: false) + if !self.rightButtonNodeImpl.isEmpty { + rightButtonsWidth += rightButtonSize.width + } + self.rightButtonNodeImpl.alpha = 1.0 + + var transition = transition + if self.rightButtonNodeImpl.frame.width.isZero || self.rightButtonNodeUpdated { + transition = .immediate + } + if case .glass = self.presentationData.theme.style { + transition.updateFrame(node: self.rightButtonNodeImpl, frame: CGRect(origin: CGPoint(x: 0.0, y: floor((44.0 - rightButtonSize.height) / 2.0)), size: rightButtonSize)) + } else { + rightTitleInset = rightButtonSize.width + leftButtonInset + 1.0 + transition.updateFrame(node: self.rightButtonNodeImpl, frame: CGRect(origin: CGPoint(x: size.width - leftButtonInset - rightButtonSize.width, y: contentVerticalOrigin + floor((nominalHeight - rightButtonSize.height) / 2.0)), size: rightButtonSize)) + } + } + self.rightButtonNodeUpdated = false + + if let leftButtonsBackgroundView = self.leftButtonsBackgroundView { + if leftButtonsWidth != 0.0 { + leftTitleInset = leftInset + 16.0 + leftButtonsWidth + 4.0 + } + + if self.backButtonNodeImpl.view.superview != nil { + transition.updateFrame(node: self.backButtonNodeImpl, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: leftButtonsWidth, height: 44.0))) + } + + let leftButtonsBackgroundFrame = CGRect(origin: CGPoint(x: leftInset + 16.0, y: contentVerticalOrigin + floor((nominalHeight - 44.0) * 0.5)), size: CGSize(width: max(44.0, leftButtonsWidth), height: 44.0)) + var leftButtonsBackgroundTransition = ComponentTransition(transition) + if leftButtonsBackgroundView.alpha == 0.0 { + leftButtonsBackgroundTransition = .immediate + } + + leftButtonsBackgroundTransition.setPosition(view: leftButtonsBackgroundView, position: leftButtonsBackgroundFrame.center) + leftButtonsBackgroundTransition.setBounds(view: leftButtonsBackgroundView, bounds: CGRect(origin: CGPoint(), size: leftButtonsBackgroundFrame.size)) + transition.updateAlpha(layer: leftButtonsBackgroundView.layer, alpha: leftButtonsWidth == 0.0 ? 0.0 : 1.0) + leftButtonsBackgroundView.update(size: leftButtonsBackgroundFrame.size, cornerRadius: leftButtonsBackgroundFrame.height * 0.5, isDark: self.presentationData.theme.overallDarkAppearance, tintColor: .init(kind: .panel, color: UIColor(white: self.presentationData.theme.overallDarkAppearance ? 0.0 : 1.0, alpha: 0.6)), isInteractive: true, isVisible: leftButtonsWidth != 0.0, transition: leftButtonsBackgroundTransition) + } + + if let rightButtonsBackgroundView = self.rightButtonsBackgroundView { + if rightButtonsWidth != 0.0 { + rightTitleInset = rightInset + 16.0 + rightButtonsWidth + 4.0 + + let rightButtonsBackgroundFrame = CGRect(origin: CGPoint(x: size.width - rightInset - 16.0 - rightButtonsWidth, y: contentVerticalOrigin + floor((nominalHeight - 44.0) * 0.5)), size: CGSize(width: rightButtonsWidth, height: 44.0)) + var rightButtonsBackgroundTransition = ComponentTransition(transition) + if rightButtonsBackgroundView.alpha == 0.0 { + rightButtonsBackgroundTransition = .immediate + } + + rightButtonsBackgroundTransition.setFrame(view: rightButtonsBackgroundView, frame: rightButtonsBackgroundFrame) + transition.updateAlpha(layer: rightButtonsBackgroundView.layer, alpha: 1.0) + rightButtonsBackgroundView.update(size: rightButtonsBackgroundFrame.size, cornerRadius: rightButtonsBackgroundFrame.height * 0.5, isDark: self.presentationData.theme.overallDarkAppearance, tintColor: .init(kind: .panel, color: UIColor(white: self.presentationData.theme.overallDarkAppearance ? 0.0 : 1.0, alpha: 0.6)), isInteractive: true, transition: rightButtonsBackgroundTransition) + } else { + transition.updateAlpha(layer: rightButtonsBackgroundView.layer, alpha: 0.0) + } + } + + leftTitleInset = floor(leftTitleInset) + if Int(leftTitleInset) % 2 != 0 { + leftTitleInset -= 1.0 + } + + if self.titleNode.view.superview != nil { + let titleSize = self.titleNode.updateLayout(CGSize(width: max(1.0, size.width - max(leftTitleInset, rightTitleInset) * 2.0), height: nominalHeight)) + + do { + var transition = transition + if self.titleNode.frame.width.isZero { + transition = .immediate + } + self.titleNode.alpha = 1.0 + + var titleOffset: CGFloat = 0.0 + if self.presentationData.theme.backgroundColor == .clear && self.presentationData.theme.separatorColor == .clear { + titleOffset += 3.0 + } + transition.updateFrame(node: self.titleNode, frame: CGRect(origin: CGPoint(x: floor((size.width - titleSize.width) / 2.0), y: contentVerticalOrigin + titleOffset + floorToScreenPixels((nominalHeight - titleSize.height) / 2.0)), size: titleSize)) + } + } + + if let titleView = self.titleView { + let titleSize = CGSize(width: max(1.0, size.width - max(leftTitleInset, rightTitleInset) * 2.0), height: nominalHeight) + let titleFrame = CGRect(origin: CGPoint(x: floor((size.width - titleSize.width) / 2.0), y: contentVerticalOrigin + floorToScreenPixels((nominalHeight - titleSize.height) / 2.0)), size: titleSize) + var titleViewTransition = transition + if titleView.frame.isEmpty { + titleViewTransition = .immediate + titleView.frame = titleFrame + } + + titleViewTransition.updateFrame(view: titleView, frame: titleFrame) + + if let titleView = titleView as? NavigationBarTitleView { + let titleWidth = size.width - (leftTitleInset > 0.0 ? leftTitleInset : rightTitleInset) - (rightTitleInset > 0.0 ? rightTitleInset : leftTitleInset) + + let _ = titleView.updateLayout(size: titleFrame.size, clearBounds: CGRect(origin: CGPoint(x: leftTitleInset - titleFrame.minX, y: 0.0), size: CGSize(width: titleWidth, height: titleFrame.height)), transition: titleViewTransition) + } + + do { + if self.hintAnimateTitleNodeOnNextLayout { + self.hintAnimateTitleNodeOnNextLayout = false + if let titleView = titleView as? NavigationBarTitleView { + titleView.animateLayoutTransition() + } + } + titleView.alpha = 1.0 + transition.updateFrame(view: titleView, frame: titleFrame) + } + } + } + + public var intrinsicCanTransitionInline: Bool = true + + public var passthroughTouches = true + + public var canTransitionInline: Bool { + if let contentNode = self.contentNode, case .replacement = contentNode.mode { + return false + } else { + return self.intrinsicCanTransitionInline + } + } + + public func contentHeight(defaultHeight: CGFloat) -> CGFloat { + var result: CGFloat = 0.0 + if let contentNode = self.contentNode { + switch contentNode.mode { + case .expansion: + result += defaultHeight + contentNode.height + case .replacement: + result += contentNode.height + } + } else { + result += defaultHeight + } + + if let _ = self.secondaryContentNode { + result += self.secondaryContentHeight * self.secondaryContentNodeDisplayFraction + } + + return result + } + + public func setContentNode(_ contentNode: NavigationBarContentNode?, animated: Bool) { + if self.contentNode !== contentNode { + if let previous = self.contentNode { + if animated { + previous.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak self, weak previous] _ in + if let strongSelf = self, let previous = previous { + if previous !== strongSelf.contentNode { + previous.removeFromSupernode() + } + } + }) + } else { + previous.removeFromSupernode() + } + } + self.contentNode = contentNode + self.contentNode?.requestContainerLayout = { [weak self] transition in + self?.requestContainerLayout(transition) + } + if let contentNode = contentNode { + contentNode.clipsToBounds = true + contentNode.layer.removeAnimation(forKey: "opacity") + if self.stripeNode.supernode != nil { + self.insertSubnode(contentNode, belowSubnode: self.stripeNode) + } else { + self.insertSubnode(contentNode, at: 0) + } + if animated { + contentNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) + } + + if case .replacement = contentNode.mode, !self.buttonsContainerNode.alpha.isZero { + self.buttonsContainerNode.alpha = 0.0 + if animated { + self.buttonsContainerNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2) + } + } + + if !self.bounds.size.width.isZero { + self.requestedLayout = true + self.layout() + } else { + self.requestLayout() + } + } else if self.buttonsContainerNode.alpha.isZero { + self.buttonsContainerNode.alpha = 1.0 + if animated { + self.buttonsContainerNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) + } + } + } + } + + public func setSecondaryContentNode(_ secondaryContentNode: ASDisplayNode?, animated: Bool = false) { + if self.secondaryContentNode !== secondaryContentNode { + if let previous = self.secondaryContentNode, previous.supernode === self.clippingNode { + if animated { + previous.layer.animateAlpha(from: previous.alpha, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak previous] finished in + if finished { + previous?.removeFromSupernode() + previous?.layer.removeAllAnimations() + } + }) + } else { + previous.removeFromSupernode() + } + } + self.secondaryContentNode = secondaryContentNode + if let secondaryContentNode = secondaryContentNode { + self.clippingNode.addSubnode(secondaryContentNode) + + if animated { + secondaryContentNode.layer.animateAlpha(from: 0.0, to: secondaryContentNode.alpha, duration: 0.3) + } + } + } + } + + public func executeBack() -> Bool { + if self.backButtonNodeImpl.isInHierarchy { + self.backButtonNodeImpl.pressed(0) + } else if self.leftButtonNodeImpl.isInHierarchy { + self.leftButtonNodeImpl.pressed(0) + } else { + self.backButtonNodeImpl.pressed(0) + } + return true + } + + public func setHidden(_ hidden: Bool, animated: Bool) { + if let contentNode = self.contentNode, case .replacement = contentNode.mode { + } else { + let targetAlpha: CGFloat = hidden ? 0.0 : 1.0 + let previousAlpha = self.buttonsContainerNode.alpha + if previousAlpha != targetAlpha { + self.buttonsContainerNode.alpha = targetAlpha + if animated { + self.buttonsContainerNode.layer.animateAlpha(from: previousAlpha, to: targetAlpha, duration: 0.2) + } + } + } + } + + override public func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { + if let result = self.additionalContentNode.view.hitTest(self.view.convert(point, to: self.additionalContentNode.view), with: event) { + return result + } + + guard let result = super.hitTest(point, with: event) else { + return nil + } + + if self.passthroughTouches && (result == self.view || result == self.buttonsContainerNode.view) { + return nil + } + + return result + } +} diff --git a/submodules/Display/Source/NavigationButtonNode.swift b/submodules/TelegramUI/Components/NavigationBarImpl/Sources/NavigationButtonNode.swift similarity index 64% rename from submodules/Display/Source/NavigationButtonNode.swift rename to submodules/TelegramUI/Components/NavigationBarImpl/Sources/NavigationButtonNode.swift index 1ebc26af8a..a991e7453f 100644 --- a/submodules/Display/Source/NavigationButtonNode.swift +++ b/submodules/TelegramUI/Components/NavigationBarImpl/Sources/NavigationButtonNode.swift @@ -1,11 +1,208 @@ import UIKit import AsyncDisplayKit +import Display +import ComponentFlow +import MultilineTextComponent -public protocol NavigationButtonCustomDisplayNode { - var isHighlightable: Bool { get } +let glassBackArrowImage: UIImage? = { + let imageSize = CGSize(width: 44.0, height: 44.0) + let topRightPoint = CGPoint(x: 24.6, y: 14.0) + let centerPoint = CGPoint(x: 17.0, y: imageSize.height * 0.5) + return generateImage(imageSize, rotatedContext: { size, context in + context.clear(CGRect(origin: CGPoint(), size: size)) + context.setStrokeColor(UIColor.white.cgColor) + context.setLineWidth(2.0) + context.setLineCap(.round) + context.setLineJoin(.round) + context.move(to: topRightPoint) + context.addLine(to: centerPoint) + context.addLine(to: CGPoint(x: topRightPoint.x, y: size.height - topRightPoint.y)) + context.strokePath() + })?.withRenderingMode(.alwaysTemplate) +}() + +private final class ItemComponent: Component { + enum Content: Equatable { + case back + case item(UIBarButtonItem) + } + + let color: UIColor + let content: Content + + init( + color: UIColor, + content: Content + ) { + self.color = color + self.content = content + } + + static func ==(lhs: ItemComponent, rhs: ItemComponent) -> Bool { + if lhs.color != rhs.color { + return false + } + if lhs.content != rhs.content { + return false + } + return true + } + + final class View: UIView { + private var iconView: UIImageView? + private var title: ComponentView? + + private var component: ItemComponent? + private weak var state: EmptyComponentState? + var isUpdating: Bool = false + + private var setEnabledListener: Int? + private var setTitleListener: Int? + + override init(frame: CGRect) { + super.init(frame: frame) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + func update(component: ItemComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { + self.isUpdating = true + defer { + self.isUpdating = false + } + + if component.content != self.component?.content { + if case let .item(item) = self.component?.content { + if let setEnabledListener = self.setEnabledListener { + self.setEnabledListener = nil + item.removeSetEnabledListener(setEnabledListener) + } + if let setTitleListener = self.setTitleListener { + self.setTitleListener = nil + item.removeSetTitleListener(setTitleListener) + } + } + + switch component.content { + case .back: + break + case let .item(item): + self.setEnabledListener = item.addSetEnabledListener { [weak self] _ in + guard let self else { + return + } + if !self.isUpdating { + self.state?.updated(transition: .immediate) + } + } + self.setTitleListener = item.addSetTitleListener { [weak self] _ in + guard let self else { + return + } + if !self.isUpdating { + self.state?.updated(transition: .immediate) + } + } + } + } + + self.component = component + self.state = state + + var iconImage: UIImage? + var titleString: String? + switch component.content { + case .back: + iconImage = glassBackArrowImage + case let .item(item): + if item.image != nil { + iconImage = item.image + } else if let title = item.title { + titleString = title + } + } + + var size = CGSize(width: 44.0, height: 44.0) + + if let iconImage { + let iconView: UIImageView + var iconTransition = transition + if let current = self.iconView { + iconView = current + } else { + iconTransition = iconTransition.withAnimation(.none) + iconView = UIImageView() + self.iconView = iconView + } + iconView.image = iconImage + iconView.tintColor = component.color + + let iconFrame = iconImage.size.centered(in: CGRect(origin: CGPoint(), size: size)) + iconTransition.setFrame(view: iconView, frame: iconFrame) + } else if let iconView = self.iconView { + self.iconView = nil + iconView.removeFromSuperview() + } + + if let titleString { + let titleFont: UIFont + if case let .item(item) = component.content, case .done = item.style { + titleFont = Font.bold(17.0) + } else { + titleFont = Font.regular(17.0) + } + + let title: ComponentView + if let current = self.title { + title = current + } else { + title = ComponentView() + self.title = title + } + let titleSize = title.update( + transition: .immediate, + component: AnyComponent(MultilineTextComponent( + text: .plain(NSAttributedString(string: titleString, font: titleFont, textColor: component.color)) + )), + environment: {}, + containerSize: CGSize(width: 200.0, height: 100.0) + ) + + let titleInset: CGFloat = 6.0 + size.width = titleInset * 2.0 + titleSize.width + + let titleFrame = CGRect(origin: CGPoint(x: titleInset, y: floorToScreenPixels((size.height - titleSize.height) * 0.5)), size: titleSize) + if let titleView = title.view { + if titleView.superview == nil { + self.addSubview(titleView) + } + titleView.frame = titleFrame + } + } else if let title = self.title { + self.title = nil + if let titleView = title.view { + titleView.removeFromSuperview() + } + } + + return size + } + } + + func makeView() -> View { + return View(frame: CGRect()) + } + + func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { + view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition) + } } private final class NavigationButtonItemNode: ImmediateTextNode { + private let isGlass: Bool + private func fontForCurrentState() -> UIFont { return self.bold ? UIFont.boldSystemFont(ofSize: 17.0) : UIFont.systemFont(ofSize: 17.0) } @@ -56,7 +253,6 @@ private final class NavigationButtonItemNode: ImmediateTextNode { } private(set) var imageNode: ASImageNode? - private let imageRippleNode: ASImageNode private var _image: UIImage? public var image: UIImage? { @@ -71,10 +267,6 @@ private final class NavigationButtonItemNode: ImmediateTextNode { imageNode.displayWithoutProcessing = true imageNode.displaysAsynchronously = false self.imageNode = imageNode - if self.imageRippleNode.supernode != nil { - self.imageRippleNode.image = nil - self.imageRippleNode.removeFromSupernode() - } self.addSubnode(imageNode) } @@ -82,10 +274,6 @@ private final class NavigationButtonItemNode: ImmediateTextNode { } else if let imageNode = self.imageNode { imageNode.removeFromSupernode() self.imageNode = nil - if self.imageRippleNode.supernode != nil { - self.imageRippleNode.image = nil - self.imageRippleNode.removeFromSupernode() - } } self.invalidateCalculatedLayout() @@ -115,14 +303,6 @@ private final class NavigationButtonItemNode: ImmediateTextNode { } } - public var rippleColor: UIColor = UIColor(rgb: 0x000000, alpha: 0.05) { - didSet { - if self.imageRippleNode.image != nil { - self.imageRippleNode.image = generateFilledCircleImage(diameter: 30.0, color: self.rippleColor) - } - } - } - public var disabledColor: UIColor = UIColor(rgb: 0xd0d0d0) { didSet { if let text = self._text { @@ -183,11 +363,8 @@ private final class NavigationButtonItemNode: ImmediateTextNode { var pointerInteraction: PointerInteraction? - override public init() { - self.imageRippleNode = ASImageNode() - self.imageRippleNode.displaysAsynchronously = false - self.imageRippleNode.displayWithoutProcessing = true - self.imageRippleNode.alpha = 0.0 + init(isGlass: Bool) { + self.isGlass = isGlass super.init() @@ -195,7 +372,9 @@ private final class NavigationButtonItemNode: ImmediateTextNode { self.isUserInteractionEnabled = true self.isExclusiveTouch = true - self.hitTestSlop = UIEdgeInsets(top: -16.0, left: -10.0, bottom: -16.0, right: -10.0) + if !isGlass { + self.hitTestSlop = UIEdgeInsets(top: -16.0, left: -10.0, bottom: -16.0, right: -10.0) + } self.displaysAsynchronously = false self.verticalAlignment = .middle @@ -231,7 +410,6 @@ private final class NavigationButtonItemNode: ImmediateTextNode { let size = CGSize(width: max(nodeSize.width, superSize.width), height: max(44.0, max(nodeSize.height, superSize.height))) let imageFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - nodeSize.width) / 2.0), y: floorToScreenPixels((size.height - nodeSize.height) / 2.0)), size: nodeSize) imageNode.frame = imageFrame - self.imageRippleNode.frame = imageFrame return size } else { superSize.height = max(44.0, superSize.height) @@ -268,7 +446,7 @@ private final class NavigationButtonItemNode: ImmediateTextNode { self.touchCount = max(0, self.touchCount - touches.count) var touchInside = true - if let touch = touches.first { + if let touch = touches.first, !self.isGlass { touchInside = self.touchInsideApparentBounds(touch) } if previousTouchCount != 0 && self.touchCount == 0 && self.isEnabled && touchInside { @@ -328,7 +506,10 @@ private final class NavigationButtonItemNode: ImmediateTextNode { } -public final class NavigationButtonNode: ContextControllerSourceNode { +public final class NavigationButtonNodeImpl: ContextControllerSourceNode, NavigationButtonNode { + private let isGlass: Bool + private var isBack: Bool = false + private var nodes: [NavigationButtonItemNode] = [] private var disappearingNodes: [(frame: CGRect, size: CGSize, node: NavigationButtonItemNode)] = [] @@ -357,16 +538,6 @@ public final class NavigationButtonNode: ContextControllerSourceNode { } } - public var rippleColor: UIColor = UIColor(rgb: 0x000000, alpha: 0.05) { - didSet { - if !self.rippleColor.isEqual(oldValue) { - for node in self.nodes { - node.rippleColor = self.rippleColor - } - } - } - } - public var disabledColor: UIColor = UIColor(rgb: 0xd0d0d0) { didSet { if !self.disabledColor.isEqual(oldValue) { @@ -384,18 +555,20 @@ public final class NavigationButtonNode: ContextControllerSourceNode { } } - override public init() { + public init(isGlass: Bool) { + self.isGlass = isGlass + super.init() self.isAccessibilityElement = false self.isGestureEnabled = false } - var manualText: String { + public var manualText: String { return self.nodes.first?.text ?? "" } - var manualAlpha: CGFloat = 1.0 { + public var manualAlpha: CGFloat = 1.0 { didSet { for node in self.nodes { node.alpha = self.manualAlpha @@ -411,14 +584,15 @@ public final class NavigationButtonNode: ContextControllerSourceNode { } } - func updateManualText(_ text: String, isBack: Bool = true) { + public func updateManualText(_ text: String, isBack: Bool = true) { + self.isBack = isBack + let node: NavigationButtonItemNode if self.nodes.count > 0 { node = self.nodes[0] } else { - node = NavigationButtonItemNode() + node = NavigationButtonItemNode(isGlass: self.isGlass) node.color = self.color - node.rippleColor = self.rippleColor node.layer.layerTintColor = self.contentsColor?.cgColor node.highlightChanged = { [weak node, weak self] value in if let strongSelf = self, let node = node { @@ -444,7 +618,9 @@ public final class NavigationButtonNode: ContextControllerSourceNode { node.bold = false node.isEnabled = true node.node = nil - node.hitTestSlop = isBack ? UIEdgeInsets(top: 0.0, left: -20.0, bottom: 0.0, right: 0.0) : UIEdgeInsets() + if !self.isGlass { + node.hitTestSlop = isBack ? UIEdgeInsets(top: 0.0, left: -20.0, bottom: 0.0, right: 0.0) : UIEdgeInsets() + } if 1 < self.nodes.count { for i in 1 ..< self.nodes.count { @@ -454,15 +630,14 @@ public final class NavigationButtonNode: ContextControllerSourceNode { } } - func updateItems(_ items: [UIBarButtonItem], animated: Bool) { + public func updateItems(_ items: [UIBarButtonItem], animated: Bool) { for i in 0 ..< items.count { let node: NavigationButtonItemNode if self.nodes.count > i { node = self.nodes[i] } else { - node = NavigationButtonItemNode() + node = NavigationButtonItemNode(isGlass: self.isGlass) node.color = self.color - node.rippleColor = self.rippleColor node.layer.layerTintColor = self.contentsColor?.cgColor node.highlightChanged = { [weak node, weak self] value in if let strongSelf = self, let node = node { @@ -523,27 +698,36 @@ public final class NavigationButtonNode: ContextControllerSourceNode { } public func updateLayout(constrainedSize: CGSize, isLandscape: Bool, isLeftAligned: Bool) -> CGSize { - var nodeOrigin = CGPoint() + var nodeOrigin = CGPoint(x: 0.0, y: 0.0) var totalHeight: CGFloat = 0.0 for i in 0 ..< self.nodes.count { - if i != 0 { + if i != 0 && !self.isGlass { nodeOrigin.x += 15.0 } let node = self.nodes[i] var nodeSize = node.updateLayout(constrainedSize) + var nodeInset: CGFloat = 0.0 + if self.isGlass { + if node.image == nil && node.node == nil { + nodeInset += 10.0 + } + if nodeSize.width + nodeInset * 2.0 < 44.0 { + nodeInset = floorToScreenPixels((44.0 - nodeSize.width) * 0.5) + } + } nodeSize.width = ceil(nodeSize.width) nodeSize.height = ceil(nodeSize.height) totalHeight = max(totalHeight, nodeSize.height) - node.frame = CGRect(origin: CGPoint(x: nodeOrigin.x, y: floor((totalHeight - nodeSize.height) / 2.0)), size: nodeSize) - nodeOrigin.x += node.bounds.width - if isLandscape { + node.frame = CGRect(origin: CGPoint(x: nodeOrigin.x + nodeInset, y: floor((totalHeight - nodeSize.height) / 2.0)), size: nodeSize) + nodeOrigin.x += nodeInset + node.bounds.width + nodeInset + if isLandscape && !self.isGlass { nodeOrigin.x += 16.0 } - if node.node == nil && node.imageNode != nil && i == self.nodes.count - 1 { + if !self.isGlass && node.node == nil && node.imageNode != nil && i == self.nodes.count - 1 { nodeOrigin.x -= 5.0 } } @@ -557,11 +741,32 @@ public final class NavigationButtonNode: ContextControllerSourceNode { return CGSize(width: nodeOrigin.x, height: totalHeight) } - func internalHitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { + override public func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { if self.nodes.count == 1 { - return self.nodes[0].view + if self.isGlass && self.isBack { + if self.bounds.contains(point) { + return self.nodes[0].view + } + } + if self.bounds.contains(point) { + return self.nodes[0].view + } else { + return nil + } } else { return super.hitTest(point, with: event) } } + + var isEmpty: Bool { + if self.isBack { + return false + } + for node in self.nodes { + if node.bounds.width != 0.0 { + return false + } + } + return true + } } diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderNavigationButton.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderNavigationButton.swift index 3da02f5c74..f625dec007 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderNavigationButton.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderNavigationButton.swift @@ -260,7 +260,7 @@ final class PeerInfoHeaderNavigationButton: HighlightableButtonNode { case .back: text = presentationData.strings.Common_Back accessibilityText = presentationData.strings.Common_Back - icon = NavigationBar.backArrowImage(color: .white) + icon = navigationBarBackArrowImage(color: .white) case .edit: text = presentationData.strings.Common_Edit accessibilityText = text diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderNode.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderNode.swift index 28c5cb6ce1..9618ed73c8 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderNode.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderNode.swift @@ -648,11 +648,11 @@ final class PeerInfoHeaderNode: ASDisplayNode { let avatarOverlayFarme = self.editingContentNode.convert(self.editingContentNode.avatarNode.frame, to: self) transition.updateFrame(node: self.avatarOverlayNode, frame: avatarOverlayFarme) - var transitionSourceHeight: CGFloat = 0.0 - var transitionFraction: CGFloat = 0.0 - var transitionSourceAvatarFrame: CGRect? - var transitionSourceTitleFrame = CGRect() - var transitionSourceSubtitleFrame = CGRect() + let transitionSourceHeight: CGFloat = 0.0 + let transitionFraction: CGFloat = 0.0 + let transitionSourceAvatarFrame: CGRect? = nil + let transitionSourceTitleFrame = CGRect() + let transitionSourceSubtitleFrame = CGRect() let avatarFrame = CGRect(origin: CGPoint(x: floor((width - avatarSize) / 2.0), y: statusBarHeight + 22.0), size: CGSize(width: avatarSize, height: avatarSize)) @@ -766,7 +766,7 @@ final class PeerInfoHeaderNode: ASDisplayNode { let backgroundBannerAlpha: CGFloat var effectiveSeparatorAlpha: CGFloat - if let navigationTransition = self.navigationTransition { + /*if let navigationTransition = self.navigationTransition { transitionSourceHeight = navigationTransition.sourceNavigationBar.backgroundNode.bounds.height transitionFraction = navigationTransition.fraction @@ -799,7 +799,7 @@ final class PeerInfoHeaderNode: ASDisplayNode { self.avatarListNode.animateAvatarCollapse(transition: transition) } self.avatarClippingNode.clipsToBounds = false - } else { + } else*/ do { let backgroundTransitionStepDistance: CGFloat = 50.0 var backgroundTransitionDistance: CGFloat = navigationHeight + panelWithAvatarHeight - backgroundTransitionStepDistance if self.isSettings || self.isMyProfile { diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift index c05923ba09..b093bed921 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift @@ -11446,7 +11446,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro } } - private func activateSearch() { + func activateSearch() { guard let (layout, navigationBarHeight) = self.validLayout, self.searchDisplayController == nil else { return } @@ -13604,6 +13604,7 @@ public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen, KeyShortc let baseNavigationBarPresentationData = NavigationBarPresentationData(presentationData: self.presentationData) super.init(navigationBarPresentationData: NavigationBarPresentationData( theme: NavigationBarTheme( + overallDarkAppearance: true, buttonColor: .white, disabledButtonColor: .white, primaryTextColor: .white, @@ -13786,22 +13787,11 @@ public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen, KeyShortc } if self.chatLocation.peerId != nil { - /*self.navigationBar?.shouldTransitionInline = { [weak self] in - guard let strongSelf = self else { - return false - } - if strongSelf.navigationItem.leftBarButtonItem != nil { - return false - } - if strongSelf.controllerNode.scrollNode.view.contentOffset.y > .ulpOfOne { - return false - } - if strongSelf.controllerNode.headerNode.isAvatarExpanded { - return false - } - return false - }*/ self.navigationBar?.makeCustomTransitionNode = { [weak self] other, isInteractive in + if "".isEmpty { + return nil + } + guard let strongSelf = self else { return nil } @@ -14173,9 +14163,9 @@ public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen, KeyShortc chatNavigationStack = summary.peerNavigationItems.filter({ $0 != ChatNavigationStackItem(peerId: self.peerId, threadId: self.chatLocation.threadId) }) } - if !chatNavigationStack.isEmpty { - self.navigationBar?.backButtonNode.isGestureEnabled = true - self.navigationBar?.backButtonNode.activated = { [weak self] gesture, _ in + if !chatNavigationStack.isEmpty, let backButtonNode = self.navigationBar?.backButtonNode as? ContextControllerSourceNode { + backButtonNode.isGestureEnabled = true + backButtonNode.activated = { [weak self] gesture, _ in guard let strongSelf = self, let backButtonNode = strongSelf.navigationBar?.backButtonNode, let navigationController = strongSelf.navigationController as? NavigationController else { gesture.cancel() return @@ -14296,6 +14286,10 @@ public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen, KeyShortc self.controllerNode.headerNode.navigationButtonContainer.performAction?(.edit, nil, nil) } + override public func tabBarActivateSearch() { + self.controllerNode.activateSearch() + } + public static func openSavedMessagesMoreMenu(context: AccountContext, sourceController: ViewController, isViewingAsTopics: Bool, sourceView: UIView, gesture: ContextGesture?) { let _ = (context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: context.account.peerId)) |> deliverOnMainQueue).startStandalone(next: { peer in @@ -14463,7 +14457,7 @@ private final class PeerInfoNavigationTransitionNode: ASDisplayNode, CustomNavig } func setup(topNavigationBar: NavigationBar, bottomNavigationBar: NavigationBar) { - if let _ = bottomNavigationBar.userInfo as? PeerInfoNavigationSourceTag { + /*if let _ = bottomNavigationBar.userInfo as? PeerInfoNavigationSourceTag { self.topNavigationBar = topNavigationBar self.bottomNavigationBar = bottomNavigationBar } else { @@ -14475,54 +14469,10 @@ private final class PeerInfoNavigationTransitionNode: ASDisplayNode, CustomNavig topNavigationBar.isHidden = true bottomNavigationBar.isHidden = true - if let topNavigationBar = self.topNavigationBar, let bottomNavigationBar = self.bottomNavigationBar { + if let _ = self.topNavigationBar, let bottomNavigationBar = self.bottomNavigationBar { self.addSubnode(bottomNavigationBar.additionalContentNode) - - /*if let headerView = bottomNavigationBar.customHeaderContentView as? ChatListHeaderComponent.View { - if let previousBackButtonArrow = headerView.makeTransitionBackArrowView(accentColor: self.presentationData.theme.rootController.navigationBar.accentTextColor) { - self.previousBackButtonArrow = previousBackButtonArrow - self.view.addSubview(previousBackButtonArrow) - } - if let previousBackButton = headerView.makeTransitionBackButtonView(accentColor: self.presentationData.theme.rootController.navigationBar.accentTextColor) { - self.previousBackButton = previousBackButton - self.view.addSubview(previousBackButton) - } - } else*/ do { - if let previousBackButtonArrow = bottomNavigationBar.makeTransitionBackArrowView(accentColor: self.presentationData.theme.rootController.navigationBar.accentTextColor) { - self.previousBackButtonArrow = previousBackButtonArrow - self.view.addSubview(previousBackButtonArrow) - } - if let previousBackButton = bottomNavigationBar.makeTransitionBackButtonView(accentColor: self.presentationData.theme.rootController.navigationBar.accentTextColor) { - self.previousBackButton = previousBackButton - self.view.addSubview(previousBackButton) - } - } - - if let currentBackButtonArrow = topNavigationBar.makeTransitionBackArrowNode(accentColor: .white) { - self.currentBackButtonArrow = currentBackButtonArrow - //self.addSubnode(currentBackButtonArrow) - } - if let headerView = bottomNavigationBar.customHeaderContentView as? ChatListHeaderComponent.View { - let _ = headerView - } else { - if let previousBackButtonBadge = bottomNavigationBar.makeTransitionBadgeNode() { - self.previousBackButtonBadge = previousBackButtonBadge - self.addSubnode(previousBackButtonBadge) - } - } - - if let currentBackButton = topNavigationBar.makeTransitionBackButtonNode(accentColor: .white) { - self.currentBackButton = currentBackButton - //self.addSubnode(currentBackButton) - } - - if let headerView = bottomNavigationBar.customHeaderContentView as? ChatListHeaderComponent.View { - if let previousRightButton = headerView.rightButtonView?.layer.snapshotContentTree() { - self.previousRightButton = previousRightButton - self.view.layer.addSublayer(previousRightButton) - } - } else { + do { if let avatarNavigationNode = bottomNavigationBar.rightButtonNode.singleCustomNode as? ChatAvatarNavigationNode, let previousAvatarView = avatarNavigationNode.view.snapshotContentTree() { self.previousAvatarView = previousAvatarView self.view.addSubview(previousAvatarView) @@ -14547,11 +14497,7 @@ private final class PeerInfoNavigationTransitionNode: ASDisplayNode, CustomNavig } var previousTitleView: UIView? - if let headerView = bottomNavigationBar.customHeaderContentView as? ChatListHeaderComponent.View { - if let componentView = headerView.titleContentView as? ChatTitleComponent.View { - previousTitleView = componentView.contentView - } - } else { + do { previousTitleView = bottomNavigationBar.titleView } @@ -14571,7 +14517,7 @@ private final class PeerInfoNavigationTransitionNode: ASDisplayNode, CustomNavig self.previousStatusNode = (previousStatusContainerNode, previousStatusNode) self.addSubnode(previousStatusContainerNode) } - } + }*/ } func update(containerSize: CGSize, fraction: CGFloat, transition: ContainedViewLayoutTransition) { @@ -14582,13 +14528,7 @@ private final class PeerInfoNavigationTransitionNode: ASDisplayNode, CustomNavig let fraction = self.reverseFraction ? (1.0 - fraction) : fraction if let previousBackButtonArrow = self.previousBackButtonArrow { - if let headerView = bottomNavigationBar.customHeaderContentView as? ChatListHeaderComponent.View { - if let backArrowView = headerView.backArrowView { - let previousBackButtonArrowFrame = backArrowView.convert(backArrowView.bounds, to: bottomNavigationBar.view) - previousBackButtonArrow.frame = previousBackButtonArrowFrame - transition.updateAlpha(layer: previousBackButtonArrow.layer, alpha: fraction) - } - } else { + do { let previousBackButtonArrowFrame = bottomNavigationBar.backButtonArrow.view.convert(bottomNavigationBar.backButtonArrow.view.bounds, to: bottomNavigationBar.view) previousBackButtonArrow.frame = previousBackButtonArrowFrame transition.updateAlpha(layer: previousBackButtonArrow.layer, alpha: fraction) @@ -14596,13 +14536,7 @@ private final class PeerInfoNavigationTransitionNode: ASDisplayNode, CustomNavig } if let previousBackButton = self.previousBackButton { - /*if let headerView = bottomNavigationBar.customHeaderContentView as? ChatListHeaderComponent.View { - if let backButtonTitleView = headerView.backButtonTitleView { - let previousBackButtonFrame = backButtonTitleView.convert(backButtonTitleView.bounds, to: bottomNavigationBar.view) - previousBackButton.frame = previousBackButtonFrame - transition.updateAlpha(layer: previousBackButton.layer, alpha: fraction) - } - } else*/ do { + do { let previousBackButtonFrame = bottomNavigationBar.backButtonNode.view.convert(bottomNavigationBar.backButtonNode.view.bounds, to: bottomNavigationBar.view) previousBackButton.frame = previousBackButtonFrame transition.updateAlpha(layer: previousBackButton.layer, alpha: fraction) @@ -14610,13 +14544,7 @@ private final class PeerInfoNavigationTransitionNode: ASDisplayNode, CustomNavig } if let previousRightButton = self.previousRightButton { - if let headerView = bottomNavigationBar.customHeaderContentView as? ChatListHeaderComponent.View { - if let rightButtonView = headerView.rightButtonView { - let previousRightButtonFrame = rightButtonView.convert(rightButtonView.bounds, to: bottomNavigationBar.view) - previousRightButton.frame = previousRightButtonFrame - transition.updateAlpha(layer: previousRightButton, alpha: fraction) - } - } else { + do { let previousRightButtonFrame = bottomNavigationBar.rightButtonNode.view.convert(bottomNavigationBar.rightButtonNode.view.bounds, to: bottomNavigationBar.view) previousRightButton.frame = previousRightButtonFrame transition.updateAlpha(layer: previousRightButton, alpha: fraction) @@ -14645,11 +14573,7 @@ private final class PeerInfoNavigationTransitionNode: ASDisplayNode, CustomNavig } var previousTitleView: UIView? - if let headerView = bottomNavigationBar.customHeaderContentView as? ChatListHeaderComponent.View { - if let componentView = headerView.titleContentView as? ChatTitleComponent.View { - previousTitleView = componentView.contentView - } - } else { + do { previousTitleView = bottomNavigationBar.titleView } diff --git a/submodules/TelegramUI/Components/PeerSelectionController/Sources/PeerSelectionController.swift b/submodules/TelegramUI/Components/PeerSelectionController/Sources/PeerSelectionController.swift index 0dc5f6b517..9bcb407971 100644 --- a/submodules/TelegramUI/Components/PeerSelectionController/Sources/PeerSelectionController.swift +++ b/submodules/TelegramUI/Components/PeerSelectionController/Sources/PeerSelectionController.swift @@ -245,7 +245,7 @@ public final class PeerSelectionControllerImpl: ViewController, PeerSelectionCon private func updateThemeAndStrings() { self.statusBar.statusBarStyle = self.presentationData.theme.rootController.statusBarStyle.style - self.navigationBar?.updatePresentationData(NavigationBarPresentationData(presentationData: self.presentationData)) + self.navigationBar?.updatePresentationData(NavigationBarPresentationData(presentationData: self.presentationData), transition: .immediate) self.searchContentNode?.updateThemeAndPlaceholder(theme: self.presentationData.theme, placeholder: self.presentationData.strings.Common_Search) self.title = self.customTitle ?? self.presentationData.strings.Conversation_ForwardTitle self.navigationItem.backBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.Common_Back, style: .plain, target: nil, action: nil) diff --git a/submodules/TelegramUI/Components/Settings/LanguageSelectionScreen/Sources/LanguageSelectionScreen.swift b/submodules/TelegramUI/Components/Settings/LanguageSelectionScreen/Sources/LanguageSelectionScreen.swift index 9d698608da..c2f3c3ac4b 100644 --- a/submodules/TelegramUI/Components/Settings/LanguageSelectionScreen/Sources/LanguageSelectionScreen.swift +++ b/submodules/TelegramUI/Components/Settings/LanguageSelectionScreen/Sources/LanguageSelectionScreen.swift @@ -85,7 +85,7 @@ public class LanguageSelectionScreen: ViewController { private func updateThemeAndStrings() { self.statusBar.statusBarStyle = self.presentationData.theme.rootController.statusBarStyle.style - self.navigationBar?.updatePresentationData(NavigationBarPresentationData(presentationData: self.presentationData)) + self.navigationBar?.updatePresentationData(NavigationBarPresentationData(presentationData: self.presentationData), transition: .immediate) self.searchContentNode?.updateThemeAndPlaceholder(theme: self.presentationData.theme, placeholder: self.presentationData.strings.Common_Search) self.title = self.presentationData.strings.Settings_AppLanguage self.navigationItem.backBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.Common_Back, style: .plain, target: nil, action: nil) diff --git a/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/Sources/BackButton.swift b/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/Sources/BackButton.swift index 271cae3d65..ff04a480c3 100644 --- a/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/Sources/BackButton.swift +++ b/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/Sources/BackButton.swift @@ -149,7 +149,7 @@ final class PeerInfoHeaderNavigationButton: HighlightableButtonNode { case .back: text = presentationData.strings.Common_Back accessibilityText = presentationData.strings.Common_Back - icon = NavigationBar.backArrowImage(color: .white) + icon = navigationBarBackArrowImage(color: .white) case .edit: text = presentationData.strings.Common_Edit accessibilityText = text diff --git a/submodules/TelegramUI/Components/Settings/ThemeAccentColorScreen/Sources/ThemeAccentColorController.swift b/submodules/TelegramUI/Components/Settings/ThemeAccentColorScreen/Sources/ThemeAccentColorController.swift index f4c58a78c6..762ea62da1 100644 --- a/submodules/TelegramUI/Components/Settings/ThemeAccentColorScreen/Sources/ThemeAccentColorController.swift +++ b/submodules/TelegramUI/Components/Settings/ThemeAccentColorScreen/Sources/ThemeAccentColorController.swift @@ -374,7 +374,7 @@ public final class ThemeAccentColorController: ViewController { }, ready: self._ready) self.controllerNode.themeUpdated = { [weak self] theme in if let strongSelf = self { - strongSelf.navigationBar?.updatePresentationData(NavigationBarPresentationData(presentationTheme: theme, presentationStrings: strongSelf.presentationData.strings)) + strongSelf.navigationBar?.updatePresentationData(NavigationBarPresentationData(presentationTheme: theme, presentationStrings: strongSelf.presentationData.strings), transition: .immediate) strongSelf.segmentedTitleView.theme = theme } } diff --git a/submodules/TelegramUI/Components/Settings/TimezoneSelectionScreen/Sources/TimezoneSelectionScreen.swift b/submodules/TelegramUI/Components/Settings/TimezoneSelectionScreen/Sources/TimezoneSelectionScreen.swift index d83ae3e0fb..a3247a880a 100644 --- a/submodules/TelegramUI/Components/Settings/TimezoneSelectionScreen/Sources/TimezoneSelectionScreen.swift +++ b/submodules/TelegramUI/Components/Settings/TimezoneSelectionScreen/Sources/TimezoneSelectionScreen.swift @@ -82,7 +82,7 @@ public class TimezoneSelectionScreen: ViewController { private func updateThemeAndStrings() { self.statusBar.statusBarStyle = self.presentationData.theme.rootController.statusBarStyle.style - self.navigationBar?.updatePresentationData(NavigationBarPresentationData(presentationData: self.presentationData)) + self.navigationBar?.updatePresentationData(NavigationBarPresentationData(presentationData: self.presentationData), transition: .immediate) self.searchContentNode?.updateThemeAndPlaceholder(theme: self.presentationData.theme, placeholder: self.presentationData.strings.Common_Search) self.title = self.presentationData.strings.Settings_AppLanguage self.navigationItem.backBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.Common_Back, style: .plain, target: nil, action: nil) diff --git a/submodules/TelegramUI/Components/Settings/WallpaperGalleryScreen/Sources/WallpaperGalleryController.swift b/submodules/TelegramUI/Components/Settings/WallpaperGalleryScreen/Sources/WallpaperGalleryController.swift index 0d63c6d710..64cae7771d 100644 --- a/submodules/TelegramUI/Components/Settings/WallpaperGalleryScreen/Sources/WallpaperGalleryController.swift +++ b/submodules/TelegramUI/Components/Settings/WallpaperGalleryScreen/Sources/WallpaperGalleryController.swift @@ -396,7 +396,7 @@ public class WallpaperGalleryController: ViewController { self.title = self.presentationData.strings.WallpaperPreview_Title } self.statusBar.statusBarStyle = self.presentationData.theme.rootController.statusBarStyle.style - self.navigationBar?.updatePresentationData(NavigationBarPresentationData(presentationData: self.presentationData)) + self.navigationBar?.updatePresentationData(NavigationBarPresentationData(presentationData: self.presentationData), transition: .immediate) self.toolbarNode?.updateThemeAndStrings(theme: self.presentationData.theme, strings: self.presentationData.strings) self.patternPanelNode?.updateTheme(self.presentationData.theme) diff --git a/submodules/TelegramUI/Components/Settings/WallpaperGridScreen/Sources/ThemeColorsGridController.swift b/submodules/TelegramUI/Components/Settings/WallpaperGridScreen/Sources/ThemeColorsGridController.swift index 77ed39bb0e..909646c1a0 100644 --- a/submodules/TelegramUI/Components/Settings/WallpaperGridScreen/Sources/ThemeColorsGridController.swift +++ b/submodules/TelegramUI/Components/Settings/WallpaperGridScreen/Sources/ThemeColorsGridController.swift @@ -216,7 +216,7 @@ public final class ThemeColorsGridController: ViewController, AttachmentContaina private func updateThemeAndStrings() { self.title = self.presentationData.strings.WallpaperColors_Title self.statusBar.statusBarStyle = self.presentationData.theme.rootController.statusBarStyle.style - self.navigationBar?.updatePresentationData(NavigationBarPresentationData(presentationData: self.presentationData)) + self.navigationBar?.updatePresentationData(NavigationBarPresentationData(presentationData: self.presentationData), transition: .immediate) if self.isNodeLoaded { self.controllerNode.updatePresentationData(self.presentationData) diff --git a/submodules/TelegramUI/Components/Settings/WallpaperGridScreen/Sources/ThemeGridController.swift b/submodules/TelegramUI/Components/Settings/WallpaperGridScreen/Sources/ThemeGridController.swift index bd601204c0..6921f99619 100644 --- a/submodules/TelegramUI/Components/Settings/WallpaperGridScreen/Sources/ThemeGridController.swift +++ b/submodules/TelegramUI/Components/Settings/WallpaperGridScreen/Sources/ThemeGridController.swift @@ -128,7 +128,7 @@ public final class ThemeGridController: ViewController { } self.statusBar.statusBarStyle = self.presentationData.theme.rootController.statusBarStyle.style - self.navigationBar?.updatePresentationData(NavigationBarPresentationData(presentationData: self.presentationData)) + self.navigationBar?.updatePresentationData(NavigationBarPresentationData(presentationData: self.presentationData), transition: .immediate) self.searchContentNode?.updateThemeAndPlaceholder(theme: self.presentationData.theme, placeholder: self.presentationData.strings.Wallpaper_Search) if self.isNodeLoaded { diff --git a/submodules/TelegramUI/Sources/AppDelegate.swift b/submodules/TelegramUI/Sources/AppDelegate.swift index eab0c4176c..04382e8445 100644 --- a/submodules/TelegramUI/Sources/AppDelegate.swift +++ b/submodules/TelegramUI/Sources/AppDelegate.swift @@ -42,6 +42,7 @@ import TelegramUIDeclareEncodables import ContextMenuScreen import MetalEngine import RecaptchaEnterprise +import NavigationBarImpl #if canImport(AppCenter) import AppCenter @@ -330,6 +331,10 @@ private func extractAccountManagerState(records: AccountRecordsView @@ -738,10 +735,10 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G case .inline, .standard(.embedded): navigationBarPresentationData = nil default: - navigationBarPresentationData = NavigationBarPresentationData(presentationData: self.presentationData, hideBackground: self.context.sharedContext.immediateExperimentalUISettings.playerEmbedding ? true : false, hideBadge: false) + navigationBarPresentationData = NavigationBarPresentationData(presentationData: self.presentationData, hideBackground: false, hideBadge: false, style: .glass) } - self.moreBarButton = MoreHeaderButton(color: self.presentationData.theme.rootController.navigationBar.buttonColor) + self.moreBarButton = MoreHeaderButton(color: self.presentationData.theme.chat.inputPanel.inputControlColor) self.moreBarButton.isUserInteractionEnabled = true super.init(context: context, navigationBarPresentationData: navigationBarPresentationData, mediaAccessoryPanelVisibility: mediaAccessoryPanelVisibility, locationBroadcastPanelSource: locationBroadcastPanelSource, groupCallPanelSource: groupCallPanelSource) @@ -6298,18 +6295,15 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G let presentationTheme: PresentationTheme if let forcedNavigationBarTheme = self.forcedNavigationBarTheme { presentationTheme = forcedNavigationBarTheme - navigationBarTheme = NavigationBarTheme(rootControllerTheme: forcedNavigationBarTheme, hideBackground: false, hideBadge: true) - } else if self.hasEmbeddedTitleContent { - presentationTheme = self.presentationData.theme - navigationBarTheme = NavigationBarTheme(rootControllerTheme: defaultDarkPresentationTheme, hideBackground: self.context.sharedContext.immediateExperimentalUISettings.playerEmbedding ? true : false, hideBadge: true) + navigationBarTheme = NavigationBarTheme(rootControllerTheme: forcedNavigationBarTheme, hideBackground: false, hideBadge: true, edgeEffectColor: .clear, style: .glass) } else { presentationTheme = self.presentationData.theme - navigationBarTheme = NavigationBarTheme(rootControllerTheme: self.presentationData.theme, hideBackground: self.context.sharedContext.immediateExperimentalUISettings.playerEmbedding ? true : false, hideBadge: false) + navigationBarTheme = NavigationBarTheme(rootControllerTheme: self.presentationData.theme, hideBackground: false, hideBadge: false, edgeEffectColor: .clear, style: .glass) } - self.navigationBar?.updatePresentationData(NavigationBarPresentationData(theme: navigationBarTheme, strings: NavigationBarStrings(presentationStrings: self.presentationData.strings))) + self.navigationBar?.updatePresentationData(NavigationBarPresentationData(theme: navigationBarTheme, strings: NavigationBarStrings(presentationStrings: self.presentationData.strings)), transition: .immediate) - self.chatTitleView?.updateThemeAndStrings(theme: presentationTheme, strings: self.presentationData.strings, hasEmbeddedTitleContent: self.hasEmbeddedTitleContent) + self.chatTitleView?.updateThemeAndStrings(theme: presentationTheme, strings: self.presentationData.strings) } enum PinnedReferenceMessage { @@ -6728,9 +6722,9 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } } - if !chatNavigationStack.isEmpty { - self.chatDisplayNode.navigationBar?.backButtonNode.isGestureEnabled = true - self.chatDisplayNode.navigationBar?.backButtonNode.activated = { [weak self] gesture, _ in + if !chatNavigationStack.isEmpty, let backButtonNode = self.chatDisplayNode.navigationBar?.backButtonNode as? ContextControllerSourceNode { + backButtonNode.isGestureEnabled = true + backButtonNode.activated = { [weak self] gesture, _ in guard let strongSelf = self, let backButtonNode = strongSelf.chatDisplayNode.navigationBar?.backButtonNode, let navigationController = strongSelf.effectiveNavigationController else { gesture.cancel() return diff --git a/submodules/TelegramUI/Sources/ChatControllerNode.swift b/submodules/TelegramUI/Sources/ChatControllerNode.swift index e7efdff948..2207f173f8 100644 --- a/submodules/TelegramUI/Sources/ChatControllerNode.swift +++ b/submodules/TelegramUI/Sources/ChatControllerNode.swift @@ -218,8 +218,6 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate { private let inputPanelClippingNode: SparseNode let inputPanelBackgroundNode: NavigationBackgroundNode - private var navigationBarBackgroundContent: WallpaperBubbleBackgroundNode? - private var intrinsicInputPanelBackgroundNodeSize: CGSize? private var inputPanelBottomBackgroundSeparatorBaseOffset: CGFloat = 0.0 private var plainInputSeparatorAlpha: CGFloat? @@ -238,6 +236,7 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate { private var floatingTopicsPanelContainer: ChatControllerTitlePanelNodeContainer private var floatingTopicsPanel: (view: ComponentView, component: ChatFloatingTopicsPanel)? + private var topBackgroundEdgeEffectNode: WallpaperEdgeEffectNode? private var bottomBackgroundEdgeEffectNode: WallpaperEdgeEffectNode? private(set) var inputPanelNode: ChatInputPanelNode? @@ -1392,6 +1391,11 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate { } } + //TODO:localize + if "".isEmpty { + hasTranslationPanel = false + } + /*#if DEBUG if "".isEmpty { hasTranslationPanel = true @@ -2187,6 +2191,28 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate { } } + var topBackgroundEdgeEffectNode: WallpaperEdgeEffectNode? + if let current = self.topBackgroundEdgeEffectNode { + topBackgroundEdgeEffectNode = current + } else { + if let value = self.backgroundNode.makeEdgeEffectNode() { + topBackgroundEdgeEffectNode = value + self.topBackgroundEdgeEffectNode = value + self.historyNodeContainer.view.superview?.insertSubview(value.view, aboveSubview: self.historyNodeContainer.view) + } + } + if let topBackgroundEdgeEffectNode { + let blurFrame = CGRect(origin: CGPoint(), size: CGSize(width: layout.size.width, height: navigationBarHeight + 24.0)) + transition.updateFrame(node: topBackgroundEdgeEffectNode, frame: blurFrame) + topBackgroundEdgeEffectNode.update( + rect: blurFrame, + edge: WallpaperEdgeEffectEdge(edge: .top, size: 40.0), + blur: true, + containerSize: wallpaperBounds.size, + transition: transition + ) + } + var bottomBackgroundEdgeEffectNode: WallpaperEdgeEffectNode? if let current = self.bottomBackgroundEdgeEffectNode { bottomBackgroundEdgeEffectNode = current @@ -2205,6 +2231,7 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate { bottomBackgroundEdgeEffectNode.update( rect: blurFrame, edge: WallpaperEdgeEffectEdge(edge: .bottom, size: 80.0), + blur: false, containerSize: wallpaperBounds.size, transition: transition ) @@ -2502,11 +2529,6 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate { })*/ dismissedFloatingTopicsPanelView.removeFromSuperview() } - - if let navigationBarBackgroundContent = self.navigationBarBackgroundContent { - transition.updateFrame(node: navigationBarBackgroundContent, frame: CGRect(origin: .zero, size: CGSize(width: layout.size.width, height: navigationBarHeight + (titleAccessoryPanelBackgroundHeight ?? 0.0) + (translationPanelHeight ?? 0.0))), beginWithCurrentState: true) - navigationBarBackgroundContent.update(rect: CGRect(origin: .zero, size: CGSize(width: layout.size.width, height: navigationBarHeight + (titleAccessoryPanelBackgroundHeight ?? 0.0) + (translationPanelHeight ?? 0.0))), within: layout.size, transition: transition) - } transition.updateFrame(node: self.contentDimNode, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: layout.size.width, height: apparentInputBackgroundFrame.origin.y))) @@ -3485,22 +3507,6 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate { self.updatePlainInputSeparator(transition: .immediate) self.backgroundNode.updateBubbleTheme(bubbleTheme: chatPresentationInterfaceState.theme, bubbleCorners: chatPresentationInterfaceState.bubbleCorners) - - if self.backgroundNode.hasExtraBubbleBackground() { - if self.navigationBarBackgroundContent == nil { - if let navigationBarBackgroundContent = self.backgroundNode.makeBubbleBackground(for: .free) { - self.navigationBarBackgroundContent = navigationBarBackgroundContent - - navigationBarBackgroundContent.allowsGroupOpacity = true - navigationBarBackgroundContent.implicitContentUpdate = false - navigationBarBackgroundContent.alpha = 0.3 - self.navigationBar?.insertSubnode(navigationBarBackgroundContent, at: 1) - } - } - } else { - self.navigationBarBackgroundContent?.removeFromSupernode() - self.navigationBarBackgroundContent = nil - } } let keepSendButtonEnabled = chatPresentationInterfaceState.interfaceState.forwardMessageIds != nil || chatPresentationInterfaceState.interfaceState.editMessage != nil diff --git a/submodules/TelegramUI/Sources/ChatInterfaceTitlePanelNodes.swift b/submodules/TelegramUI/Sources/ChatInterfaceTitlePanelNodes.swift index 2c827245d8..4bb2d9de4d 100644 --- a/submodules/TelegramUI/Sources/ChatInterfaceTitlePanelNodes.swift +++ b/submodules/TelegramUI/Sources/ChatInterfaceTitlePanelNodes.swift @@ -8,6 +8,10 @@ import ComponentFlow import ChatSideTopicsPanel func titlePanelForChatPresentationInterfaceState(_ chatPresentationInterfaceState: ChatPresentationInterfaceState, context: AccountContext, currentPanel: ChatTitleAccessoryPanelNode?, controllerInteraction: ChatControllerInteraction?, interfaceInteraction: ChatPanelInterfaceInteraction?, force: Bool) -> ChatTitleAccessoryPanelNode? { + if "".isEmpty { + return nil + } + if !force, case .standard(.embedded) = chatPresentationInterfaceState.mode { return nil } diff --git a/submodules/TelegramUI/Sources/ChatSearchResultsController.swift b/submodules/TelegramUI/Sources/ChatSearchResultsController.swift index 4e35b65cbd..c686bf1d6d 100644 --- a/submodules/TelegramUI/Sources/ChatSearchResultsController.swift +++ b/submodules/TelegramUI/Sources/ChatSearchResultsController.swift @@ -43,7 +43,7 @@ final class ChatSearchResultsController: ViewController { |> deliverOnMainQueue).startStrict(next: { [weak self] presentationData in if let strongSelf = self { strongSelf.presentationData = presentationData - strongSelf.navigationBar?.updatePresentationData(NavigationBarPresentationData(presentationTheme: presentationData.theme, presentationStrings: presentationData.strings)) + strongSelf.navigationBar?.updatePresentationData(NavigationBarPresentationData(presentationTheme: presentationData.theme, presentationStrings: presentationData.strings), transition: .immediate) strongSelf.controllerNode.updatePresentationData(presentationData) } }) diff --git a/submodules/TelegramUI/Sources/ComposeController.swift b/submodules/TelegramUI/Sources/ComposeController.swift index f4881a504b..e2557f5ac5 100644 --- a/submodules/TelegramUI/Sources/ComposeController.swift +++ b/submodules/TelegramUI/Sources/ComposeController.swift @@ -93,7 +93,7 @@ public class ComposeControllerImpl: ViewController, ComposeController { private func updateThemeAndStrings() { self.statusBar.statusBarStyle = self.presentationData.theme.rootController.statusBarStyle.style - self.navigationBar?.updatePresentationData(NavigationBarPresentationData(presentationData: self.presentationData)) + self.navigationBar?.updatePresentationData(NavigationBarPresentationData(presentationData: self.presentationData), transition: .immediate) self.searchContentNode?.updateThemeAndPlaceholder(theme: self.presentationData.theme, placeholder: self.presentationData.strings.Common_Search) self.title = self.presentationData.strings.Compose_NewMessage self.navigationItem.backBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.Common_Back, style: .plain, target: nil, action: nil) diff --git a/submodules/TelegramUI/Sources/ContactMultiselectionController.swift b/submodules/TelegramUI/Sources/ContactMultiselectionController.swift index 043b097745..a4b097b5f7 100644 --- a/submodules/TelegramUI/Sources/ContactMultiselectionController.swift +++ b/submodules/TelegramUI/Sources/ContactMultiselectionController.swift @@ -226,7 +226,7 @@ class ContactMultiselectionControllerImpl: ViewController, ContactMultiselection private func updateThemeAndStrings() { self.statusBar.statusBarStyle = self.presentationData.theme.rootController.statusBarStyle.style - self.navigationBar?.updatePresentationData(NavigationBarPresentationData(presentationData: self.presentationData)) + self.navigationBar?.updatePresentationData(NavigationBarPresentationData(presentationData: self.presentationData), transition: .immediate) self.navigationItem.backBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.Common_Back, style: .plain, target: nil, action: nil) self.updateTitle() self.contactsNode.updatePresentationData(self.presentationData) diff --git a/submodules/TelegramUI/Sources/ContactSelectionController.swift b/submodules/TelegramUI/Sources/ContactSelectionController.swift index 5af605104d..cb2e41ff78 100644 --- a/submodules/TelegramUI/Sources/ContactSelectionController.swift +++ b/submodules/TelegramUI/Sources/ContactSelectionController.swift @@ -307,7 +307,7 @@ class ContactSelectionControllerImpl: ViewController, ContactSelectionController if case .glass = self.style { glass = true } - self.navigationBar?.updatePresentationData(NavigationBarPresentationData(theme: NavigationBarTheme(rootControllerTheme: self.presentationData.theme, hideBackground: glass, hideSeparator: glass), strings: NavigationBarStrings(presentationStrings: self.presentationData.strings))) + self.navigationBar?.updatePresentationData(NavigationBarPresentationData(theme: NavigationBarTheme(rootControllerTheme: self.presentationData.theme, hideBackground: glass, hideSeparator: glass), strings: NavigationBarStrings(presentationStrings: self.presentationData.strings)), transition: .immediate) (self.searchContentNode as? NavigationBarSearchContentNode)?.updateThemeAndPlaceholder(theme: self.presentationData.theme, placeholder: self.presentationData.strings.Common_Search) self.title = self.titleProducer(self.presentationData.strings) self.tabBarItem.title = self.presentationData.strings.Contacts_Title diff --git a/submodules/TelegramUIPreferences/Sources/ExperimentalUISettings.swift b/submodules/TelegramUIPreferences/Sources/ExperimentalUISettings.swift index 319cb1690c..77f4684457 100644 --- a/submodules/TelegramUIPreferences/Sources/ExperimentalUISettings.swift +++ b/submodules/TelegramUIPreferences/Sources/ExperimentalUISettings.swift @@ -32,7 +32,6 @@ public struct ExperimentalUISettings: Codable, Equatable { public var chatListPhotos: Bool public var knockoutWallpaper: Bool public var foldersTabAtBottom: Bool - public var playerEmbedding: Bool public var preferredVideoCodec: String? public var disableVideoAspectScaling: Bool public var enableVoipTcp: Bool @@ -80,7 +79,6 @@ public struct ExperimentalUISettings: Codable, Equatable { chatListPhotos: false, knockoutWallpaper: false, foldersTabAtBottom: false, - playerEmbedding: false, preferredVideoCodec: nil, disableVideoAspectScaling: false, enableVoipTcp: false, @@ -129,7 +127,6 @@ public struct ExperimentalUISettings: Codable, Equatable { chatListPhotos: Bool, knockoutWallpaper: Bool, foldersTabAtBottom: Bool, - playerEmbedding: Bool, preferredVideoCodec: String?, disableVideoAspectScaling: Bool, enableVoipTcp: Bool, @@ -175,7 +172,6 @@ public struct ExperimentalUISettings: Codable, Equatable { self.chatListPhotos = chatListPhotos self.knockoutWallpaper = knockoutWallpaper self.foldersTabAtBottom = foldersTabAtBottom - self.playerEmbedding = playerEmbedding self.preferredVideoCodec = preferredVideoCodec self.disableVideoAspectScaling = disableVideoAspectScaling self.enableVoipTcp = enableVoipTcp @@ -225,7 +221,6 @@ public struct ExperimentalUISettings: Codable, Equatable { self.chatListPhotos = (try container.decodeIfPresent(Int32.self, forKey: "chatListPhotos") ?? 0) != 0 self.knockoutWallpaper = (try container.decodeIfPresent(Int32.self, forKey: "knockoutWallpaper") ?? 0) != 0 self.foldersTabAtBottom = (try container.decodeIfPresent(Int32.self, forKey: "foldersTabAtBottom") ?? 0) != 0 - self.playerEmbedding = (try container.decodeIfPresent(Int32.self, forKey: "playerEmbedding") ?? 0) != 0 self.preferredVideoCodec = try container.decodeIfPresent(String.self.self, forKey: "preferredVideoCodec") self.disableVideoAspectScaling = (try container.decodeIfPresent(Int32.self, forKey: "disableVideoAspectScaling") ?? 0) != 0 self.enableVoipTcp = (try container.decodeIfPresent(Int32.self, forKey: "enableVoipTcp") ?? 0) != 0 @@ -275,7 +270,6 @@ public struct ExperimentalUISettings: Codable, Equatable { try container.encode((self.chatListPhotos ? 1 : 0) as Int32, forKey: "chatListPhotos") try container.encode((self.knockoutWallpaper ? 1 : 0) as Int32, forKey: "knockoutWallpaper") try container.encode((self.foldersTabAtBottom ? 1 : 0) as Int32, forKey: "foldersTabAtBottom") - try container.encode((self.playerEmbedding ? 1 : 0) as Int32, forKey: "playerEmbedding") try container.encodeIfPresent(self.preferredVideoCodec, forKey: "preferredVideoCodec") try container.encode((self.disableVideoAspectScaling ? 1 : 0) as Int32, forKey: "disableVideoAspectScaling") try container.encode((self.enableVoipTcp ? 1 : 0) as Int32, forKey: "enableVoipTcp") diff --git a/submodules/WallpaperBackgroundNode/BUILD b/submodules/WallpaperBackgroundNode/BUILD index ccd9dff870..5dfa9be2b1 100644 --- a/submodules/WallpaperBackgroundNode/BUILD +++ b/submodules/WallpaperBackgroundNode/BUILD @@ -68,6 +68,7 @@ swift_library( "//submodules/AnimatedStickerNode:AnimatedStickerNode", "//submodules/TelegramAnimatedStickerNode:TelegramAnimatedStickerNode", "//submodules/Components/HierarchyTrackingLayer:HierarchyTrackingLayer", + "//submodules/TelegramUI/Components/EdgeEffect", ], visibility = [ "//visibility:public", diff --git a/submodules/WallpaperBackgroundNode/Sources/WallpaperBackgroundNode.swift b/submodules/WallpaperBackgroundNode/Sources/WallpaperBackgroundNode.swift index 8c96748f85..13eb697cf1 100644 --- a/submodules/WallpaperBackgroundNode/Sources/WallpaperBackgroundNode.swift +++ b/submodules/WallpaperBackgroundNode/Sources/WallpaperBackgroundNode.swift @@ -16,6 +16,7 @@ import AppBundle import AnimatedStickerNode import TelegramAnimatedStickerNode import HierarchyTrackingLayer +import EdgeEffect private let motionAmount: CGFloat = 32.0 @@ -97,7 +98,7 @@ public struct WallpaperEdgeEffectEdge: Equatable { } public protocol WallpaperEdgeEffectNode: ASDisplayNode { - func update(rect: CGRect, edge: WallpaperEdgeEffectEdge, containerSize: CGSize, transition: ContainedViewLayoutTransition) + func update(rect: CGRect, edge: WallpaperEdgeEffectEdge, blur: Bool, containerSize: CGSize, transition: ContainedViewLayoutTransition) } public protocol WallpaperBackgroundNode: ASDisplayNode { @@ -1796,11 +1797,13 @@ private final class WallpaperEdgeEffectNodeImpl: ASDisplayNode, WallpaperEdgeEff private struct Params: Equatable { let rect: CGRect let edge: WallpaperEdgeEffectEdge + let blur: Bool let containerSize: CGSize - init(rect: CGRect, edge: WallpaperEdgeEffectEdge, containerSize: CGSize) { + init(rect: CGRect, edge: WallpaperEdgeEffectEdge, blur: Bool, containerSize: CGSize) { self.rect = rect self.edge = edge + self.blur = blur self.containerSize = containerSize } } @@ -1813,6 +1816,8 @@ private final class WallpaperEdgeEffectNodeImpl: ASDisplayNode, WallpaperEdgeEff private let overlayNode: ASDisplayNode private let maskView: UIImageView + private var blurView: VariableBlurView? + private weak var parentNode: WallpaperBackgroundNodeImpl? private var index: Int? private var params: Params? @@ -1870,7 +1875,7 @@ private final class WallpaperEdgeEffectNodeImpl: ASDisplayNode, WallpaperEdgeEff self.containerNode.insertSubnode(gradientNode, at: 0) if let params = self.params { - self.updateImpl(rect: params.rect, edge: params.edge, containerSize: params.containerSize, transition: .immediate) + self.updateImpl(rect: params.rect, edge: params.edge, blur: params.blur, containerSize: params.containerSize, transition: .immediate) } } } else { @@ -1889,26 +1894,26 @@ private final class WallpaperEdgeEffectNodeImpl: ASDisplayNode, WallpaperEdgeEff } } - func update(rect: CGRect, edge: WallpaperEdgeEffectEdge, containerSize: CGSize, transition: ContainedViewLayoutTransition) { - let params = Params(rect: rect, edge: edge, containerSize: containerSize) + func update(rect: CGRect, edge: WallpaperEdgeEffectEdge, blur: Bool, containerSize: CGSize, transition: ContainedViewLayoutTransition) { + let params = Params(rect: rect, edge: edge, blur: blur, containerSize: containerSize) if self.params != params { self.params = params - self.updateImpl(rect: params.rect, edge: params.edge, containerSize: params.containerSize, transition: transition) + self.updateImpl(rect: params.rect, edge: params.edge, blur: params.blur, containerSize: params.containerSize, transition: transition) } } - private func updateImpl(rect: CGRect, edge: WallpaperEdgeEffectEdge, containerSize: CGSize, transition: ContainedViewLayoutTransition) { + private func updateImpl(rect: CGRect, edge: WallpaperEdgeEffectEdge, blur: Bool, containerSize: CGSize, transition: ContainedViewLayoutTransition) { transition.updateFrame(node: self.containerMaskingNode, frame: CGRect(origin: CGPoint(), size: rect.size)) transition.updateBounds(node: self.containerNode, bounds: CGRect(origin: CGPoint(x: rect.minX, y: rect.minY), size: rect.size)) if self.maskView.image?.size.height != edge.size { - let baseGradientAlpha: CGFloat = 0.75 + let baseGradientAlpha: CGFloat = blur ? 0.55 : 0.75 let numSteps = 8 let firstStep = 1 let firstLocation = 0.0 let colors: [UIColor] = (0 ..< numSteps).map { i in if i < firstStep { - return UIColor(white: 1.0, alpha: 1.0) + return UIColor(white: 1.0, alpha: 0.0) } else { let step: CGFloat = CGFloat(i - firstStep) / CGFloat(numSteps - firstStep - 1) let value: CGFloat = bezierPoint(0.42, 0.0, 0.58, 1.0, step) @@ -1931,7 +1936,14 @@ private final class WallpaperEdgeEffectNodeImpl: ASDisplayNode, WallpaperEdgeEff )?.stretchableImage(withLeftCapWidth: 0, topCapHeight: Int(edge.size)) } - transition.updateFrame(view: self.maskView, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: rect.size)) + let maskFrame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: rect.size) + transition.updatePosition(layer: self.maskView.layer, position: maskFrame.center) + transition.updateBounds(layer: self.maskView.layer, bounds: CGRect(origin: CGPoint(), size: maskFrame.size)) + if case .top = edge.edge { + self.maskView.transform = CGAffineTransformMakeScale(1.0, -1.0) + } else { + self.maskView.transform = CGAffineTransformIdentity + } transition.updateFrame(node: self.overlayNode, frame: CGRect(origin: CGPoint(), size: containerSize)) @@ -1939,6 +1951,50 @@ private final class WallpaperEdgeEffectNodeImpl: ASDisplayNode, WallpaperEdgeEff transition.updateFrame(node: gradientNode, frame: CGRect(origin: CGPoint(), size: containerSize)) } transition.updateFrame(layer: self.patternImageLayer, frame: CGRect(origin: CGPoint(), size: containerSize)) + + if blur { + let gradientMaskLayer = SimpleGradientLayer() + let baseGradientAlpha: CGFloat = 1.0 + let numSteps = 8 + let firstStep = 1 + let firstLocation = 0.8 + gradientMaskLayer.colors = (0 ..< numSteps).map { i in + if i < firstStep { + return UIColor(white: 1.0, alpha: 1.0).cgColor + } else { + let step: CGFloat = CGFloat(i - firstStep) / CGFloat(numSteps - firstStep - 1) + let value: CGFloat = 1.0 - bezierPoint(0.42, 0.0, 0.58, 1.0, step) + return UIColor(white: 1.0, alpha: baseGradientAlpha * value).cgColor + } + } + gradientMaskLayer.locations = (0 ..< numSteps).map { i -> NSNumber in + if i < firstStep { + return 0.0 as NSNumber + } else { + let step: CGFloat = CGFloat(i - firstStep) / CGFloat(numSteps - firstStep - 1) + return (firstLocation + (1.0 - firstLocation) * step) as NSNumber + } + } + + let blurView: VariableBlurView + if let current = self.blurView { + blurView = current + } else { + blurView = VariableBlurView(gradientMask: self.maskView.image ?? UIImage(), maxBlurRadius: 8.0) + blurView.layer.mask = gradientMaskLayer + self.view.insertSubview(blurView, at: 0) + self.blurView = blurView + } + transition.updateFrame(view: blurView, frame: bounds) + if let maskLayer = blurView.layer.mask { + transition.updateFrame(layer: maskLayer, frame: bounds) + maskLayer.transform = CATransform3DMakeScale(1.0, -1.0, 1.0) + } + blurView.transform = self.maskView.transform + } else if let blurView = self.blurView { + self.blurView = nil + blurView.removeFromSuperview() + } } } diff --git a/submodules/WebSearchUI/Sources/WebSearchGalleryController.swift b/submodules/WebSearchUI/Sources/WebSearchGalleryController.swift index 28452c546a..dba4d51172 100644 --- a/submodules/WebSearchUI/Sources/WebSearchGalleryController.swift +++ b/submodules/WebSearchUI/Sources/WebSearchGalleryController.swift @@ -60,7 +60,7 @@ final class WebSearchGalleryControllerPresentationArguments { } class WebSearchGalleryController: ViewController { - private static let navigationTheme = NavigationBarTheme(buttonColor: .white, disabledButtonColor: UIColor(rgb: 0x525252), primaryTextColor: .white, backgroundColor: .clear, enableBackgroundBlur: false, separatorColor: .clear, badgeBackgroundColor: .clear, badgeStrokeColor: .clear, badgeTextColor: .clear) + private static let navigationTheme = NavigationBarTheme(overallDarkAppearance: false, buttonColor: .white, disabledButtonColor: UIColor(rgb: 0x525252), primaryTextColor: .white, backgroundColor: .clear, enableBackgroundBlur: false, separatorColor: .clear, badgeBackgroundColor: .clear, badgeStrokeColor: .clear, badgeTextColor: .clear) private var galleryNode: GalleryControllerNode { return self.displayNode as! GalleryControllerNode diff --git a/submodules/WebUI/Sources/WebAppController.swift b/submodules/WebUI/Sources/WebAppController.swift index 4cee7d24e9..ca9010bd6b 100644 --- a/submodules/WebUI/Sources/WebAppController.swift +++ b/submodules/WebUI/Sources/WebAppController.swift @@ -3621,6 +3621,7 @@ public final class WebAppController: ViewController, AttachmentContainable { if let backgroundColor = self.controllerNode.headerColor, let textColor = self.controllerNode.headerPrimaryTextColor { navigationBarPresentationData = NavigationBarPresentationData( theme: NavigationBarTheme( + overallDarkAppearance: false, buttonColor: textColor, disabledButtonColor: textColor, primaryTextColor: textColor, @@ -3639,7 +3640,7 @@ public final class WebAppController: ViewController, AttachmentContainable { strings: NavigationBarStrings(back: "", close: "") ) } - self.navigationBar?.updatePresentationData(navigationBarPresentationData) + self.navigationBar?.updatePresentationData(navigationBarPresentationData, transition: .immediate) } @objc fileprivate func cancelPressed() {