Skip to content

Commit

Permalink
Merging with latest develop and resolving AssertNode conflict
Browse files Browse the repository at this point in the history
  • Loading branch information
JaroslavTulach committed Jan 13, 2024
2 parents 6d890de + 5b91f16 commit e2dbd65
Show file tree
Hide file tree
Showing 111 changed files with 2,970 additions and 704 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -1014,6 +1014,7 @@
- [Export of non-existing symbols results in error][7960]
- [Upgrade GraalVM to 23.1.0 JDK21][7991]
- [Added opt-in type checks of return type][8502]
- [Introduce Arrow language][8512]
- [DataflowError.withoutTrace doesn't store stacktrace][8608]

[3227]: https://github.com/enso-org/enso/pull/3227
Expand Down Expand Up @@ -1166,6 +1167,7 @@
[7960]: https://github.com/enso-org/enso/pull/7960
[7991]: https://github.com/enso-org/enso/pull/7991
[8502]: https://github.com/enso-org/enso/pull/8502
[8512]: https://github.com/enso-org/enso/pull/8512
[8608]: https://github.com/enso-org/enso/pull/8608

# Enso 2.0.0-alpha.18 (2021-10-12)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {
averagePositionPlacement,
mouseDictatedPlacement,
nonDictatedPlacement,
previousNodeDictatedPlacement,
Expand Down Expand Up @@ -447,6 +448,54 @@ describe('Mouse dictated placement', () => {
})
})

describe('Average position placement', () => {
function environment(selectedNodeRects: Rect[], nonSelectedNodeRects: Rect[]): Environment {
return {
screenBounds,
nodeRects: [...selectedNodeRects, ...nonSelectedNodeRects],
selectedNodeRects,
get mousePosition() {
return getMousePosition()
},
}
}

function options(): { horizontalGap: number; verticalGap: number } {
return {
get horizontalGap() {
return getHorizontalGap()
},
get verticalGap() {
return getVerticalGap()
},
}
}

test('One selected, no other nodes', () => {
const X = 1100
const Y = 700
const selectedNodeRects = [rectAt(X, Y)]
const result = averagePositionPlacement(nodeSize, environment(selectedNodeRects, []), options())
expect(result).toEqual({ position: new Vec2(X, Y), pan: undefined })
})

test('Multiple selected, no other nodes', () => {
const selectedNodeRects = [rectAt(1000, 600), rectAt(1300, 800)]
const result = averagePositionPlacement(nodeSize, environment(selectedNodeRects, []), options())
expect(result).toEqual({ position: new Vec2(1150, 700), pan: undefined })
})

test('Average position occupied', () => {
const selectedNodeRects = [rectAt(1000, 600), rectAt(1300, 800)]
const result = averagePositionPlacement(
nodeSize,
environment(selectedNodeRects, [rectAt(1150, 700)]),
options(),
)
expect(result).toEqual({ position: new Vec2(1150, 744), pan: undefined })
})
})

// === Helpers for debugging ===

