GithubHelp home page GithubHelp logo

tommylitommy / electron-custom-menu-bar Goto Github PK

View Code? Open in Web Editor NEW

This project forked from saisandeepvaddi/electron-custom-menu-bar

0.0 0.0 0.0 399 KB

License: Creative Commons Zero v1.0 Universal

HTML 13.09% JavaScript 76.36% CSS 10.56%

electron-custom-menu-bar's Introduction

Creating a custom menu bar in Electron

Do you want to replace your electron app's menu bar to look something cool? Let's see how to build a custom menu bar by building a similar one to slack's menu bar.

Pre-requisite

Basics of ElectronJS. Check this tutorial to get started.

Resources

Finished code is available at https://github.com/saisandeepvaddi/electron-custom-menu-bar

What we'll build

Here is what it is going to look when we finish.

Result image before clicking on menu

Result image with menu open

Result image with mouse over close

Set up electron project

Set up a minimal electron app from electron's official quick start github repo.

# Clone the Quick Start repository
$ git clone https://github.com/electron/electron-quick-start

# Go into the repository
$ cd electron-quick-start

# Install the dependencies and run
$ npm install && npm start

Main process code

When you first run npm start you will see a window with a default menu bar attached to it. To replace it with our menu bar, we need to do two things. In the main.js file we have,

  1. Set the frame: false in the options object for new BrowserWindow({frame: false, ...other-options}). This will create a window without toolbars, borders, etc., Check frameless-window for more details.
  2. Register an event listener on ipcMain which receives a mouse click position when the mouse is clicked on the hamburger icon.
// main.js

  mainWindow = new BrowserWindow({
    width: 800,
    height: 600,
    webPreferences: {
      preload: path.join(__dirname, "preload.js")
      // (NOT RECOMMENDED)
      // If true, we can skip attaching functions from ./menu-functions.js to window object in preload.js.
      // And, instead, we can use electron APIs directly in renderer.js
      // From Electron v5, nodeIntegration is set to false by default. And it is recommended to use preload.js to get access to only required Node.js apis.
      // nodeIntegration: true
    },
    frame: false //Remove frame to hide default menu
  });
  // ...other stuff
}

// Register an event listener. When ipcRenderer sends mouse click co-ordinates, show menu at that position.
ipcMain.on(`display-app-menu`, function(e, args) {
  if (isWindows && mainWindow) {
    menu.popup({
      window: mainWindow,
      x: args.x,
      y: args.y
    });
  }
});

// ... other stuff.

Create a file called menu-functions.js and define these functions. All the functions here take electron's BrowserWindow object (mainWindow in this app) and run minimize, maximize, close, open menu actions which we need to trigger from our custom menu bar.

// menu-functions.js
const { remote, ipcRenderer } = require("electron");

function getCurrentWindow() {
  return remote.getCurrentWindow();
}

function openMenu(x, y) {
  ipcRenderer.send(`display-app-menu`, { x, y });
}

function minimizeWindow(browserWindow = getCurrentWindow()) {
  if (browserWindow.minimizable) {
    // browserWindow.isMinimizable() for old electron versions
    browserWindow.minimize();
  }
}

function maximizeWindow(browserWindow = getCurrentWindow()) {
  if (browserWindow.maximizable) {
    // browserWindow.isMaximizable() for old electron versions
    browserWindow.maximize();
  }
}

function unmaximizeWindow(browserWindow = getCurrentWindow()) {
  browserWindow.unmaximize();
}

function maxUnmaxWindow(browserWindow = getCurrentWindow()) {
  if (browserWindow.isMaximized()) {
    browserWindow.unmaximize();
  } else {
    browserWindow.maximize();
  }
}

function closeWindow(browserWindow = getCurrentWindow()) {
  browserWindow.close();
}

function isWindowMaximized(browserWindow = getCurrentWindow()) {
  return browserWindow.isMaximized();
}

module.exports = {
  getCurrentWindow,
  openMenu,
  minimizeWindow,
  maximizeWindow,
  unmaximizeWindow,
  maxUnmaxWindow,
  isWindowMaximized,
  closeWindow
};

We need to attach these functions to the window object which we can use in the renderer process. If you are using older versions (<5.0.0) of electron or you set nodeIntegration: true in BrowserWindow's options, you can use the above menu-functions.js file directly in the renderer process. Electron new versions have it false set by default for security reasons.

// preload.js
const { remote } = require("electron");
const {
  getCurrentWindow,
  openMenu,
  minimizeWindow,
  unmaximizeWindow,
  maxUnmaxWindow,
  isWindowMaximized,
  closeWindow
} = require("./menu-functions");

window.addEventListener("DOMContentLoaded", () => {
  window.getCurrentWindow = getCurrentWindow;
  window.openMenu = openMenu;
  window.minimizeWindow = minimizeWindow;
  window.unmaximizeWindow = unmaximizeWindow;
  window.maxUnmaxWindow = maxUnmaxWindow;
  window.isWindowMaximized = isWindowMaximized;
  window.closeWindow = closeWindow;
});

We need a menu now. Create a simple menu in a new menu.js file. You can learn how to add your own options to the menu at official docs. Electron has some easy to follow documentation with examples.

// menu.js

const { app, Menu } = require("electron");

const isMac = process.platform === "darwin";

