Skip to content

Commit

Permalink
Make lots of progress.
Browse files Browse the repository at this point in the history
  • Loading branch information
grantjbutler committed Nov 23, 2021
1 parent 7444930 commit d6742ae
Show file tree
Hide file tree
Showing 26 changed files with 643 additions and 43 deletions.
4 changes: 4 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,18 @@
},
"main": "background.js",
"dependencies": {
"@headlessui/vue": "^1.4.2",
"@heroicons/vue": "^1.0.5",
"@tailwindcss/aspect-ratio": "^0.3.0",
"core-js": "^3.6.5",
"obs-websocket-js": "^4.0.3",
"uuid": "^8.3.2",
"vue": "^3.0.0",
"vuex": "^4.0.0-0"
},
"devDependencies": {
"@types/electron-devtools-installer": "^2.2.0",
"@types/uuid": "^8.3.3",
"@typescript-eslint/eslint-plugin": "^4.18.0",
"@typescript-eslint/parser": "^4.18.0",
"@vue/cli-plugin-babel": "~4.5.0",
Expand Down
17 changes: 12 additions & 5 deletions src/App.vue
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
<template>
<div class="flex flex-row h-screen">
<sidebar class="flex-none" :component="rootComponent"></sidebar>
<sidebar class="flex-none" :component="rootComponent" :selectedComponent="selectedComponent" @selectComponent="selectComponent"></sidebar>
<preview :component="rootComponent"></preview>
<controls class="flex-none"></controls>
<controls class="flex-none" :component="selectedComponent"></controls>
</div>
</template>

Expand All @@ -11,20 +11,27 @@ import { defineComponent, ref } from 'vue';
import Preview from './components/Preview.vue';
import Sidebar from './components/Sidebar.vue';
import Controls from './components/Controls.vue';
import RootComponent from './layout/RootComponent';
import Component from './layout/Component';
export default defineComponent({
name: 'App',
components: {
Preview,
Sidebar,
Controls
Controls,
},
setup() {
const rootComponent = ref(new Component())
const rootComponent = ref(new RootComponent())
const selectedComponent = ref<Component | null>(null)
const selectComponent = (component: Component | null) => {
selectedComponent.value = component
}
return {
rootComponent
rootComponent,
selectedComponent,
selectComponent
}
}
});
Expand Down
34 changes: 33 additions & 1 deletion src/background.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,40 @@
'use strict'

import { app, protocol, BrowserWindow } from 'electron'
import { app, protocol, BrowserWindow, ipcMain, Menu, IpcMainEvent, MenuItemConstructorOptions, MenuItem } from 'electron'
import { createProtocol } from 'vue-cli-plugin-electron-builder/lib'
import installExtension, { VUEJS3_DEVTOOLS } from 'electron-devtools-installer'
const isDevelopment = process.env.NODE_ENV !== 'production'

function installClickHandler<Item extends MenuItemConstructorOptions | MenuItem>(template: Item[], event: IpcMainEvent): Item[] {
return template.map(item => {
if (item.submenu) {
if (Array.isArray(item.submenu)) {
item.submenu = installClickHandler(item.submenu, event)
} else {
item.submenu.items = installClickHandler(item.submenu.items, event)
}
}

if (!item.id) { return item; }

item.click = () => { event.reply(`context-menu:click:${item.id}`) }

return item;
})
}

function installMenus(): void {
ipcMain.on('show-context-menu', (event, template: MenuItemConstructorOptions[]) => {
const window = BrowserWindow.fromWebContents(event.sender);
if (!window) {
return;
}

const menu = Menu.buildFromTemplate(installClickHandler(template, event));
menu.popup({ window })
});
}

// Scheme must be registered before the app is ready
protocol.registerSchemesAsPrivileged([
{ scheme: 'app', privileges: { secure: true, standard: true } }
Expand Down Expand Up @@ -66,6 +96,8 @@ app.on('ready', async () => {
}
}
createWindow()

installMenus();
})

// Exit cleanly on request from parent process in development mode.
Expand Down
19 changes: 19 additions & 0 deletions src/components/Components/FlexComponentView.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<template>
<div class="flex" :class="{ 'flex-col': component.direction == 'vertical', 'flex-row': component.direction == 'horizontal' }">
<component v-for="child in component.children" :key="child.id" :is="child.viewComponent" :component="child"></component>
</div>
</template>

<script lang="ts">
import { defineComponent, PropType } from 'vue'
import FlexComponent from '@/layout/FlexComponent'
export default defineComponent({
props: {
component: {
type: Object as PropType<FlexComponent>,
required: true
}
}
})
</script>
19 changes: 19 additions & 0 deletions src/components/Components/SourceComponentView.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<template>
<div class="bg-black aspect-w-16 aspect-h-9">
<span v-text="component.source"></span>
</div>
</template>

<script lang="ts">
import { defineComponent, PropType } from 'vue'
import SourceComponent from '@/layout/SourceComponent'
export default defineComponent({
props: {
component: {
type: Object as PropType<SourceComponent>,
required: true
}
}
})
</script>
25 changes: 21 additions & 4 deletions src/components/Controls.vue
Original file line number Diff line number Diff line change
@@ -1,13 +1,30 @@
<template>
<div class="w-64 bg-gray-200">
Controls
<div class="w-72 bg-gray-200 p-2">
<div v-if="component">
<div>
<p>Base Component Controls</p>
</div>

<component :is="component.controlsComponent" :component="component"></component>
</div>
</div>
</template>

<script lang="ts">
import { defineComponent } from 'vue'
import Component from '@/layout/Component'
import { defineComponent, PropType } from 'vue'
import FlexComponentControls from './Controls/FlexComponentControls.vue'
import SourceComponentControls from './Controls/SourceComponentControls.vue'
export default defineComponent({
components: {
FlexComponentControls,
SourceComponentControls
},
props: {
component: {
type: Object as PropType<Component>
}
}
})
</script>
37 changes: 37 additions & 0 deletions src/components/Controls/FlexComponentControls.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<template>
<div>
<p>Flex Component</p>
<div>
<label>Direction</label>
<select :value="component.direction" @change="setDirection">
<option value="horizontal">Horizontal</option>
<option value="vertical">Vertical</option>
</select>
</div>
</div>
</template>

<script lang="ts">
import FlexComponent from '@/layout/FlexComponent'
import { defineComponent, PropType, toRefs } from 'vue'
export default defineComponent({
props: {
component: {
type: Object as PropType<FlexComponent>,
required: true
}
},
setup(props) {
const { component } = toRefs(props)
const setDirection = (direction: 'horizontal' | 'vertical') => {
component.value.direction = direction
}
return {
setDirection
}
}
})
</script>
39 changes: 39 additions & 0 deletions src/components/Controls/SourceComponentControls.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<template>
<div>
<p>Source Component</p>
<div>
<label>Source</label>
<select :value="component.source" @change="setSource">
<option value="">None</option>
<option value="MC Ninja">MC Ninja</option>
<option value="Game capture">Game capture</option>
<option value="facecam">facecam</option>
</select>
</div>
</div>
</template>

<script lang="ts">
import SourceComponent from '@/layout/SourceComponent'
import { defineComponent, PropType, toRefs } from 'vue'
export default defineComponent({
props: {
component: {
type: Object as PropType<SourceComponent>,
required: true
}
},
setup(props) {
const { component } = toRefs(props)
const setSource = (source: string) => {
component.value.source = source
}
return {
setSource
}
},
})
</script>
37 changes: 37 additions & 0 deletions src/components/Menu/ContextMenuProviding.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { ipcRenderer } from 'electron'
import { defineComponent, h } from 'vue'

export default defineComponent({
setup(props, context) {
return () => {
const defaultSlot = context.slots.default?.() ?? []
const menuSlot = context.slots.menu?.() ?? []

return h(
'div',
{
onContextmenu: () => {
const [node] = menuSlot
if (!node) {
return;
}

const component = node.component
if (!component) {
return;
}

const buildItem = component.exposed?.buildItem
if (!buildItem) {
throw new Error();
}

const menu = buildItem();
ipcRenderer.send('show-context-menu', JSON.parse(JSON.stringify(menu)));
}
},
[...defaultSlot, ...menuSlot]
)
}
},
})
18 changes: 18 additions & 0 deletions src/components/Menu/Menu.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { defineComponent } from 'vue'
import useMenuBuilding from './useMenuBuilding';

export default defineComponent({
setup(props, context) {
const { buildItem, buildMenu } = useMenuBuilding()

context.expose({
buildItem
})

return () => {
const children = context.slots.default?.()
buildMenu(children ?? [])
return children
}
},
})
48 changes: 48 additions & 0 deletions src/components/Menu/MenuItem.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { defineComponent, Text, ref } from 'vue'
import { v4 as uuidv4 } from 'uuid'
import { ipcRenderer } from 'electron';
import MenuItemDefinition from './MenuItemDefinition'

export default defineComponent({
emits: ['click'],
setup(props, context) {
const label = ref('');
const id = ref(uuidv4())

const buildItem = (): MenuItemDefinition => {
return {
id: id.value,
label: label.value
}
}

context.expose({
buildItem
})

ipcRenderer.on(`context-menu:click:${id.value}`, () => {
context.emit('click');
})

return () => {
if (!context.slots.default) {
throw new Error();
}

const children = context.slots.default()
const [firstChild, ...others] = children

if (others.length > 0) {
throw new Error();
}

if (firstChild.type != Text) {
throw new Error();
}

label.value = firstChild.children as string

return null
}
}
})
15 changes: 15 additions & 0 deletions src/components/Menu/MenuItemDefinition.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
interface MenuItem {
id: string
label: string
}

interface MenuSubmenu {
label: string
submenu: MenuItemDefinition[]
}

type MenuItemDefinition = MenuItem | MenuSubmenu | {
type: 'separator'
}

export default MenuItemDefinition;
Loading

0 comments on commit d6742ae

Please sign in to comment.