import AppIntents import SwiftUI import WidgetKit // 主屏 / 锁屏小组件「云剪贴板」(#6):在控制中心两控件之外,提供一个可放主屏的交互式小组件,含上传 / // 拉取两个按钮(iOS 17+ 交互式小组件,按钮直接跑 AppIntent,无需打开 app)。触发结果经 WidgetFeedback // 弹本地通知反馈。按钮复用与控制中心相同的 Upload/PullClipboardIntent。 struct ClipboardWidgetEntry: TimelineEntry { let date: Date } // 纯按钮小组件,无动态内容:单条永不刷新的时间线即可(交互由按钮的 AppIntent 驱动)。 struct ClipboardWidgetProvider: TimelineProvider { func placeholder(in context: Context) -> ClipboardWidgetEntry { ClipboardWidgetEntry(date: Date()) } func getSnapshot(in context: Context, completion: @escaping (ClipboardWidgetEntry) -> Void) { completion(ClipboardWidgetEntry(date: Date())) } func getTimeline(in context: Context, completion: @escaping (Timeline) -> Void) { completion(Timeline(entries: [ ClipboardWidgetEntry(date: Date()) ], policy: .never)) } } struct ClipboardWidgetView: View { @Environment(\.widgetFamily) private var family var entry: ClipboardWidgetEntry var body: some View { VStack(alignment: .leading, spacing: 10) { HStack(spacing: 6) { Image(systemName: "doc.on.clipboard") .foregroundStyle(.tint) Text(t("ios.widget.clipboardName")) .font(.caption).bold() .lineLimit(1) } // 小尺寸纵向堆叠两按钮;中尺寸并排。 if family == .systemSmall { actionButton(t("ios.control.upload"), "square.and.arrow.up", UploadClipboardIntent()) actionButton(t("ios.control.pull"), "square.and.arrow.down", PullClipboardIntent()) } else { HStack(spacing: 10) { actionButton(t("ios.control.upload"), "square.and.arrow.up", UploadClipboardIntent()) actionButton(t("ios.control.pull"), "square.and.arrow.down", PullClipboardIntent()) } } } .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .leading) .padding() .containerBackground(.fill.tertiary, for: .widget) } private func actionButton(_ title: String, _ symbol: String, _ intent: some AppIntent) -> some View { Button(intent: intent) { Label(title, systemImage: symbol) .font(.caption2) .lineLimit(1) .frame(maxWidth: .infinity) } .buttonStyle(.bordered) .tint(.cdropAccent) } } struct CloudClipboardWidget: Widget { var body: some WidgetConfiguration { StaticConfiguration(kind: "net.commilitia.cdrop.widget.clipboard", provider: ClipboardWidgetProvider()) { entry in ClipboardWidgetView(entry: entry) } .configurationDisplayName(t("ios.widget.clipboardName")) .description(t("ios.widget.clipboardDesc")) .supportedFamilies([ .systemSmall, .systemMedium ]) } }