Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add-node buttons #9247

Merged
merged 10 commits into from
Mar 8, 2024
4 changes: 2 additions & 2 deletions app/gui2/e2e/collapsingAndEntering.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,15 +65,15 @@ test('Collapsing nodes', async ({ page }) => {

// Widgets may "steal" clicks, so we always click at icon.
await locate
.graphNodeByBinding(page, 'ten')
.graphNodeByBinding(page, 'prod')
.locator('.icon')
.click({ modifiers: ['Shift'] })
await locate
.graphNodeByBinding(page, 'sum')
.locator('.icon')
.click({ modifiers: ['Shift'] })
await locate
.graphNodeByBinding(page, 'prod')
.graphNodeByBinding(page, 'ten')
.locator('.icon')
.click({ modifiers: ['Shift'] })

Expand Down
1 change: 1 addition & 0 deletions app/gui2/e2e/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ provideVisualizationConfig._mock(
},
],
updateType() {},
addNode() {},
},
app,
)
Expand Down
13 changes: 9 additions & 4 deletions app/gui2/e2e/selectingNodes.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,16 @@ test('Selecting nodes by click', async ({ page }) => {
await expect(node1).not.toBeSelected()
await expect(node2).not.toBeSelected()

const deselectAll = async () => {
await page.keyboard.press('Escape')
await expect(node1).not.toBeSelected()
await expect(node2).not.toBeSelected()
}

await locate.graphNodeIcon(node1).click()
await expect(node1).toBeSelected()
await expect(node2).not.toBeSelected()
await deselectAll()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This line forfeits one of the covered cases: that clicking at non-selected node also deselect previous one.


await locate.graphNodeIcon(node2).click()
await expect(node1).not.toBeSelected()
Expand All @@ -23,14 +30,12 @@ test('Selecting nodes by click', async ({ page }) => {
await locate.graphNodeIcon(node1).click({ modifiers: ['Shift'] })
await expect(node1).toBeSelected()
await expect(node2).toBeSelected()
await deselectAll()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here also: I want to test that even having two nodes selected, when clicking one, the other is deselected.


await locate.graphNodeIcon(node2).click()
await expect(node1).not.toBeSelected()
await expect(node2).toBeSelected()

await page.mouse.click(600, 200)
await expect(node1).not.toBeSelected()
await expect(node2).not.toBeSelected()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another case removed: clicking at the background should deselect all nodes. I actually had regressions there: please restore it.

await deselectAll()
})

test('Selecting nodes by area drag', async ({ page }) => {
Expand Down
13 changes: 10 additions & 3 deletions app/gui2/src/assets/icons.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
111 changes: 64 additions & 47 deletions app/gui2/src/components/CircularMenu.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
<script setup lang="ts">
import SmallPlusButton from '@/components/SmallPlusButton.vue'
import SvgIcon from '@/components/SvgIcon.vue'
import ToggleIcon from '@/components/ToggleIcon.vue'
import { Vec2 } from '@/util/data/vec2'

const props = defineProps<{
isOutputContextEnabledGlobally: boolean
Expand All @@ -17,68 +19,75 @@ const emit = defineEmits<{
startEditingComment: []
openFullMenu: []
delete: []
addNode: [pos: Vec2 | undefined]
}>()
</script>

<template>
<div
:class="`${props.isFullMenuVisible ? 'CircularMenu full' : 'CircularMenu partial'}`"
@pointerdown.stop
@pointerup.stop
@click.stop
>
<div v-if="!isFullMenuVisible" class="More" @pointerdown.stop="emit('openFullMenu')"></div>
<SvgIcon
v-if="isFullMenuVisible"
name="comment"
class="icon-container button slot2"
:alt="`Edit comment`"
@click.stop="emit('startEditingComment')"
/>
<SvgIcon
v-if="isFullMenuVisible"
name="trash2"
class="icon-container button slot4"
:alt="`Delete component`"
@click.stop="emit('delete')"
/>
<ToggleIcon
icon="eye"
class="icon-container button slot5"
:alt="`${props.isVisualizationVisible ? 'Hide' : 'Show'} visualization`"
:modelValue="props.isVisualizationVisible"
@update:modelValue="emit('update:isVisualizationVisible', $event)"
/>
<SvgIcon
name="edit"
class="icon-container button slot6"
data-testid="edit-button"
@click.stop="emit('startEditing')"
/>
<ToggleIcon
:icon="props.isOutputContextEnabledGlobally ? 'no_auto_replay' : 'auto_replay'"
class="icon-container button slot7"
:class="{ 'output-context-overridden': props.isOutputContextOverridden }"
:alt="`${
props.isOutputContextEnabledGlobally != props.isOutputContextOverridden ?
'Disable'
: 'Enable'
} output context`"
:modelValue="props.isOutputContextOverridden"
@update:modelValue="emit('update:isOutputContextOverridden', $event)"
<div class="CircularMenu" @pointerdown.stop @pointerup.stop @click.stop>
<div class="circle" :class="`${props.isFullMenuVisible ? 'full' : 'partial'}`">
<div v-if="!isFullMenuVisible" class="More" @pointerdown.stop="emit('openFullMenu')"></div>
<SvgIcon
v-if="isFullMenuVisible"
name="comment"
class="icon-container button slot2"
:alt="`Edit comment`"
@click.stop="emit('startEditingComment')"
/>
<SvgIcon
v-if="isFullMenuVisible"
name="trash2"
class="icon-container button slot4"
:alt="`Delete component`"
@click.stop="emit('delete')"
/>
<ToggleIcon
icon="eye"
class="icon-container button slot5"
:alt="`${props.isVisualizationVisible ? 'Hide' : 'Show'} visualization`"
:modelValue="props.isVisualizationVisible"
@update:modelValue="emit('update:isVisualizationVisible', $event)"
/>
<SvgIcon
name="edit"
class="icon-container button slot6"
data-testid="edit-button"
@click.stop="emit('startEditing')"
/>
<ToggleIcon
:icon="props.isOutputContextEnabledGlobally ? 'no_auto_replay' : 'auto_replay'"
class="icon-container button slot7"
:class="{ 'output-context-overridden': props.isOutputContextOverridden }"
:alt="`${
props.isOutputContextEnabledGlobally != props.isOutputContextOverridden ?
'Disable'
: 'Enable'
} output context`"
:modelValue="props.isOutputContextOverridden"
@update:modelValue="emit('update:isOutputContextOverridden', $event)"
/>
</div>
<SmallPlusButton
v-if="!isVisualizationVisible"
class="below-slot5"
@addNode="emit('addNode', $event)"
/>
</div>
</template>

<style scoped>
.CircularMenu {
user-select: none;
position: absolute;
user-select: none;
pointer-events: none;
}

.circle {
position: relative;
left: -36px;
top: -36px;
width: 114px;
height: 114px;
pointer-events: none;

> * {
pointer-events: all;
Expand Down Expand Up @@ -126,6 +135,7 @@ const emit = defineEmits<{
backdrop-filter: var(--blur-app-bg);
background: var(--color-app-bg);
z-index: -2;
pointer-events: all;

&:after {
content: '...';
Expand All @@ -144,6 +154,7 @@ const emit = defineEmits<{
padding: 0;
border: none;
opacity: 30%;
pointer-events: all;
}

.toggledOn {
Expand Down Expand Up @@ -202,6 +213,12 @@ const emit = defineEmits<{
top: 80px;
}

.below-slot5 {
position: absolute;
top: calc(108px - 36px);
pointer-events: all;
}

.slot6 {
position: absolute;
top: 69.46px;
Expand Down
10 changes: 8 additions & 2 deletions app/gui2/src/components/GraphEditor.vue
Original file line number Diff line number Diff line change
Expand Up @@ -394,13 +394,18 @@ function showComponentBrowser(nodePosition?: Vec2, usage?: Usage) {
componentBrowserVisible.value = true
}

function startCreatingNodeFromButton() {
function addNodeFromSelection() {
const targetPos =
placementPositionForSelection() ??
nonDictatedPlacement(DEFAULT_NODE_SIZE, placementEnvironment.value).position
showComponentBrowser(targetPos)
}
kazcw marked this conversation as resolved.
Show resolved Hide resolved

function addNodeAt(pos: Vec2 | undefined) {
if (!pos) return addNodeFromSelection()
showComponentBrowser(pos)
}

function hideComponentBrowser() {
graphStore.editedNodeInfo = undefined
componentBrowserVisible.value = false
Expand Down Expand Up @@ -618,6 +623,7 @@ function handleEdgeDrop(source: AstId, position: Vec2) {
<GraphNodes
@nodeOutputPortDoubleClick="handleNodeOutputPortDoubleClick"
@nodeDoubleClick="(id) => stackNavigator.enterNode(id)"
@addNode="addNodeAt($event)"
/>
</div>
<GraphEdges :navigator="graphNavigator" @createNodeFromEdge="handleEdgeDrop" />
Expand Down Expand Up @@ -645,7 +651,7 @@ function handleEdgeDrop(source: AstId, position: Vec2) {
@zoomIn="graphNavigator.scale *= 1.1"
@zoomOut="graphNavigator.scale *= 0.9"
/>
<PlusButton @pointerdown.stop @click.stop="startCreatingNodeFromButton()" @pointerup.stop />
<PlusButton @pointerdown.stop @click.stop="addNodeFromSelection()" @pointerup.stop />
<Transition>
<Suspense ref="codeEditorArea">
<CodeEditor v-if="showCodeEditor" />
Expand Down
3 changes: 3 additions & 0 deletions app/gui2/src/components/GraphEditor/GraphNode.vue
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ const emit = defineEmits<{
outputPortClick: [portId: AstId]
outputPortDoubleClick: [portId: AstId]
doubleClick: []
addNode: [pos: Vec2 | undefined]
'update:edited': [cursorPosition: number]
'update:rect': [rect: Rect]
'update:visualizationId': [id: Opt<VisualizationIdentifier>]
Expand Down Expand Up @@ -416,6 +417,7 @@ const documentation = computed<string | undefined>({
@startEditingComment="editingComment = true"
@openFullMenu="openFullMenu"
@delete="emit('delete')"
@addNode="emit('addNode', $event)"
/>
<GraphVisualization
v-if="isVisualizationVisible"
Expand All @@ -434,6 +436,7 @@ const documentation = computed<string | undefined>({
@update:visible="emit('update:visualizationVisible', $event)"
@update:fullscreen="emit('update:visualizationFullscreen', $event)"
@update:width="emit('update:visualizationWidth', $event)"
@addNode="emit('addNode', $event)"
/>
<Suspense>
<GraphNodeComment
Expand Down
2 changes: 2 additions & 0 deletions app/gui2/src/components/GraphEditor/GraphNodes.vue
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ const navigator = injectGraphNavigator(true)
const emit = defineEmits<{
nodeOutputPortDoubleClick: [portId: AstId]
nodeDoubleClick: [nodeId: NodeId]
addNode: [pos: Vec2 | undefined]
}>()

function nodeIsDragged(movedId: NodeId, offset: Vec2) {
Expand Down Expand Up @@ -54,6 +55,7 @@ const uploadingFiles = computed<[FileName, File][]>(() => {
@outputPortClick="graphStore.createEdgeFromOutput($event)"
@outputPortDoubleClick="emit('nodeOutputPortDoubleClick', $event)"
@doubleClick="emit('nodeDoubleClick', id)"
@addNode="emit('addNode', $event)"
@update:edited="graphStore.setEditedNode(id, $event)"
@update:rect="graphStore.updateNodeRect(id, $event)"
@update:visualizationId="
Expand Down
2 changes: 2 additions & 0 deletions app/gui2/src/components/GraphEditor/GraphVisualization.vue
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ const emit = defineEmits<{
'update:visible': [visible: boolean]
'update:fullscreen': [fullscreen: boolean]
'update:width': [width: number]
addNode: [pos: Vec2 | undefined]
}>()

const visPreprocessor = ref(DEFAULT_VISUALIZATION_CONFIGURATION)
Expand Down Expand Up @@ -292,6 +293,7 @@ provideVisualizationConfig({
},
hide: () => emit('update:visible', false),
updateType: (id) => emit('update:id', id),
addNode: (pos) => emit('addNode', pos),
})

const effectiveVisualization = computed(() => {
Expand Down
Loading
Loading