Instantly share code, notes, and snippets.
Last active
December 7, 2023 23:23
-
Star
1
(1)
You must be signed in to star a gist -
Fork
0
(0)
You must be signed in to fork a gist
-
Save palmin/c2110ec689747a20406afaab523c7673 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import SwiftUI | |
#if os(iOS) | |
import UIKit | |
struct BranchPickerButton: UIViewRepresentable { | |
public var title: String = "" | |
public let producer: () async throws -> [(String, String)] | |
@Binding public var value: String | |
func makeUIView(context: Context) -> UIButton { | |
var config = UIButton.Configuration.plain() | |
config.title = value.isEmpty ? NSLocalizedString("Default", comment: "") : value | |
config.image = UIImage(systemName: "chevron.up.chevron.down") | |
config.imagePlacement = .trailing | |
config.imagePadding = 5 | |
config.preferredSymbolConfigurationForImage = .init(pointSize: 12, weight: .regular) | |
config.titleAlignment = .trailing | |
config.contentInsets = .zero | |
let button = UIButton(configuration: config, primaryAction: nil) | |
button.contentHorizontalAlignment = .trailing | |
let defererred = UIDeferredMenuElement { [weak button] callback in | |
Task { | |
let items: [(String, String)] | |
do { | |
items = try await producer() | |
} catch { | |
let warning = UIAction(title: error.localizedDescription, | |
image: UIImage(systemName: "exclamationmark.triangle"), handler: { _ in }) | |
warning.attributes = .disabled | |
callback([warning]) | |
return | |
} | |
var elements = [UIMenuElement]() | |
for (title, value) in items { | |
let element = UIAction(title: title, handler: { _ in | |
button?.setTitle(title, for: .normal) | |
self.value = value | |
}) | |
if(self.value == value) { | |
element.state = .on | |
} | |
elements.append(element) | |
} | |
callback(elements) | |
} | |
} | |
button.showsMenuAsPrimaryAction = true | |
button.menu = UIMenu(title: self.title, children: [defererred]) | |
return button | |
} | |
func updateUIView(_ button: UIButton, context: Context) { | |
button.configuration?.title = value.isEmpty ? NSLocalizedString("Default", comment: "") : value | |
} | |
} | |
#endif | |
#if os(macOS) | |
import AppKit | |
fileprivate class BranchPickerPopupButton : NSPopUpButton { | |
var callback: (String) -> Void = { _ in } | |
var producer: () async throws -> [(String, String)] = { [] } | |
var value = "" | |
@objc func picked(_ sender: NSMenuItem) { | |
if let string = sender.representedObject as? String { | |
value = string | |
callback(string) | |
} | |
} | |
@objc func refreshMenu() { | |
guard let menu = menu else { return } | |
// Populate the menu asynchronously | |
Task { | |
do { | |
let items = try await producer() | |
menu.removeAllItems() | |
var selected: NSMenuItem? | |
for (title, itemValue) in items { | |
let menuItem = NSMenuItem(title: title, | |
action: #selector(BranchPickerPopupButton.picked), | |
keyEquivalent: "") | |
menuItem.target = self | |
menuItem.representedObject = itemValue | |
if value == itemValue { | |
selected = menuItem | |
} | |
menu.addItem(menuItem) | |
} | |
select(selected) | |
} catch { | |
menu.removeAllItems() | |
let warningItem = NSMenuItem(title: error.localizedDescription, action: nil, keyEquivalent: "") | |
warningItem.image = NSImage(systemSymbolName: "exclamationmark.triangle", accessibilityDescription: nil) | |
warningItem.isEnabled = false | |
menu.addItem(warningItem) | |
} | |
} | |
} | |
} | |
struct BranchPickerButton: NSViewRepresentable { | |
var title: String = "" | |
let producer: () async throws -> [(String, String)] | |
@Binding var value: String | |
func makeNSView(context: Context) -> NSPopUpButton { | |
let popUpButton = BranchPickerPopupButton() | |
popUpButton.callback = { value = $0 } | |
popUpButton.producer = producer | |
NotificationCenter.default.addObserver(popUpButton, selector: #selector(BranchPickerPopupButton.refreshMenu), | |
name: NSPopUpButton.willPopUpNotification, | |
object: popUpButton) | |
let deferredMenuElement = NSMenuItem() | |
deferredMenuElement.title = title | |
deferredMenuElement.submenu = NSMenu(title: title) | |
popUpButton.menu = deferredMenuElement.submenu | |
let title = value.isEmpty ? NSLocalizedString("Default", comment: "") : value | |
let menuItem = NSMenuItem(title: title, action: nil, keyEquivalent: "") | |
popUpButton.menu?.addItem(menuItem) | |
return popUpButton | |
} | |
func updateNSView(_ nsView: NSPopUpButton, context: Context) { | |
let button = nsView as? BranchPickerPopupButton | |
button?.value = value | |
let title = value.isEmpty ? NSLocalizedString("Default", comment: "") : value | |
let menuItem = NSMenuItem(title: title, action: nil, keyEquivalent: "") | |
button?.menu?.removeAllItems() | |
button?.menu?.addItem(menuItem) | |
} | |
} | |
#endif |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment