ElectronでWindows98風のGUIをMacで動かす

6 min read読了の目安(約5800字

懐かしのWindows98

こんにちは。カズ之助です。
唐突ですが、私の初めてのPCはWindows95で、二代目のPCはWindows98でした(自分語り)。
当時幼稚園児の私にシステムエンジニアの父親が誕生日プレゼントに父親のお下がりでVAIO PCG-C1XFをもらった記憶があります。幼稚園の年中さんのときにもらって、小学3年くらいまで使ってた記憶があります。

現在はMacを使っていますが「MacでもWindows98の落ち着いた懐かしのGUIを動かしたい!」というわけで、ElectronとReactと98.cssを使ってサクッと作っていきたいと思います。

ちなみにElectronでOS独自の機能を使用しているわけじゃないのでMacじゃなくても、最新のWindowsやLinuxでも動きます。たぶんね。
(Macって書くとタイトル映えすると思ったんや……許してクレメンス……)
最新のWindows10で98風のウインドウがあったらそれはそれで面白いかもね!

できたもの


どうですか?Mac特有の角丸ウインドウではあるものの、それっぽくないですか???
ちゃんと右上の[x]を押すとウインドウが閉じられます。

Electronの準備

Electronは数々の先駆者の人たちの知恵を集めてElectronでReactを動かすためのテンプレート的なものを(個人用に)作成しました。ご自由にお使いください……。先駆者の方々マジでありがとう。

CDN形式で98.cssを読み込む

public/index.htmlhead内に98.cssのCDNを書き込みましょう。

98.cssのcdn
<link
  rel="stylesheet"
  href="https://unpkg.com/98.css"
>

これで98.cssが使えるようになります(それはそう)。やったね!

ウインドウの設定をする

ウインドウの設定をしましょう。フレームは非表示で作ります。

テンプレートのelectron.jsのウインドウの設定を変更します。

ウインドウの設定
mainWindow = new BrowserWindow({
    frame: false,
    width: 900,
    height: 680,
    webPreferences: {
        nodeIntegration: false
	preload: `${__dirname}/preload.js`,
    },
})

preload.jsにはこれを書いてください。

preload.js
const { ipcRenderer } = require('electron')
window.ipcRenderer = ipcRenderer

これを書くことで、React側からipcRendererを使用することができます……

タイトルバーを作る

さて、ここからいよいよ本題です。まずはタイトルバーを作っていきましょう。
タイトルバーはドラッグ可能である必要があります。

src/components内にTitleBar.jstitle.cssを作成しましょう。
本当はTitleBar.js内にコンポーネントCSSを書きたいんですが、-webkitから始まるパラメータをコンポーネントCSSに書く方法がわからない……

さて、title.cssから書き始めていきましょう。

title.css
.title-bar {
    -webkit-app-region: drag
}

.title-bar Button {
    -webkit-app-region: no-drag
}

ここでは、title-barはドラッグ可能に指定しています。
こうすることでウインドウをドラッグして端っこに持っていったら最大化とか画面半分まで拡大とかもできるようになります。
でも、これだけだとボタンが押せなくなってしまいます(クリック時にOSに処理が持っていかれてしまうため)。なので、ボタンのところだけはドラッグを無効化してやりましょう。

次はTitleBar.jsです。
ウインドウを閉じるなどの処理はElectron側で行うため、Electronへの通信を行うipcRendererwindowから読み込んでおきます。

TitleBar.js
import './title.css'
const { ipcRenderer } = window

const TitleBar = (props) => {
    return (
        <div class="title-bar">
            <div class="title-bar-text">{props.title}</div>
            <div class="title-bar-controls">
                <button aria-label="Close" onClick={exit}></button>
            </div>
        </div>
    )
}

const exit = () => {
    ipcRenderer.send('exit', 'exit')
}

export default TitleBar

今回はpropsを使ってタイトルバーのテキストを変更できるようにしてみました。
次はApp.jsxの方を変更していきます。

App.jsx
import React from 'react';
import TitleBar from './components/TitleBar'

function App() {

  const windowStyle = {
    "height": "100%"
  }
  
  return (
    <div className="App window" style={windowStyle}>
      <TitleBar title="This is Title" />
    </div>
  );
}

export default App;

この状態でyarn startをすると、

こんな感じのウインドウが立ち上がると思います。なつかしいですね!
でも、この状態で[x]を押してもウインドウが閉じません。まだelectron側で閉じる処理を書いてないからね。ちゃちゃっと書いちゃいましょう。electron.jsの最後に追記する感じに書いちゃってください。

electron.jsの一部
ipcMain.on('exit', () => {
    mainWindow.close();
})

これを書いて保存したらもう一度yarn startしてみましょう。今度は[x]を押すとウインドウが閉じると思います。やったね!

ウインドウのスタイルを当てる

タイトルバーができたので今回は7割ほど完成しました。
あとはウインドウの中身を書いていけばいいだけです。いいですね。楽ですね。

では、早速中身をかいていきたいのですが、その前にApp.jsxを修正する必要があります。

App.jsx
import React from 'react';
import TitleBar from './components/TitleBar'

function App() {

  // 追加!
  const bodyStyle = {
    "width": "calc(100% - 5px)",
    "height": "calc(100vh - 45px)"
  }
  
  const windowStyle = {
    "height": "100%"
  }
  
  return (
    <div className="App window" style={windowStyle}>
      <TitleBar title="This is Title" />
      
      {/* 追加! */}
      <div className="window-body" style={bodyStyle}>
      </biv>
      
    </div>
  );
}

export default App;

追加したところにコメントを書いてあります。
bodyStyleを定義して、空のdivを作成してwindow-bodyというクラスを指定して、bodyStyleを当てています。
これで実行してみましょう!

どうですか!?!?!?一気に昔懐かしのWindows感出てきませんでしたか????
下に少し隙間が空いているのが気になりますが、中身を書くと消えるので気にせず次行きましょう!

ウインドウの中身を書いていく

それではついにウインドウの中身を書いていきましょう。ここまでくれば9割方完成です!
src/pagesの中にTop.jsを作成しましょう。

今回もpropsから内容を受け取るように書いていきます。

Top.js
function Top (props) {
    return (
        <div>
            <p>{props.text}</p>
	</div>
    )
}
export default Top

では、App.jsxから、このページを読み込んで表示しましょう。

App.jsx
import React from 'react';
import TitleBar from './components/TitleBar'

// 追加!
import Top from './pages/Top'

function App() {
  const bodyStyle = {
    "width": "calc(100% - 5px)",
    "height": "calc(100vh - 45px)"
  }
  const windowStyle = {
    "height": "100%"
  }
  return (
    <div className="App window" style={windowStyle}>
      <TitleBar title="This is Title" />
      <div className="window-body" style={bodyStyle}>
        {/* 追加! */}
        <Top text="Hello World! Windows98"/>
      </div>
    </div>
  );
}

export default App;

さて、ここまで書けたら実行しましょう!

どうですか!?いい感じじゃないですか??


違和感


#007e7dで構成された壁紙の上にウインドウを置くとそれっぽい……!!

あとは98.cssのページからいろいろいじってみると面白いかもしれません。


サンプルコードをいろいろ追加してみた
なんかいい感じに98っぽくなるのでぜひぜひ試してみてくださいね!

追記

Zennの公式Twitterでピックアップされました!!(うれしい)

https://twitter.com/zenn_dev/status/1367003056865308676?s=20

やったー!

この記事に贈られたバッジ