1

I used frame: false to make the title bar disappear and then I try to make the button's function from others respond but it doesn't work

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">my app - some page</p>
      <a href="#" style="text-decoration: none;"><p class="title-button" id="min-btn">-</p></a>
      <a href="#" style="text-decoration: none;"><p class="title-button" id="max-btn">◻</p></a>
      <a href="#" style="text-decoration: none;"><p class="title-button" id="close-btn">×</p></a>
    </div>
    <div class="application-container">
      ...
    </div>

    <script>
      document.querySelector('button[data-title="Developer Tool"]').addEventListener('click', () => {
        window.open('devTool.html', "_blank", "width=1200,height=714,resizable=false,,autoHideMenuBar=true"); // local file
      })
      const remote = require('electron').remote;

      document.getElementById("min-btn").addEventListener("click", function (e) {
        var window = remote.getCurrentWindow();
        window.minimize(); 
      });
      
      document.getElementById("max-btn").addEventListener("click", function (e) {
        var window = remote.getCurrentWindow();
        if (!window.isMaximized()) {
          window.maximize();          
        } else {
          window.unmaximize();
        }
      });
      
      document.getElementById("close-btn").addEventListener("click", function (e) {
        var window = remote.getCurrentWindow();
        window.close();
      }); 
    </script>
    <script src="index.js"></script>
    
  </body>
</html>

index.js is a blank file

