Skip to content
This repository has been archived by the owner on Jan 10, 2023. It is now read-only.

Commit

Permalink
Merge pull request #174 from google/vn-7-modelrefactor
Browse files Browse the repository at this point in the history
- Refactored WorkspaceLayout so its layout hierarchy is being managed…
  • Loading branch information
vicng authored Sep 14, 2016
2 parents b165b6b + 7341c3a commit 978cddd
Show file tree
Hide file tree
Showing 16 changed files with 726 additions and 608 deletions.
8 changes: 6 additions & 2 deletions Blockly/Blockly.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@
FAC035071D516EE10017C1C8 /* FieldDropdownLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = FAC035061D516EE10017C1C8 /* FieldDropdownLayout.swift */; };
FAC035111D5170B20017C1C8 /* FieldVariableLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = FAC035101D5170B20017C1C8 /* FieldVariableLayout.swift */; };
FAC1D2971BFA7E2E0019CE45 /* Input+Builder.swift in Sources */ = {isa = PBXBuildFile; fileRef = FAC1D2961BFA7E2E0019CE45 /* Input+Builder.swift */; };
FACBAB591D8371220063A975 /* WorkspaceLayoutCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = FACBAB581D8371220063A975 /* WorkspaceLayoutCoordinator.swift */; };
FAD3ABF71BCDD7CD00C0B254 /* LayoutFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = FAD3ABF61BCDD7CD00C0B254 /* LayoutFactory.swift */; };
FAD6522E1CB5DDFB00F73F11 /* ViewFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = FAD6522D1CB5DDFB00F73F11 /* ViewFactory.swift */; };
FAD6523F1CB7210C00F73F11 /* DefaultBlockGroupLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = FAD6523B1CB7210C00F73F11 /* DefaultBlockGroupLayout.swift */; };
Expand Down Expand Up @@ -375,6 +376,7 @@
FAC035061D516EE10017C1C8 /* FieldDropdownLayout.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FieldDropdownLayout.swift; sourceTree = "<group>"; };
FAC035101D5170B20017C1C8 /* FieldVariableLayout.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FieldVariableLayout.swift; sourceTree = "<group>"; };
FAC1D2961BFA7E2E0019CE45 /* Input+Builder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Input+Builder.swift"; sourceTree = "<group>"; };
FACBAB581D8371220063A975 /* WorkspaceLayoutCoordinator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WorkspaceLayoutCoordinator.swift; sourceTree = "<group>"; };
FAD3ABF61BCDD7CD00C0B254 /* LayoutFactory.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LayoutFactory.swift; sourceTree = "<group>"; };
FAD6522D1CB5DDFB00F73F11 /* ViewFactory.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ViewFactory.swift; sourceTree = "<group>"; };
FAD6523B1CB7210C00F73F11 /* DefaultBlockGroupLayout.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DefaultBlockGroupLayout.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -458,6 +460,7 @@
FAA8700F1C64273E000C7C61 /* LayoutHelper.swift */,
FA4E840A1CAE4AAE009FB0CD /* ToolboxLayout.swift */,
FA2726791B83C54900777B49 /* WorkspaceLayout.swift */,
FACBAB581D8371220063A975 /* WorkspaceLayoutCoordinator.swift */,
FA9D2FBC1C111A1300D0E528 /* WorkspaceFlowLayout.swift */,
);
path = Layout;
Expand Down Expand Up @@ -820,10 +823,10 @@
children = (
FAABDD621CA20BA400F9E7D4 /* BlockBumper.swift */,
FAE5576D1BE0219C0019D0D4 /* ConnectionManager.swift */,
FAE557781BE02B270019D0D4 /* Dragger.swift */,
FAFAEE701CDC0D2F00698179 /* NameManager.swift */,
300CABD01D5CF816000E43B2 /* ConnectionValidator.swift */,
300CABE71D5E8606000E43B2 /* DefaultConnectionValidator.swift */,
FAE557781BE02B270019D0D4 /* Dragger.swift */,
FAFAEE701CDC0D2F00698179 /* NameManager.swift */,
);
path = Control;
sourceTree = "<group>";
Expand Down Expand Up @@ -1025,6 +1028,7 @@
FAC034F61D5159FE0017C1C8 /* FieldDateLayout.swift in Sources */,
FA41C41B1C582AB800D46967 /* FieldDropdownView.swift in Sources */,
FAC035071D516EE10017C1C8 /* FieldDropdownLayout.swift in Sources */,
FACBAB591D8371220063A975 /* WorkspaceLayoutCoordinator.swift in Sources */,
FAF5005D1D50669E009E4B24 /* FieldCheckboxLayout.swift in Sources */,
FA6232521B6B12CF00F1EF42 /* Field.swift in Sources */,
FAFAEE6D1CDBD5AB00698179 /* InsetTextField.swift in Sources */,
Expand Down
3 changes: 2 additions & 1 deletion Blockly/Code/Common/BlocklyError.swift
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@ public class BlocklyError: NSError {
FileNotFound = 600,
FileNotReadable = 601,
IllegalState = 700,
IllegalArgument = 701
IllegalArgument = 701,
IllegalOperation = 702
}
public typealias Code = BKYBlocklyErrorCode

