Refactor some controls (#114)

* Refactor some controls

* Feedback
This commit is contained in:
Will Scullin 2022-05-15 14:57:21 -07:00 committed by GitHub
parent c648735b8a
commit 9a940935af
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 129 additions and 61 deletions

View File

@ -606,6 +606,9 @@ button:focus {
padding: 0; padding: 0;
margin-left: 10px; margin-left: 10px;
width: 42px; width: 42px;
display: flex;
justify-content: center;
align-items: center;
} }
#reset:hover { #reset:hover {
@ -614,8 +617,11 @@ button:focus {
} }
#reset:active { #reset:active {
background: #22150A; background-color: #22150A;
border: 5px outset #44372C; border-left: 3px solid #44372C;
border-top: 3px solid #44372C;
border-right: 3px solid #000000;
border-bottom: 3px solid #000000;
} }
#keyboard .key-OPEN_APPLE.active div { #keyboard .key-OPEN_APPLE.active div {

View File

@ -49,8 +49,8 @@ export const Apple2 = (props: Apple2Props) => {
...props, ...props,
}; };
const apple2 = new Apple2Impl(options); const apple2 = new Apple2Impl(options);
setApple2(apple2);
apple2.ready.then(() => { apple2.ready.then(() => {
setApple2(apple2);
const io = apple2.getIO(); const io = apple2.getIO();
const cpu = apple2.getCPU(); const cpu = apple2.getCPU();
setIO(io); setIO(io);

View File

@ -0,0 +1,51 @@
import { h } from 'preact';
import { useCallback, useContext, useEffect, useState } from 'preact/hooks';
import { ControlButton } from './ControlButton';
import { OptionsContext } from './OptionsContext';
import { Audio, SOUND_ENABLED_OPTION } from '../ui/audio';
import { Apple2 as Apple2Impl } from '../apple2';
/**
* AudioControl component properties.
*/
export interface AudioControlProps {
apple2: Apple2Impl | undefined;
}
/**
* Control that instantiates the Audio object and provides
* a control to mute and unmute audio.
*
* @param apple2 The Apple2 object
* @returns AudioControl component
*/
export const AudioControl = ({ apple2 }: AudioControlProps) => {
const [audioEnabled, setAudioEnabled] = useState(false);
const [audio, setAudio] = useState<Audio>();
const options = useContext(OptionsContext);
useEffect(() => {
if (apple2) {
const io = apple2.getIO();
const audio = new Audio(io);
options.addOptions(audio);
setAudio(audio);
setAudioEnabled(audio.isEnabled());
}
}, [apple2]);
const doToggleSound = useCallback(() => {
const on = !audio?.isEnabled();
options.setOption(SOUND_ENABLED_OPTION, on);
setAudioEnabled(on);
}, [audio]);
return (
<ControlButton
onClick={doToggleSound}
title="Toggle Sound"
disabled={!audio}
icon={audioEnabled ? 'volume-up' : 'volume-off'}
/>
);
};

View File

@ -6,6 +6,7 @@ import { h, JSX } from 'preact';
export interface ControlButtonProps { export interface ControlButtonProps {
icon: string; icon: string;
title: string; title: string;
disabled?: boolean;
onClick: JSX.MouseEventHandler<HTMLButtonElement>; onClick: JSX.MouseEventHandler<HTMLButtonElement>;
} }
@ -17,8 +18,8 @@ export interface ControlButtonProps {
* @param onClick Click callback * @param onClick Click callback
* @returns Control Button component * @returns Control Button component
*/ */
export const ControlButton = ({ icon, title, onClick }: ControlButtonProps) => ( export const ControlButton = ({ icon, title, onClick, ...props }: ControlButtonProps) => (
<button onClick={onClick} title={title}> <button onClick={onClick} title={title} {...props} >
<i class={`fas fa-${icon}`}></i> <i class={`fas fa-${icon}`}></i>
</button> </button>
); );

View File

@ -3,11 +3,12 @@ import { useCallback, useContext, useEffect, useState } from 'preact/hooks';
import { CPUMeter } from './CPUMeter'; import { CPUMeter } from './CPUMeter';
import { Inset } from './Inset'; import { Inset } from './Inset';
import { useHotKey } from './hooks/useHotKey'; import { useHotKey } from './hooks/useHotKey';
import { Apple2 as Apple2Impl } from '../apple2'; import { AudioControl } from './AudioControl';
import { Audio, SOUND_ENABLED_OPTION } from '../ui/audio';
import { OptionsModal} from './OptionsModal'; import { OptionsModal} from './OptionsModal';
import { OptionsContext } from './OptionsContext'; import { OptionsContext } from './OptionsContext';
import { PauseControl } from './PauseControl';
import { ControlButton } from './ControlButton'; import { ControlButton } from './ControlButton';
import { Apple2 as Apple2Impl } from '../apple2';
import { JoyStick } from '../ui/joystick'; import { JoyStick } from '../ui/joystick';
import { Screen, SCREEN_FULL_PAGE } from '../ui/screen'; import { Screen, SCREEN_FULL_PAGE } from '../ui/screen';
import { System } from '../ui/system'; import { System } from '../ui/system';
@ -29,51 +30,25 @@ interface ControlStripProps {
* @returns ControlStrip component * @returns ControlStrip component
*/ */
export const ControlStrip = ({ apple2, e }: ControlStripProps) => { export const ControlStrip = ({ apple2, e }: ControlStripProps) => {
const [running, setRunning] = useState(true);
const [audio, setAudio] = useState<Audio>();
const [audioEnabled, setAudioEnabled] = useState(false);
const [showOptions, setShowOptions] = useState(false); const [showOptions, setShowOptions] = useState(false);
const options = useContext(OptionsContext); const options = useContext(OptionsContext);
useEffect(() => { useEffect(() => {
if (apple2) { if (apple2) {
apple2.ready.then(() => { const io = apple2.getIO();
const io = apple2.getIO(); const vm = apple2.getVideoModes();
const vm = apple2.getVideoModes();
const system = new System(io, e); const system = new System(io, e);
options.addOptions(system); options.addOptions(system);
const joystick = new JoyStick(io); const joystick = new JoyStick(io);
options.addOptions(joystick); options.addOptions(joystick);
const screen = new Screen(vm); const screen = new Screen(vm);
options.addOptions(screen); options.addOptions(screen);
const audio = new Audio(io);
options.addOptions(audio);
setAudio(audio);
setAudioEnabled(audio.isEnabled());
}).catch(console.error);
} }
}, [apple2]); }, [apple2]);
const doPause = useCallback(() => {
apple2?.stop();
setRunning(false);
}, [apple2]);
const doRun = useCallback(() => {
apple2?.run();
setRunning(true);
}, [apple2]);
const doToggleSound = useCallback(() => {
const on = !audio?.isEnabled();
options.setOption(SOUND_ENABLED_OPTION, on);
setAudioEnabled(on);
}, [audio]);
const doReset = useCallback(() => const doReset = useCallback(() =>
apple2?.reset() apple2?.reset()
, [apple2]); , [apple2]);
@ -106,32 +81,16 @@ export const ControlStrip = ({ apple2, e }: ControlStripProps) => {
<OptionsModal isOpen={showOptions} onClose={doCloseOptions} /> <OptionsModal isOpen={showOptions} onClose={doCloseOptions} />
<Inset> <Inset>
<CPUMeter apple2={apple2} /> <CPUMeter apple2={apple2} />
{running ? ( <PauseControl apple2={apple2} />
<ControlButton <AudioControl apple2={apple2} />
onClick={doPause}
title="Pause"
icon="pause"
/>
) : (
<ControlButton
onClick={doRun}
title="Run"
icon="play"
/>
)}
<ControlButton
onClick={doToggleSound}
title="Toggle Sound"
icon={audioEnabled ? 'volume-up' : 'volume-off'}
/>
<div style={{flexGrow: 1}} /> <div style={{flexGrow: 1}} />
<ControlButton onClick={doReadme} title="About" icon="info" /> <ControlButton onClick={doReadme} title="About" icon="info" />
<ControlButton onClick={doShowOptions} title="Options (F4)" icon="cog" /> <ControlButton onClick={doShowOptions} title="Options (F4)" icon="cog" />
</Inset> </Inset>
{e && ( {e && (
<button id="reset" onClick={doReset}> <div id="reset" onClick={doReset}>
Reset Reset
</button> </div>
)} )}
</div> </div>
); );

View File

@ -0,0 +1,51 @@
import { h, Fragment } from 'preact';
import { useCallback, useState } from 'preact/hooks';
import { Apple2 as Apple2Impl } from 'js/apple2';
import { ControlButton } from './ControlButton';
/**
* PauseControl component properties.
*/
export interface PauseControlProps {
apple2: Apple2Impl | undefined;
}
/**
* Provides a control to pause and unpause the CPU.
*
* @param apple2 The Apple2 object
* @returns PauseControl component
*/
export const PauseControl = ({ apple2 }: PauseControlProps) => {
const [running, setRunning] = useState(true);
const doPause = useCallback(() => {
apple2?.stop();
setRunning(false);
}, [apple2]);
const doRun = useCallback(() => {
apple2?.run();
setRunning(true);
}, [apple2]);
return (
<>
{running ? (
<ControlButton
onClick={doPause}
disabled={!apple2}
title="Pause"
icon="pause"
/>
) : (
<ControlButton
onClick={doRun}
disabled={!apple2}
title="Run"
icon="play"
/>
)}
</>
);
};