Blender4iOS/Sources/UI/ContentView.swift

153 lines
4.6 KiB
Swift

// ContentView.swift
// Root layout workspace header, dynamic panel layout, status bar.
import SwiftUI
struct ContentView: View {
@State private var context = MetalContext()
@State private var workspace = WorkspaceManager()
@State private var currentMode: EditMode = .object
var body: some View {
GeometryReader { geo in
VStack(spacing: 0) {
// Workspace header
WorkspaceHeader(
workspace: workspace,
currentMode: $currentMode,
context: context
)
// Dynamic workspace layout
WorkspaceLayoutView(
workspace: workspace,
context: context
)
// Status bar
StatusBar(mode: currentMode, sceneGraph: context.sceneGraph)
}
}
.ignoresSafeArea(.keyboard)
.preferredColorScheme(.dark)
}
}
// MARK: - Workspace Header
struct WorkspaceHeader: View {
let workspace: WorkspaceManager
@Binding var currentMode: EditMode
let context: MetalContext
var body: some View {
HStack(spacing: 10) {
// App icon
Image(systemName: "cube.fill")
.font(.title3)
.foregroundStyle(.orange)
Divider().frame(height: 20)
// Workspace tabs
ScrollView(.horizontal, showsIndicators: false) {
HStack(spacing: 4) {
ForEach(WorkspaceType.allCases) { ws in
WorkspaceTab(
type: ws,
isActive: workspace.activeWorkspace == ws
) {
workspace.switchWorkspace(to: ws)
}
}
}
}
Divider().frame(height: 20)
// Mode picker
Menu {
ForEach(EditMode.allCases) { mode in
Button {
currentMode = mode
} label: {
Label(mode.rawValue, systemImage: mode.icon)
}
}
} label: {
Label(currentMode.rawValue, systemImage: currentMode.icon)
.font(.caption.weight(.medium))
.padding(.horizontal, 8)
.padding(.vertical, 5)
.background(.ultraThinMaterial, in: RoundedRectangle(cornerRadius: 5))
}
Divider().frame(height: 20)
// Add mesh menu
Menu {
ForEach(PrimitiveType.allCases) { prim in
Button {
context.addPrimitive(prim)
} label: {
Label(prim.rawValue, systemImage: prim.icon)
}
}
} label: {
Label("Add", systemImage: "plus.circle")
.font(.caption.weight(.medium))
}
Button { context.deleteSelected() } label: {
Image(systemName: "trash")
.font(.caption)
}
.tint(.secondary)
Spacer()
// Timeline toggle
Button {
withAnimation(.easeInOut(duration: 0.2)) {
workspace.showTimeline.toggle()
}
} label: {
Image(systemName: "play.rectangle")
.font(.caption)
.foregroundStyle(workspace.showTimeline ? .orange : .secondary)
}
}
.padding(.horizontal, 12)
.padding(.vertical, 6)
.background(.ultraThinMaterial)
}
}
// MARK: - Workspace Tab
struct WorkspaceTab: View {
let type: WorkspaceType
let isActive: Bool
let action: () -> Void
var body: some View {
Button(action: action) {
HStack(spacing: 4) {
Image(systemName: type.icon)
.font(.system(size: 10))
Text(type.rawValue)
.font(.system(size: 11, weight: isActive ? .semibold : .regular))
}
.padding(.horizontal, 10)
.padding(.vertical, 5)
.background(
isActive
? Color.orange.opacity(0.15)
: Color.clear,
in: RoundedRectangle(cornerRadius: 5)
)
.foregroundStyle(isActive ? .orange : .secondary)
}
.buttonStyle(.plain)
}
}