Skip to content

Instantly share code, notes, and snippets.

@StickyCube
Created June 27, 2016 22:52
Show Gist options
  • Star 37 You must be signed in to star a gist
  • Fork 10 You must be signed in to fork a gist
  • Save StickyCube/ed79421bc53cba38f5b74b060d3f15fa to your computer and use it in GitHub Desktop.
Save StickyCube/ed79421bc53cba38f5b74b060d3f15fa to your computer and use it in GitHub Desktop.
Electron click through transparency example
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Test App</title>
</head>
<style>
html, body {
height: 100%;
}
body {
margin: 0px;
box-sizing: border-box;
}
#mount {
width: 100%;
height: 100%;
}
</style>
<body>
<div id="mount"></div>
<script src="./node_modules/react/dist/react.js"></script>
<script src="./node_modules/react-dom/dist/react-dom.js"></script>
<scriot src="./node_modules/es6-shim/es6-shim.js"></script>
<script src="./node_modules/babel-standalone/babel.js"></script>
<script type="text/babel">
const { Component } = React;
const { render } = ReactDOM;
const { ipcRenderer } = require('electron');
const mount = document.getElementById('mount');
const containerStyle = {
width: '100%',
height: '100%',
backgroundColor: 'white',
textAlign: 'center',
padding: '10px',
boxSizing: 'border-box'
};
class App extends Component {
constructor () {
super(...arguments);
this.state = { counter: 0 };
}
onClick () {
this.setState({
counter: this.state.counter + 1
});
}
setChildCss (rule) {
ipcRenderer.send(
'ClickableRegion::set-child-css',
`body { ${rule} !important; }`
);
}
render () {
return (
<div style={containerStyle}>
<p>This window has setIgnoreMouseEvents(true)</p>
<h1>{ this.state.counter }</h1>
{
/**
* One drawback here is that :hover css pseudoclass is not picked up
* setChildCss is a hacky workaround which sets { cursor: pointer }
* on the body of the transparent child
*/
}
<button
onClick={ () => this.onClick() }
onMouseEnter={ () => this.setChildCss('cursor:pointer') }
onMouseLeave={ () => this.setChildCss('cursor:initial') }
>
Click me
</button>
</div>
);
}
}
render(
<App/>,
mount
);
</script>
</body>
</html>
const { app, BrowserWindow, ipcMain } = require('electron');
const path = require('path');
const INDEX_HTML = path.join('file://', __dirname, 'index.html');
const TRANSPARENT_HTML = path.join('file://', __dirname, 'transparent.html');
const CHILD_PADDING = 50;
const addClickableRegion = options => {
const { parent } = options;
const parentBounds = parent.getBounds();
const {
width = parentBounds.width,
height = parentBounds.height,
x = 0,
y = 0
} = options;
// create a child window, setting the position based on the parent's bounds
const childWindow = new BrowserWindow({
parent,
x: parentBounds.x + x,
y: parentBounds.y + y,
width: width || parentBounds.width,
height: height || parentBounds.height,
// disable pretty much everything
transparent: true,
frame: false,
skipTaskbar: true,
movable: false,
resizable: false,
maximizable: false,
minimizable: false
});
// this is a dirty workaround to set the cursor style when hovering over the button
ipcMain.on(
'ClickableRegion::set-child-css',
(e, css) => childWindow.webContents.insertCSS(css)
);
// When the transpoarent child captures a mouse event, it is forwarded to the parent
// and mapped to it's coordinates
ipcMain.on(
'ClickableRegion::mouse-event',
(e, data) => {
parent.webContents.sendInputEvent(Object.assign(
data,
{
x: x + data.x,
y: y + data.y
}
));
}
);
childWindow.loadURL(TRANSPARENT_HTML);
};
const onAppReady = function () {
let parent = new BrowserWindow({
width: 300,
height: 300,
show: false,
resizable: false,
transparent: true,
frame: false
});
// make the parent window ignore all mouse events so we can click through it
parent.setIgnoreMouseEvents(true);
parent.once('close', () => {
parent = null;
});
parent.webContents.once('did-finish-load', () => {
// add a transparent clickable child window to capture the mouse events
addClickableRegion({
parent,
x: CHILD_PADDING,
y: CHILD_PADDING,
width: 200,
height: 200
});
// could do this in index.html
parent.webContents.insertCSS(`body { padding:${CHILD_PADDING}px !important; }`);
parent.show();
});
parent.loadURL(INDEX_HTML);
};
app.on('ready', onAppReady);
{
"name": "window-resize-issue",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"app": "npm install && node_modules/.bin/electron --disable-gpu --enable-transparent-visuals ."
},
"author": "stickycube",
"license": "ISC",
"dependencies": {
"babel-standalone": "^6.10.3",
"electron-prebuilt": "1.2.5",
"es6-shim": "^0.35.1",
"react": "^15.1.0",
"react-dom": "^15.1.0"
}
}
<!DOCTYPE html>
<html>
<head>
<style>
html, body {
margin: 0px;
width: 100%;
height: 100%;
}
</style>
</head>
<!-- handlers for supported mouse events on body -->
<body
onmouseup="onEvent('mouseUp', event)"
onmousedown="onEvent('mouseDown', event)"
onmouseenter="onEvent('mouseEnter', event)"
onmouseleave="onEvent('mouseLeave', event)"
onmousemove="onEvent('mouseMove', event)"
>
<script>
const {ipcRenderer} = require('electron');
const getEventModifiers = evt => [
{ field: 'ctrlKey', name: 'control' },
{ field: 'shiftKey', name: 'shift' },
{ field: 'altKey', name: 'alt' },
{ field: 'metaKey', name: 'meta' }
].filter(elm => evt[elm.field]).map(elm => elm.name);
const getEventButton = evt => {
switch (evt.button) {
case 2:
return 'right';
case 1:
return 'middle';
case 0:
default:
return 'left';
}
}
window.onEvent = function (type, evt) {
// send the event to ipcMain
return ipcRenderer.send('ClickableRegion::mouse-event', {
type,
modifiers: getEventModifiers(evt),
button: getEventButton(evt),
x: evt.clientX,
y: evt.clientY,
globalX: evt.screenX,
globalY: evt.screenY,
movementX: evt.movementX,
movementY: evt.movementY,
clickCount: evt.detail,
});
}
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment