0

I'm trying to make my custom title button but it seems like some error has occur-ed. I tried the method from this one but not working with error message.

main.js:

const { app, BrowserWindow } = require('electron');
const path = require('path');
const electronIpcMain = require('electron').ipcMain;

// Handle creating/removing shortcuts on Windows when installing/uninstalling.
// eslint-disable-next-line global-require
if (require('electron-squirrel-startup')) {
  app.quit();
}

const createWindow = () => {
  // Create the browser window.
  const mainWindow = new BrowserWindow({
    width: 1370,
    height: 755,
    resizable: false,
    autoHideMenuBar: true,
    webPreferences: {
      preload: path.join(__dirname, 'preload.js'),
      contextIsolation: true,
      nodeIntegration: false,
    },
    icon: path.join(__dirname, 'res/applogo.png'),
    frame: false,
    movable: false,
  });
  // and load the index.html of the app.
  mainWindow.loadFile(path.join(__dirname, 'index.html'));
  
  // Open the DevTools.
  mainWindow.webContents.openDevTools();
};


// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.on('ready', createWindow);

// Quit when all windows are closed, except on macOS. There, it's common
// for applications and their menu bar to stay active until the user quits
// explicitly with Cmd + Q.
app.on('window-all-closed', () => {
  if (process.platform !== 'darwin') {
    app.quit();
  }
});

app.on('activate', () => {
  // On OS X it's common to re-create a window in the app when the
  // dock icon is clicked and there are no other windows open.
  if (BrowserWindow.getAllWindows().length === 0) {
    createWindow();
  }
});

//set applogo.png to every window
app.on("browser-window-created", (event, window) => {
  window.setIcon(path.join(__dirname, 'res/applogo.png'))
});

//win btns
electronIpcMain.on('window:minimize', () => {
  window.minimize();
})

electronIpcMain.on('window:maximize', () => {
  window.maximize();
})

electronIpcMain.on('window:restore', () => {
  window.restore();
})

electronIpcMain.on('window:close', () => {
  window.close();
})

preload.js:

// Import the necessary Electron components.
const contextBridge = require('electron').contextBridge;
const ipcRenderer = require('electron').ipcRenderer;

// White-listed channels.
const ipc = {
    'render': {
        // From render to main.
        'send': [
            'window:minimize', // Channel names
            'window:maximize',
            'window:restore',
            'window:close'
        ],
        // From main to render.
        'receive': [],
        // From render to main and back again.
        'sendReceive': []
    }
};

// Exposed protected methods in the render process.
contextBridge.exposeInMainWorld(
    // Allowed 'ipcRenderer' methods.
    'ipcRender', {
        // From render to main.
        send: (channel, args) => {
            let validChannels = ipc.render.send;
            if (validChannels.includes(channel)) {
                ipcRenderer.send(channel, args);
            }
        },
        // From main to render.
        receive: (channel, listener) => {
            let validChannels = ipc.render.receive;
            if (validChannels.includes(channel)) {
                // Deliberately strip event as it includes `sender`.
                ipcRenderer.on(channel, (event, ...args) => listener(...args));
            }
        },
        // From render to main and back again.
        invoke: (channel, args) => {
            let validChannels = ipc.render.sendReceive;
            if (validChannels.includes(channel)) {
                return ipcRenderer.invoke(channel, args);
            }
        }
    }
);

index.html:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <title>my app</title>
    <link rel="stylesheet" href="index.css" />
  </head>
  <body>
    <div class="title-container">
      <img src="res/applogo.png" style="width: 22px; height: 22px;">
      <div style="width: 5px; height: 22px;"></div>
      <p class="title-text">Phigros Fanmade - Editor - Chart Name</p>
      <div class="title-button" id="min-btn">
        <img src="res/icons/titleButton/minimize_button.png" style="width: 20px; height: 20px; margin: 1px;">
      </div>
      <div class="title-button" id="res-btn">
        <img src="res/icons/titleButton/restore_button.png" style="width: 20px; height: 20px; margin: 1px;">
      </div>
      <div class="title-button" id="max-btn">
        <img src="res/icons/titleButton/maximize_button.png" style="width: 20px; height: 20px; margin: 1px;">
      </div>
      <div class="title-button" id="clo-btn">
        <img src="res/icons/titleButton/close_button.png" style="width: 20px; height: 20px; margin: 1px;">
      </div>
    </div>
    <div class="application-container">
      <!-- ... -->
    </div>

    <script>
      //developer tool open command
      document.querySelector("div[data-title='Developer Tool']").addEventListener('click', () => {
        window.open('devTool.html', "_blank", "width=1200,height=714,resizable=false,autoHideMenuBar=true,frame=false");
      })
      
    </script>
    <script src="index.js"></script>
  </body>

  <script>
    //title buttons call commands
    document.getElementById('min-btn').addEventListener('click', () => {
      window.ipcRender.send('window:minimize');
    });

    document.getElementById('max-btn').addEventListener('click', () => {
      window.ipcRender.send('window:maximize');
    });

    document.getElementById('res-btn').addEventListener('click', () => {
      window.ipcRender.send('window:restore');
    });

    document.getElementById('clo-btn').addEventListener('click', () => {
      window.ipcRender.send('window:close');
    });
  </script>
</html>

index.js is empty and index.css is just basic stylings.

Error Message:

A JavaScript error occurred in the main process Uncaught Exception: ReferenceError: window is not defined at IpcMainImpl. (C:\Users...\src\main.js:64:3) at IpcMainImpl.emit (node:events:527:28) at EventEmitter. (node:electron/js2c/browser_init:161:11014) at EventEmiiter.emit (node:events:527:28)

