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 #171 from google/vn-6-animation
Browse files Browse the repository at this point in the history
- Added animations (for LTR only).
  • Loading branch information
vicng authored Sep 8, 2016
2 parents 1cd169f + fc0153d commit b165b6b
Show file tree
Hide file tree
Showing 24 changed files with 599 additions and 360 deletions.
100 changes: 53 additions & 47 deletions Blockly/Code/Control/Dragger.swift
Original file line number Diff line number Diff line change
Expand Up @@ -64,34 +64,36 @@ public class Dragger: NSObject {
return
}

// Remove any existing gesture data for the layout
clearGestureDataForBlockLayout(layout)
Layout.animate {
// Remove any existing gesture data for the layout
self.clearGestureDataForBlockLayout(layout)

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

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

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

// Start a new connection group for this block group layout
if let newConnectionGroup =
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
)

_dragGestureData[layout.uuid] = dragGestureData
// 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
}
}
}

Expand Down Expand Up @@ -131,34 +133,38 @@ public class Dragger: NSObject {
- Parameter layout: The given block layout
*/
public func finishDraggingBlockLayout(layout: BlockLayout) {
// Remove the highlight for this block
layout.highlighted = false
layout.rootBlockGroupLayout?.dragging = false
Layout.animate {
// Remove the highlight for this block
layout.highlighted = false
layout.rootBlockGroupLayout?.dragging = false

// If this block can be connected to anything, connect it.
if let drag = self._dragGestureData[layout.uuid],
let connectionPair = self.findBestConnectionForDrag(drag)
{
self.connectPair(connectionPair)

// If this block can be connected to anything, connect it.
if let drag = _dragGestureData[layout.uuid],
let connectionPair = findBestConnectionForDrag(drag)
{
connectPair(connectionPair)
self.clearGestureDataForBlockLayout(layout,
moveConnectionsToGroup: connectionPair.fromConnectionManagerGroup)
} else {
self.clearGestureDataForBlockLayout(layout)

clearGestureDataForBlockLayout(layout,
moveConnectionsToGroup: connectionPair.fromConnectionManagerGroup)
} else {
clearGestureDataForBlockLayout(layout)
}
// Update the workspace canvas size since it may have changed (this was purposely skipped
// 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()
}

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

// Update the highlighted connections for all other drags (due to potential changes in block
// sizes)
for (_, gestureData) in _dragGestureData {
updateHighlightedConnectionForDrag(gestureData)
// Update the highlighted connections for all other drags (due to potential changes in block
// sizes)
for (_, gestureData) in self._dragGestureData {
self.updateHighlightedConnectionForDrag(gestureData)
}
}

// Update the workspace canvas size since it may have changed (this was purposely skipped
// during the drag for performance reasons, so we have to update it now)
workspaceLayout?.updateCanvasSize()
}

/**
Expand Down
8 changes: 6 additions & 2 deletions Blockly/Code/Layout/BlockGroupLayout.swift
Original file line number Diff line number Diff line change
Expand Up @@ -131,8 +131,12 @@ public class BlockGroupLayout: Layout {
transferredLayouts.append(blockLayout)
}

// Transfer the layouts over to `newBlockGroupLayout`
appendBlockLayouts(transferredLayouts, updateLayout: false)
Layout.doNotAnimate {
// Transfer the layouts over to `newBlockGroupLayout`. Do not animate these changes, as these
// layouts need to start any potential animations from locations relative to its new parent
// (not its old parent).
self.appendBlockLayouts(transferredLayouts, updateLayout: false)
}

if updateLayouts {
updateLayoutUpTree()
Expand Down
68 changes: 58 additions & 10 deletions Blockly/Code/Layout/Layout.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public protocol LayoutDelegate: class {
- Parameter flags: Set of flags indicating which parts of the layout that need to be updated from
the UI side.
*/
func layoutDidChange(layout: Layout, withFlags flags: LayoutFlag)
func layoutDidChange(layout: Layout, withFlags flags: LayoutFlag, animated: Bool)
}

/**
Expand Down Expand Up @@ -200,7 +200,7 @@ public class Layout: NSObject {
*/
public final func sendChangeEventWithFlags(flags: LayoutFlag) {
// Send change event
delegate?.layoutDidChange(self, withFlags: flags)
delegate?.layoutDidChange(self, withFlags: flags, animated: animateChangeEvent)
}

/**
Expand Down Expand Up @@ -241,21 +241,22 @@ public class Layout: NSObject {
return
}

// Keep track of old parent
let oldParentLayout = layout.parentLayout

// Add this child layout and set its parent
childLayouts.insert(layout)
layout.parentLayout = self

// Remove the child layout from its old parent (if necessary)
if let oldParentLayout = oldParentLayout {
oldParentLayout.childLayouts.remove(layout)
// Update its new relative position to where it is in its new layout tree
if let previousParentLayout = oldParentLayout {
previousParentLayout.childLayouts.remove(layout)
// Update the layout's relative position to where it is in its new layout tree
layout.relativePosition = layout.absolutePosition - absolutePosition
// With the new relative position, refresh the view positions for this part of the tree
// With its new relative position, refresh the view positions for this part of the tree
layout.refreshViewPositionsForTree()
}

// Add this child layout and set its parent
childLayouts.insert(layout)
layout.parentLayout = self

// Fire hierachy listeners
hierarchyListeners.forEach {
$0.layout(self, didAdoptChildLayout: layout, fromOldParentLayout: oldParentLayout)
Expand Down Expand Up @@ -373,6 +374,53 @@ public class Layout: NSObject {
}
}

// MARK: - Layout Animation

extension Layout {
// TODO:(#173) Once the model/layout has been refactored so layout hierarchy changes aren't made
// automatically on connection changes, revisit whether an animation stack is needed.

/// Stack that keeps track of whether future layout code changes should be animated.
private static var _animationStack = [Bool]()

/**
Executes a given code block, where layout changes made inside this code block are animated.

- Note: If there is an inner call to `Layout.doNotAnimate(:)` inside the given code block,
that call will not animate its layout changes.
- Parameter code: The code block to execute, with layout animations enabled.
*/
public static func animate(code: () throws -> Void) rethrows {
_animationStack.append(true)
try code()
_animationStack.popLast()
}

/**
Executes a given code block, where layout changes made inside this code block are not animated.

- Note: If there is an inner call to `Layout.animate(:)` inside the given code block, that
call will animate its layout changes.
- Parameter code: The code block to execute, with layout animations disabled.
*/
public static func doNotAnimate(code: () throws -> Void) rethrows {
_animationStack.append(false)
try code()
_animationStack.popLast()
}

/**
Returns whether the next change event that is sent out via `sendChangeEvent(:)` should be
animated or not.
*/
public var animateChangeEvent: Bool {
// Take the most recent value of what's on the animation stack to determine if the next change
// should be animated. If nothing is on the stack, do not animate by default.
// TODO(#169): Animations don't work in RTL yet, so they've been disabled.
return !engine.rtl && Layout._animationStack.last ?? false
}
}

