Skip to content

Commit

Permalink
Another set of small fixes (#8946)
Browse files Browse the repository at this point in the history
* `Enter` key accepts the edit in Numeric and Text input
* Newly added node are selected
* Placement of new nodes takes opened visualizations into account.

[Screencast from 2024-02-02 13-02-26.webm](https://github.com/enso-org/enso/assets/3919101/0f328824-1cbc-4b07-988e-dbf17b94dc2f)
  • Loading branch information
farmaazon authored Feb 2, 2024
1 parent 8eeef71 commit 1a8b82e
Show file tree
Hide file tree
Showing 9 changed files with 42 additions and 14 deletions.
12 changes: 11 additions & 1 deletion app/gui2/e2e/componentBrowser.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { expect, test } from '@playwright/test'
import { expect, test, type Page } from '@playwright/test'
import assert from 'assert'
import os from 'os'
import * as actions from './actions'
Expand All @@ -7,6 +7,11 @@ import * as locate from './locate'

const ACCEPT_SUGGESTION_SHORTCUT = os.platform() === 'darwin' ? 'Meta+Enter' : 'Control+Enter'

async function deselectAllNodes(page: Page) {
await page.keyboard.press('Escape')
await expect(page.locator('.GraphNode.selected')).toHaveCount(0)
}

test('Different ways of opening Component Browser', async ({ page }) => {
await actions.goToGraph(page)
const nodeCount = await locate.graphNode(page).count()
Expand Down Expand Up @@ -73,9 +78,11 @@ test('Accepting suggestion', async ({ page }) => {
'.',
'read_text',
])
await customExpect.toBeSelected(locate.graphNode(page).last())

// Clicking at highlighted entry
nodeCount = await locate.graphNode(page).count()
await deselectAllNodes(page)
await locate.addNewNodeButton(page).click()
await locate.componentBrowserSelectedEntry(page).first().click()
await expect(locate.componentBrowser(page)).not.toBeVisible()
Expand All @@ -85,9 +92,11 @@ test('Accepting suggestion', async ({ page }) => {
'.',
'read',
])
await customExpect.toBeSelected(locate.graphNode(page).last())

// Accepting with Enter
nodeCount = await locate.graphNode(page).count()
await deselectAllNodes(page)
await locate.addNewNodeButton(page).click()
await page.keyboard.press('Enter')
await expect(locate.componentBrowser(page)).not.toBeVisible()
Expand All @@ -97,6 +106,7 @@ test('Accepting suggestion', async ({ page }) => {
'.',
'read',
])
await customExpect.toBeSelected(locate.graphNode(page).last())
})

test('Accepting any written input', async ({ page }) => {
Expand Down
4 changes: 4 additions & 0 deletions app/gui2/e2e/customExpect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,7 @@ export function toExist(locator: Locator) {
// https://playwright.dev/docs/api/class-locatorassertions#locator-assertions-to-be-visible
return expect(locator.first()).toBeVisible()
}

export function toBeSelected(locator: Locator) {
return expect(locator).toHaveClass(/(?<=^| )selected(?=$| )/)
}
1 change: 1 addition & 0 deletions app/gui2/mock/providers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ export const graphSelection: GraphSelection = {
anchor: undefined,
deselectAll: () => {},
handleSelectionOf: () => {},
setSelection: () => {},
hoveredNode: undefined,
hoveredPort: undefined,
isSelected: () => false,
Expand Down
16 changes: 11 additions & 5 deletions app/gui2/src/components/GraphEditor.vue
Original file line number Diff line number Diff line change
Expand Up @@ -118,9 +118,9 @@ const interactionBindingsHandler = interactionBindings.handler({
// used as the source of the placement. This means, for example, the selected nodes when creating from a selection
// or the node that is being edited when creating from a port double click.
function environmentForNodes(nodeIds: IterableIterator<NodeId>): Environment {
const nodeRects = [...graphStore.nodeRects.values()]
const nodeRects = graphStore.visibleNodeAreas
const selectedNodeRects = [...nodeIds]
.map((id) => graphStore.nodeRects.get(id))
.map((id) => graphStore.vizRects.get(id) ?? graphStore.nodeRects.get(id))
.filter((item): item is Rect => item !== undefined)
const screenBounds = graphNavigator.viewport
const mousePosition = graphNavigator.sceneMousePos
Expand Down Expand Up @@ -207,9 +207,9 @@ const graphBindingsHandler = graphBindings.handler({
let right = -Infinity
let bottom = -Infinity
const nodesToCenter =
nodeSelection.selected.size === 0 ? graphStore.currentNodeIds : nodeSelection.selected
nodeSelection.selected.size === 0 ? graphStore.db.nodeIdToNode.keys() : nodeSelection.selected
for (const id of nodesToCenter) {
const rect = graphStore.nodeRects.get(id)
const rect = graphStore.vizRects.get(id) ?? graphStore.nodeRects.get(id)
if (!rect) continue
left = Math.min(left, rect.left)
right = Math.max(right, rect.right)
Expand Down Expand Up @@ -418,7 +418,13 @@ function onComponentBrowserCommit(content: string, requiredImports: RequiredImpo
} else {
// We finish creating a new node.
const metadata = undefined
graphStore.createNode(componentBrowserNodePosition.value, content, metadata, requiredImports)
const createdNode = graphStore.createNode(
componentBrowserNodePosition.value,
content,
metadata,
requiredImports,
)
if (createdNode) nodeSelection.setSelection(new Set([createdNode]))
}
}
// Finish interaction. This should also hide component browser.
Expand Down
2 changes: 1 addition & 1 deletion app/gui2/src/components/GraphEditor/dragging.ts
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ export function useDragging() {
createSnapGrid() {
const nonDraggedRects = computed(() => {
const nonDraggedNodes = iteratorFilter(
graphStore.currentNodeIds.values(),
graphStore.db.nodeIdToNode.keys(),
(id) => !this.draggedNodes.has(id),
)
return Array.from(nonDraggedNodes, (id) => graphStore.nodeRects.get(id)!)
Expand Down
1 change: 1 addition & 0 deletions app/gui2/src/components/widgets/EnsoTextInputWidget.vue
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ function blur() {
v-model="editedValue"
class="value"
:style="inputStyle"
@keydown.enter.stop="($event.target as HTMLInputElement).blur()"
@focus="focus"
@blur="blur"
/>
Expand Down
1 change: 1 addition & 0 deletions app/gui2/src/components/widgets/NumericInputWidget.vue
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@ function focus() {
v-model="editedValue"
class="value"
:style="inputStyle"
@keydown.enter.stop="($event.target as HTMLInputElement).blur()"
@blur="blur"
@focus="focus"
/>
Expand Down
1 change: 1 addition & 0 deletions app/gui2/src/composables/selection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ export function useSelection<T>(
},
deselectAll: () => selected.clear(),
isSelected: (element: T) => selected.has(element),
setSelection,
handleSelectionOf,
hoveredNode,
hoveredPort,
Expand Down
18 changes: 11 additions & 7 deletions app/gui2/src/stores/graph/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { nonDictatedPlacement } from '@/components/ComponentBrowser/placement'
import type { PortId } from '@/providers/portInfo'
import type { WidgetUpdate } from '@/providers/widgetRegistry'
import { GraphDb, type NodeId } from '@/stores/graph/graphDatabase'
import { GraphDb, asNodeId, type NodeId } from '@/stores/graph/graphDatabase'
import {
addImports,
filterOutRedundantImports,
Expand All @@ -19,6 +19,7 @@ import type { Opt } from '@/util/data/opt'
import { Rect } from '@/util/data/rect'
import { Vec2 } from '@/util/data/vec2'
import { map, set } from 'lib0'
import { iteratorFilter } from 'lib0/iterator'
import { defineStore } from 'pinia'
import type { ExpressionUpdate, StackItem } from 'shared/languageServerTypes'
import {
Expand Down Expand Up @@ -57,6 +58,11 @@ export const useGraphStore = defineStore('graph', () => {

const nodeRects = reactive(new Map<NodeId, Rect>())
const vizRects = reactive(new Map<NodeId, Rect>())
// The currently visible nodes' areas (including visualization).
const visibleNodeAreas = computed(() => {
const existing = iteratorFilter(nodeRects.entries(), ([id]) => db.nodeIdToNode.has(id))
return Array.from(existing, ([id, rect]) => vizRects.get(id) ?? rect)
})

const db = new GraphDb(
suggestionDb.entries,
Expand All @@ -66,7 +72,6 @@ export const useGraphStore = defineStore('graph', () => {
const portInstances = reactive(new Map<PortId, Set<PortViewInstance>>())
const editedNodeInfo = ref<NodeEditInfo>()
const methodAst = ref<Ast.Function>()
const currentNodeIds = ref(new Set<NodeId>())

const unconnectedEdge = ref<UnconnectedEdge>()

Expand Down Expand Up @@ -138,7 +143,7 @@ export const useGraphStore = defineStore('graph', () => {
if (methodAst.value) {
const rawFunc = moduleData.value.toRaw(methodAst.value.id)
assert(rawFunc?.type === RawAst.Tree.Type.Function)
currentNodeIds.value = db.readFunctionAst(
db.readFunctionAst(
methodAst.value,
rawFunc,
textContentLocal,
Expand Down Expand Up @@ -222,6 +227,7 @@ export const useGraphStore = defineStore('graph', () => {
const assignment = Ast.Assignment.new(edit, ident, rhs)
functionBlock.push(assignment)
commitEdit(edit)
return asNodeId(rhs.id)
}

function addMissingImports(edit: MutableModule, newImports: RequiredImport[]) {
Expand Down Expand Up @@ -317,9 +323,7 @@ export const useGraphStore = defineStore('graph', () => {
if (!nodeAst) return
if (rect.pos.equals(Vec2.Zero) && !nodeAst.nodeMetadata.get('position')) {
const { position } = nonDictatedPlacement(rect.size, {
nodeRects: [...nodeRects.entries()]
.filter(([id]) => db.nodeIdToNode.get(id))
.map(([id, rect]) => vizRects.get(id) ?? rect),
nodeRects: visibleNodeAreas.value,
// The rest of the properties should not matter.
selectedNodeRects: [],
screenBounds: Rect.Zero,
Expand Down Expand Up @@ -530,10 +534,10 @@ export const useGraphStore = defineStore('graph', () => {
editedNodeInfo,
unconnectedEdge,
edges,
currentNodeIds,
moduleCode,
nodeRects,
vizRects,
visibleNodeAreas,
unregisterNodeRect,
methodAst,
astModule,
Expand Down

0 comments on commit 1a8b82e

Please sign in to comment.