Blender4iOS/Sources/Workspace/WorkspaceManager.swift

167 lines
5.1 KiB
Swift

// WorkspaceManager.swift
// Controls which workspace layout is active and panel sizing/visibility.
import Observation
import SwiftUI
// MARK: - Workspace definitions
enum WorkspaceType: String, CaseIterable, Identifiable {
case modeling = "Modeling"
case uvTexture = "UV / Texture Bake"
case greasePencil = "Grease Pencil"
case animation = "Animation"
case rendering = "Rendering"
case sculpting = "Sculpting"
var id: String { rawValue }
var icon: String {
switch self {
case .modeling: "cube"
case .uvTexture: "square.grid.3x3.topleft.filled"
case .greasePencil: "pencil.tip"
case .animation: "play.rectangle"
case .rendering: "sun.max.fill"
case .sculpting: "paintbrush.pointed.fill"
}
}
/// Default panel configuration for each workspace.
var defaultPanels: [PanelSlot] {
switch self {
case .modeling:
[
.init(.toolSidebar, edge: .leading, size: 0.04),
.init(.viewport3D, edge: .center, size: 0.66),
.init(.outliner, edge: .trailing, size: 0.15),
.init(.properties, edge: .trailing, size: 0.15),
]
case .uvTexture:
[
.init(.toolSidebar, edge: .leading, size: 0.04),
.init(.viewport3D, edge: .center, size: 0.40),
.init(.uvEditor, edge: .center, size: 0.36),
.init(.textureBake, edge: .trailing, size: 0.20),
]
case .greasePencil:
[
.init(.brushPanel, edge: .leading, size: 0.06),
.init(.gpCanvas, edge: .center, size: 0.64),
.init(.gpLayers, edge: .trailing, size: 0.16),
.init(.onionSkin, edge: .trailing, size: 0.14),
]
case .animation:
[
.init(.toolSidebar, edge: .leading, size: 0.04),
.init(.viewport3D, edge: .center, size: 0.70),
.init(.outliner, edge: .trailing, size: 0.12),
.init(.properties, edge: .trailing, size: 0.14),
]
case .rendering:
[
.init(.viewport3D, edge: .center, size: 0.70),
.init(.renderSettings, edge: .trailing, size: 0.30),
]
case .sculpting:
[
.init(.brushPanel, edge: .leading, size: 0.06),
.init(.viewport3D, edge: .center, size: 0.76),
.init(.properties, edge: .trailing, size: 0.18),
]
}
}
}
// MARK: - Panel identity
enum PanelID: String, Identifiable, CaseIterable {
case toolSidebar = "Tools"
case viewport3D = "3D Viewport"
case uvEditor = "UV Editor"
case textureBake = "Texture Bake"
case brushPanel = "Brushes"
case gpCanvas = "GP Canvas"
case gpLayers = "Layers"
case onionSkin = "Onion Skin"
case outliner = "Outliner"
case properties = "Properties"
case renderSettings = "Render"
case timeline = "Timeline"
var id: String { rawValue }
}
enum PanelEdge {
case leading, center, trailing, bottom
}
struct PanelSlot: Identifiable {
let id: PanelID
let edge: PanelEdge
var size: CGFloat // proportion of total width (01)
var isVisible: Bool = true
var minSize: CGFloat = 0.04
init(_ id: PanelID, edge: PanelEdge, size: CGFloat, minSize: CGFloat = 0.04) {
self.id = id
self.edge = edge
self.size = size
self.minSize = minSize
}
}
// MARK: - Manager
@Observable
final class WorkspaceManager {
var activeWorkspace: WorkspaceType {
didSet { panels = activeWorkspace.defaultPanels }
}
var panels: [PanelSlot]
// Bottom panels (shared across workspaces)
var showTimeline: Bool = false
var timelineHeight: CGFloat = 0.2
init(workspace: WorkspaceType = .modeling) {
self.activeWorkspace = workspace
self.panels = workspace.defaultPanels
}
// MARK: - Panel queries
func panel(_ id: PanelID) -> PanelSlot? {
panels.first { $0.id == id }
}
var leadingPanels: [PanelSlot] {
panels.filter { $0.edge == .leading && $0.isVisible }
}
var centerPanels: [PanelSlot] {
panels.filter { $0.edge == .center && $0.isVisible }
}
var trailingPanels: [PanelSlot] {
panels.filter { $0.edge == .trailing && $0.isVisible }
}
// MARK: - Panel mutation
func togglePanel(_ id: PanelID) {
guard let idx = panels.firstIndex(where: { $0.id == id }) else { return }
panels[idx].isVisible.toggle()
}
func resizePanel(_ id: PanelID, to newSize: CGFloat) {
guard let idx = panels.firstIndex(where: { $0.id == id }) else { return }
panels[idx].size = max(panels[idx].minSize, min(newSize, 0.8))
}
func switchWorkspace(to type: WorkspaceType) {
withAnimation(.easeInOut(duration: 0.25)) {
activeWorkspace = type
}
}
}