Thank you for reading so far, please help me if you're able to. If you need me to provide more info, I'll update it as soon as possible when I see it. Thank you.

1 Answers1

3

The primary problem is the scope of window within your main.js file.

The variable window is used within your electronIpcMain.on() functions, but it is not declared prior. As a result, you receive the window is not defined at error message.

To rectify this, you will need to add let window; before the createWindow() const.

Additionally, I would just declare the createWindow object as a function and not a const. This will simplify things. Additionally, this createWindow() function should return the Electron window object, allowing you to manipulate it further at a late time should you need to do so, like when you are minimizing, maximizing, restoring and closing. Other manipulations may be x, y, width, height, etc.


I have simplified the below main.js file to only include the above stated changes and code needed for a minimum working example.

main.js (main process)

const { app, BrowserWindow } = require('electron');
const path = require('path');
const electronIpcMain = require('electron').ipcMain;

// Declare window in the (file) scope, so it can be accessed by other functions in this file
let window;

function createWindow() {
    // This window const is function scoped, therefore not accessible outside this function
    const window = new BrowserWindow({
        width: 1370,
        height: 755,
        resizable: false,
        autoHideMenuBar: true,
        webPreferences: {
            preload: path.join(__dirname, 'preload.js'),
            contextIsolation: true,
            nodeIntegration: false,
        },
        frame: false,
        movable: false,
    });

    window.loadFile(path.join(__dirname, 'index.html'))
        // The loadFile() function returns a promise, so let's use it correctly below
        .then(() => {window.webContents.openDevTools();})

    // Return this function scoped window const
    return window;
}

app.on('ready', () => {
    // Assign the returned value of the createWindow() function to this (file) scoped variable.
    window = createWindow();
});

app.on('window-all-closed', () => {
    if (process.platform !== 'darwin') {
        app.quit();
    }
});

app.on('activate', () => {
    if (BrowserWindow.getAllWindows().length === 0) {
        // Assign the returned value of the createWindow() function to this (file) scoped variable.
        window = createWindow();
    }
});

app.on('browser-window-created', (event, window) => {
    window.setIcon(path.join(__dirname, 'res/applogo.png'));
});

electronIpcMain.on('window:minimize', () => {
    // Now we can access the window variable
    window.minimize();
})

electronIpcMain.on('window:maximize', () => {
    // Now we can access the window variable
    window.maximize();
})

electronIpcMain.on('window:restore', () => {
    // Now we can access the window variable
    window.restore();
})

electronIpcMain.on('window:close', () => {
    // Now we can access the window variable
    window.close();
})

Your preload.js script remains unchanged.

preload.js (main process)

// Import the necessary Electron components.
const contextBridge = require('electron').contextBridge;
const ipcRenderer = require('electron').ipcRenderer;

// White-listed channels.
const ipc = {
    'render': {
        // From render to main.
        'send': [
            'window:minimize',
            'window:maximize',
            'window:restore',
            'window:close'
        ],
        // From main to render.
        'receive': [],
        // From render to main and back again.
        'sendReceive': []
    }
};

// Exposed protected methods in the render process.
contextBridge.exposeInMainWorld(
    // Allowed 'ipcRenderer' methods.
    'ipcRender', {
        // From render to main.
        send: (channel, args) => {
            let validChannels = ipc.render.send;
            if (validChannels.includes(channel)) {
                ipcRenderer.send(channel, args);
            }
        },
        // From main to render.
        receive: (channel, listener) => {
            let validChannels = ipc.render.receive;
            if (validChannels.includes(channel)) {
                // Deliberately strip event as it includes `sender`.
                ipcRenderer.on(channel, (event, ...args) => listener(...args));
            }
        },
        // From render to main and back again.
        invoke: (channel, args) => {
            let validChannels = ipc.render.sendReceive;
            if (validChannels.includes(channel)) {
                return ipcRenderer.invoke(channel, args);
            }
        }
    }
);

Lastly, I have simplified the index.html file for a minimum reproducible example.

index.html (render process)

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>my app</title>
        <meta http-equiv="Content-Security-Policy" content="script-src 'self' 'unsafe-inline';"/>
    </head>
    <body>
        <div>
            <div class="title-button" id="min-btn"> Minimize </div>
            <div class="title-button" id="res-btn"> Restore </div>
            <div class="title-button" id="max-btn"> Maximize </div>
            <div class="title-button" id="clo-btn"> Close </div>
        </div>
    </body>

    <script>
        document.getElementById('min-btn').addEventListener('click', () => {
            window.ipcRender.send('window:minimize');
        });

        document.getElementById('res-btn').addEventListener('click', () => {
            window.ipcRender.send('window:restore');
        });

        document.getElementById('max-btn').addEventListener('click', () => {
            window.ipcRender.send('window:maximize');
        });

        document.getElementById('clo-btn').addEventListener('click', () => {
            window.ipcRender.send('window:close');
        });
    </script>
</html>
midnight-coding
  • 2,857
  • 2
  • 17
  • 27
  • @IchorDragon No problem at all using [`app.on('browser-window-created', () => { ... ));`](https://www.electronjs.org/docs/latest/api/app#event-browser-window-created). That said, it will not show as you have created your window with `frame: false`. Of course, any other windows you may create that still has a frame, will show the icon. I have updated the answer to include that section of code as well. – midnight-coding Oct 31 '22 at 08:13