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