diff --git a/config/webpack.common.js b/config/webpack.common.js index 036b1c3..907e2b4 100644 --- a/config/webpack.common.js +++ b/config/webpack.common.js @@ -86,6 +86,10 @@ const Entry = () => ({ import: source("./Electron/Electron.ts"), dependOn: ["Service"] }, + + SimulatorWindow: { + import: source("./Electron/SimulatorWindow.ts"), + } }); /** diff --git a/config/webpack.electron.js b/config/webpack.electron.js index de970fd..803b274 100644 --- a/config/webpack.electron.js +++ b/config/webpack.electron.js @@ -10,6 +10,7 @@ module.exports = (env) => { entry: { Service: AllEntry.Service, Electron: AllEntry.Electron, + SimulatorWindow: AllEntry.SimulatorWindow }, output: Output("[name].js"), diff --git a/package.json b/package.json index b5390c7..cbcc7e5 100644 --- a/package.json +++ b/package.json @@ -9,6 +9,7 @@ "build-lab": "npm run clean & webpack --mode development --config ./config/webpack.lab.js", "release-lab": "npm run clean & webpack --mode production --no-devtool --config ./config/webpack.lab.js", "hmr-web": "webpack serve --open --config ./config/webpack.web.js", + "hmr-desktop": "webpack serve --open --config ./config/webpack.desktop.js", "build-web": "npm run clean & webpack --mode development --config ./config/webpack.web.js", "release-web": "npm run clean & webpack --mode production --no-devtool --config ./config/webpack.web.js", "build-desktop-web": "npm run clean & webpack --mode development --config ./config/webpack.desktop.js", @@ -26,6 +27,7 @@ "release-electron": "webpack --mode production --no-devtool --config ./config/webpack.electron.js", "electron-cache": "set ELECTRON_SKIP_BINARY_DOWNLOAD=& set ELECTRON_MIRROR=https://npmmirror.com/mirrors/electron/& set ELECTRON_CUSTOM_DIR={{ version }}& node ./node_modules/electron/install.js", "electron": "set LIVING_TOGETHER_BASE_PATH=./build& npx electron ./build/Electron.js", + "hmr-electron": "npm run build-electron & set LIVING_TOGETHER_SERVICE=http://127.0.0.1:12000& npm run electron", "build-run-electron": "npm run build-desktop-web & npm run copy-fluent-icon & npm run build-electron & npm run electron", "release-run-electron": "npm run release-desktop-web & npm run copy-fluent-icon & npm run release-electron & npm run electron" }, diff --git a/source/Component/HeaderBar/HeaderBar.scss b/source/Component/HeaderBar/HeaderBar.scss index 5f284ed..1894c59 100644 --- a/source/Component/HeaderBar/HeaderBar.scss +++ b/source/Component/HeaderBar/HeaderBar.scss @@ -35,20 +35,20 @@ div.header-bar { div.header-windows-action { height: 100%; - width: 135px; - min-width: 135px; + width: 145px; + min-width: 145px; display: flex; // 在 Electron 中用于拖拽窗口 -webkit-app-region: no-drag; div.action-button { - width: 45px; - height: 45px; + width: 100%; + height: 100%; display: flex; justify-content: center; align-items: center; - font-size: .5em; + font-size: .8em; } div.action-button:hover { @@ -74,4 +74,21 @@ div.header-bar { background-color: rgba($color: #FFFFFF, $alpha: .1); } } +} + +div.header-bar.desktop-header-bar { + font-size: .9em; + + div.title > i, div.fps-view > i { + font-size: 18px; + } + + div.title > span { + display: inline-block; + padding-left: 5px; + } + + div.title { + padding-left: 5px; + } } \ No newline at end of file diff --git a/source/Component/HeaderBar/HeaderBar.tsx b/source/Component/HeaderBar/HeaderBar.tsx index 2be43d2..36e884f 100644 --- a/source/Component/HeaderBar/HeaderBar.tsx +++ b/source/Component/HeaderBar/HeaderBar.tsx @@ -4,6 +4,7 @@ import { useStatusWithEvent, useStatus, IMixinStatusProps } from "@Context/Statu import { useSettingWithEvent, IMixinSettingProps, Platform } from "@Context/Setting"; import { Theme, BackgroundLevel, FontLevel } from "@Component/Theme/Theme"; import { LocalizationTooltipHost } from "@Component/Localization/LocalizationTooltipHost"; +import { useElectron, IMixinElectronProps } from "@Context/Electron"; import { I18N } from "@Component/Localization/Localization"; import "./HeaderBar.scss"; @@ -78,7 +79,8 @@ class HeaderFpsView extends Component { public render() { return @@ -88,7 +90,10 @@ class HeaderWindowsAction extends Component {
-
+
this.props.electron?.close()} + >
@@ -115,8 +120,13 @@ class HeaderBar extends Component, E extends Record>(consumer: Consumer, keyName: string) { +function superConnect(consumer: Consumer, keyName: string) { return (components: R): R => { return ((props: any) => { diff --git a/source/Context/Electron.ts b/source/Context/Electron.ts new file mode 100644 index 0000000..63518df --- /dev/null +++ b/source/Context/Electron.ts @@ -0,0 +1,20 @@ +import { createContext } from "react"; +import { superConnect } from "@Context/Context"; +import { ISimulatorAPI } from "@Electron/SimulatorAPI"; + +interface IMixinElectronProps { + electron?: ISimulatorAPI; +} + +const ElectronContext = createContext({} as ISimulatorAPI); + +ElectronContext.displayName = "Electron"; +const ElectronProvider = ElectronContext.Provider; +const ElectronConsumer = ElectronContext.Consumer; + +/** + * 修饰器 + */ +const useElectron = superConnect(ElectronConsumer, "electron"); + +export { useElectron, ElectronProvider, IMixinElectronProps, ISimulatorAPI }; \ No newline at end of file diff --git a/source/Electron/Electron.ts b/source/Electron/Electron.ts index e274c75..3f54936 100644 --- a/source/Electron/Electron.ts +++ b/source/Electron/Electron.ts @@ -1,5 +1,6 @@ -import { app, BrowserWindow } from "electron"; +import { app, BrowserWindow, ipcMain } from "electron"; import { Service } from "@Service/Service"; +import { join as pathJoin } from "path"; const ENV = process.env ?? {}; class ElectronApp { @@ -14,6 +15,11 @@ class ElectronApp { public async runService() { + if (ENV.LIVING_TOGETHER_SERVICE) { + this.serviceUrl = ENV.LIVING_TOGETHER_SERVICE; + return; + } + let defaultPort: number | undefined = parseInt(ENV.LIVING_TOGETHER_DEFAULT_PORT ?? ""); if (isNaN(defaultPort)) defaultPort = undefined; @@ -22,7 +28,7 @@ class ElectronApp { ); } - public mainWindows?: BrowserWindow; + public simulatorWindow?: BrowserWindow; public async runMainThread() { @@ -30,14 +36,36 @@ class ElectronApp { await this.runService(); - this.mainWindows = new BrowserWindow({ + let preload = pathJoin(__dirname, "./SimulatorWindow.js"); + + // if (ENV.LIVING_TOGETHER_BASE_PATH) { + // preload = pathJoin(__dirname, ENV.LIVING_TOGETHER_BASE_PATH, "./SimulatorWindow.js"); + // } + + this.simulatorWindow = new BrowserWindow({ width: 800, height: 600, titleBarStyle: 'hidden', frame: false, + minWidth: 460, + minHeight: 300, + webPreferences: { preload } }); - this.mainWindows.loadURL(this.serviceUrl); + this.simulatorWindow.loadURL(this.serviceUrl); + + this.handelSimulatorWindowBehavior(); + + app.on('window-all-closed', function () { + if (process.platform !== 'darwin') app.quit() + }) + } + + private handelSimulatorWindowBehavior() { + + ipcMain.on("close", () => { + this.simulatorWindow?.close(); + }); } } diff --git a/source/Electron/SimulatorAPI.ts b/source/Electron/SimulatorAPI.ts new file mode 100644 index 0000000..bce5984 --- /dev/null +++ b/source/Electron/SimulatorAPI.ts @@ -0,0 +1,25 @@ + +interface ISimulatorAPI { + + /** + * 关闭窗口 + */ + close: () => void; + + /** + * 最大化窗口 + */ + maximize: () => void; + + /** + * 取消最大化 + */ + unMaximize: () => void; + + /** + * 是否处于最大化状态 + */ + isMaximized: () => boolean; +} + +export { ISimulatorAPI } \ No newline at end of file diff --git a/source/Electron/SimulatorWindow.ts b/source/Electron/SimulatorWindow.ts new file mode 100644 index 0000000..9035380 --- /dev/null +++ b/source/Electron/SimulatorWindow.ts @@ -0,0 +1,19 @@ +import { contextBridge, ipcRenderer } from "electron"; +import { ISimulatorAPI } from "@Electron/SimulatorAPI" + +const API: ISimulatorAPI = { + + close() { + ipcRenderer.send("close"); + }, + + maximize(){}, + + unMaximize(){}, + + isMaximized(){ + return false + } +} + +contextBridge.exposeInMainWorld("API", API); \ No newline at end of file diff --git a/source/Page/SimulatorDesktop/SimulatorDesktop.tsx b/source/Page/SimulatorDesktop/SimulatorDesktop.tsx index 08d4c68..55a6a50 100644 --- a/source/Page/SimulatorDesktop/SimulatorDesktop.tsx +++ b/source/Page/SimulatorDesktop/SimulatorDesktop.tsx @@ -1,7 +1,9 @@ import { Component, ReactNode } from "react"; import { SettingProvider, Setting, Platform } from "@Context/Setting"; import { Theme, BackgroundLevel, FontLevel } from "@Component/Theme/Theme"; +import { ISimulatorAPI } from "@Electron/SimulatorAPI"; import { StatusProvider, Status } from "@Context/Status"; +import { ElectronProvider } from "@Context/Electron"; import { ClassicRenderer } from "@GLRender/ClassicRenderer"; import { initializeIcons } from '@fluentui/font-icons-mdl2'; import { RootContainer } from "@Component/Container/RootContainer"; @@ -16,6 +18,11 @@ import "./SimulatorDesktop.scss"; initializeIcons("./font-icon/"); class SimulatorDesktop extends Component { + + /** + * Electron API + */ + public electron: ISimulatorAPI; /** * 全局设置 @@ -47,6 +54,16 @@ class SimulatorDesktop extends Component { individual.position[2] = (Math.random() - .5) * 2; }) }; + + (window as any).setting = this.setting; + (window as any).status = this.status; + + this.electron = {} as ISimulatorAPI; + if ((window as any).API) { + this.electron = (window as any).API; + } else { + console.error("SimulatorDesktop: Can't find electron API"); + } } public componentDidMount() { @@ -82,7 +99,9 @@ class SimulatorDesktop extends Component { public render(): ReactNode { return - {this.renderContent()} + + {this.renderContent()} + } @@ -94,9 +113,9 @@ class SimulatorDesktop extends Component { fontLevel={FontLevel.Level3} > - +