vChewing-macOS/Packages/vChewing_Shared/Sources/Shared/UserDef/UserDefRenderable.swift

94 lines
2.8 KiB
Swift

// (c) 2022 and onwards The vChewing Project (MIT-NTL License).
// ====================
// This code is released under the MIT license (SPDX-License-Identifier: MIT)
// ... with NTL restriction stating that:
// No trademark license is granted to use the trade names, trademarks, service
// marks, or product names of Contributor, except as required to fulfill notice
// requirements defined in MIT License.
import Foundation
import SwiftUI
// MARK: - UserDefRenderable
@available(macOS 10.15, *)
public struct UserDefRenderable<Value>: Identifiable {
public let def: UserDef
public let binding: Binding<Value>
public let options: [Dictionary<Int, String>.Element]
public var id: String { def.rawValue }
public var metaData: UserDef.MetaData? { def.metaData }
public init(_ userDef: UserDef, binding: Binding<Value>) {
def = userDef
self.binding = binding
options = (def.metaData?.options ?? [:]).sorted(by: { $0.key < $1.key })
}
public typealias RawFormat = (key: UserDef, value: Binding<Value>)
@ViewBuilder
public func render() -> some View {
EmptyView()
}
public var hasInlineDescription: Bool {
guard let meta = def.metaData else { return false }
return meta.description != nil || meta.inlinePrompt != nil || meta.minimumOS > 10.9
}
@ViewBuilder
public func descriptionView() -> some View {
if let metaData = metaData {
if hasInlineDescription { Spacer().frame(height: 6) }
let descText = metaData.description
let promptText = metaData.inlinePrompt
let descriptionSource: [String] = [promptText, descText].compactMap { $0 }
if !descriptionSource.isEmpty {
ForEach(Array(descriptionSource.enumerated()), id: \.offset) { _, i18nKey in
Text(LocalizedStringKey(i18nKey)).settingsDescription()
}
}
if metaData.minimumOS > 10.9 {
Group {
Text("") + Text(LocalizedStringKey("This feature requires macOS \(metaData.minimumOS.description) and above."))
}.settingsDescription()
}
}
}
}
public extension UserDefRenderable<Any> {
func batch(_ input: [RawFormat]) -> [UserDefRenderable<Value>] {
input.compactMap { metaPair in
metaPair.key.bind(binding)
}
}
}
extension [UserDefRenderable<Any>]: Identifiable {
public var id: String { map(\.id).description }
}
// MARK: - UserDef metaData Extension
public extension UserDef {
func bind<Value>(_ binding: Binding<Value>) -> UserDefRenderable<Value> {
UserDefRenderable(self, binding: binding)
}
}
// MARK: - Private View Extension
@available(macOS 10.15, *)
private extension View {
func settingsDescription(maxWidth: CGFloat? = .infinity) -> some View {
controlSize(.small)
.frame(maxWidth: maxWidth, alignment: .leading)
// TODO: Use `.foregroundStyle` when targeting macOS 12.
.foregroundColor(.secondary)
}
}