Changed layout, three-column

This commit is contained in:
2024-05-03 21:08:14 -05:00
parent 473d7bd88c
commit 607cfe3b9f
2 changed files with 86 additions and 50 deletions
+69 -18
View File
@@ -1,4 +1,4 @@
import React, { useState, useCallback, useRef } from 'react' import React, {useState, useCallback, useRef, useEffect} from 'react'
import ForgeEditor from "./forge_editor" import ForgeEditor from "./forge_editor"
import EmulatorDisplay from "./emulator_display" import EmulatorDisplay from "./emulator_display"
import { WasmCPU, assemble_snippet, compile_forge } from '../pkg/vweb.js' import { WasmCPU, assemble_snippet, compile_forge } from '../pkg/vweb.js'
@@ -14,17 +14,42 @@ export default function({ src: defaultSrc }) {
// Whether the emulator should be running. Has to be a ref because the CPU setTimeout loop won't ever see changes in it otherwise // Whether the emulator should be running. Has to be a ref because the CPU setTimeout loop won't ever see changes in it otherwise
const running = useRef(false) const running = useRef(false)
const [currentFile, setCurrentFile] = useState(() => (Object.keys(window.localStorage)[0] || 'example'))
useEffect(() => {
if (!currentFile) {
setCurrentFile('example')
window.localStorage.setItem('example', defaultSrc)
}
}, [])
const selectFile = useCallback((name) => {
setSrc(window.localStorage.getItem(name))
setCurrentFile(name)
}, [])
const addFile = useCallback(() => {
const fileList = Object.keys(window.localStorage)
let name = null
while(!name || fileList.indexOf(name) >= 0) {
name = prompt('Create new file named:')
}
window.localStorage[name] = ''
selectFile(name)
}, [])
const removeFile = useCallback((name) => {}, [])
// The current Forge source. Our initial state tries to look something up from localStorage, // The current Forge source. Our initial state tries to look something up from localStorage,
// then if that fails uses the prop. // then if that fails uses the prop.
const [src, setSrc] = useState(() => { const [src, setSrc] = useState(() => {
return window.localStorage.getItem('src') || undent(defaultSrc) return window.localStorage.getItem(currentFile) || undent(defaultSrc)
}) })
// When we update the src, also update the key in localStorage // When we update the src, also update the key in localStorage
const updateSrc = useCallback((newSrc) => { const updateSrc = useCallback((newSrc) => {
setSrc(newSrc) setSrc(newSrc)
window.localStorage.setItem('src', newSrc) window.localStorage.setItem(currentFile, newSrc)
}, []) }, [currentFile])
// Callback for the build button // Callback for the build button
const build = useCallback(() => { const build = useCallback(() => {
@@ -71,21 +96,42 @@ export default function({ src: defaultSrc }) {
setStatus('Stopped by user') setStatus('Stopped by user')
}, []) }, [])
let content;
if (activeTab === 'assembly') {
content = <pre>{assembly}</pre>
} else if (activeTab === 'errors') {
content = <pre>{errors}</pre>
} else if (activeTab === 'editor') {
content = <ForgeEditor build={build} run={run} updateSrc={updateSrc} src={src} fileName={currentFile}/>
}
return ( return (
<> <>
<Tabbar activeTab={activeTab} setActiveTab={setActiveTab} anyErrors={!!errors}/> <Tabbar activeTab={activeTab} setActiveTab={setActiveTab} anyErrors={!!errors}/>
<Toolbar activeTab={activeTab} running={running.current} build={build} run={run} stop={stop}/> <Toolbar activeTab={activeTab} running={running.current} build={build} run={run} stop={stop} addFile={addFile} removeFile={removeFile}/>
<div className='content'> <FileList fileList={Object.keys(window.localStorage)} selectFile={selectFile}/>
{activeTab === 'editor' && <ForgeEditor build={build} run={run} updateSrc={updateSrc} src={src}/>} <div className='content'>{content}</div>
{activeTab === 'assembly' && <pre>{assembly}</pre>} <EmulatorDisplay cpu={cpu}/>
{activeTab === 'display' && <EmulatorDisplay cpu={cpu}/>}
{activeTab === 'errors' && <pre>{errors}</pre>}
</div>
<Status message={status}/> <Status message={status}/>
</> </>
) )
} }
// A list of "files" stored in localStorage, that we can display / build / compile
function FileList({ fileList, selectFile }) {
const clickFile = useCallback((event) => {
const name = event.target.getAttribute('data-name')
selectFile(name)
event.preventDefault()
}, [selectFile])
const fileRows = fileList.map(file => <div className='file' onClick={clickFile} data-name={file} key={file}>{file}</div>)
return (
<div className='files'>
{fileRows}
</div>
)
}
// A tabbar of the various pages in the simulator // A tabbar of the various pages in the simulator
function Tabbar({ activeTab, setActiveTab, anyErrors }) { function Tabbar({ activeTab, setActiveTab, anyErrors }) {
let onChangeTab = useCallback((event) => { let onChangeTab = useCallback((event) => {
@@ -97,7 +143,6 @@ function Tabbar({ activeTab, setActiveTab, anyErrors }) {
return ( return (
<div className='tabbar'> <div className='tabbar'>
<a className={classNames('editor')} onClick={onChangeTab} data-tab='editor'>[Editor]</a> <a className={classNames('editor')} onClick={onChangeTab} data-tab='editor'>[Editor]</a>
<a className={classNames('display')} onClick={onChangeTab} data-tab='display'>[Display]</a>
<a className={classNames('assembly')} onClick={onChangeTab} data-tab='assembly'>[Assembly]</a> <a className={classNames('assembly')} onClick={onChangeTab} data-tab='assembly'>[Assembly]</a>
{anyErrors && <a className={classNames('errors')} onClick={onChangeTab} data-tab='errors'>[Errors]</a>} {anyErrors && <a className={classNames('errors')} onClick={onChangeTab} data-tab='errors'>[Errors]</a>}
</div> </div>
@@ -105,7 +150,7 @@ function Tabbar({ activeTab, setActiveTab, anyErrors }) {
} }
// Toolbar of the controls for the simulator // Toolbar of the controls for the simulator
function Toolbar({ activeTab, running, compile, build, run, stop }) { function Toolbar({activeTab, running, compile, build, run, stop, addFile, removeFile }) {
let buildBtn, runBtn let buildBtn, runBtn
// TODO: Make this assemble on assembly tab, make that editable // TODO: Make this assemble on assembly tab, make that editable
buildBtn = <a className='build' onClick={build}>[Build]</a> buildBtn = <a className='build' onClick={build}>[Build]</a>
@@ -117,14 +162,20 @@ function Toolbar({ activeTab, running, compile, build, run, stop }) {
} }
return ( return (
<div className='buttons'> <>
{buildBtn} <div className='file-buttons'>
{runBtn} <a className='new' onClick={addFile}>[new]</a>
</div> <a className='del' onClick={removeFile}>[del]</a>
</div>
<div className='buttons'>
{buildBtn}
{runBtn}
</div>
</>
) )
} }
// The short status line at the bottom // The short status line at the bottom
function Status({ message }) { function Status({message}) {
return <div className='status'>{message}</div> return <div className='status'>{message}</div>
} }
+17 -32
View File
@@ -10,18 +10,19 @@ a { color: #cccccc; }
.simulator { .simulator {
display: grid; display: grid;
grid-template: grid-template:
"tab" 1em "filebtns tab btns" 1em
"btns" 1em "files content screen " 1fr
"content" 1fr "files status screen" 1em / 16em 1fr calc(640px + 3em);
"status" 1em / 1fr;
row-gap: 0.3em; row-gap: 0.3em;
} }
/* Setting where things go in the grid */ /* Setting where things go in the grid */
.tabbar { grid-area: tab } .tabbar { grid-area: tab }
.buttons { grid-area: btns } .buttons { grid-area: btns }
.file-buttons { grid-area: filebtns }
.content { grid-area: content } .content { grid-area: content }
.status { grid-area: status } .status { grid-area: status }
.files { grid-area: files }
/* Make the content fill the remainder of the screen */ /* Make the content fill the remainder of the screen */
.content { .content {
@@ -40,48 +41,32 @@ a { color: #cccccc; }
height: calc(100vh - 5em) height: calc(100vh - 5em)
} }
/*!* Make the textarea look presentable (TODO: replace with CodeMirror) *!*/
/*.content textarea {*/
/* height: 100%;*/
/* width: 100%;*/
/* padding: 0;*/
/* margin: 0;*/
/* background: #000000;*/
/* color: #a0a0a0;*/
/* border: none;*/
/* resize: none;*/
/* outline: none;*/
/*}*/
/*.forge-editor textarea:focus {*/
/* border: none;*/
/* outline: none;*/
/*}*/
/* Status, btns, tabs all look right */ /* Status, btns, tabs all look right */
.status, .buttons, .tabbar { font-weight: bold; } .status, .buttons, .file-buttons, .tabbar { font-weight: bold; }
.tabbar a, .buttons a { cursor: pointer; } .tabbar a, .buttons a, .file-buttons a, .file { cursor: pointer; }
/* Make the display the right size by centering the canvas in a rectangle*/ /* Make the display the right size by centering the canvas in a rectangle */
/* The display is in the screen section of the main grid but it's also got to define its own grid */
.display { .display {
grid-area: screen;
height: 100%; height: 100%;
display: grid; display: grid;
grid-template: grid-template:
"lt top rt" 1fr "lt top rt" 1fr
"lt screen rt" calc(480px + 4em) "lt screen rt" calc(480px + 2em)
"lt btm rt" 1fr / 1fr calc(640px + 4em) 1fr; "lt btm rt" 1fr / 1fr calc(640px + 2em) 1fr;
} }
/* The beige monitor border around the screen */ /* The beige monitor border around the screen */
.display { background-color: #0f0f0f } .display { background-color: #0f0f0f }
.display canvas { .display canvas {
grid-area: screen; grid-area: screen;
border: 2em solid #bb9; border: 1em solid #bb9;
border-radius: 2em; border-radius: 1em;
} }
/* Colors of tabs and buttons */ /* Colors of tabs and buttons */
.tabbar a.active { background-color: #686868; } .tabbar a.active { background-color: #686868; }
.buttons .run { color: #66bb66 } .buttons .run, .file-buttons .new { color: #66bb66 }
.buttons .stop { color: #bb6666 } .buttons .stop, .file-buttons .del { color: #bb6666 }
.buttons .build { color: #aaaaff; } .buttons .build { color: #aaaaff; }