Skip to content

Commit

Permalink
More progress.
Browse files Browse the repository at this point in the history
- Start to style like a native app.
- Update preview rendering.
  • Loading branch information
grantjbutler committed Nov 29, 2021
1 parent 4c1dd84 commit 9afd89a
Show file tree
Hide file tree
Showing 31 changed files with 471 additions and 153 deletions.
19 changes: 15 additions & 4 deletions src/App.vue
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
<template>
<div class="flex flex-row h-screen">
<sidebar class="flex-none"></sidebar>
<preview></preview>
<controls class="flex-none"></controls>
<div class="flex flex-col h-screen text-system-text">
<nav-bar v-if="isMacOS" class="flex-none"></nav-bar>
<div class="flex-1 flex flex-row items-stretch">
<sidebar class="flex-none"></sidebar>
<preview></preview>
<controls class="flex-none"></controls>
</div>
</div>
</template>

Expand All @@ -11,13 +14,21 @@ import { defineComponent } from 'vue';
import Preview from './components/Preview.vue';
import Sidebar from './components/Sidebar.vue';
import Controls from './components/Controls.vue';
import NavBar from './components/NavBar.vue';
import { useIsMacOS } from './integration/platform';
export default defineComponent({
name: 'App',
components: {
Preview,
Sidebar,
Controls,
NavBar,
},
setup() {
return {
isMacOS: useIsMacOS()
}
}
});
</script>
Expand Down
17 changes: 17 additions & 0 deletions src/Preferences.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<template>
<div>Preferences</div>
</template>

<script lang="ts">
import { defineComponent } from 'vue'
export default defineComponent({
name: 'Preferences',
})
</script>

<style>
@tailwind base;
@tailwind components;
@tailwind utilities;
</style>
69 changes: 33 additions & 36 deletions src/background.ts
Original file line number Diff line number Diff line change
@@ -1,52 +1,31 @@
'use strict'

import { app, protocol, BrowserWindow, ipcMain, Menu, IpcMainEvent, MenuItemConstructorOptions, MenuItem } from 'electron'
import { app, protocol, BrowserWindow } from 'electron'
import { createProtocol } from 'vue-cli-plugin-electron-builder/lib'
import installExtension, { VUEJS3_DEVTOOLS } from 'electron-devtools-installer'
import { installContextMenuHandling, configureApplicationMenu } from './electron/menus';
import emitter from './electron/events';
import { openPreferences } from './electron/preferences-window';
import { injectSystemColors } from './electron/colors';
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 } }
])

let mainWindow: BrowserWindow | null = null;

