Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions client/web/.eslintrc.js
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
module.exports = {
root: true,
env: {
browser: true,
node: true,
es2020: true,
},
extends: ["plugin:vue/vue3-essential", "@vue/airbnb", "@vue/typescript/recommended", "plugin:prettier-vue/recommended", "prettier"],
parserOptions: {
Expand Down
74 changes: 73 additions & 1 deletion client/web/src/components/panels/LayerTree.vue
Original file line numberDiff line numberDiff line change
Expand Up@@ -20,7 +20,14 @@
<div class="layer-visibility">
<IconButton :icon="layer.visible ? 'EyeVisible' : 'EyeHidden'" @click="toggleLayerVisibility(layer.path)" :size="24" :title="layer.visible ? 'Visible' : 'Hidden'" />
</div>
<div class="layer">
<div
class="layer"
:class="{ selected: layer.layer_data.selected }"
@click.shift.exact="handleShiftClick(layer)"
@click.ctrl.exact="handleControlClick(layer)"
@click.alt.exact="handleControlClick(layer)"
Comment thread
Keavon marked this conversation as resolved.
@click.exact="handleClick(layer)"
>
<div class="layer-thumbnail"></div>
<div class="layer-type-icon">
<Icon :icon="'NodeTypePath'" title="Path" />
Expand DownExpand Up@@ -68,6 +75,10 @@
margin-left: 4px;
padding-left: 16px;
}
.selected {
background: var(--color-accent);
Comment thread
Keavon marked this conversation as resolved.
color: var(--color-f-white);
}

& + .layer-row {
margin-top: 2px;
Expand DownExpand Up@@ -118,6 +129,65 @@ export default defineComponent({
const { toggle_layer_visibility } = await wasm;
toggle_layer_visibility(path);
},
async handleControlClick(clickedLayer: LayerPanelEntry) {
const index = this.layers.indexOf(clickedLayer);
clickedLayer.layer_data.selected = !clickedLayer.layer_data.selected;
this.selectionRangeEndLayer = undefined;
this.selectionRangeStartLayer =
this.layers.slice(index).filter((layer) => layer.layer_data.selected)[0] ||
this.layers
.slice(0, index)
.reverse()
.filter((layer) => layer.layer_data.selected)[0];
this.updateSelection();
},
async handleShiftClick(clickedLayer: LayerPanelEntry) {
// The two paths of the range are stored in selectionRangeStartLayer and selectionRangeEndLayer
// So for a new Shift+Click, select all layers between selectionRangeStartLayer and selectionRangeEndLayer(stored in prev Sft+C)
this.selectionRangeEndLayer = clickedLayer;
this.selectionRangeStartLayer = (this.selectionRangeStartLayer as LayerPanelEntry) || clickedLayer;
this.clearSelection();
this.fillSelectionRange(this.selectionRangeStartLayer, this.selectionRangeEndLayer, true);
this.updateSelection();
},

async handleClick(clickedLayer: LayerPanelEntry) {
this.selectionRangeStartLayer = clickedLayer;
this.selectionRangeEndLayer = clickedLayer;
this.clearSelection();
clickedLayer.layer_data.selected = true;
this.updateSelection();
},
async fillSelectionRange(start: LayerPanelEntry, end: LayerPanelEntry, selected = true) {
const startIndex = this.layers.indexOf(start);
const endIndex = this.layers.indexOf(end);
const [min, max] = [startIndex, endIndex].sort();
for (let i = min; i <= max; i += 1) {
this.layers[i].layer_data.selected = selected;
}
},
async clearSelection() {
this.layers.forEach((layer) => {
layer.layer_data.selected = false;
});
},
async updateSelection() {
const paths = this.layers.filter((layer) => layer.layer_data.selected).map((layer) => layer.path);
const length = paths.reduce((acc, cur) => acc + cur.length, 0) + paths.length - 1;
const output = new BigUint64Array(length);
Comment thread
Keavon marked this conversation as resolved.
let i = 0;
paths.forEach((path, index) => {
output.set(path, i);
i += path.length;
if (index < paths.length) {
// eslint-disable-next-line no-bitwise
output[i] = (1n << 64n) - 1n;
}
i += 1;
});
const { select_layers } = await wasm;
select_layers(output);
},
},
mounted() {
registerResponseHandler(ResponseType.ExpandFolder, (responseData: Response) => {
Expand All@@ -140,6 +210,8 @@ export default defineComponent({
MenuDirection,
SeparatorType,
layers: [] as Array<LayerPanelEntry>,
selectionRangeStartLayer: undefined as LayerPanelEntry | undefined,
selectionRangeEndLayer: undefined as LayerPanelEntry | undefined,
};
},
components: {
Expand Down
15 changes: 13 additions & 2 deletions client/web/src/response-handler.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -118,19 +118,30 @@ export interface LayerPanelEntry {
name: string;
visible: boolean;
layer_type: LayerType;
collapsed: boolean;
path: BigUint64Array;
layer_data: LayerData;
}
function newLayerPanelEntry(input: any): LayerPanelEntry {
return {
name: input.name,
visible: input.visible,
layer_type: newLayerType(input.layer_type),
collapsed: input.collapsed,
layer_data: newLayerData(input.layer_data),
path: new BigUint64Array(input.path.map((n: number) => BigInt(n))),
};
}

export interface LayerData {
expanded: boolean;
selected: boolean;
}
function newLayerData(input: any): LayerData {
return {
expanded: input.expanded,
selected: input.selected,
};
}

export enum LayerType {
Folder = "Folder",
Shape = "Shape",
Expand Down