mirror of
https://github.com/felixrieseberg/windows95.git
synced 2026-05-09 00:24:09 +00:00
Redesign launcher UI with 98.css
Replace the sparse start/settings cards with a Win95-styled launcher: - Start screen is now a 'Welcome to Windows 95' dialog with a gradient side stripe, a rotating tip box, and a button column. - Settings is a tabbed Properties sheet (Floppy / Network / State) with group boxes, sunken read-only path fields, and OK/Cancel. - Vendored 98.css and its Pixelated MS Sans Serif fonts; dropped the old 95css-based .btn/.card/.nav classes. - Removed the bottom taskbar nav (start-menu.tsx) — navigation now goes through the dialog buttons so the launcher isn't mistaken for the running OS.
This commit is contained in:
@@ -1,117 +1,77 @@
|
||||
@import "./status.less";
|
||||
@import "./emulator.less";
|
||||
@import "./info.less";
|
||||
@import "./settings.less";
|
||||
@import "./start.less";
|
||||
@import "./settings.less";
|
||||
|
||||
/* GENERAL RESETS */
|
||||
// 98.css uses the actual MS Sans Serif bitmap font and pixel-exact bevels.
|
||||
// Everything below is layout — the chrome comes from 98.css.
|
||||
|
||||
html, body {
|
||||
@win-teal: #008080;
|
||||
@win-silver: silver;
|
||||
@win-font: "Pixelated MS Sans Serif", Arial, sans-serif;
|
||||
|
||||
* {
|
||||
user-select: none;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
body {
|
||||
background: #000;
|
||||
}
|
||||
|
||||
body.paused > #emulator {
|
||||
display: none;
|
||||
font-family: @win-font;
|
||||
-webkit-font-smoothing: none;
|
||||
image-rendering: pixelated;
|
||||
}
|
||||
|
||||
body.paused {
|
||||
background: #008080;
|
||||
font-family: Courier;
|
||||
background: @win-teal;
|
||||
|
||||
> #emulator {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
#buttons {
|
||||
user-select: none;
|
||||
button:not(:disabled),
|
||||
li[role="tab"],
|
||||
.title-bar-controls button:not(:disabled) {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
button:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
// 98.css renders button text via text-shadow (color: transparent) so the
|
||||
// bitmap font stays crisp; <img> children need their own alignment.
|
||||
button img {
|
||||
height: 16px;
|
||||
width: 16px;
|
||||
margin-right: 4px;
|
||||
vertical-align: -3px;
|
||||
}
|
||||
|
||||
p {
|
||||
font-family: @win-font;
|
||||
font-size: 11px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
code {
|
||||
font-family: "Courier New", monospace;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
section {
|
||||
display: flex;
|
||||
position: absolute;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
inset: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.card {
|
||||
width: 75%;
|
||||
max-width: 700px;
|
||||
min-width: 400px;
|
||||
|
||||
.card-title {
|
||||
img {
|
||||
margin-right: 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.nav-link > img,
|
||||
.btn > img {
|
||||
height: 24px;
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
.windows95 {
|
||||
* {
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
*:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
nav .nav-link,
|
||||
nav .nav-logo {
|
||||
height: 37px;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
nav .nav-logo img {
|
||||
margin-left: 2px;
|
||||
max-height: 20px;
|
||||
}
|
||||
|
||||
nav .nav-logo > span {
|
||||
position: absolute;
|
||||
top: 9px;
|
||||
left: 37px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.btn {
|
||||
height: 40px;
|
||||
padding-top: 3px;
|
||||
}
|
||||
|
||||
.btn:focus {
|
||||
border-color: #fff #000 #000 #fff;
|
||||
outline: 5px auto -webkit-focus-ring-color;
|
||||
}
|
||||
|
||||
.btn.active:before,
|
||||
.btn:focus:before,
|
||||
button.active:before,
|
||||
button:focus:before,
|
||||
input[type=submit].active:before,
|
||||
input[type=submit]:focus:before {
|
||||
border-color: #dedede grey grey #dedede;
|
||||
}
|
||||
|
||||
.card {
|
||||
// Fix link colors
|
||||
.link, .link:active, .link:link, .link:visited, a, a:active, a:link, a:visited {
|
||||
color: #008080;
|
||||
text-decoration: underline;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
// Ensure a-elements in fieldsets receive click events
|
||||
fieldset:before {
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,21 +1,71 @@
|
||||
#floppy-path {
|
||||
font-size: .6rem;
|
||||
width: 100%;
|
||||
height: 30px;
|
||||
padding-left: 8px;
|
||||
border-color: #000 #fff #fff #000;
|
||||
border-style: solid;
|
||||
border-width: 2px;
|
||||
background-color: #c3c3c3;
|
||||
line-height: 27px;
|
||||
}
|
||||
.settings-window {
|
||||
width: 460px;
|
||||
|
||||
#file-input {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.settings {
|
||||
legend > img {
|
||||
margin-right: 5px;
|
||||
> .window-body {
|
||||
margin: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.settings-panel {
|
||||
padding: 3px;
|
||||
|
||||
> .window-body {
|
||||
margin: 12px;
|
||||
min-height: 220px;
|
||||
}
|
||||
|
||||
fieldset {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.settings-row {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 12px;
|
||||
margin-bottom: 12px;
|
||||
|
||||
p {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.settings-icon {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.settings-window .field-row-stacked {
|
||||
margin-bottom: 12px;
|
||||
|
||||
input[type="text"] {
|
||||
width: 100%;
|
||||
font-family: "Pixelated MS Sans Serif", Arial;
|
||||
}
|
||||
|
||||
input[type="text"]:read-only {
|
||||
background-color: #fff;
|
||||
color: #222;
|
||||
}
|
||||
}
|
||||
|
||||
.settings-buttons {
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
|
||||
button {
|
||||
min-width: 110px;
|
||||
}
|
||||
}
|
||||
|
||||
.settings-footer {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 6px;
|
||||
margin-top: 10px;
|
||||
|
||||
button {
|
||||
min-width: 75px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,99 @@
|
||||
#section-start {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
// "Welcome to Windows" splash — modelled on the real first-boot dialog.
|
||||
|
||||
> small {
|
||||
margin-top: 25px;
|
||||
font-size: .8rem;
|
||||
.welcome {
|
||||
width: 540px;
|
||||
}
|
||||
|
||||
.welcome-body {
|
||||
display: flex;
|
||||
align-items: stretch;
|
||||
gap: 12px;
|
||||
margin: 4px;
|
||||
min-height: 250px;
|
||||
}
|
||||
|
||||
.welcome-stripe {
|
||||
width: 26px;
|
||||
background: linear-gradient(180deg, #000 0%, navy 60%, #1084d0 100%);
|
||||
position: relative;
|
||||
flex-shrink: 0;
|
||||
|
||||
span {
|
||||
position: absolute;
|
||||
bottom: 8px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%) rotate(180deg);
|
||||
writing-mode: vertical-rl;
|
||||
color: #fff;
|
||||
font-weight: 700;
|
||||
font-size: 14px;
|
||||
letter-spacing: 1px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
|
||||
.welcome-main {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 8px 4px;
|
||||
}
|
||||
|
||||
.welcome-title {
|
||||
font-family: "Times New Roman", serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
font-weight: 400;
|
||||
font-size: 24px;
|
||||
margin: 0 0 14px;
|
||||
color: #000;
|
||||
|
||||
span {
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
small {
|
||||
color: #fff;
|
||||
font-size: 24px;
|
||||
font-weight: 700;
|
||||
vertical-align: baseline;
|
||||
margin-left: 1px;
|
||||
}
|
||||
}
|
||||
|
||||
.welcome-tip {
|
||||
flex: 1;
|
||||
background: #ffffe1;
|
||||
box-shadow: inset -1px -1px #fff, inset 1px 1px grey, inset -2px -2px #dfdfdf,
|
||||
inset 2px 2px #0a0a0a;
|
||||
padding: 12px 14px;
|
||||
|
||||
.welcome-tip-header {
|
||||
border-bottom: 1px solid grey;
|
||||
box-shadow: 0 1px 0 #fff;
|
||||
padding-bottom: 6px;
|
||||
margin-bottom: 8px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.welcome-actions {
|
||||
width: 130px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
padding: 8px 4px;
|
||||
flex-shrink: 0;
|
||||
|
||||
button {
|
||||
width: 100%;
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
.welcome-spacer {
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
|
||||
2
src/less/vendor/98.css
vendored
Normal file
2
src/less/vendor/98.css
vendored
Normal file
File diff suppressed because one or more lines are too long
BIN
src/less/vendor/ms_sans_serif.woff
vendored
Normal file
BIN
src/less/vendor/ms_sans_serif.woff
vendored
Normal file
Binary file not shown.
BIN
src/less/vendor/ms_sans_serif.woff2
vendored
Normal file
BIN
src/less/vendor/ms_sans_serif.woff2
vendored
Normal file
Binary file not shown.
BIN
src/less/vendor/ms_sans_serif_bold.woff
vendored
Normal file
BIN
src/less/vendor/ms_sans_serif_bold.woff
vendored
Normal file
Binary file not shown.
BIN
src/less/vendor/ms_sans_serif_bold.woff2
vendored
Normal file
BIN
src/less/vendor/ms_sans_serif_bold.woff2
vendored
Normal file
Binary file not shown.
@@ -8,12 +8,16 @@ interface CardSettingsProps {
|
||||
setCdrom: (cdrom: File) => void;
|
||||
setSmbSharePath: (path: string) => void;
|
||||
pickFolder: () => Promise<string | null>;
|
||||
navigate: (to: string) => void;
|
||||
floppy?: File;
|
||||
cdrom?: File;
|
||||
smbSharePath: string;
|
||||
}
|
||||
|
||||
type Tab = "floppy" | "network" | "state";
|
||||
|
||||
interface CardSettingsState {
|
||||
tab: Tab;
|
||||
isStateReset: boolean;
|
||||
}
|
||||
|
||||
@@ -29,187 +33,166 @@ export class CardSettings extends React.Component<
|
||||
this.onResetState = this.onResetState.bind(this);
|
||||
|
||||
this.state = {
|
||||
tab: "floppy",
|
||||
isStateReset: false,
|
||||
};
|
||||
}
|
||||
|
||||
public render() {
|
||||
const { tab } = this.state;
|
||||
|
||||
return (
|
||||
<section>
|
||||
<div className="card settings">
|
||||
<div className="card-header">
|
||||
<h2 className="card-title">
|
||||
<img src="../../static/settings.png" />
|
||||
Settings
|
||||
</h2>
|
||||
</div>
|
||||
<div className="card-body">
|
||||
{this.renderCdrom()}
|
||||
<hr />
|
||||
{this.renderFloppy()}
|
||||
<hr />
|
||||
{this.renderSmbShare()}
|
||||
<hr />
|
||||
{this.renderState()}
|
||||
<div className="window settings-window">
|
||||
<div className="title-bar">
|
||||
<div className="title-bar-text">windows95 Properties</div>
|
||||
<div className="title-bar-controls">
|
||||
<button aria-label="Help" disabled />
|
||||
<button
|
||||
aria-label="Close"
|
||||
onClick={() => this.props.navigate("start")}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<div className="window-body">
|
||||
<menu role="tablist">
|
||||
{this.renderTab("floppy", "Floppy Drive")}
|
||||
{this.renderTab("network", "Network Share")}
|
||||
{this.renderTab("state", "Machine State")}
|
||||
</menu>
|
||||
<div className="window settings-panel" role="tabpanel">
|
||||
<div className="window-body">
|
||||
{tab === "floppy" && this.renderFloppy()}
|
||||
{tab === "network" && this.renderSmbShare()}
|
||||
{tab === "state" && this.renderState()}
|
||||
</div>
|
||||
</div>
|
||||
<div className="settings-footer">
|
||||
<button
|
||||
className="default"
|
||||
onClick={() => this.props.navigate("start")}
|
||||
>
|
||||
OK
|
||||
</button>
|
||||
<button onClick={() => this.props.navigate("start")}>Cancel</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
public renderCdrom() {
|
||||
// CD is currently not working, so.. let's return nothing.
|
||||
return null;
|
||||
|
||||
const { cdrom } = this.props;
|
||||
|
||||
private renderTab(id: Tab, label: string) {
|
||||
return (
|
||||
<fieldset>
|
||||
<legend>
|
||||
<img src="../../static/cdrom.png" />
|
||||
CD-ROM
|
||||
</legend>
|
||||
<input
|
||||
id="cdrom-input"
|
||||
type="file"
|
||||
onChange={this.onChangeCdrom}
|
||||
style={{ display: "none" }}
|
||||
/>
|
||||
<p>
|
||||
windows95 comes with a virtual CD drive. It can mount images in the
|
||||
"iso" format.
|
||||
</p>
|
||||
<p id="floppy-path">
|
||||
{cdrom ? `Inserted CD: ${cdrom?.name}` : `No CD mounted`}
|
||||
</p>
|
||||
<button
|
||||
className="btn"
|
||||
onClick={() =>
|
||||
(document.querySelector("#cdrom-input") as any).click()
|
||||
}
|
||||
>
|
||||
<img src="../../static/select-cdrom.png" />
|
||||
<span>Mount CD</span>
|
||||
</button>
|
||||
</fieldset>
|
||||
<li
|
||||
role="tab"
|
||||
aria-selected={this.state.tab === id}
|
||||
onClick={() => this.setState({ tab: id })}
|
||||
>
|
||||
<a href="#">{label}</a>
|
||||
</li>
|
||||
);
|
||||
}
|
||||
|
||||
public renderSmbShare() {
|
||||
const { smbSharePath } = this.props;
|
||||
|
||||
return (
|
||||
<fieldset>
|
||||
<legend>Network Share</legend>
|
||||
<p>
|
||||
A folder on your computer is exposed inside Windows 95 as a
|
||||
network drive. From inside Windows, open Start → Run and type{" "}
|
||||
<code>\\HOST\HOST</code> to browse it, or use Map Network Drive to
|
||||
give it a drive letter.
|
||||
</p>
|
||||
<p>
|
||||
Shared folder: <code>{smbSharePath}</code>
|
||||
</p>
|
||||
<button
|
||||
className="btn"
|
||||
onClick={async () => {
|
||||
const picked = await this.props.pickFolder();
|
||||
if (picked) this.props.setSmbSharePath(picked);
|
||||
}}
|
||||
>
|
||||
<span>Choose folder</span>
|
||||
</button>
|
||||
</fieldset>
|
||||
);
|
||||
}
|
||||
|
||||
public renderFloppy() {
|
||||
private renderFloppy() {
|
||||
const { floppy } = this.props;
|
||||
|
||||
return (
|
||||
<fieldset>
|
||||
<legend>
|
||||
<img src="../../static/floppy.png" />
|
||||
Floppy
|
||||
</legend>
|
||||
<legend>Drive A:</legend>
|
||||
<input
|
||||
id="floppy-input"
|
||||
type="file"
|
||||
onChange={this.onChangeFloppy}
|
||||
style={{ display: "none" }}
|
||||
/>
|
||||
<p>
|
||||
windows95 comes with a virtual floppy drive. It can mount floppy disk
|
||||
images in the "img" format.
|
||||
</p>
|
||||
<p>
|
||||
Back in the 90s and before CD-ROMs became a popular, software was
|
||||
typically distributed on floppy disks. Some developers have since
|
||||
released their apps or games for free, usually on virtual floppy disks
|
||||
using the "img" format.
|
||||
</p>
|
||||
<p>
|
||||
Once you've mounted a disk image, you might have to boot your virtual
|
||||
windows95 machine from scratch.
|
||||
</p>
|
||||
<p id="floppy-path">
|
||||
{floppy
|
||||
? `Inserted Floppy Disk: ${floppy.name}`
|
||||
: `No floppy mounted`}
|
||||
</p>
|
||||
<button
|
||||
className="btn"
|
||||
onClick={() =>
|
||||
(document.querySelector("#floppy-input") as any).click()
|
||||
}
|
||||
>
|
||||
<img src="../../static/select-floppy.png" />
|
||||
<span>Mount floppy disk</span>
|
||||
</button>
|
||||
</fieldset>
|
||||
);
|
||||
}
|
||||
|
||||
public renderState() {
|
||||
const { isStateReset } = this.state;
|
||||
const { bootFromScratch } = this.props;
|
||||
|
||||
return (
|
||||
<fieldset>
|
||||
<legend>
|
||||
<img src="../../static/reset.png" />
|
||||
Reset machine state
|
||||
</legend>
|
||||
<div>
|
||||
<div className="settings-row">
|
||||
<img className="settings-icon" src="../../static/floppy.png" />
|
||||
<p>
|
||||
windows95 stores changes to your machine (like saved files) in a
|
||||
state file. If you encounter any trouble, you can reset your state
|
||||
or boot Windows 95 from scratch.{" "}
|
||||
<strong>All your changes will be lost.</strong>
|
||||
windows95 ships with a virtual 3½" floppy drive. Mount an{" "}
|
||||
<code>.img</code> disk image here, then boot the machine to read it
|
||||
from inside Windows.
|
||||
</p>
|
||||
</div>
|
||||
<div className="field-row-stacked">
|
||||
<label htmlFor="floppy-path">Mounted image</label>
|
||||
<input
|
||||
id="floppy-path"
|
||||
type="text"
|
||||
readOnly
|
||||
value={floppy ? floppy.name : "(No disk in drive)"}
|
||||
/>
|
||||
</div>
|
||||
<div className="settings-buttons">
|
||||
<button
|
||||
className="btn"
|
||||
onClick={this.onResetState}
|
||||
disabled={isStateReset}
|
||||
style={{ marginRight: "5px" }}
|
||||
onClick={() =>
|
||||
(document.querySelector("#floppy-input") as any).click()
|
||||
}
|
||||
>
|
||||
<img src="../../static/reset-state.png" />
|
||||
{isStateReset ? "State reset" : "Reset state"}
|
||||
</button>
|
||||
<button className="btn" onClick={bootFromScratch}>
|
||||
<img src="../../static/boot-fresh.png" />
|
||||
Boot from scratch
|
||||
Mount image...
|
||||
</button>
|
||||
</div>
|
||||
</fieldset>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle a change in the floppy input
|
||||
*
|
||||
* @param event
|
||||
*/
|
||||
private renderSmbShare() {
|
||||
const { smbSharePath } = this.props;
|
||||
|
||||
return (
|
||||
<fieldset>
|
||||
<legend>\\HOST\HOST</legend>
|
||||
<div className="settings-row">
|
||||
<img className="settings-icon" src="../../static/show-disk-image.png" />
|
||||
<p>
|
||||
A folder on your computer is exposed inside Windows 95 as a network
|
||||
drive. From inside Windows, open Start → Run and type{" "}
|
||||
<code>\\HOST\HOST</code> — or use Map Network Drive to give it a
|
||||
letter.
|
||||
</p>
|
||||
</div>
|
||||
<div className="field-row-stacked">
|
||||
<label htmlFor="smb-path">Shared folder</label>
|
||||
<input id="smb-path" type="text" readOnly value={smbSharePath} />
|
||||
</div>
|
||||
<div className="settings-buttons">
|
||||
<button
|
||||
onClick={async () => {
|
||||
const picked = await this.props.pickFolder();
|
||||
if (picked) this.props.setSmbSharePath(picked);
|
||||
}}
|
||||
>
|
||||
Choose folder...
|
||||
</button>
|
||||
</div>
|
||||
</fieldset>
|
||||
);
|
||||
}
|
||||
|
||||
private renderState() {
|
||||
const { isStateReset } = this.state;
|
||||
const { bootFromScratch } = this.props;
|
||||
|
||||
return (
|
||||
<fieldset>
|
||||
<legend>Reset</legend>
|
||||
<div className="settings-row">
|
||||
<img className="settings-icon" src="../../static/reset.png" />
|
||||
<p>
|
||||
Changes to your machine (saved files, installed programs) are stored
|
||||
in a state file. If something breaks, you can either discard that
|
||||
state or boot a fresh copy of Windows from scratch.{" "}
|
||||
<strong>All your changes will be lost.</strong>
|
||||
</p>
|
||||
</div>
|
||||
<div className="settings-buttons">
|
||||
<button onClick={this.onResetState} disabled={isStateReset}>
|
||||
{isStateReset ? "State has been reset" : "Reset state"}
|
||||
</button>
|
||||
<button onClick={bootFromScratch}>Boot from scratch</button>
|
||||
</div>
|
||||
</fieldset>
|
||||
);
|
||||
}
|
||||
|
||||
private onChangeFloppy(event: React.ChangeEvent<HTMLInputElement>) {
|
||||
const floppyFile =
|
||||
event.target.files && event.target.files.length > 0
|
||||
@@ -223,27 +206,19 @@ export class CardSettings extends React.Component<
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle a change in the cdrom input
|
||||
*
|
||||
* @param event
|
||||
*/
|
||||
private onChangeCdrom(event: React.ChangeEvent<HTMLInputElement>) {
|
||||
const CdromFile =
|
||||
const cdromFile =
|
||||
event.target.files && event.target.files.length > 0
|
||||
? event.target.files[0]
|
||||
: null;
|
||||
|
||||
if (CdromFile) {
|
||||
this.props.setCdrom(CdromFile);
|
||||
if (cdromFile) {
|
||||
this.props.setCdrom(cdromFile);
|
||||
} else {
|
||||
console.log(`Cdrom: Input changed but no file selected`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the state reset
|
||||
*/
|
||||
private async onResetState() {
|
||||
await resetState();
|
||||
this.setState({ isStateReset: true });
|
||||
|
||||
@@ -2,18 +2,64 @@ import * as React from "react";
|
||||
|
||||
export interface CardStartProps {
|
||||
startEmulator: () => void;
|
||||
navigate: (to: string) => void;
|
||||
}
|
||||
|
||||
export class CardStart extends React.Component<CardStartProps, {}> {
|
||||
const TIPS = [
|
||||
"Press the Escape key at any time to release or recapture your mouse cursor.",
|
||||
"You can mount a floppy image from Settings before booting to install vintage software.",
|
||||
"Map a host folder as a network drive: open Start → Run inside Windows and type \\\\HOST\\HOST.",
|
||||
"Your machine state is saved automatically when you quit. Reset it from Settings if things get weird.",
|
||||
"Use the Machine menu in the menubar to send Ctrl+Alt+Del and other special key combos.",
|
||||
];
|
||||
|
||||
export class CardStart extends React.Component<CardStartProps> {
|
||||
private tip = TIPS[Math.floor(Math.random() * TIPS.length)];
|
||||
|
||||
public render() {
|
||||
return (
|
||||
<section id="section-start">
|
||||
<button className="btn" id="win95" onClick={this.props.startEmulator}>
|
||||
<img src="../../static/run.png" />
|
||||
<span>Start Windows 95</span>
|
||||
</button>
|
||||
<small>Hit ESC to lock or unlock your mouse</small>
|
||||
</section>
|
||||
<div className="window welcome" id="welcome-window">
|
||||
<div className="title-bar">
|
||||
<div className="title-bar-text">Welcome</div>
|
||||
<div className="title-bar-controls">
|
||||
<button aria-label="Minimize" disabled />
|
||||
<button aria-label="Maximize" disabled />
|
||||
<button aria-label="Close" disabled />
|
||||
</div>
|
||||
</div>
|
||||
<div className="window-body welcome-body">
|
||||
<aside className="welcome-stripe">
|
||||
<span>Windows 95</span>
|
||||
</aside>
|
||||
<div className="welcome-main">
|
||||
<h1 className="welcome-title">
|
||||
Welcome to <span>Windows</span>
|
||||
<small>95</small>
|
||||
</h1>
|
||||
|
||||
<div className="welcome-tip">
|
||||
<div className="welcome-tip-header">
|
||||
<strong>Did you know...</strong>
|
||||
</div>
|
||||
<p>{this.tip}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="welcome-actions">
|
||||
<button
|
||||
id="win95"
|
||||
className="default"
|
||||
onClick={this.props.startEmulator}
|
||||
>
|
||||
<u>S</u>tart Windows 95
|
||||
</button>
|
||||
<button onClick={() => this.props.navigate("settings")}>
|
||||
S<u>e</u>ttings...
|
||||
</button>
|
||||
<div className="welcome-spacer" />
|
||||
<button disabled>What's New</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@ import { ipcRenderer, shell, webUtils } from "electron";
|
||||
import { CONSTANTS, IPC_COMMANDS } from "../constants";
|
||||
import { getDiskImageSize } from "../utils/disk-image-size";
|
||||
import { CardStart } from "./card-start";
|
||||
import { StartMenu } from "./start-menu";
|
||||
import { CardSettings } from "./card-settings";
|
||||
import { EmulatorInfo } from "./emulator-info";
|
||||
import { getStatePath } from "./utils/get-state-path";
|
||||
@@ -217,6 +216,9 @@ export class Emulator extends React.Component<{}, EmulatorState> {
|
||||
return null;
|
||||
}
|
||||
|
||||
const navigate = (target: string) =>
|
||||
this.setState({ currentUiCard: target as "start" | "settings" });
|
||||
|
||||
let card;
|
||||
|
||||
if (currentUiCard === "settings") {
|
||||
@@ -233,22 +235,16 @@ export class Emulator extends React.Component<{}, EmulatorState> {
|
||||
floppy={floppyFile}
|
||||
cdrom={cdromFile}
|
||||
smbSharePath={this.state.smbSharePath}
|
||||
navigate={navigate}
|
||||
/>
|
||||
);
|
||||
} else {
|
||||
card = <CardStart startEmulator={this.startEmulator} />;
|
||||
card = (
|
||||
<CardStart startEmulator={this.startEmulator} navigate={navigate} />
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{card}
|
||||
<StartMenu
|
||||
navigate={(target) =>
|
||||
this.setState({ currentUiCard: target as "start" | "settings" })
|
||||
}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
return <section>{card}</section>;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
import * as React from "react";
|
||||
|
||||
export interface StartMenuProps {
|
||||
navigate: (to: string) => void;
|
||||
}
|
||||
|
||||
export class StartMenu extends React.Component<StartMenuProps, {}> {
|
||||
constructor(props: StartMenuProps) {
|
||||
super(props);
|
||||
|
||||
this.navigate = this.navigate.bind(this);
|
||||
}
|
||||
|
||||
public render() {
|
||||
return (
|
||||
<nav className="nav nav-bottom">
|
||||
<a onClick={this.navigate} href="#" id="start" className="nav-link">
|
||||
<img src="../../static/start.png" alt="Start" />
|
||||
<span>Start</span>
|
||||
</a>
|
||||
<div className="nav-menu">
|
||||
<a
|
||||
onClick={this.navigate}
|
||||
href="#"
|
||||
id="settings"
|
||||
className="nav-link"
|
||||
>
|
||||
<img src="../../static/settings.png" />
|
||||
<span>Settings</span>
|
||||
</a>
|
||||
</div>
|
||||
</nav>
|
||||
);
|
||||
}
|
||||
|
||||
private navigate(event: React.SyntheticEvent<HTMLAnchorElement>) {
|
||||
this.props.navigate(event.currentTarget.id);
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,7 @@
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<title>windows95</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="stylesheet" href="../src/less/vendor/95css.css">
|
||||
<link rel="stylesheet" href="../src/less/vendor/98.css">
|
||||
<link rel="stylesheet" href="../src/less/root.less">
|
||||
<!-- libv86 -->
|
||||
</head>
|
||||
|
||||
Reference in New Issue
Block a user