swiftbricks/Sources/SwiftBricks/Renderer/ControlRenderer.swift
yumoqing 7bc06097b0 feat: add VideoPlayer widget with shared AVPlayer via Store
- New VideoPlayerControl.swift wrapping AVKit VideoPlayer
- Prefers shared AVPlayer from Store (e.g. PlayerBridge injection)
- Falls back to options.url for standalone use
- Schema.swift: add autoplay option for VideoPlayer
- ControlRenderer: register VideoPlayer widget type
- Add .gitignore to exclude .build/
2026-06-22 09:52:27 +08:00

175 lines
5.6 KiB
Swift
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import SwiftUI
/// JSON schemaSwiftUI
/// SwiftBricksBrickswidget
public struct ControlRenderer: View {
let schema: ControlSchema
@ObservedObject var engine: BricksEngine
public init(schema: ControlSchema, engine: BricksEngine) {
self.schema = schema
self.engine = engine
}
public var body: some View {
renderWidget(schema)
.frame(
maxWidth: resolveWidth(schema.options),
maxHeight: resolveHeight(schema.options),
alignment: resolveAlignment(schema.options)
)
}
// MARK: - Widget
@ViewBuilder
func renderWidget(_ schema: ControlSchema) -> some View {
let type = schema.widgettype
// widget
if let factory = engine.getWidgetFactory(type: type) {
factory(schema, engine)
} else {
renderKnownWidget(schema, type: type)
}
}
@ViewBuilder
func renderKnownWidget(_ schema: ControlSchema, type: String) -> some View {
switch type {
//
case "Text":
TextControl(schema: schema, engine: engine)
case "Title1":
TitleControl(schema: schema, engine: engine, level: 1)
case "Title2":
TitleControl(schema: schema, engine: engine, level: 2)
case "Title3":
TitleControl(schema: schema, engine: engine, level: 3)
case "Title4":
TitleControl(schema: schema, engine: engine, level: 4)
case "Title5":
TitleControl(schema: schema, engine: engine, level: 5)
case "Title6":
TitleControl(schema: schema, engine: engine, level: 6)
case "Label":
LabelControl(schema: schema, engine: engine)
//
case "Input", "UiStr":
InputControl(schema: schema, engine: engine)
case "Textarea", "UiText":
TextareaControl(schema: schema, engine: engine)
case "UiNumber":
NumberControl(schema: schema, engine: engine)
case "UiDate":
DateControl(schema: schema, engine: engine)
case "Select", "UiCode":
SelectControl(schema: schema, engine: engine)
//
case "Button":
ButtonControl(schema: schema, engine: engine)
//
case "VBox", "Card":
VBoxControl(schema: schema, engine: engine)
case "HBox":
HBoxControl(schema: schema, engine: engine)
case "VScrollPanel", "HScrollPanel":
ScrollPanelControl(schema: schema, engine: engine)
case "Filler":
Spacer().frame(maxWidth: .infinity, maxHeight: .infinity)
case "DynamicColumn":
DynamicColumnControl(schema: schema, engine: engine)
//
case "Tabular", "DataViewer":
TabularControl(schema: schema, engine: engine)
case "Form":
FormControl(schema: schema, engine: engine)
case "InlineForm":
InlineFormControl(schema: schema, engine: engine)
//
case "TabView":
TabViewControl(schema: schema, engine: engine)
case "Menu":
MenuControl(schema: schema, engine: engine)
//
case "PopupWindow":
PopupWindowControl(schema: schema, engine: engine)
// HTML
case "Html":
HtmlControl(schema: schema, engine: engine)
// Image
case "Image":
ImageControl(schema: schema, engine: engine)
// VideoPlayer
case "VideoPlayer":
VideoPlayerControl(schema: schema, engine: engine)
// urlwidget
case "urlwidget":
UrlWidgetControl(schema: schema, engine: engine)
default:
// widgettype
VStack {
Text("⚠️ Unknown: \(type)")
.font(.caption)
.foregroundColor(.secondary)
}
}
}
// MARK: -
private func resolveWidth(_ opts: ControlOptions) -> CGFloat? {
if let w = opts.width {
if w == "100%" { return nil } // 使framemaxWidth
if let px = parsePixels(w) { return px }
}
return nil
}
private func resolveHeight(_ opts: ControlOptions) -> CGFloat? {
if let h = opts.height {
if h == "100%" { return nil }
if let px = parsePixels(h) { return px }
}
return nil
}
private func resolveAlignment(_ opts: ControlOptions) -> Alignment {
switch opts.alignItems {
case "center": return .center
case "flex-end", "end": return .trailing
case "flex-start", "start": return .leading
default: return .topLeading
}
}
private func parsePixels(_ str: String) -> CGFloat? {
let cleaned = str.replacingOccurrences(of: "px", with: "")
return CGFloat(Double(cleaned) ?? 0)
}
}
// MARK: -
struct SubWidgetsView: View {
let subwidgets: [ControlSchema]
@ObservedObject var engine: BricksEngine
var body: some View {
ForEach(Array(subwidgets.enumerated()), id: \.offset) { _, sub in
ControlRenderer(schema: sub, engine: engine)
}
}
}