async function createWindow() {
// Create the browser window.
const win = new BrowserWindow({
width: 1050,
height: 600,
minHeight: 600,
minWidth: 1050,
show: false,
vibrancy: 'sidebar',
titleBarStyle: process.platform === 'darwin' ? 'hiddenInset' : 'default',
webPreferences: {

// Use pluginOptions.nodeIntegration, leave this alone
Expand All @@ -57,15 +36,23 @@ async function createWindow() {
}
})

win.once('ready-to-show', () => {
injectSystemColors(win);

win.show();
});

if (process.env.WEBPACK_DEV_SERVER_URL) {
// Load the url of the dev server if in development mode
await win.loadURL(process.env.WEBPACK_DEV_SERVER_URL as string)
if (!process.env.IS_TEST) win.webContents.openDevTools()
} else {
createProtocol('app')
// Load the index.html when not in development
win.loadURL('app://./index.html')
}

win.on('closed', () => { mainWindow = null; });

mainWindow = win;
}

// Quit when all windows are closed.
Expand All @@ -80,7 +67,7 @@ app.on('window-all-closed', () => {
app.on('activate', () => {
// On macOS it's common to re-create a window in the app when the
// dock icon is clicked and there are no other windows open.
if (BrowserWindow.getAllWindows().length === 0) createWindow()
if (!mainWindow) createWindow()
})

// This method will be called when Electron has finished
Expand All @@ -92,14 +79,24 @@ app.on('ready', async () => {
try {
await installExtension(VUEJS3_DEVTOOLS)
} catch (e) {
console.error('Vue Devtools failed to install:', e.toString())
console.error(`Vue Devtools failed to install: ${e}`)
}
}

if (!process.env.WEBPACK_DEV_SEVER_URL) {
createProtocol('app');
}

createWindow()

installMenus();
installContextMenuHandling();
configureApplicationMenu();
})

emitter.on('open-preferences', () => {
openPreferences();
});

// Exit cleanly on request from parent process in development mode.
if (isDevelopment) {
if (process.platform === 'win32') {
Expand Down
4 changes: 2 additions & 2 deletions src/components/Controls.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<template>
<div class="w-72 bg-gray-200 p-2">
<div class="w-72 bg-system-background-window p-2">
<div v-if="component">
<div v-for="(control, index) in controls" :key="index">
<component :is="control" :component="component"></component>
Expand All @@ -10,7 +10,7 @@

<script lang="ts">
import { Component } from '@/layout';
import { key } from '@/store'
import { key } from '@/store/app'
import { computed, defineComponent } from 'vue'
import { useStore } from 'vuex'
import ControlComponents from './Controls/Registry';
Expand Down
2 changes: 1 addition & 1 deletion src/components/Controls/FlexComponentControls.vue
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@

<script lang="ts">
import { FlexComponent } from '@/layout'
import { key } from '@/store'
import { key } from '@/store/app'
import { FLEX_SET_DIRECTION, FLEX_SET_DISTRIBUTION, FLEX_SET_SPACING } from '@/store/mutation-types'
import { computed, defineComponent, PropType, toRefs } from 'vue'
import { useStore } from 'vuex'
Expand Down
2 changes: 1 addition & 1 deletion src/components/Controls/InsetComponentControls.vue
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@

<script lang="ts">
import InsetComponent from '@/layout/InsetComponent';
import { key } from '@/store'
import { key } from '@/store/app'
import { INSET_SET_INSETS } from '@/store/mutation-types';
import { computed, defineComponent, PropType, toRefs } from 'vue'
import { useStore } from 'vuex'
Expand Down
2 changes: 1 addition & 1 deletion src/components/Controls/SourceComponentControls.vue
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@

<script lang="ts">
import SourceComponent from '@/layout/SourceComponent'
import { key } from '@/store'
import { key } from '@/store/app'
import { SOURCE_SET_SOURCE } from '@/store/mutation-types'
import { computed, defineComponent, PropType, toRefs } from 'vue'
import { useStore } from 'vuex'
Expand Down
6 changes: 6 additions & 0 deletions src/components/NavBar.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<template>
<div class="h-10 app-region-drag flex flex-row">
<div class="flex-none w-72"></div>
<div class="bg-gray-100 dark:bg-gray-800 w-full"></div>
</div>
</template>
93 changes: 62 additions & 31 deletions src/components/Preview.vue
Original file line number Diff line number Diff line change
@@ -1,54 +1,85 @@
<template>
<div class="flex-1 bg-gray-400">
<div class="flex flex-col h-screen p-4 justify-center items-center">
<div class="flex flex-row max-h-full w-full" style="aspect-ratio: 16 / 9">
<div class="bg-red-600 shadow-lg w-full relative" style="aspect-ratio: 16 / 9" v-if="component" v-observe-resize="canvasDidChangeSize" ref="canvas">
<node-view v-if="node" :node="node"/>
</div>
</div>
</div>
<div class="bg-gray-400 overflow-hidden w-full relative" v-observe-resize="didChangeSize">
<canvas ref="canvas" class='bg-yellow-100 absolute' width="1920" height="1080"></canvas>
</div>
</template>

<script lang="ts">
import { key } from '@/store'
import { SET_PREVIEW_SIZE } from '@/store/mutation-types'
import Size from '@/layout/Size'
import NodeView from './Preview/NodeView.vue';
import { computed, defineComponent, onMounted, ref } from 'vue'
import { key } from '@/store/app'
import { computed, defineComponent, ref, watch } from 'vue'
import { useStore } from 'vuex'
import { LayoutNode } from '@/layout'
import { Size } from '@/layout'
export default defineComponent({
name: 'Preview',
components: {
NodeView
},
setup() {
const store = useStore(key)
const canvas = ref<HTMLElement | null>(null)
const canvasDidChangeSize = (newSize: Size) => {
store.commit(SET_PREVIEW_SIZE, newSize)
const size = ref(new Size(1920, 1080));
const canvas = ref<HTMLCanvasElement | null>(null);
const didChangeSize = (newSize: Size) => {
let el = canvas.value
if (!el) { return; }
el.style.transformOrigin = '0 0';
let aspectRatio = 16 / 9;
let width = Math.min(newSize.width, newSize.height * aspectRatio);
let height = Math.min(newSize.height, newSize.width / aspectRatio);
if (newSize.width > newSize.height) {
el.style.transform = `scale(${width / 1920}, ${width / aspectRatio / 1080})`
el.style.left = ((newSize.width - width) / 2) + 'px';
el.style.top = ((newSize.height - (width / aspectRatio)) / 2) + 'px';
} else {
el.style.transform = `scale(${height * aspectRatio / 1920}, ${height / 1080})`
el.style.left = ((newSize.width - (height * aspectRatio)) / 2) + 'px';
el.style.top = ((newSize.height - height) / 2) + 'px';
}
}
onMounted(() => {
if (!canvas.value) {
return;
let node = computed(() => store.state.rootNode)
const renderNode = (node: LayoutNode, context: CanvasRenderingContext2D) => {
context.save();
if (node.isContainer) {
context.setLineDash([4, 2]);
} else {
context.setLineDash([]);
}
const computedStyle = window.getComputedStyle(canvas.value);
context.strokeRect(node.frame.x, node.frame.y, node.frame.width, node.frame.height);
context.translate(node.frame.x, node.frame.y);
node.children.forEach(node => renderNode(node, context));
context.restore();
}
const render = () => {
const rootNode = node.value;
if (!rootNode) { return; }
const context = canvas.value?.getContext('2d')
if (!context) { return }
context.clearRect(0, 0, 1920, 1080);
renderNode(rootNode, context);
}
const width = parseInt(computedStyle.width, 10);
const height = parseInt(computedStyle.height, 10);
watch(node, () => {
render();
})
canvasDidChangeSize(new Size(width, height));
});
watch(canvas, () => {
render();
})
return {
canvas,
canvasDidChangeSize,
node: computed(() => store.state.rootNode),
component: computed(() => store.state.rootComponent)
didChangeSize,
node,
size
}
}
})
Expand Down
5 changes: 2 additions & 3 deletions src/components/Sidebar.vue
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
<template>
<div class="w-72 p-2 bg-gray-200 h-screen">
<div class="w-72 bg-gray-200 macos:bg-transparent">
<tree-control v-if="component" :component="component"></tree-control>
</div>
</template>

<script lang="ts">
import { key } from '@/store';
import { key } from '@/store/app';
import { computed, defineComponent } from 'vue'
import { useStore } from 'vuex';
import TreeControl from './Sidebar/TreeControl.vue';
Expand All @@ -17,7 +17,6 @@ export default defineComponent({
},
setup() {
const store = useStore(key)
return {
component: computed(() => store.state.rootComponent)
}
Expand Down
Loading

0 comments on commit 9afd89a

Please sign in to comment.