summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/renderer/editor/library.tsx111
-rw-r--r--src/renderer/editor/types.ts23
-rw-r--r--src/renderer/editor/util.ts9
3 files changed, 90 insertions, 53 deletions
diff --git a/src/renderer/editor/library.tsx b/src/renderer/editor/library.tsx
index 4b7dac7..840bdc9 100644
--- a/src/renderer/editor/library.tsx
+++ b/src/renderer/editor/library.tsx
@@ -1,46 +1,65 @@
import * as React from 'react';
+const { useCallback, useMemo } = React;
import { makeStyles, Theme } from '@material-ui/core/styles';
import Box from '@material-ui/core/Box';
+import Button from '@material-ui/core/Button';
import Container from '@material-ui/core/Container';
-import { Tiling } from './types';
-import { useReadFile } from './util';
+import * as Color from 'color';
+import * as glob from 'fast-glob';
+
+import { Tiling, TilingMeta } from './types';
+import { usePromise, useReadFile, readJSON } from './util';
const useStyles = makeStyles((theme: Theme) => ({
- container: {
+ grid: {
display: 'flex',
flexWrap: 'wrap',
+ margin: theme.spacing(-0.5),
},
tile: {
imageRendering: 'pixelated',
- border: `solid 1px ${theme.palette.divider}`,
+ borderStyle: 'solid',
+ borderWidth: 1,
+ borderColor: theme.palette.divider,
background: theme.palette.background.paper,
+ position: 'relative',
+ width: 130,
+ height: 130,
+ margin: theme.spacing(0.5),
+ // cursor: 'pointer',
+ // '&:hover': {
+ // borderColor: theme.palette.text.secondary,
+ // boxShadow: `0 0 2px 1px ${theme.palette.text.secondary}`,
+ // },
+ },
+ img: {
+ position: 'absolute',
+ zIndex: 1,
width: 128,
height: 128,
- margin: 4,
+ },
+ label: {
+ position: 'absolute',
+ zIndex: 2,
+ left: 0,
+ right: 0,
+ bottom: 0,
+ height: '37.5%',
+ padding: theme.spacing(1),
+ background: Color(theme.palette.background.default)
+ .fade(0.3)
+ .string(),
},
}));
-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' },
- },
-];
+function tilingSprite(tiling: Tiling): string {
+ const x = (tiling.meta.width - 1) / 2;
+ const y = (tiling.meta.height - 1) / 2;
+ return `project/tiling/${tiling.id}/${x}_${y}.png`;
+}
interface TilingDisplayProps {
tiling: Tiling;
@@ -49,35 +68,53 @@ interface TilingDisplayProps {
function TilingDisplay({ tiling }: TilingDisplayProps): JSX.Element | null {
const classes = useStyles();
- let name: string;
+ const path = tilingSprite(tiling);
+ const image = useReadFile(path);
+ const src = useMemo(() => (image ? `data:image/png;base64,${image.toString('base64')}` : undefined), [image]);
+ return (
+ <div className={classes.tile}>
+ <img className={classes.img} src={src} />
+ <div className={classes.label}>{tiling.meta.name}</div>
+ </div>
+ );
+}
- switch (tiling.size) {
- case 1:
- name = tiling.sprite.name;
- break;
- case 3:
- name = tiling.sprites[1][1].name;
- break;
- }
+async function listTilings(): Promise<string[]> {
+ const matches = await glob('project/tiling/*/meta.json');
+ return matches.map((m) => m.split('/')[2]);
+}
- const path = `static/resources/sprite/tile/${name}.png`;
- const image = useReadFile(path, 'base64');
+async function loadTilingMeta(id: string): Promise<TilingMeta> {
+ const path = `project/tiling/${id}/meta.json`;
+ const meta = await readJSON(path);
+ return meta as TilingMeta;
+}
- return <img src={image ? `data:image/png;base64,${image}` : undefined} className={classes.tile} />;
+async function loadTilings(): Promise<Tiling[]> {
+ const tilings = await listTilings();
+ return Promise.all(
+ tilings.map((id) => [id, loadTilingMeta(id)] as const).map(async ([id, p]) => ({ id, meta: await p })),
+ );
}
export function Library(): JSX.Element {
const classes = useStyles();
+ const mkLoadTilings = useCallback(() => loadTilings(), []);
+ const tilings = usePromise(mkLoadTilings) ?? [];
+
return (
<Container>
- <Box display='inline-block'>
- <div className={classes.container}>
+ <Box mb={2}>
+ <div className={classes.grid}>
{tilings.map((tiling, i) => (
<TilingDisplay key={i} tiling={tiling} />
))}
</div>
</Box>
+ <Button variant='contained' color='primary'>
+ Add
+ </Button>
</Container>
);
}
diff --git a/src/renderer/editor/types.ts b/src/renderer/editor/types.ts
index 95e1bd2..4235bf4 100644
--- a/src/renderer/editor/types.ts
+++ b/src/renderer/editor/types.ts
@@ -1,16 +1,11 @@
-export type Tuple3<T> = [T, T, T];
-export type Grid3x3<T> = Tuple3<Tuple3<T>>;
-
-export type Sprite = {
+export interface TilingMeta {
name: string;
-};
+ desc?: string;
+ width: number;
+ height: number;
+}
-export type Tiling =
- | {
- size: 1;
- sprite: Sprite;
- }
- | {
- size: 3;
- sprites: Grid3x3<Sprite>;
- };
+export interface Tiling {
+ id: string;
+ meta: TilingMeta;
+}
diff --git a/src/renderer/editor/util.ts b/src/renderer/editor/util.ts
index b2331bb..f816de4 100644
--- a/src/renderer/editor/util.ts
+++ b/src/renderer/editor/util.ts
@@ -25,7 +25,12 @@ export function usePromise<T>(f: () => Promise<T>): T | null {
return value;
}
-export function useReadFile(path: string, encoding: BufferEncoding): string | null {
- const readFile = useCallback(() => fs.promises.readFile(path, encoding), [path, encoding]);
+export function useReadFile(path: string): Buffer | null {
+ const readFile = useCallback(() => fs.promises.readFile(path), [path]);
return usePromise(readFile);
}
+
+export async function readJSON(path: string): Promise<unknown> {
+ const content = await fs.promises.readFile(path, 'utf8');
+ return JSON.parse(content);
+}