Created
June 27, 2016 22:52
-
-
Save StickyCube/ed79421bc53cba38f5b74b060d3f15fa to your computer and use it in GitHub Desktop.
Electron click through transparency example
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!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> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"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" | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!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