Skip to content

Commit

Permalink
loading data via electron
Browse files Browse the repository at this point in the history
  • Loading branch information
agmcleod committed Nov 10, 2019
1 parent cb84439 commit e1f9fd9
Show file tree
Hide file tree
Showing 7 changed files with 218 additions and 175 deletions.
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
"@material-ui/core": "^4.5.1",
"axios": "^0.18.1",
"formik": "^1.5.8",
"lodash": "^4.17.15",
"react": "^16.8.6",
"react-dom": "^16.8.6",
"react-redux": "^7.1.1",
Expand All @@ -16,6 +17,7 @@
"redux-starter-kit": "^0.7.0",
"redux-thunk": "^2.3.0",
"styled-components": "^4.2.0",
"xml-js": "^1.6.11",
"yup": "^0.27.0"
},
"scripts": {
Expand Down
5 changes: 3 additions & 2 deletions src/App.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React from 'react'
import { Provider } from 'react-redux'

import { ELECTRON_EVENTS } from 'common/constants'
import { store } from 'common/store'
import { Main } from './core/Main'

Expand All @@ -12,9 +13,9 @@ export const App = () => {
)
}

window.ipcRenderer.on('save-requested', (ev, filePath) => {
window.ipcRenderer.on(ELECTRON_EVENTS.save_requested, (ev, filePath) => {
const { components } = store.getState()
window.ipcRenderer.send('save-data', {
window.ipcRenderer.send(ELECTRON_EVENTS.save_data, {
data: JSON.stringify({ components }),
filePath
})
Expand Down
14 changes: 13 additions & 1 deletion src/common/constants.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,19 @@
export const FIELD_TYPES = {
const FIELD_TYPES = {
array_string: 'array_string',
array_number: 'array_number',
number: 'number',
string: 'string',
boolean: 'boolean'
}

const ELECTRON_EVENTS = {
save_data: 'save-data',
save_requested: 'save-requested',
import_map: 'import-map',
import_map_success: 'import-map-success'
}

module.exports = {
FIELD_TYPES,
ELECTRON_EVENTS
}
139 changes: 50 additions & 89 deletions src/common/parseMapData.js
Original file line number Diff line number Diff line change
@@ -1,89 +1,50 @@
export const parseMapData = (fileField, callback) => {
const tmxReader = new FileReader()

const images = []
let tmxFile = ''

for (const file of fileField.current.files) {
const { name, type } = file
if (/\.tmx$/.test(name)) {
tmxFile = file
} else if (/^image/.test(type)) {
images.push(file)
} else {
console.log('unrecognized file', file)
}
}

if (tmxFile) {
tmxReader.readAsText(tmxFile)
tmxReader.onload = f => {
const xml = f.target.result
const parser = new DOMParser()
const xmlDoc = parser.parseFromString(xml, 'text/xml')
const map = xmlDoc.getElementsByTagName('map')[0]

// assuming one tileset for a given level
const tileset = map.getElementsByTagName('tileset')[0]
const imageTag = tileset.getElementsByTagName('image')[0]
const imageFile = images.find(
image => image.name === imageTag.getAttribute('source')
)
if (!imageFile) {
alert(
`Tileset ${imageTag.getAttribute(
'source'
)} not found in uploaded files`
)

return
}

const imageReader = new FileReader()
imageReader.readAsDataURL(imageFile)
const tilesetImage = new Image()
imageReader.onload = f => {
tilesetImage.src = f.target.result
const layers = map.getElementsByTagName('layer')

const layerData = []

for (const layer of layers) {
layerData.push({
id: parseInt(layer.getAttribute('id'), 10),
name: layer.getAttribute('name'),
width: parseInt(layer.getAttribute('width'), 10),
height: parseInt(layer.getAttribute('height'), 10),
data: layer
.getElementsByTagName('data')[0]
.textContent.replace('\n', '')
.split(',')
.map(v => parseInt(v, 10))
})
}

const mapData = {
width: parseInt(map.getAttribute('width'), 10),
height: parseInt(map.getAttribute('height'), 10),
tileWidth: parseInt(map.getAttribute('tilewidth'), 10),
tileHeight: parseInt(map.getAttribute('tileheight'), 10),
tileset: {
firstgid: parseInt(tileset.getAttribute('firstgid'), 10),
name: tileset.getAttribute('name'),
tileWidth: parseInt(tileset.getAttribute('tilewidth'), 10),
tileHeight: parseInt(tileset.getAttribute('tileheight'), 10),
spacing: parseInt(tileset.getAttribute('spacing'), 10),
margin: parseInt(tileset.getAttribute('margin'), 10),
tilecount: parseInt(tileset.getAttribute('tilecount'), 10),
columns: parseInt(tileset.getAttribute('columns'), 10)
},
layers: layerData
}

callback(null, mapData, tilesetImage)
}

imageReader.onerror = e => callback(e)
}
}
}
export const parseMapData = (xml, images, callback) => {
if (xml) {
const parser = new DOMParser()
const xmlDoc = parser.parseFromString(xml, 'text/xml')
const map = xmlDoc.getElementsByTagName('map')[0]

// assuming one tileset for a given level
const tileset = map.getElementsByTagName('tileset')[0]

const tilesetImage = new Image()
tilesetImage.src = images[0].data
const layers = map.getElementsByTagName('layer')

const layerData = []

for (const layer of layers) {
layerData.push({
id: parseInt(layer.getAttribute('id'), 10),
name: layer.getAttribute('name'),
width: parseInt(layer.getAttribute('width'), 10),
height: parseInt(layer.getAttribute('height'), 10),
data: layer
.getElementsByTagName('data')[0]
.textContent.replace('\n', '')
.split(',')
.map(v => parseInt(v, 10))
})
}

const mapData = {
width: parseInt(map.getAttribute('width'), 10),
height: parseInt(map.getAttribute('height'), 10),
tileWidth: parseInt(map.getAttribute('tilewidth'), 10),
tileHeight: parseInt(map.getAttribute('tileheight'), 10),
tileset: {
firstgid: parseInt(tileset.getAttribute('firstgid'), 10),
name: tileset.getAttribute('name'),
tileWidth: parseInt(tileset.getAttribute('tilewidth'), 10),
tileHeight: parseInt(tileset.getAttribute('tileheight'), 10),
spacing: parseInt(tileset.getAttribute('spacing'), 10),
margin: parseInt(tileset.getAttribute('margin'), 10),
tilecount: parseInt(tileset.getAttribute('tilecount'), 10),
columns: parseInt(tileset.getAttribute('columns'), 10)
},
layers: layerData
}

callback(null, mapData, tilesetImage)
}
}
150 changes: 78 additions & 72 deletions src/core/Main/Main.js
Original file line number Diff line number Diff line change
@@ -1,72 +1,78 @@
import React, { useState } from 'react'
import Container from '@material-ui/core/Container'
import Button from '@material-ui/core/Button'
import Tabs from '@material-ui/core/Tabs'
import Tab from '@material-ui/core/Tab'

import { renderMap } from 'common/renderMap'
import { parseMapData } from 'common/parseMapData'
import { TabPanel } from 'common/components/TabPanel'
import { Components } from './Components'
import { FileInput } from './styledComponents'

export const Main = () => {
const fileField = React.createRef()
const canvas = React.createRef()

const [currentTab, setTab] = useState(0)

return (
<Container>
<h1>Level</h1>
<div>
<label htmlFor='file'>Select .tmx & corresponding tileset images</label>
<FileInput
id='file'
type='file'
ref={fileField}
multiple
onChange={() =>
parseMapData(fileField, (err, mapData, tilesetImage) => {
if (err) {
console.error(err)
} else {
const canvas = document.querySelector('#canvas')
canvas.width = mapData.width * mapData.tileWidth
canvas.height = mapData.height * mapData.tileHeight

const ctx = canvas.getContext('2d')
ctx.fillStyle = '#ccc'
ctx.fillRect(0, 0, canvas.width, canvas.height)

tilesetImage.onload = () =>
renderMap(ctx, mapData, tilesetImage)
}
})
}
/>
<label htmlFor='file'>
<Button variant='contained' component='span'>
Upload
</Button>
</label>
</div>
<Tabs
value={currentTab}
indicatorColor='primary'
textColor='primary'
centered
onChange={(_, newValue) => setTab(newValue)}
>
<Tab label='scene' />
<Tab label='components' />
</Tabs>
<TabPanel index={0} value={currentTab}>
<canvas id='canvas' ref={canvas} />
</TabPanel>
<TabPanel index={1} value={currentTab}>
<Components />
</TabPanel>
</Container>
)
}
import React, { useEffect, useState } from 'react'
import Container from '@material-ui/core/Container'
import Button from '@material-ui/core/Button'
import Tabs from '@material-ui/core/Tabs'
import Tab from '@material-ui/core/Tab'

import { ELECTRON_EVENTS } from 'common/constants'
import { renderMap } from 'common/renderMap'
import { parseMapData } from 'common/parseMapData'
import { TabPanel } from 'common/components/TabPanel'
import { Components } from './Components'

export const Main = () => {
const canvas = React.createRef()

const [currentTab, setTab] = useState(0)

useEffect(() => {
const listener = (ev, mapData, images) => {
console.log('mapData', mapData, 'images', images)
parseMapData(mapData, images, (err, mapData, tilesetImage) => {
if (err) {
console.error(err)
} else {
const canvas = document.querySelector('#canvas')
canvas.width = mapData.width * mapData.tileWidth
canvas.height = mapData.height * mapData.tileHeight

const ctx = canvas.getContext('2d')
ctx.fillStyle = '#ccc'
ctx.fillRect(0, 0, canvas.width, canvas.height)

tilesetImage.onload = () => renderMap(ctx, mapData, tilesetImage)
}
})
}

window.ipcRenderer.on(ELECTRON_EVENTS.import_map_success, listener)

return () =>
window.ipcRenderer.removeListener(
ELECTRON_EVENTS.import_map_success,
listener
)
}, [])

return (
<Container>
<h1>Level</h1>
<div>
<label htmlFor='file'>Select .tmx file</label>
<Button
variant='contained'
component='span'
onClick={() => window.ipcRenderer.send(ELECTRON_EVENTS.import_map)}
>
Select
</Button>
</div>
<Tabs
value={currentTab}
indicatorColor='primary'
textColor='primary'
centered
onChange={(_, newValue) => setTab(newValue)}
>
<Tab label='scene' />
<Tab label='components' />
</Tabs>
<TabPanel index={0} value={currentTab}>
<canvas id='canvas' ref={canvas} />
</TabPanel>
<TabPanel index={1} value={currentTab}>
<Components />
</TabPanel>
</Container>
)
}
Loading

0 comments on commit e1f9fd9

Please sign in to comment.