windowManage.ts 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  1. import { join } from "node:path";
  2. import { BrowserWindow, shell } from "electron";
  3. import { isLinux, isMac, isWin } from "../utils";
  4. import { destroyTray } from "./trayManage";
  5. import { getStore } from "./storeManage";
  6. import { getIsForceQuit } from "./appManage";
  7. import { registerShortcuts, unregisterShortcuts } from "./shortcutManage";
  8. const url = process.env.VITE_DEV_SERVER_URL;
  9. let mainWindow: BrowserWindow | null = null;
  10. let splashWindow: BrowserWindow | null = null;
  11. const store = getStore();
  12. function createSplashWindow() {
  13. splashWindow = new BrowserWindow({
  14. frame: false,
  15. width: 200,
  16. height: 200,
  17. resizable: false,
  18. transparent: true,
  19. });
  20. splashWindow.loadFile(global.pathConfig.splashHtml);
  21. splashWindow.on("closed", () => {
  22. splashWindow = null;
  23. });
  24. }
  25. export function createMainWindow() {
  26. createSplashWindow();
  27. mainWindow = new BrowserWindow({
  28. title: "OpenIM",
  29. icon: join(global.pathConfig.publicPath, "favicon.ico"),
  30. frame: false,
  31. show: false,
  32. minWidth: 680,
  33. minHeight: 550,
  34. titleBarStyle: "hiddenInset",
  35. webPreferences: {
  36. preload: global.pathConfig.preload,
  37. // Warning: Enable nodeIntegration and disable contextIsolation is not secure in production
  38. // Consider using contextBridge.exposeInMainWorld
  39. // Read more on https://www.electronjs.org/docs/latest/tutorial/context-isolation
  40. nodeIntegration: false,
  41. contextIsolation: true,
  42. sandbox: false,
  43. devTools: true,
  44. webSecurity: false,
  45. },
  46. });
  47. if (process.env.VITE_DEV_SERVER_URL) {
  48. // Open devTool if the app is not packaged
  49. mainWindow.loadURL(url);
  50. } else {
  51. mainWindow.loadFile(global.pathConfig.indexHtml);
  52. }
  53. // // Make all links open with the browser, not with the application
  54. mainWindow.webContents.setWindowOpenHandler(({ url }) => {
  55. if (url.startsWith("https:")) shell.openExternal(url);
  56. return { action: "deny" };
  57. });
  58. mainWindow.on("focus", () => {
  59. mainWindow?.flashFrame(false);
  60. registerShortcuts();
  61. });
  62. mainWindow.on("blur", () => {
  63. unregisterShortcuts();
  64. });
  65. mainWindow.on("close", (e) => {
  66. if (
  67. getIsForceQuit() ||
  68. !mainWindow.isVisible() ||
  69. store.get("closeAction") === "quit"
  70. ) {
  71. mainWindow = null;
  72. destroyTray();
  73. } else {
  74. e.preventDefault();
  75. if (isMac && mainWindow.isFullScreen()) {
  76. mainWindow.setFullScreen(false);
  77. }
  78. mainWindow?.hide();
  79. }
  80. });
  81. return mainWindow;
  82. }
  83. export function splashEnd() {
  84. splashWindow?.close();
  85. mainWindow?.show();
  86. }
  87. // utils
  88. export const isExistMainWindow = (): boolean =>
  89. !!mainWindow && !mainWindow?.isDestroyed();
  90. export const isShowMainWindow = (): boolean => {
  91. if (!mainWindow) return false;
  92. return mainWindow.isVisible() && (isWin ? true : mainWindow.isFocused());
  93. };
  94. export const closeWindow = () => {
  95. if (!mainWindow) return;
  96. mainWindow.close();
  97. };
  98. export const sendEvent = (name: string, ...args: any[]) => {
  99. if (!mainWindow) return;
  100. mainWindow.webContents.send(name, ...args);
  101. };
  102. export const minimize = () => {
  103. if (!mainWindow) return;
  104. mainWindow.minimize();
  105. };
  106. export const updateMaximize = () => {
  107. if (!mainWindow) return;
  108. if (mainWindow.isMaximized()) {
  109. mainWindow.unmaximize();
  110. } else {
  111. mainWindow.maximize();
  112. }
  113. };
  114. export const toggleHide = () => {
  115. if (!mainWindow) return;
  116. mainWindow.isVisible() ? mainWindow.hide() : mainWindow.show();
  117. };
  118. export const toggleMinimize = () => {
  119. if (!mainWindow) return;
  120. if (mainWindow.isMinimized()) {
  121. if (!mainWindow.isVisible()) {
  122. mainWindow.show();
  123. }
  124. mainWindow.restore();
  125. mainWindow.focus();
  126. } else {
  127. mainWindow.minimize();
  128. }
  129. };
  130. export const showWindow = () => {
  131. if (!mainWindow) return;
  132. if (mainWindow.isMinimized()) {
  133. mainWindow.restore();
  134. }
  135. if (mainWindow.isVisible()) {
  136. mainWindow.focus();
  137. } else {
  138. mainWindow.show();
  139. }
  140. };
  141. export const hideWindow = () => {
  142. if (!mainWindow) return;
  143. mainWindow.hide();
  144. };
  145. export const setFullScreen = (isFullscreen: boolean): boolean => {
  146. if (!mainWindow) return false;
  147. if (isLinux) {
  148. // linux It needs to be resizable before it can be full screen
  149. if (isFullscreen) {
  150. mainWindow.setResizable(isFullscreen);
  151. mainWindow.setFullScreen(isFullscreen);
  152. } else {
  153. mainWindow.setFullScreen(isFullscreen);
  154. mainWindow.setResizable(isFullscreen);
  155. }
  156. } else {
  157. mainWindow.setFullScreen(isFullscreen);
  158. }
  159. return isFullscreen;
  160. };
  161. export const getWebContents = (): Electron.WebContents => {
  162. if (!mainWindow) throw new Error("main window is undefined");
  163. return mainWindow.webContents;
  164. };
  165. export const toggleDevTools = () => {
  166. if (!mainWindow) return;
  167. if (mainWindow.webContents.isDevToolsOpened()) {
  168. mainWindow.webContents.closeDevTools();
  169. } else {
  170. mainWindow.webContents.openDevTools({
  171. mode: "detach",
  172. });
  173. }
  174. };