Expand Down
32 changes: 17 additions & 15 deletions Blockly/Code/Control/BlockBumper.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,17 +22,19 @@ import Foundation
public class BlockBumper: NSObject {
// MARK: - Properties

/// The X and Y amount to bump blocks away from each other, specified as a Workspace coordinate
/// system unit
public var bumpDistance: CGFloat

/// The workspace layout where blocks are being bumped
public var workspaceLayout: WorkspaceLayout?
/// The workspace layout coordinator where blocks are being bumped
public weak var workspaceLayoutCoordinator: WorkspaceLayoutCoordinator?

// MARK: - Initializers
/// Convenience property for `self.workspaceLayoutCoordinator?.workspaceLayout`
private var workspaceLayout: WorkspaceLayout? {
return workspaceLayoutCoordinator?.workspaceLayout
}

public init(bumpDistance: CGFloat) {
self.bumpDistance = bumpDistance
/// The X and Y amount to bump blocks away from each other, specified as a Workspace coordinate
/// system unit. This value is read from `self.workspaceLayout.config` using the key
/// `LayoutConfig.BlockBumpDistance`. If no value exists for that key, this defaults to `0`.
private var bumpDistance: CGFloat {
return workspaceLayout?.config.unitFor(LayoutConfig.BlockBumpDistance).workspaceUnit ?? 0
}

// MARK: - Public
Expand All @@ -52,8 +54,8 @@ public class BlockBumper: NSObject {
return
}

let dx = stationaryConnection.position.x + self.bumpDistance - impingingConnection.position.x
let dy = stationaryConnection.position.y + self.bumpDistance - impingingConnection.position.y
let dx = stationaryConnection.position.x + bumpDistance - impingingConnection.position.x
let dy = stationaryConnection.position.y + bumpDistance - impingingConnection.position.y
let newPosition = WorkspacePointMake(
blockGroupLayout.absolutePosition.x + dx,
blockGroupLayout.absolutePosition.y + dy)
Expand Down Expand Up @@ -100,14 +102,14 @@ public class BlockBumper: NSObject {
*/
private func bumpBlockLayoutOfConnectionAwayFromNeighbours(connection: Connection) {
guard
let connectionManager = workspaceLayout?.connectionManager,
let connectionManager = workspaceLayoutCoordinator?.connectionManager,
let rootBlockGroupLayout = connection.sourceBlock.layout?.rootBlockGroupLayout else
{
return
}

let neighbours =
connectionManager.stationaryNeighboursForConnection(connection, maxRadius: self.bumpDistance)
connectionManager.stationaryNeighboursForConnection(connection, maxRadius: bumpDistance)

for neighbour in neighbours {
// Bump away from the first neighbour that isn't in the same block group as the target
Expand All @@ -126,14 +128,14 @@ public class BlockBumper: NSObject {
*/
private func bumpAllBlocksNearConnection(connection: Connection) {
guard
let connectionManager = workspaceLayout?.connectionManager,
let connectionManager = workspaceLayoutCoordinator?.connectionManager,
let rootBlockGroupLayout = connection.sourceBlock.layout?.rootBlockGroupLayout else
{
return
}

let neighbours =
connectionManager.stationaryNeighboursForConnection(connection, maxRadius: self.bumpDistance)
connectionManager.stationaryNeighboursForConnection(connection, maxRadius: bumpDistance)

for neighbour in neighbours {
// Only bump blocks that aren't in the same block group as the target connection's block group
Expand Down
184 changes: 43 additions & 141 deletions Blockly/Code/Control/Dragger.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,34 +20,22 @@ Controller for dragging blocks around in the workspace.
*/
@objc(BKYDragger)
public class Dragger: NSObject {
// MARK: - Static Properties

/**
Blocks "snap" toward each other at the end of drags if they have compatible connections
near each other. This is the farthest they can snap. This value is in the UIView coordinate
system.
*/
private static let MAX_SNAP_DISTANCE: CGFloat = 25

// MARK: - Properties

/// The workspace layout where blocks are being dragged
public var workspaceLayout: WorkspaceLayout? {
public var workspaceLayoutCoordinator: WorkspaceLayoutCoordinator? {
didSet {
if workspaceLayout == oldValue {
if workspaceLayoutCoordinator == oldValue {
return
}

// Reset all gesture data and update the block bumper with the new workspace layout
// Reset all gesture data
_dragGestureData.keys.forEach { clearGestureData(forUUID: $0) }
_blockBumper.workspaceLayout = workspaceLayout
}
}
/// Stores the data for each active drag gesture, keyed by the corresponding block view's layout
/// uuid
private var _dragGestureData = [String: DragGestureData]()
/// Object responsible for bumping blocks out of the way
private let _blockBumper = BlockBumper(bumpDistance: Dragger.MAX_SNAP_DISTANCE)

// MARK: - Public

Expand All @@ -60,7 +48,12 @@ public class Dragger: NSObject {
system
*/
public func startDraggingBlockLayout(layout: BlockLayout, touchPosition: WorkspacePoint) {
if !layout.block.draggable {
guard let workspaceLayoutCoordinator = self.workspaceLayoutCoordinator,
let connectionManager = workspaceLayoutCoordinator.connectionManager
where layout.block.draggable &&
workspaceLayoutCoordinator.workspaceLayout
.allVisibleBlockLayoutsInWorkspace().contains(layout) else
{
return
}

Expand All @@ -70,30 +63,33 @@ public class Dragger: NSObject {

// Disconnect this block from its previous or output connections prior to moving it
let block = layout.block
block.previousConnection?.disconnect()
block.outputConnection?.disconnect()
if let previousConnection = block.previousConnection {
workspaceLayoutCoordinator.disconnect(previousConnection)
}
if let outputConnection = block.outputConnection {
workspaceLayoutCoordinator.disconnect(outputConnection)
}

// Highlight this block
layout.highlighted = true
layout.rootBlockGroupLayout?.dragging = true

// Bring its block group layout to the front
self.workspaceLayout?.bringBlockGroupLayoutToFront(layout.rootBlockGroupLayout)
self.workspaceLayoutCoordinator?.workspaceLayout.bringBlockGroupLayoutToFront(
layout.rootBlockGroupLayout)

// Start a new connection group for this block group layout
if let newConnectionGroup =
self.workspaceLayout?.connectionManager.startGroupForBlock(block)
{
// Keep track of the gesture data for this drag
let dragGestureData = DragGestureData(
blockLayout: layout,
blockLayoutStartPosition: layout.absolutePosition,
touchStartPosition: touchPosition,
connectionGroup: newConnectionGroup
)

self._dragGestureData[layout.uuid] = dragGestureData
}
let newConnectionGroup = connectionManager.startGroupForBlock(block)

// Keep track of the gesture data for this drag
let dragGestureData = DragGestureData(
blockLayout: layout,
blockLayoutStartPosition: layout.absolutePosition,
touchStartPosition: touchPosition,
connectionGroup: newConnectionGroup
)

self._dragGestureData[layout.uuid] = dragGestureData
}
}

Expand Down Expand Up @@ -133,6 +129,10 @@ public class Dragger: NSObject {
- Parameter layout: The given block layout
*/
public func finishDraggingBlockLayout(layout: BlockLayout) {
guard let workspaceLayoutCoordinator = self.workspaceLayoutCoordinator else {
return
}

Layout.animate {
// Remove the highlight for this block
layout.highlighted = false
Expand All @@ -142,7 +142,7 @@ public class Dragger: NSObject {
if let drag = self._dragGestureData[layout.uuid],
let connectionPair = self.findBestConnectionForDrag(drag)
{
self.connectPair(connectionPair)
workspaceLayoutCoordinator.connectPair(connectionPair)

self.clearGestureDataForBlockLayout(layout,
moveConnectionsToGroup: connectionPair.fromConnectionManagerGroup)
Expand All @@ -153,11 +153,11 @@ public class Dragger: NSObject {
// during the drag for performance reasons, so we have to update it now). Also, there is
// no need to call this method in the `if` part of this `if/else` block, since
// `self.connectPair(:)` implicitly calls it already.
self.workspaceLayout?.updateCanvasSize()
workspaceLayoutCoordinator.workspaceLayout.updateCanvasSize()
}

// Bump any neighbours of the block layout
self._blockBumper.bumpNeighboursOfBlockLayout(layout)
workspaceLayoutCoordinator.blockBumper.bumpNeighboursOfBlockLayout(layout)

// Update the highlighted connections for all other drags (due to potential changes in block
// sizes)
Expand Down Expand Up @@ -199,114 +199,13 @@ public class Dragger: NSObject {
}

// Move connections to a different group in the connection manager
workspaceLayout?.connectionManager
workspaceLayoutCoordinator?.connectionManager?
.mergeGroup(gestureData.connectionGroup, intoGroup: group)

removeHighlightedConnectionForDrag(gestureData)
_dragGestureData[uuid] = nil
}

/**
Connects a pair of connections, disconnecting and possibly reattaching any existing connections,
depending on the operation.

- Parameter connectionPair: The pair to connect
*/
private func connectPair(connectionPair: ConnectionManager.ConnectionPair) {
let moving = connectionPair.moving
let target = connectionPair.target

do {
switch (moving.type) {
case .InputValue:
try connectValueConnections(superior: moving, inferior: target)
case .OutputValue:
try connectValueConnections(superior: target, inferior: moving)
case .NextStatement:
try connectStatementConnections(superior: moving, inferior: target)
case .PreviousStatement:
try connectStatementConnections(superior: target, inferior: moving)
}
} catch let error as NSError {
bky_assertionFailure("Could not connect pair together: \(error)")
}
}

/**
Connects two value connections. If a block was previously connected to the superior connection,
this method attempts to reattach it to the end of the inferior connection's block input value
chain. If unsuccessful, the disconnected block is bumped away.
*/
private func connectValueConnections(superior superior: Connection, inferior: Connection) throws {
let previouslyConnectedBlock = superior.targetBlock

// NOTE: Layouts are automatically re-computed after disconnecting/reconnecting
superior.disconnect()
inferior.disconnect()
try superior.connectTo(inferior)

// Bring the entire block group layout to the front
if let workspaceLayout = self.workspaceLayout,
let rootBlockGroupLayout = superior.sourceBlock?.layout?.rootBlockGroupLayout
{
workspaceLayout.bringBlockGroupLayoutToFront(rootBlockGroupLayout)
}

if let previousOutputConnection = previouslyConnectedBlock?.outputConnection {
if let lastInputConnection = inferior.sourceBlock?.lastInputValueConnectionInChain()
where lastInputConnection.canConnectTo(previousOutputConnection)
{
// Try to reconnect previously connected block to the end of the input value chain
try lastInputConnection.connectTo(previousOutputConnection)
} else {
// Bump previously connected block away from the superior connection
_blockBumper.bumpBlockLayoutOfConnection(previousOutputConnection,
awayFromConnection: superior)
}
}
}

/**
Connects two statement connections. If a block was previously connected to the superior
connection, this method attempts to reattach it to the end of the inferior connection's block
chain. If unsuccessful, the disconnected block is bumped away.

- Parameter superior: A connection of type `.NextStatement`
- Parameter inferior: A connection of type `.PreviousStatement`
- Throws:
`BlocklyError`: Thrown if the previous/next statements could not be connected together or if
the previously disconnected block could not be re-connected to the end of the block chain.
*/
private func connectStatementConnections(superior superior: Connection, inferior: Connection)
throws
{
let previouslyConnectedBlock = superior.targetBlock

// NOTE: Layouts are automatically re-computed after disconnecting/reconnecting
superior.disconnect()
inferior.disconnect()
try superior.connectTo(inferior)

// Bring the entire block group layout to the front
if let workspaceLayout = self.workspaceLayout,
let rootBlockGroupLayout = superior.sourceBlock?.layout?.rootBlockGroupLayout
{
workspaceLayout.bringBlockGroupLayoutToFront(rootBlockGroupLayout)
}

if let previousConnection = previouslyConnectedBlock?.previousConnection {
if let lastConnection = inferior.sourceBlock?.lastBlockInChain().nextConnection
where lastConnection.canConnectTo(previousConnection)
{
// Reconnect previously connected block to the end of the block chain
try lastConnection.connectTo(previousConnection)
} else {
// Bump previously connected block away from the superior connection
_blockBumper.bumpBlockLayoutOfConnection(previousConnection, awayFromConnection: superior)
}
}
}

/**
Updates the highlighted connection for a dragged block.

Expand Down Expand Up @@ -346,11 +245,14 @@ public class Dragger: NSObject {
private func findBestConnectionForDrag(drag: DragGestureData)
-> ConnectionManager.ConnectionPair?
{
if let workspaceLayout = self.workspaceLayout {
let maxRadius = workspaceLayout.engine.workspaceUnitFromViewUnit(Dragger.MAX_SNAP_DISTANCE)
if let workspaceLayout = workspaceLayoutCoordinator?.workspaceLayout,
let connectionManager = workspaceLayoutCoordinator?.connectionManager
{
let maxRadius = workspaceLayout.config.unitFor(
LayoutConfig.BlockSnapDistance, defaultValue: LayoutConfig.Unit(0)).workspaceUnit

return workspaceLayout.connectionManager.findBestConnectionForGroup(drag.connectionGroup,
maxRadius: maxRadius)
return
connectionManager.findBestConnectionForGroup(drag.connectionGroup, maxRadius: maxRadius)
}
return nil
}
Expand Down
Loading

0 comments on commit 978cddd

Please sign in to comment.