// MARK: - Layout Flag

/**
Expand Down
33 changes: 33 additions & 0 deletions Blockly/Code/Layout/LayoutConfig.swift
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,9 @@ public class LayoutConfig: NSObject {
/// used for each one, specified as a UIView coordinate system unit.
public static let FieldTextFieldMaximumWidth = LayoutConfig.newPropertyKey()

/// [`Double`] The animation duration to use when running animatable code inside a `LayoutView`.
public static let ViewAnimationDuration = LayoutConfig.newPropertyKey()

// MARK: - Properties

// NOTE: Separate dictionaries were created for each type of value as casting specific values
Expand All @@ -129,6 +132,9 @@ public class LayoutConfig: NSObject {
/// Dictionary mapping property keys to `CGFloat` values
private var _floats = Dictionary<PropertyKey, CGFloat>()

/// Dictionary mapping property keys to `Double` values
private var _doubles = Dictionary<PropertyKey, Double>()

// MARK: - Initializers

public override init() {
Expand All @@ -152,6 +158,8 @@ public class LayoutConfig: NSObject {

setEdgeInsets(EdgeInsets(4, 8, 4, 8), forKey: LayoutConfig.FieldTextFieldInsetPadding)
setFloat(300, forKey: LayoutConfig.FieldTextFieldMaximumWidth)

setDouble(0.3, forKey: LayoutConfig.ViewAnimationDuration)
}

// MARK: - Public
Expand Down Expand Up @@ -295,6 +303,31 @@ public class LayoutConfig: NSObject {
return _colors[key] ?? (defaultValue != nil ? setColor(defaultValue, forKey: key) : nil)
}

/**
Maps a `Double` value to a specific `PropertyKey`.

- Parameter double: The `Double` value
- Parameter key: The `PropertyKey` (e.g. `LayoutConfig.ViewAnimationDuration`)
- Returns: The `Double` that was set.
*/
public func setDouble(double: Double, forKey key: PropertyKey) -> Double {
_doubles[key] = double
return double
}