const template = [
  {
    label: "File",
    submenu: [isMac ? { role: "close" } : { role: "quit" }]
  }
];

const menu = Menu.buildFromTemplate(template);
Menu.setApplicationMenu(menu);

module.exports = {
  menu
};

We are done on the main process side. Now, let's build our custom menu bar. If you see the menu in the image, you'll see that we have these things on our menu bar.

  1. On the left side, a hamburger icon which is where the menu will open.
  2. On the right side, we have minimize button, maximize-unmaximize button, and close button.

I used fontawesome js file from fontawesome.com for icons. Add it to HTML's <head> tag. I removed Content-Security-Policy meta tags to allow fontawesome js file to run for now. In production, make sure you properly allow which code should run. Check CSP for more details.

<!-- index.html -->
<!DOCTYPE html>
<html>
 <head>
   <meta charset="UTF-8" />
   <!-- https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP -->

   <title>My Awesome App</title>
   <link rel="stylesheet" href="style.css" />
   <script src="https://kit.fontawesome.com/1c9144b004.js" crossorigin="anonymous"></script>
   </head>
 </head>
 <body>
   <div id="menu-bar">
     <div class="left" role="menu">
       <button class="menubar-btn" id="menu-btn"><i class="fas fa-bars"></i></button>
       <h5>My Awesome App</h5>
     </div>
     <div class="right">
       <button class="menubar-btn" id="minimize-btn"><i class="fas fa-window-minimize"></i></button>
       <button class="menubar-btn" id="max-unmax-btn"><i class="far fa-square"></i></button>
       <button class="menubar-btn" id="close-btn"><i class="fas fa-times"></i></button>
     </div>
   </div>
   <div class="container">
     Hello there!
   </div>

   <!-- You can also require other files to run in this process -->
   <script src="./renderer.js"></script>
 </body>
</html>
/* style.css */

body {
  padding: 0;
  margin: 0;
  font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif;
  color: white;
}

#menu-bar {
  display: flex;
  justify-content: space-between;
  align-items: center;
  height: 30px;
  background: #34475a;
  -webkit-app-region: drag;
}

#menu-bar > div {
  height: 100%;
  display: flex;
  justify-content: space-between;
  align-items: center;
}

.menubar-btn {
  -webkit-app-region: no-drag;
}

.container {
  height: calc(100vh - 30px);
  background: #34475ab0;
  color: white;
  display: flex;
  justify-content: center;
  align-items: center;
  font-size: 2em;
}

button {
  height: 100%;
  padding: 0 15px;
  border: none;
  background: transparent;
  outline: none;
}

button:hover {
  background: rgba(221, 221, 221, 0.2);
}

#close-btn:hover {
  background: rgb(255, 0, 0);
}

button i {
  color: white;
}

Now your window should look like this. Awesome. We are almost there.

Result image before clicking on menu

If you guessed it, none of the buttons in the menu bar work. Because we didn't add onclick event listeners for them. Let's do that. Remember we attached some utility functions to the window object in preload.js? We'll use them in button click listeners.

// renderer.js

window.addEventListener("DOMContentLoaded", () => {
  const menuButton = document.getElementById("menu-btn");
  const minimizeButton = document.getElementById("minimize-btn");
  const maxUnmaxButton = document.getElementById("max-unmax-btn");
  const closeButton = document.getElementById("close-btn");

  menuButton.addEventListener("click", e => {
    // Opens menu at (x,y) coordinates of mouse click on the hamburger icon.
    window.openMenu(e.x, e.y);
  });

  minimizeButton.addEventListener("click", e => {
    window.minimizeWindow();
  });

  maxUnmaxButton.addEventListener("click", e => {
    const icon = maxUnmaxButton.querySelector("i.far");

    window.maxUnmaxWindow();

    // Change the middle maximize-unmaximize icons.
    if (window.isWindowMaximized()) {
      icon.classList.remove("fa-square");
      icon.classList.add("fa-clone");
    } else {
      icon.classList.add("fa-square");
      icon.classList.remove("fa-clone");
    }
  });

  closeButton.addEventListener("click", e => {
    window.closeWindow();
  });
});

That's it. Restart your app with npm run start and your new menu bar buttons should work.

NOTE: Some parts of code are removed in the above scripts for brevity. You can get the full code at https://github.com/saisandeepvaddi/electron-custom-menu-bar.

If you want to see a bigger electron app with a lot more stuff, check the https://github.com/saisandeepvaddi/ten-hands app which uses the similar style menu bar (custom style menu bar is visible only on Windows for now though. ๐Ÿ˜”) but built with React and TypeScript. I wrote this tutorial after using this menu bar there.

Thank you. ๐Ÿ™

electron-custom-menu-bar's People

Contributors

aldaircruz avatar appins avatar betamos avatar bhaveshgohel avatar bratsos avatar chankruze avatar ckerr avatar clemens-tolboom avatar codebytere avatar dependabot-preview[bot] avatar dependabot-support avatar dependabot[bot] avatar falkendk avatar galeksandrp avatar hum4n01d avatar ionicabizau avatar izuzak avatar jlord avatar kevinsawicki avatar maddhruv avatar marshallofsound avatar phanect avatar posva avatar saisandeepvaddi avatar sebastianschmidt avatar shiftkey avatar tcyrus avatar thompsonemerson avatar vhashimotoo avatar zeke avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.