Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
89 changes: 33 additions & 56 deletions Fabric Editor/Views/ContentView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,54 +8,46 @@
import SwiftUI
import Fabric

struct ScrollGeometry: Equatable {
let offset: CGPoint
let containerSize: CGSize
}

struct ContentView: View {

@Binding var document: FabricDocument

@GestureState private var magnifyBy = 1.0

@Environment(\.undoManager) private var undoManager

@State private var magnifyBy = 1.0
@State private var finalMagnification = 1.0
@State private var viewportAnchor: UnitPoint = .center

@State private var hitTestEnable:Bool = true
@State private var columnVisibility = NavigationSplitViewVisibility.doubleColumn
@State private var inspectorVisibility:Bool = false
@State private var contentOffset: CGPoint = .zero
@State private var scrollOffset: CGPoint = .zero
@State private var zoomAnchor: UnitPoint = .center
@State private var hoverLocation: CGPoint = .zero
@State private var isMagnifying = false


var body: some View {

NavigationSplitView(columnVisibility: self.$columnVisibility)
{
NodeRegisitryView(graph: document.graph, scrollOffset: $scrollOffset)

} detail: {

// Movable Canvas
VStack(alignment: .leading, spacing:0)
{
Divider()

Spacer()

HStack(spacing:5)
{
Text("Root Patch")
.font(.headline)
.onTapGesture { self.document.graph.activeSubGraph = nil }

if let _ = self.document.graph.activeSubGraph
{
Text(">")
.font(.headline)

Text("Todo: Graphs Need Names")
.font(.headline)
}
Expand All @@ -64,9 +56,9 @@ struct ContentView: View {
.padding(.horizontal)

Spacer()

Divider()

ZStack
{
// Render behind nodes ?
Expand All @@ -82,30 +74,23 @@ struct ContentView: View {
{
NodeCanvas()
.frame(width: 10000, height: 10000)
.scaleEffect(finalMagnification * magnifyBy, anchor: zoomAnchor)
.environment(self.document.graph)
.scaleEffect(finalMagnification * magnifyBy, anchor: viewportAnchor)
.gesture(
MagnificationGesture()
MagnifyGesture()
.onChanged { value in
if !isMagnifying {
isMagnifying = true
let canvasX = 1.0 - ((contentOffset.x + hoverLocation.x) / 10000)
let canvasY = (contentOffset.y + hoverLocation.y) / 10000
zoomAnchor = UnitPoint(x: canvasX.clamped(to: 0...1), y: canvasY.clamped(to: 0...1))
}
}
.updating($magnifyBy) { value, gestureState, _ in
gestureState = value
magnifyBy = value.magnification
}
.onEnded { value in
finalMagnification *= value
finalMagnification *= value.magnification
finalMagnification = min(max(finalMagnification, 0.25), 4.0)
isMagnifying = false
magnifyBy = 1.0
}
)
.environment(self.document.graph)
.allowsHitTesting(self.hitTestEnable)
.id("canvas")
.task {
self.document.graph.undoManager = undoManager
try? await Task.sleep(for: .milliseconds(100))
if let firstNode = self.document.graph.nodes.first {
let targetPoint = UnitPoint(
Expand All @@ -117,26 +102,24 @@ struct ContentView: View {
}
}
.defaultScrollAnchor(.center)
.onContinuousHover { phase in
if case .active(let location) = phase {
hoverLocation = location
}
}
}
.onScrollGeometryChange(for: ScrollGeometry.self) { geometry in
return ScrollGeometry(offset: geometry.contentOffset, containerSize: geometry.containerSize)
} action: { _, new in
contentOffset = new.offset
let center = CGPoint(x: 5000, y: 5000)
scrollOffset = (new.offset - center) + (new.containerSize / 2)
.onScrollGeometryChange(for: CGPoint.self) { geometry in
let center = CGPoint(x: geometry.contentSize.width / 2,
y: geometry.contentSize.height / 2)
let offset = (geometry.contentOffset - center) + (geometry.containerSize / 2)
let viewportCenter = offset + CGPoint(x: 5000, y: 5000)
viewportAnchor = UnitPoint(x: viewportCenter.x / 10000, y: viewportCenter.y / 10000)
return offset
} action: { _, newScrollOffset in
scrollOffset = newScrollOffset
}
.onScrollPhaseChange { oldPhase, newPhase in
self.hitTestEnable = !newPhase.isScrolling
}

}
}

.inspector(isPresented: self.$inspectorVisibility)
{
NodeSelectionInspector()
Expand All @@ -151,7 +134,7 @@ struct ContentView: View {
self.inspectorVisibility.toggle()
}
}

}
}

Expand All @@ -161,9 +144,3 @@ struct ContentView: View {
#Preview {
ContentView(document: .constant(FabricDocument()))
}

extension Comparable {
func clamped(to range: ClosedRange<Self>) -> Self {
return min(max(self, range.lowerBound), range.upperBound)
}
}
34 changes: 31 additions & 3 deletions Fabric/Graph/Graph.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ internal import AnyCodable
public let id:UUID
public let version: Graph.Version
@ObservationIgnored public let context:Context
@ObservationIgnored public weak var undoManager: UndoManager?

public private(set) var nodes: [Node]

Expand Down Expand Up @@ -288,16 +289,26 @@ internal import AnyCodable
self.maybeAddNodeToScene(node)
self.nodes.append(node)
node.graph = self

undoManager?.registerUndo(withTarget: self) { graph in
graph.delete(node: node)
}
undoManager?.setActionName("Add Node")
shouldUpdateConnections.toggle()
}

self.updateRenderingNodes()
// self.autoConnect(node: node)
}

func delete(node:Node)
{
let savedOffset = node.offset
let savedConnections = node.ports.flatMap { port in
port.connections.map { (port, $0) }
}
node.ports.forEach { $0.disconnectAll() }

if let activeSubGraph
{
activeSubGraph.delete(node: node)
Expand All @@ -306,8 +317,25 @@ internal import AnyCodable
{
self.maybeDeleteNodeFromScene(node)
self.nodes.removeAll { $0.id == node.id }

undoManager?.registerUndo(withTarget: self) { [weak self] graph in
guard let self = self else { return }
node.offset = savedOffset
self.nodes.append(node)
node.graph = self
self.maybeAddNodeToScene(node)
graph.shouldUpdateConnections.toggle()

DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
for (port, connectedPort) in savedConnections {
port.connect(to: connectedPort)
}
}
}
undoManager?.setActionName("Delete Node")
shouldUpdateConnections.toggle()
}

self.updateRenderingNodes()
}

Expand Down
18 changes: 15 additions & 3 deletions Fabric/Graph/Port/NodePort.swift
Original file line number Diff line number Diff line change
Expand Up @@ -123,8 +123,14 @@ public class NodePort<Value : FabricPort>: Port

print("Connections: \(self.debugDescription)) - \(self.connections)")
print("Connections: \(other.debugDescription) - \(other.connections)")

self.node?.graph?.undoManager?.registerUndo(withTarget: self) { port in
port.connect(to: other)
}
self.node?.graph?.undoManager?.setActionName("Disconnect Ports")
self.node?.graph?.shouldUpdateConnections.toggle()
}

override public func connect(to other: Port)
{
if let other = other as? NodePort<Value>
Expand Down Expand Up @@ -210,10 +216,16 @@ public class NodePort<Value : FabricPort>: Port

print("Connections: \(self.debugDescription)) - \(self.connections)")
print("Connections: \(other.debugDescription) - \(other.connections)")

self.send(self.value, force: true)

self.node?.graph?.undoManager?.registerUndo(withTarget: self) { port in
port.disconnect(from: other)
}
self.node?.graph?.undoManager?.setActionName("Connect Ports")
self.node?.graph?.shouldUpdateConnections.toggle()
}

public func send(_ v: Value?, force:Bool = false)
{
if self.value != v || force
Expand Down
1 change: 0 additions & 1 deletion Fabric/Views/Nodes/NodeInletView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@ struct NodeInletView: View
}

self.port.connect(to: targetPort)
self.graph.shouldUpdateConnections.toggle()
}
)
.anchorPreference(
Expand Down
1 change: 0 additions & 1 deletion Fabric/Views/Nodes/NodeOutletView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,6 @@ struct NodeOutletView: View
}

self.port.connect(to: targetPort)
self.graph.shouldUpdateConnections.toggle()
}
)
.anchorPreference(
Expand Down