/**
Returns the `Double` value that is mapped to a specific `PropertyKey`.

- Parameter key: The `PropertyKey` (e.g. `LayoutConfig.ViewAnimationDuration`)
- Parameter defaultValue: [Optional] If no `Double` was found for `key`, this value is
automatically assigned to `key` and used instead.
- Returns: The `key`'s value
*/
@inline(__always)
public func doubleFor(key: PropertyKey, defaultValue: Double = 0) -> Double {
return _doubles[key] ?? setDouble(defaultValue, forKey: key)
}

/**
Maps a `EdgeInsets` value to a specific `PropertyKey`.

Expand Down
27 changes: 18 additions & 9 deletions Blockly/Code/Layout/WorkspaceLayout.swift
Original file line number Diff line number Diff line change
Expand Up @@ -371,8 +371,12 @@ extension WorkspaceLayout: ConnectionTargetDelegate {
(aConnection.sourceInput?.layout?.blockGroupLayout ?? // For input values or statements
aConnection.sourceBlock.layout?.parentBlockGroupLayout) // For a block's next statement
{
blockGroupLayout.appendBlockLayouts(shadowBlockLayouts, updateLayout: false)
blockGroupLayout.performLayout(includeChildren: true)
Layout.doNotAnimate {
blockGroupLayout.appendBlockLayouts(shadowBlockLayouts, updateLayout: true)
blockGroupLayout.performLayout(includeChildren: true)
blockGroupLayout.refreshViewPositionsForTree()
}

blockGroupLayout.updateLayoutUpTree()
}

Expand Down Expand Up @@ -493,11 +497,12 @@ extension WorkspaceLayout: ConnectionTargetDelegate {
let blockGroupLayout = try layoutFactory.layoutForBlockGroupLayout(engine: self.engine)
blockGroupLayout.relativePosition = sourceBlockLayout.absolutePosition

// Add this new block group layout to the workspace level
appendBlockGroupLayout(blockGroupLayout, updateLayout: false)
bringBlockGroupLayoutToFront(blockGroupLayout)
Layout.doNotAnimate {
// Add this new block group layout to the workspace level
self.appendBlockGroupLayout(blockGroupLayout, updateLayout: true)
self.bringBlockGroupLayoutToFront(blockGroupLayout)
}

// Move `sourceBlockLayout` and its followers to the new block group layout
blockGroupLayout.claimBlockLayoutAndFollowers(sourceBlockLayout, updateLayouts: true)
}

Expand All @@ -510,10 +515,14 @@ extension WorkspaceLayout: ConnectionTargetDelegate {
where emptyBlockGroupLayout.blockLayouts.count == 0 &&
emptyBlockGroupLayout.parentLayout == workspace.layout
{
// Remove this block's old parent group layout from the workspace level
removeBlockGroupLayout(emptyBlockGroupLayout, updateLayout: true)
Layout.doNotAnimate {
// Remove this block's old parent group layout from the workspace level
self.removeBlockGroupLayout(emptyBlockGroupLayout, updateLayout: true)
}
}

updateCanvasSize()
Layout.doNotAnimate {
self.updateCanvasSize()
}
}
}
Loading

0 comments on commit b165b6b

Please sign in to comment.