function generateVueCodeForNonDictatedPlacement(newNode: Rect, rects: Rect[]) {
Expand Down
51 changes: 51 additions & 0 deletions app/gui2/src/components/ComponentBrowser/placement.ts
Original file line number Diff line number Diff line change
Expand Up @@ -128,3 +128,54 @@ export function mouseDictatedPlacement(
const nodeRadius = nodeSize.y / 2
return { position: mousePosition.add(new Vec2(nodeRadius, nodeRadius)) }
}

/** The new node should appear at the average position of selected nodes.
*
* If the desired place is already occupied by non-selected node, it should be moved down to the closest free space.
*
* Specifically, this code, in order:
* - calculates the average position of selected nodes
* - searches for all vertical spans below the initial position,
* that horizontally intersect the initial position (no horizontal gap is required between
* the new node and old nodes)
* - shifts the node down (if required) until there is sufficient vertical space -
* the height of the node, in addition to the specified gap both above and below the node.
*/
export function averagePositionPlacement(
nodeSize: Vec2,
{ screenBounds, selectedNodeRects, nodeRects }: Environment,
{ verticalGap = theme.node.vertical_gap }: PlacementOptions = {},
): Placement {
let totalPosition = new Vec2(0, 0)
let selectedNodeRectsCount = 0
for (const rect of selectedNodeRects) {
totalPosition = totalPosition.add(rect.pos)
selectedNodeRectsCount++
}
const initialPosition = totalPosition.scale(1.0 / selectedNodeRectsCount)
const nonSelectedNodeRects = []
outer: for (const rect of nodeRects) {
for (const sel of selectedNodeRects) {
if (sel.equals(rect)) {
continue outer
}
}
nonSelectedNodeRects.push(rect)
}
let top = initialPosition.y
const initialRect = new Rect(initialPosition, nodeSize)
const nodeRectsSorted = Array.from(nonSelectedNodeRects).sort((a, b) => a.top - b.top)
for (const rect of nodeRectsSorted) {
if (initialRect.intersectsX(rect) && rect.bottom + verticalGap > top) {
if (rect.top - (top + nodeSize.y) < verticalGap) {
top = rect.bottom + verticalGap
}
}
}
const finalPosition = new Vec2(initialPosition.x, top)
if (new Rect(finalPosition, nodeSize).within(screenBounds)) {
return { position: finalPosition }
} else {
return { position: finalPosition, pan: finalPosition.sub(initialPosition) }
}
}
20 changes: 16 additions & 4 deletions app/gui2/src/components/GraphEditor.vue
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ import { useGraphStore } from '@/stores/graph'
import type { RequiredImport } from '@/stores/graph/imports'
import { useProjectStore } from '@/stores/project'
import { groupColorVar, useSuggestionDbStore } from '@/stores/suggestionDatabase'
import { assert, bail } from '@/util/assert'
import { BodyBlock } from '@/util/ast/abstract'
import { colorFromString } from '@/util/colors'
import { Rect } from '@/util/data/rect'
import { Vec2 } from '@/util/data/vec2'
Expand Down Expand Up @@ -255,13 +257,23 @@ const graphBindingsHandler = graphBindings.handler({
},
collapse() {
if (keyboardBusy()) return false
const selected = nodeSelection.selected
const selected = new Set(nodeSelection.selected)
if (selected.size == 0) return
try {
const info = prepareCollapsedInfo(nodeSelection.selected, graphStore.db)
performCollapse(info)
const info = prepareCollapsedInfo(selected, graphStore.db)
const currentMethod = projectStore.executionContext.getStackTop()
const currentMethodName = graphStore.db.stackItemToMethodName(currentMethod)
if (currentMethodName == null) {
bail(`Cannot get the method name for the current execution stack item. ${currentMethod}`)
}
graphStore.editAst((module) => {
if (graphStore.moduleRoot == null) bail(`Module root is missing.`)
const topLevel = module.get(graphStore.moduleRoot)
assert(topLevel instanceof BodyBlock)
return performCollapse(info, module, topLevel, graphStore.db, currentMethodName)
})
} catch (err) {
console.log(`Error while collapsing, this is not normal. ${err}`)
console.log('Error while collapsing, this is not normal.', err)
}
},
enterNode() {
Expand Down
4 changes: 3 additions & 1 deletion app/gui2/src/components/GraphEditor/GraphNode.vue
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import { Vec2 } from '@/util/data/vec2'
import { displayedIconOf } from '@/util/getIconName'
import { setIfUndefined } from 'lib0/map'
import type { ExprId, VisualizationIdentifier } from 'shared/yjsModel'
import { computed, ref, watch, watchEffect } from 'vue'
import { computed, onUnmounted, ref, watch, watchEffect } from 'vue'
const MAXIMUM_CLICK_LENGTH_MS = 300
const MAXIMUM_CLICK_DISTANCE_SQ = 50
Expand Down Expand Up @@ -73,6 +73,8 @@ const outputPortsSet = computed(() => {
const widthOverridePx = ref<number>()
const nodeId = computed(() => props.node.rootSpan.exprId)
onUnmounted(() => graph.unregisterNodeRect(nodeId.value))
const rootNode = ref<HTMLElement>()
const contentNode = ref<HTMLElement>()
const nodeSize = useResizeObserver(rootNode)
Expand Down
Loading

0 comments on commit e2dbd65

Please sign in to comment.