163 lines
3.6 KiB
Vue
163 lines
3.6 KiB
Vue
<template>
|
|
<div class="toolbar">
|
|
<template v-for="item of toolbarItems">
|
|
<Menu
|
|
:id="item.id"
|
|
:items="item.items"
|
|
:open-sibling="currentlyOpen"
|
|
@toggle="(state, reason) => toggleEvent(item.id, state, reason)"
|
|
>{{ item.label }}</Menu
|
|
>
|
|
</template>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { computed, ref } from 'vue';
|
|
import { Editor, SelectionEvent } from '../editor';
|
|
import Menu from './menu/Menu.vue';
|
|
import { WorldFile, instancableGameObjects } from '@freeblox/engine';
|
|
import { useEditorEvents } from '../composables/use-editor-events';
|
|
import { exportToFile } from '../utils/export-file';
|
|
import { readFileToString } from '../utils/read-file';
|
|
|
|
const selection = ref(false);
|
|
const currentlyOpen = ref<string | undefined>(undefined);
|
|
|
|
const props = defineProps<{
|
|
editor: Editor;
|
|
}>();
|
|
|
|
const emit = defineEmits<{
|
|
(e: 'update'): void;
|
|
}>();
|
|
|
|
const { register } = useEditorEvents(props.editor);
|
|
|
|
const fileMenu = [
|
|
{
|
|
id: 'new',
|
|
label: 'New',
|
|
onClick: () => props.editor.events.emit('reset'),
|
|
},
|
|
{
|
|
id: 'saveas',
|
|
label: 'Save as...',
|
|
onClick: () => saveLevelToFile(),
|
|
},
|
|
{
|
|
id: 'openfile',
|
|
label: 'Open file...',
|
|
onClick: () => loadLevelFromFile(),
|
|
},
|
|
];
|
|
|
|
const editMenu = computed(() => [
|
|
{
|
|
id: 'undo',
|
|
label: 'Undo',
|
|
shortcut: 'CTRL+Z',
|
|
onClick: () => props.editor.events.emit('undo'),
|
|
},
|
|
{
|
|
id: 'redo',
|
|
label: 'Redo',
|
|
shortcut: 'CTRL+Y',
|
|
onClick: () => props.editor.events.emit('redo'),
|
|
},
|
|
{
|
|
id: 'cut',
|
|
label: 'Cut',
|
|
shortcut: 'CTRL+X',
|
|
disabled: !selection.value,
|
|
onClick: () => props.editor.events.emit('cut'),
|
|
},
|
|
{
|
|
id: 'copy',
|
|
label: 'Copy',
|
|
shortcut: 'CTRL+C',
|
|
disabled: !selection.value,
|
|
onClick: () => props.editor.events.emit('copy'),
|
|
},
|
|
{
|
|
id: 'paste',
|
|
label: 'Paste',
|
|
shortcut: 'CTRL+V',
|
|
onClick: () => props.editor.events.emit('paste', undefined),
|
|
},
|
|
{
|
|
id: 'duplicate',
|
|
label: 'Duplicate',
|
|
shortcut: 'CTRL+D',
|
|
disabled: !selection.value,
|
|
onClick: () => props.editor.events.emit('duplicate'),
|
|
},
|
|
{
|
|
id: 'delete',
|
|
label: 'Delete',
|
|
shortcut: 'Delete',
|
|
disabled: !selection.value,
|
|
onClick: () => props.editor.events.emit('delete'),
|
|
},
|
|
]);
|
|
|
|
const addMenu = computed(() =>
|
|
Object.keys(instancableGameObjects).map((item) => ({
|
|
id: item,
|
|
label: item,
|
|
onClick: () => props.editor.events.emit('insert', item),
|
|
}))
|
|
);
|
|
|
|
const toolbarItems = computed(() => [
|
|
{
|
|
id: 'file',
|
|
label: 'File',
|
|
items: fileMenu,
|
|
},
|
|
{
|
|
id: 'edit',
|
|
label: 'Edit',
|
|
items: editMenu.value,
|
|
},
|
|
{
|
|
id: 'add',
|
|
label: 'Add',
|
|
items: addMenu.value,
|
|
},
|
|
]);
|
|
|
|
const toggleEvent = (id: string, state: boolean, reason: 'user' | 'leave') => {
|
|
if (state) currentlyOpen.value = id;
|
|
else if (reason === 'user') currentlyOpen.value = undefined;
|
|
};
|
|
|
|
const saveLevelToFile = () => {
|
|
const levelData = props.editor.export('Game');
|
|
const json = JSON.stringify(levelData);
|
|
exportToFile(json, 'level.json', 'application/json');
|
|
};
|
|
|
|
const loadLevelFromFile = async () => {
|
|
const data = await readFileToString('application/json');
|
|
const obj = JSON.parse(data) as WorldFile;
|
|
props.editor.load(obj);
|
|
};
|
|
|
|
const setSelectionPreset = (event: SelectionEvent) => {
|
|
selection.value = !!event.selection?.length;
|
|
};
|
|
|
|
register('selected', setSelectionPreset);
|
|
register('deselected', setSelectionPreset);
|
|
</script>
|
|
|
|
<style lang="scss">
|
|
.toolbar {
|
|
display: flex;
|
|
flex-direction: row;
|
|
height: 36px;
|
|
background-color: #efefef;
|
|
}
|
|
</style>
|