how can I make the buttons work? Please reply in details if you can as I'm still quite new to JavaScript, thank you. If anything unclear please tell me below and I'll try to provide it.

  • Use of the (built-in or userland) remote module is no longer recommended. It was depreciated in Electron v12 and removed from Electron v14. See [here](https://www.electronjs.org/docs/latest/breaking-changes#planned-breaking-api-changes-140) for more information. Take a look at this answer [How to Close, Minimize and Maximize window in Javascript](https://stackoverflow.com/questions/70566475/how-to-close-minimize-and-maximize-window-in-javascript/72122209#72122209) as an idea on how to implement. If you are having troubles implementing it let me know. – midnight-coding Oct 11 '22 at 08:19
  • @midnight-coding I don't understand the close, min and max link it says, could you please explain more about it? or perhaps I do even need to know where the code should put at – Ichor Dragon Oct 11 '22 at 09:29

1 Answers1

1

Similar to your other SO Question, you will need to become familiar with the below Electron Browser Window - Instance Methods for more information.

On window creation, we tell the window what state it is in. EG: Maximised or restored. This 'state' is sent via IPC (via our preload.js script) to the render side Javascript to either display or hide the correct maximise / restore buttons.

Following this, we just listen for any render side title bar button clicks. Once 'clicked', send a message via IPC to the main process to control the state of the window. Depending on the request ('maximise' or 'restore'), send a message back (via IPC) to the render process to either 'show' or 'hide' (via the CSS display attribute) the correct maximise / restore button.


Here, we set the window frame state to false, load the index.html file, tell the render process (via IPC) the state of our window (restored in this case) and then finally show the window.

main.js (main process)

// Import required electron modules
const electronApp = require('electron').app;
const electronBrowserWindow = require('electron').BrowserWindow;
const electronIpcMain = require('electron').ipcMain;

// Import required Node modules
const nodePath = require('path');

// Prevent garbage collection
let window;

function createWindow() {
    const window = new electronBrowserWindow({
        x: 0,
        y: 0,
        width: 800,
        height: 600,
        frame: false,
        show: false,
        webPreferences: {
            nodeIntegration: false,
            contextIsolation: true,
            sandbox: true,
            preload: nodePath.join(__dirname, 'preload.js')
        }
    });

    window.loadFile(nodePath.join(__dirname, 'index.html'))
        // Below boolean value could be retrieved from saved application setting (json file) on start-up
        .then(() => { window.webContents.send('maximised', false); })
        .then(() => { window.show(); });

    return window;
}

electronApp.on('ready', () => {
    window = createWindow();
});

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

electronApp.on('activate', () => {
    if (electronBrowserWindow.getAllWindows().length === 0) {
        createWindow();
    }
});

// ---

electronIpcMain.on('minimise', (event) => {
    window.minimize();
})

electronIpcMain.on('maximise', (event) => {
    window.maximize();
    window.webContents.send('maximised', true);
})

electronIpcMain.on('restore', (event) => {
    window.restore();
    window.webContents.send('maximised', false);
})

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

Set our channel names used to manage the window state.

preload.js (main process)

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

// White-listed channels
const ipc = {
    'channels': {
        // From render to main
        'send': [
            'minimise',
            'maximise',
            'restore',
            'close'
        ],
        // From main to render
        'receive': [
            'maximised'
        ],
        // From main to render (once)
        'receiveOnce': [],
        // From render to main and back again
        'sendReceive': []
    }
};

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

Use of this preload.js script is as follows.

/**
 *
 * Main --> Render
 * ---------------
 * Main:    window.webContents.send('channel', data); // Data is optional.
 * Render:  window.ipcRenderer.receive('channel', (data) => { methodName(data); });
 *
 * Main --> Render (Once)
 * ----------------------
 * Main:    window.webContents.send('channel', data); // Data is optional.
 * Render:  window.ipcRenderer.receiveOnce('channel', (data) => { methodName(data); });
 *
 * Render --> Main
 * ---------------
 * Render:  window.ipcRenderer.send('channel', data); // Data is optional.
 * Main:    electronIpcMain.on('channel', (event, data) => { methodName(data); })
 *
 * Render --> Main (Once)
 * ----------------------
 * Render:  window.ipcRenderer.send('channel', data); // Data is optional.
 * Main:    electronIpcMain.once('channel', (event, data) => { methodName(data); })
 *
 * Render --> Main (Value) --> Render
 * ----------------------------------
 * Render:  window.ipcRenderer.invoke('channel', data).then((result) => { methodName(result); });
 * Main:    electronIpcMain.handle('channel', (event, data) => { return someMethod(data); });
 *
 * Render --> Main (Promise) --> Render
 * ------------------------------------
 * Render:  window.ipcRenderer.invoke('channel', data).then((result) => { methodName(result); });
 * Main:    electronIpcMain.handle('channel', async (event, data) => {
 *              return await myPromise(data)
 *                  .then((result) => { return result; })
 *          });
 *
 * Main:    function myPromise(data) { return new Promise((resolve, reject) => { ... }); }
 *
 */

Finally, let's listen for and send messages to the main process (via our preload.js script) to manage the window state.

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 style="margin: 0; padding: 0;">

        <!-- Simple use of flexbox styling + prevent user from selecting title bar contents -->
        <div style="display: flex; justify-content: space-between; padding: 0.25em; background-color: grey; -webkit-user-select: none;">

            <!-- Add title bar drag functionality by using "-webkit-app-region: drag;" -->
            <span style="flex: 1 0 auto; -webkit-app-region: drag;">
                <img src="" alt="logo" style="width: 22px; height: 22px;">
                <span class="title-text">My App - Some Page</span>
            </span>

            <!-- Prevent title bar dragging by buttons by using "-webkit-app-region: no-drag;" -->
            <span style="flex: 0 1 auto; -webkit-app-region: no-drag;">
                <input type="button" id="minimise_button" value="-">
                <input type="button" id="maximise_button" value="◻">
                <input type="button" id="restore_button" value="R">
                <input type="button" id="close_button" value="×">
            </span>
        </div>

        <div class="application-container">...</div>
    </body>

    <script>
        // Declare these elements as we use them more than once
        let maximise_button = document.getElementById('maximise_button');
        let restore_button = document.getElementById('restore_button');

        // Minimise button functionality
        document.getElementById('minimise_button').addEventListener('click', () => {
            window.ipcRenderer.send('minimise');
        });

        // Maximise button functionality
        maximise_button.addEventListener('click', () => {
            window.ipcRenderer.send('maximise');
        });

        // Restore button functionality
        restore_button.addEventListener('click', () => {
            window.ipcRenderer.send('restore');
        });

        // Close button functionality
        document.getElementById('close_button').addEventListener('click', () => {
            window.ipcRenderer.send('close');
        });

        // Toggle css "display" attribute of maximise & restore buttons depending on state of window
        window.ipcRenderer.receive('maximised', (state) => {
            maximise_button.style.display = (state) ? 'none' : 'inline-block';
            restore_button.style.display = (state) ? 'inline-block' : 'none';
        });
    </script>
</html>
midnight-coding
  • 2,857
  • 2
  • 17
  • 27