summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorMatthias Schiffer <mschiffer@universe-factory.net>2020-03-19 23:12:13 +0100
committerMatthias Schiffer <mschiffer@universe-factory.net>2020-03-19 23:12:13 +0100
commit1605e743f44b835e99b3403cb3cfc5018b697d26 (patch)
treea7d05b20bfa587a5f0cc448d66b2fc7ad392b974 /src
parent6c8f2d780acd6c41d42de6b5fc8d84e765334b64 (diff)
downloadrpgedit-1605e743f44b835e99b3403cb3cfc5018b697d26.tar
rpgedit-1605e743f44b835e99b3403cb3cfc5018b697d26.zip
editor: experiment with Material UI for sprite library implementation
Diffstat (limited to 'src')
-rw-r--r--src/renderer/editor/editor.tsx62
-rw-r--r--src/renderer/editor/library.tsx83
-rw-r--r--src/renderer/editor/types.ts16
-rw-r--r--src/renderer/editor/util.ts31
-rw-r--r--src/renderer/index.html6
5 files changed, 194 insertions, 4 deletions
diff --git a/src/renderer/editor/editor.tsx b/src/renderer/editor/editor.tsx
index fd8360a..2c57afc 100644
--- a/src/renderer/editor/editor.tsx
+++ b/src/renderer/editor/editor.tsx
@@ -1,10 +1,64 @@
import * as React from 'react';
-import Button from '@material-ui/core/Button';
+
+import 'typeface-roboto';
+
+import { createMuiTheme, makeStyles, Theme, ThemeProvider } from '@material-ui/core/styles';
+
+import CssBaseline from '@material-ui/core/CssBaseline';
+import Drawer from '@material-ui/core/Drawer';
+
+import { Library } from './library';
+
+const drawerWidth = 240;
+
+const useStyles = makeStyles((theme: Theme) => ({
+ root: {
+ display: 'flex',
+ },
+ drawer: {
+ width: drawerWidth,
+ flexShrink: 0,
+ },
+ drawerPaper: {
+ width: drawerWidth,
+ },
+ content: {
+ flexGrow: 1,
+ padding: theme.spacing(3),
+ },
+}));
+
+function EditorLayout(): JSX.Element {
+ const classes = useStyles();
+
+ return (
+ <div className={classes.root}>
+ <CssBaseline />
+ <Drawer
+ className={classes.drawer}
+ variant='permanent'
+ classes={{
+ paper: classes.drawerPaper,
+ }}
+ anchor='left'
+ ></Drawer>
+ <main className={classes.content}>
+ <Library />
+ </main>
+ </div>
+ );
+}
+
+const theme = createMuiTheme({
+ palette: {
+ type: 'dark',
+ },
+});
export function Editor(): JSX.Element {
return (
- <Button variant='contained' color='primary'>
- Hello World
- </Button>
+ <ThemeProvider theme={theme}>
+ <EditorLayout />
+ </ThemeProvider>
);
}
diff --git a/src/renderer/editor/library.tsx b/src/renderer/editor/library.tsx
new file mode 100644
index 0000000..4b7dac7
--- /dev/null
+++ b/src/renderer/editor/library.tsx
@@ -0,0 +1,83 @@
+import * as React from 'react';
+
+import { makeStyles, Theme } from '@material-ui/core/styles';
+
+import Box from '@material-ui/core/Box';
+import Container from '@material-ui/core/Container';
+
+import { Tiling } from './types';
+import { useReadFile } from './util';
+
+const useStyles = makeStyles((theme: Theme) => ({
+ container: {
+ display: 'flex',
+ flexWrap: 'wrap',
+ },
+ tile: {
+ imageRendering: 'pixelated',
+ border: `solid 1px ${theme.palette.divider}`,
+ background: theme.palette.background.paper,
+ width: 128,
+ height: 128,
+ margin: 4,
+ },
+}));
+
+const tilings: Tiling[] = [
+ {
+ size: 1,
+ sprite: { name: 'dirt' },
+ },
+ {
+ size: 1,
+ sprite: { name: 'grass' },
+ },
+ {
+ size: 1,
+ sprite: { name: 'road_left' },
+ },
+ {
+ size: 1,
+ sprite: { name: 'road_right' },
+ },
+];
+
+interface TilingDisplayProps {
+ tiling: Tiling;
+}
+
+function TilingDisplay({ tiling }: TilingDisplayProps): JSX.Element | null {
+ const classes = useStyles();
+
+ let name: string;
+
+ switch (tiling.size) {
+ case 1:
+ name = tiling.sprite.name;
+ break;
+ case 3:
+ name = tiling.sprites[1][1].name;
+ break;
+ }
+
+ const path = `static/resources/sprite/tile/${name}.png`;
+ const image = useReadFile(path, 'base64');
+
+ return <img src={image ? `data:image/png;base64,${image}` : undefined} className={classes.tile} />;
+}
+
+export function Library(): JSX.Element {
+ const classes = useStyles();
+
+ return (
+ <Container>
+ <Box display='inline-block'>
+ <div className={classes.container}>
+ {tilings.map((tiling, i) => (
+ <TilingDisplay key={i} tiling={tiling} />
+ ))}
+ </div>
+ </Box>
+ </Container>
+ );
+}
diff --git a/src/renderer/editor/types.ts b/src/renderer/editor/types.ts
new file mode 100644
index 0000000..95e1bd2
--- /dev/null
+++ b/src/renderer/editor/types.ts
@@ -0,0 +1,16 @@
+export type Tuple3<T> = [T, T, T];
+export type Grid3x3<T> = Tuple3<Tuple3<T>>;
+
+export type Sprite = {
+ name: string;
+};
+
+export type Tiling =
+ | {
+ size: 1;
+ sprite: Sprite;
+ }
+ | {
+ size: 3;
+ sprites: Grid3x3<Sprite>;
+ };
diff --git a/src/renderer/editor/util.ts b/src/renderer/editor/util.ts
new file mode 100644
index 0000000..b2331bb
--- /dev/null
+++ b/src/renderer/editor/util.ts
@@ -0,0 +1,31 @@
+import { useCallback, useEffect, useState } from 'react';
+
+import * as fs from 'fs';
+
+export function usePromise<T>(f: () => Promise<T>): T | null {
+ const [value, setValue] = useState<T | null>(null);
+
+ useEffect(() => {
+ setValue(null);
+
+ let cancelled = false;
+
+ (async (): Promise<void> => {
+ const v = await f();
+ if (!cancelled) {
+ setValue(v);
+ }
+ })();
+
+ return (): void => {
+ cancelled = true;
+ };
+ }, [f]);
+
+ return value;
+}
+
+export function useReadFile(path: string, encoding: BufferEncoding): string | null {
+ const readFile = useCallback(() => fs.promises.readFile(path, encoding), [path, encoding]);
+ return usePromise(readFile);
+}
diff --git a/src/renderer/index.html b/src/renderer/index.html
index e775d3e..3dce6f3 100644
--- a/src/renderer/index.html
+++ b/src/renderer/index.html
@@ -5,6 +5,12 @@
<meta http-equiv="Content-Security-Policy" content="script-src 'self' 'unsafe-inline';" />
<meta name="viewport" content="minimum-scale=1, initial-scale=1, width=device-width" />
<title>RPGedit</title>
+ <style>
+ html, body, #app {
+ height: 100%;
+ overflow: hidden;
+ }
+ </style>
</head>
<body>
<div id="app"></div>