Fix copy paste outside of screen (#143)
This commit is contained in:
parent
c0ff1e8129
commit
087dbd3602
|
@ -46,7 +46,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div id="display">
|
<div id="display">
|
||||||
<div class="overscan">
|
<div class="overscan">
|
||||||
<canvas id="screen" width="592" height="416"></canvas>
|
<canvas id="screen" width="592" height="416" tabindex="-1"></canvas>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="inset">
|
<div class="inset">
|
||||||
|
@ -265,7 +265,7 @@
|
||||||
</button>
|
</button>
|
||||||
</header>
|
</header>
|
||||||
<main class="modal__content" id="printer-modal-content">
|
<main class="modal__content" id="printer-modal-content">
|
||||||
<div class="paper"></div>
|
<div class="paper" tabindex="-1"></div>
|
||||||
</main>
|
</main>
|
||||||
<footer class="modal__footer">
|
<footer class="modal__footer">
|
||||||
<a id="raw_printer_output" class="button">Download Raw Output</a>
|
<a id="raw_printer_output" class="button">Download Raw Output</a>
|
||||||
|
|
|
@ -46,7 +46,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div id="display">
|
<div id="display">
|
||||||
<div class="overscan">
|
<div class="overscan">
|
||||||
<canvas id="screen" width="592" height="416"></canvas>
|
<canvas id="screen" width="592" height="416" tabindex="-1"></canvas>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="inset">
|
<div class="inset">
|
||||||
|
@ -270,7 +270,7 @@
|
||||||
</button>
|
</button>
|
||||||
</header>
|
</header>
|
||||||
<main class="modal__content" id="printer-modal-content">
|
<main class="modal__content" id="printer-modal-content">
|
||||||
<div class="paper"></div>
|
<div class="paper" tabindex="-1"></div>
|
||||||
</main>
|
</main>
|
||||||
<footer class="modal__footer">
|
<footer class="modal__footer">
|
||||||
<a id="raw_printer_output" class="button">Download Raw Output</a>
|
<a id="raw_printer_output" class="button">Download Raw Output</a>
|
||||||
|
|
|
@ -9,6 +9,7 @@ img {
|
||||||
|
|
||||||
#badge {
|
#badge {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
user-select: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
#subtitle {
|
#subtitle {
|
||||||
|
@ -18,6 +19,7 @@ img {
|
||||||
font-family: "Adobe Garamond Pro",Garamond,Times;
|
font-family: "Adobe Garamond Pro",Garamond,Times;
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
|
user-select: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.motter {
|
.motter {
|
||||||
|
@ -135,6 +137,7 @@ body {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
|
user-select: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.code {
|
.code {
|
||||||
|
@ -180,6 +183,7 @@ th {
|
||||||
color: #0f0;
|
color: #0f0;
|
||||||
border: 2px inset #888;
|
border: 2px inset #888;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
|
user-select: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
canvas {
|
canvas {
|
||||||
|
@ -308,6 +312,7 @@ canvas {
|
||||||
padding: 5px 11px;
|
padding: 5px 11px;
|
||||||
border: 1px outset #66594E;
|
border: 1px outset #66594E;
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
|
user-select: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.modal__title {
|
.modal__title {
|
||||||
|
@ -338,28 +343,34 @@ canvas {
|
||||||
|
|
||||||
.modal__footer {
|
.modal__footer {
|
||||||
text-align: right;
|
text-align: right;
|
||||||
|
user-select: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
button {
|
button,
|
||||||
|
a.button {
|
||||||
background: #44372C;
|
background: #44372C;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
padding: 2px 8px;
|
padding: 2px 8px;
|
||||||
border: 1px outset #66594E;
|
border: 1px outset #66594E;
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
font-size: 15px;
|
font-size: 15px;
|
||||||
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
button:hover {
|
button:hover,
|
||||||
|
a.button:hover {
|
||||||
background-color: #55473D;
|
background-color: #55473D;
|
||||||
border: 1px outset #66594E;
|
border: 1px outset #66594E;
|
||||||
}
|
}
|
||||||
|
|
||||||
button:active {
|
button:active,
|
||||||
|
a.button:active {
|
||||||
background-color: #22150A;
|
background-color: #22150A;
|
||||||
border: 1px outset #44372C;
|
border: 1px outset #44372C;
|
||||||
}
|
}
|
||||||
|
|
||||||
button:focus {
|
button:focus,
|
||||||
|
a.button:hover {
|
||||||
outline: none;
|
outline: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -47,7 +47,7 @@ export interface Apple2Props {
|
||||||
*/
|
*/
|
||||||
export const Apple2 = (props: Apple2Props) => {
|
export const Apple2 = (props: Apple2Props) => {
|
||||||
const { e, enhanced, sectors } = props;
|
const { e, enhanced, sectors } = props;
|
||||||
const screen = useRef<HTMLCanvasElement>(null);
|
const screenRef = useRef<HTMLCanvasElement>(null);
|
||||||
const [apple2, setApple2] = useState<Apple2Impl>();
|
const [apple2, setApple2] = useState<Apple2Impl>();
|
||||||
const [error, setError] = useState<unknown>();
|
const [error, setError] = useState<unknown>();
|
||||||
const [ready, setReady] = useState(false);
|
const [ready, setReady] = useState(false);
|
||||||
|
@ -60,6 +60,12 @@ export const Apple2 = (props: Apple2Props) => {
|
||||||
const rom = apple2?.getROM();
|
const rom = apple2?.getROM();
|
||||||
|
|
||||||
const doPaste = useCallback((event: Event) => {
|
const doPaste = useCallback((event: Event) => {
|
||||||
|
if (
|
||||||
|
(document.activeElement !== screenRef.current) &&
|
||||||
|
(document.activeElement !== document.body)
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (io) {
|
if (io) {
|
||||||
const paste = (event.clipboardData || window.clipboardData)?.getData('text');
|
const paste = (event.clipboardData || window.clipboardData)?.getData('text');
|
||||||
if (paste) {
|
if (paste) {
|
||||||
|
@ -70,6 +76,12 @@ export const Apple2 = (props: Apple2Props) => {
|
||||||
}, [io]);
|
}, [io]);
|
||||||
|
|
||||||
const doCopy = useCallback((event: Event) => {
|
const doCopy = useCallback((event: Event) => {
|
||||||
|
if (
|
||||||
|
(document.activeElement !== screenRef.current) &&
|
||||||
|
(document.activeElement !== document.body)
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (vm) {
|
if (vm) {
|
||||||
event.clipboardData?.setData('text/plain', vm.getText());
|
event.clipboardData?.setData('text/plain', vm.getText());
|
||||||
}
|
}
|
||||||
|
@ -77,9 +89,9 @@ export const Apple2 = (props: Apple2Props) => {
|
||||||
}, [vm]);
|
}, [vm]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (screen.current) {
|
if (screenRef.current) {
|
||||||
const options = {
|
const options = {
|
||||||
canvas: screen.current,
|
canvas: screenRef.current,
|
||||||
tick: () => { /* do nothing */ },
|
tick: () => { /* do nothing */ },
|
||||||
...props,
|
...props,
|
||||||
};
|
};
|
||||||
|
@ -111,12 +123,20 @@ export const Apple2 = (props: Apple2Props) => {
|
||||||
}, [props, drivesReady]);
|
}, [props, drivesReady]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
const { current } = screenRef;
|
||||||
|
|
||||||
window.addEventListener('paste', doPaste);
|
window.addEventListener('paste', doPaste);
|
||||||
window.addEventListener('copy', doCopy);
|
window.addEventListener('copy', doCopy);
|
||||||
|
|
||||||
|
current?.addEventListener('paste', doPaste);
|
||||||
|
current?.addEventListener('copy', doCopy);
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
window.removeEventListener('paste', doPaste);
|
window.removeEventListener('paste', doPaste);
|
||||||
window.removeEventListener('copy', doCopy);
|
window.removeEventListener('copy', doCopy);
|
||||||
|
|
||||||
|
current?.removeEventListener('paste', doPaste);
|
||||||
|
current?.removeEventListener('copy', doCopy);
|
||||||
};
|
};
|
||||||
}, [doCopy, doPaste]);
|
}, [doCopy, doPaste]);
|
||||||
|
|
||||||
|
@ -124,23 +144,16 @@ export const Apple2 = (props: Apple2Props) => {
|
||||||
setShowDebug((on) => !on);
|
setShowDebug((on) => !on);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const removeFocus = useCallback(() => {
|
|
||||||
if (document?.activeElement instanceof HTMLElement) {
|
|
||||||
document.activeElement.blur();
|
|
||||||
}
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.container}>
|
<div className={styles.container}>
|
||||||
<div
|
<div
|
||||||
className={cs(styles.outer, { apple2e: e, [styles.ready]: ready })}
|
className={cs(styles.outer, { apple2e: e, [styles.ready]: ready })}
|
||||||
onClick={removeFocus}
|
|
||||||
>
|
>
|
||||||
<Screen screen={screen} />
|
<Screen screenRef={screenRef} />
|
||||||
{!e ? <LanguageCard cpu={cpu} io={io} rom={rom} slot={0} /> : null}
|
{!e ? <LanguageCard cpu={cpu} io={io} rom={rom} slot={0} /> : null}
|
||||||
<Slinky io={io} slot={2} />
|
<Slinky io={io} slot={2} />
|
||||||
{!e ? <Videoterm io={io} slot={3} /> : null}
|
{!e ? <Videoterm io={io} slot={3} /> : null}
|
||||||
<Mouse cpu={cpu} screen={screen} io={io} slot={4} />
|
<Mouse cpu={cpu} screenRef={screenRef} io={io} slot={4} />
|
||||||
<ThunderClock io={io} slot={5} />
|
<ThunderClock io={io} slot={5} />
|
||||||
<Inset>
|
<Inset>
|
||||||
<Drives cpu={cpu} io={io} sectors={sectors} enhanced={enhanced} ready={drivesReady} />
|
<Drives cpu={cpu} io={io} sectors={sectors} enhanced={enhanced} ready={drivesReady} />
|
||||||
|
|
|
@ -11,7 +11,7 @@ import { useEffect } from 'preact/hooks';
|
||||||
export interface MouseProps {
|
export interface MouseProps {
|
||||||
cpu: CPU6502 | undefined;
|
cpu: CPU6502 | undefined;
|
||||||
io: Apple2IO | undefined;
|
io: Apple2IO | undefined;
|
||||||
screen: RefObject<HTMLCanvasElement>;
|
screenRef: RefObject<HTMLCanvasElement>;
|
||||||
slot: slot;
|
slot: slot;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -24,14 +24,14 @@ export interface MouseProps {
|
||||||
* @param slot Slot to register card in
|
* @param slot Slot to register card in
|
||||||
* @returns Mouse component
|
* @returns Mouse component
|
||||||
*/
|
*/
|
||||||
export const Mouse = ({ cpu, screen, io, slot }: MouseProps) => {
|
export const Mouse = ({ cpu, screenRef, io, slot }: MouseProps) => {
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (cpu && io && screen.current) {
|
if (cpu && io && screenRef.current) {
|
||||||
const mouseUI = new MouseUI(screen.current);
|
const mouseUI = new MouseUI(screenRef.current);
|
||||||
const mouse = new MouseCard(cpu, mouseUI);
|
const mouse = new MouseCard(cpu, mouseUI);
|
||||||
io.setSlot(slot, mouse);
|
io.setSlot(slot, mouse);
|
||||||
}
|
}
|
||||||
}, [cpu, io, screen, slot]);
|
}, [cpu, io, screenRef, slot]);
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
|
|
|
@ -80,7 +80,7 @@ export const Printer = ({ io, slot }: PrinterProps) => {
|
||||||
<>
|
<>
|
||||||
<Modal isOpen={isOpen} onClose={onClose} title="Printer">
|
<Modal isOpen={isOpen} onClose={onClose} title="Printer">
|
||||||
<ModalContent>
|
<ModalContent>
|
||||||
<pre className={styles.printer}>
|
<pre className={styles.printer} tabIndex={-1} >
|
||||||
{content}
|
{content}
|
||||||
</pre>
|
</pre>
|
||||||
</ModalContent>
|
</ModalContent>
|
||||||
|
|
|
@ -6,7 +6,7 @@ import styles from './css/Screen.module.css';
|
||||||
* Screen properties
|
* Screen properties
|
||||||
*/
|
*/
|
||||||
export interface ScreenProps {
|
export interface ScreenProps {
|
||||||
screen: Ref<HTMLCanvasElement>;
|
screenRef: Ref<HTMLCanvasElement>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -16,7 +16,7 @@ export interface ScreenProps {
|
||||||
* @param screen Canvas element reference
|
* @param screen Canvas element reference
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export const Screen = ({ screen }: ScreenProps) => {
|
export const Screen = ({ screenRef }: ScreenProps) => {
|
||||||
return (
|
return (
|
||||||
<div className={styles.display}>
|
<div className={styles.display}>
|
||||||
<div className={styles.overscan}>
|
<div className={styles.overscan}>
|
||||||
|
@ -24,7 +24,7 @@ export const Screen = ({ screen }: ScreenProps) => {
|
||||||
className={styles.screen}
|
className={styles.screen}
|
||||||
width="592"
|
width="592"
|
||||||
height="416"
|
height="416"
|
||||||
ref={screen}
|
ref={screenRef}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -26,6 +26,7 @@
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
|
user-select: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media only screen and (min-resolution: 1.25dppx) {
|
@media only screen and (min-resolution: 1.25dppx) {
|
||||||
|
|
|
@ -11,4 +11,5 @@
|
||||||
color: #0f0;
|
color: #0f0;
|
||||||
border: 2px inset #888;
|
border: 2px inset #888;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
|
user-select: none;
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
user-select: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.reset:hover {
|
.reset:hover {
|
||||||
|
|
|
@ -26,6 +26,7 @@
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
|
user-select: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media only screen and (min-resolution: 1.25dppx) {
|
@media only screen and (min-resolution: 1.25dppx) {
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
|
|
||||||
.badge {
|
.badge {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
user-select: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.subtitle {
|
.subtitle {
|
||||||
|
@ -28,4 +29,5 @@
|
||||||
font-family: "Adobe Garamond Pro", Garamond, Times, serif;
|
font-family: "Adobe Garamond Pro", Garamond, Times, serif;
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
|
user-select: none;
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,7 +47,7 @@ const Variable = ({ variable }: { variable: ApplesoftVariable }) => {
|
||||||
<tr>
|
<tr>
|
||||||
<td>{name}{TYPE_SYMBOL[type]}{arrayStr}</td>
|
<td>{name}{TYPE_SYMBOL[type]}{arrayStr}</td>
|
||||||
<td>{TYPE_NAME[type]}{isArray ? ' Array' : ''}</td>
|
<td>{TYPE_NAME[type]}{isArray ? ' Array' : ''}</td>
|
||||||
<td>{isArray ? formatArray(value) : value}</td>
|
<td><pre tabIndex={-1}>{isArray ? formatArray(value) : value}</pre></td>
|
||||||
</tr>
|
</tr>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -103,7 +103,7 @@ export const Applesoft = ({ apple2 }: ApplesoftProps) => {
|
||||||
return (
|
return (
|
||||||
<div className={styles.column}>
|
<div className={styles.column}>
|
||||||
<span className={debuggerStyles.subHeading}>Listing</span>
|
<span className={debuggerStyles.subHeading}>Listing</span>
|
||||||
<pre className={styles.listing}>{listing}</pre>
|
<pre className={styles.listing} tabIndex={-1}>{listing}</pre>
|
||||||
<span className={debuggerStyles.subHeading}>Variables</span>
|
<span className={debuggerStyles.subHeading}>Variables</span>
|
||||||
<div className={styles.variables}>
|
<div className={styles.variables}>
|
||||||
<table>
|
<table>
|
||||||
|
|
|
@ -156,21 +156,21 @@ export const CPU = ({ apple2 }: CPUProps) => {
|
||||||
<div className={debuggerStyles.row}>
|
<div className={debuggerStyles.row}>
|
||||||
<div className={debuggerStyles.column}>
|
<div className={debuggerStyles.column}>
|
||||||
<span className={debuggerStyles.subHeading}>Registers</span>
|
<span className={debuggerStyles.subHeading}>Registers</span>
|
||||||
<pre>
|
<pre tabIndex={-1}>
|
||||||
{registers}
|
{registers}
|
||||||
</pre>
|
</pre>
|
||||||
<span className={debuggerStyles.subHeading}>Trace</span>
|
<span className={debuggerStyles.subHeading}>Trace</span>
|
||||||
<pre className={styles.trace}>
|
<pre className={styles.trace} tabIndex={-1}>
|
||||||
{trace}
|
{trace}
|
||||||
</pre>
|
</pre>
|
||||||
<span className={debuggerStyles.subHeading}>ZP</span>
|
<span className={debuggerStyles.subHeading}>ZP</span>
|
||||||
<pre className={styles.zeroPage}>
|
<pre className={styles.zeroPage} tabIndex={-1}>
|
||||||
{zeroPage}
|
{zeroPage}
|
||||||
</pre>
|
</pre>
|
||||||
</div>
|
</div>
|
||||||
<div className={debuggerStyles.column}>
|
<div className={debuggerStyles.column}>
|
||||||
<span className={debuggerStyles.subHeading}>Stack</span>
|
<span className={debuggerStyles.subHeading}>Stack</span>
|
||||||
<pre className={styles.stack}>
|
<pre className={styles.stack} tabIndex={-1}>
|
||||||
{stack}
|
{stack}
|
||||||
</pre>
|
</pre>
|
||||||
</div>
|
</div>
|
||||||
|
@ -185,7 +185,7 @@ export const CPU = ({ apple2 }: CPUProps) => {
|
||||||
className={cs({ [styles.invalid]: !memoryPageValid })}
|
className={cs({ [styles.invalid]: !memoryPageValid })}
|
||||||
/>
|
/>
|
||||||
{memoryPageValid ? null : ERROR_ICON}
|
{memoryPageValid ? null : ERROR_ICON}
|
||||||
<pre className={styles.zp}>
|
<pre className={styles.zp} tabIndex={-1}>
|
||||||
{memory}
|
{memory}
|
||||||
</pre>
|
</pre>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -908,15 +908,39 @@ function onLoaded(
|
||||||
* Input Handling
|
* Input Handling
|
||||||
*/
|
*/
|
||||||
|
|
||||||
window.addEventListener('paste', (event: Event) => {
|
const screenElement = document.querySelector('#screen')!;
|
||||||
|
|
||||||
|
const doPaste = (event: Event) => {
|
||||||
const paste = (event.clipboardData || window.clipboardData)!.getData('text');
|
const paste = (event.clipboardData || window.clipboardData)!.getData('text');
|
||||||
io.setKeyBuffer(paste);
|
io.setKeyBuffer(paste);
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
};
|
||||||
|
|
||||||
|
const doCopy = (event: Event) => {
|
||||||
|
event.clipboardData!.setData('text/plain', vm.getText());
|
||||||
|
event.preventDefault();
|
||||||
|
};
|
||||||
|
|
||||||
|
window.addEventListener('paste', (event: Event) => {
|
||||||
|
if (document.activeElement && document.activeElement !== document.body) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
doPaste(event);
|
||||||
});
|
});
|
||||||
|
|
||||||
window.addEventListener('copy', (event: Event) => {
|
window.addEventListener('copy', (event: Event) => {
|
||||||
event.clipboardData!.setData('text/plain', vm.getText());
|
if (document.activeElement && document.activeElement !== document.body) {
|
||||||
event.preventDefault();
|
return;
|
||||||
|
}
|
||||||
|
doCopy(event);
|
||||||
|
});
|
||||||
|
|
||||||
|
screenElement.addEventListener('copy', (event: Event) => {
|
||||||
|
doCopy(event);
|
||||||
|
});
|
||||||
|
|
||||||
|
screenElement.addEventListener('paste', (event: Event) => {
|
||||||
|
doPaste(event);
|
||||||
});
|
});
|
||||||
|
|
||||||
if (navigator.standalone) {
|
if (navigator.standalone) {
|
||||||
|
|
Loading…
Reference in New Issue