Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
signalapp
GitHub Repository: signalapp/Signal-iOS
Path: blob/main/Signal/src/ViewControllers/ContextMenus/ContextMenuButton.swift
1 views
//
// Copyright 2024 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
//

import SignalServiceKit
import UIKit

/// A button that shows a fixed context menu when tapped.
class ContextMenuButton: UIButton {
    override var intrinsicContentSize: CGSize { .zero }

    private let onWillDisplayContextMenu: () -> Void
    private let onDidDismissContextMenu: () -> Void

    /// Creates a context menu button with the given actions.
    init(
        actions: [UIMenuElement],
        onWillDisplayContextMenu: @escaping () -> Void = {},
        onDidDismissContextMenu: @escaping () -> Void = {},
    ) {
        self.onWillDisplayContextMenu = onWillDisplayContextMenu
        self.onDidDismissContextMenu = onDidDismissContextMenu
        super.init(frame: .zero)
        setActions(actions: actions)
    }

    /// Creates an empty context menu button. Callers should subsequently call
    /// ``setActions(actions:)`` manually to populate the button.
    init(
        empty: Void,
        onWillDisplayContextMenu: @escaping () -> Void = {},
        onDidDismissContextMenu: @escaping () -> Void = {},
    ) {
        self.onWillDisplayContextMenu = onWillDisplayContextMenu
        self.onDidDismissContextMenu = onDidDismissContextMenu
        super.init(frame: .zero)
        setActions(actions: [])
    }

    /// Set the actions for this button's context menu.
    func setActions(actions: [UIMenuElement]) {
        showsMenuAsPrimaryAction = true
        menu = UIMenu(children: actions)
    }

    @available(*, unavailable, message: "Use other initializer!")
    required init?(coder: NSCoder) {
        owsFail("Use other initializer!")
    }

    // MARK: -

    override func contextMenuInteraction(
        _ interaction: UIContextMenuInteraction,
        willDisplayMenuFor configuration: UIContextMenuConfiguration,
        animator: (any UIContextMenuInteractionAnimating)?,
    ) {
        super.contextMenuInteraction(interaction, willDisplayMenuFor: configuration, animator: animator)
        onWillDisplayContextMenu()
    }

    override func contextMenuInteraction(
        _ interaction: UIContextMenuInteraction,
        willEndFor configuration: UIContextMenuConfiguration,
        animator: (any UIContextMenuInteractionAnimating)?,
    ) {
        super.contextMenuInteraction(interaction, willEndFor: configuration, animator: animator)
        onDidDismissContextMenu()
    }
}