-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Create cropper and screenshot editor mode
- Loading branch information
Showing
17 changed files
with
1,114 additions
and
30 deletions.
There are no files selected for viewing
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
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
const fs = require('fs'); | ||
const path = require('path') | ||
|
||
fs.copyFile( | ||
path.join(__dirname, '../node_modules/screenshot-desktop/lib/win32/screenCapture_1.3.2.bat'), | ||
path.join(__dirname, '../build/screenCapture_1.3.2.bat'), | ||
(err) => { | ||
if(err) { | ||
console.log(err); | ||
} | ||
}); | ||
|
||
fs.copyFile( | ||
path.join(__dirname, '../node_modules/screenshot-desktop/lib/win32/app.manifest'), | ||
path.join(__dirname, '../build/app.manifest'), | ||
(err) => { | ||
if(err) { | ||
console.log(err); | ||
} | ||
}); |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,145 @@ | ||
import React, { useState, useEffect, useRef } from 'react'; | ||
import { remote, ipcRenderer } from 'electron'; | ||
import { isEqual } from 'lodash'; | ||
import cx from 'classnames'; | ||
import querystring from 'querystring'; | ||
import './styles.scss'; | ||
|
||
interface Rect { | ||
startX?: number, | ||
startY?: number, | ||
width?: number, | ||
height?: number, | ||
}; | ||
|
||
const Cropper: React.FC = () => { | ||
const params = querystring.parse(window.location.search.slice(1)); | ||
let dragging = false; | ||
const DEFAULT_RECT = { | ||
startX: 0, | ||
startY: 0, | ||
width: 0, | ||
height: 0 | ||
}; | ||
const canvas = useRef(null); | ||
const [frame, setFrame] = useState<Rect>({...DEFAULT_RECT}); | ||
|
||
const bounds = remote.getCurrentWindow().getBounds(); | ||
|
||
useEffect(() => { | ||
let rect : Rect = {}; | ||
|
||
const startPainting = (e: MouseEvent) => { | ||
e.preventDefault(); | ||
rect = { width: 0, height: 0}; | ||
rect.startX = e.pageX - canvas.current.offsetLeft; | ||
rect.startY = e.pageY - canvas.current.offsetTop; | ||
dragging = true; | ||
ipcRenderer.send('CLOSE_CAMERA'); | ||
ipcRenderer.send('CLOSE_TOOLS'); | ||
setFrame(DEFAULT_RECT); | ||
}; | ||
|
||
const getRectPoints = (rect: Rect) => { | ||
return { | ||
x: rect.width >= 0 ? rect.startX : rect.startX - Math.abs(rect.width), | ||
y: rect.height >= 0 ? rect.startY : rect.startY - Math.abs(rect.height), | ||
} | ||
} | ||
|
||
const finishPainting = () => { | ||
dragging = false; | ||
removeListeners(); | ||
const { x, y } = getRectPoints(rect); | ||
if(params.screenshot) { | ||
ipcRenderer.send('TAKE_SCREENSHOT', { | ||
x, | ||
y, | ||
height: Math.abs(rect.height), | ||
width: Math.abs(rect.width) | ||
}) | ||
} | ||
}; | ||
|
||
const draw = (event : MouseEvent) => { | ||
if(!dragging ) return; | ||
|
||
rect.width = (event.pageX - canvas.current.offsetLeft) - rect.startX; | ||
rect.height = (event.pageY - canvas.current.offsetTop) - rect.startY ; | ||
const { x, y } = getRectPoints(rect); | ||
setFrame({ | ||
startX: x, | ||
startY: y, | ||
width: Math.abs(rect.width), | ||
height: Math.abs(rect.height) | ||
}); | ||
} | ||
|
||
function removeListeners() { | ||
canvas.current.removeEventListener('mousedown', startPainting); | ||
canvas.current.removeEventListener('mouseup', finishPainting); | ||
canvas.current.removeEventListener('mousemove', draw); | ||
} | ||
|
||
canvas.current.addEventListener('mousedown', startPainting); | ||
canvas.current.addEventListener('mouseup', finishPainting); | ||
canvas.current.addEventListener('mousemove', draw); | ||
|
||
return () => { | ||
removeListeners(); | ||
} | ||
}, []); | ||
|
||
const firstDrag = isEqual(DEFAULT_RECT, frame); | ||
|
||
return ( | ||
<div className={cx('cropper', { 'first-drag': firstDrag })} ref={canvas}> | ||
{ | ||
firstDrag && | ||
<div className="drag-instructions"> | ||
<strong>Drag</strong> to make a selection | ||
</div> | ||
} | ||
{ | ||
!firstDrag && | ||
<> | ||
<div | ||
style={{ | ||
width: '100%', | ||
height: `${frame.startY}px`, | ||
}} | ||
className="top" /> | ||
<div className="middle"> | ||
<div | ||
style={{ | ||
width: frame.startX, | ||
}} | ||
className="left" /> | ||
<div | ||
style={{ | ||
left: frame.startX, | ||
top: frame.startY, | ||
width: frame.width, | ||
height: frame.height | ||
}} | ||
className="frame"> | ||
</div> | ||
<div | ||
style={{ | ||
width: `${bounds.width - frame.width - frame.startX}px`, | ||
}} | ||
className="right" /> | ||
</div> | ||
<div | ||
style={{ | ||
width: '100%', | ||
height: `${bounds.height - frame.height - frame.startY}px`, | ||
}} | ||
className="bottom" /> | ||
</> | ||
} | ||
</div> | ||
); | ||
} | ||
|
||
export default Cropper; |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
.cropper { | ||
height: 100vh; | ||
width: 100vw; | ||
|
||
&.first-drag { | ||
display: flex; | ||
justify-content: center; | ||
align-items: center; | ||
background-color: #233245; | ||
opacity: 0.5; | ||
|
||
.drag-instructions { | ||
width: 300px; | ||
height: 100px; | ||
display: flex; | ||
justify-content: center; | ||
align-items: center; | ||
background-color: #FFF; | ||
font-size: 20px; | ||
border-radius: 10px; | ||
} | ||
} | ||
|
||
.top, .bottom, .left, .right { | ||
background-color: #233245; | ||
opacity: 0.5; | ||
} | ||
|
||
.middle { | ||
display: flex; | ||
flex: 1; | ||
} | ||
|
||
.frame.ready { | ||
display: flex; | ||
justify-content: center; | ||
align-items: center; | ||
|
||
.button:hover { | ||
cursor: pointer; | ||
} | ||
} | ||
} |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
import React, { useEffect, useRef, useState } from 'react'; | ||
import { ipcRenderer, remote, shell } from 'electron'; | ||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' | ||
import jimp from 'jimp'; | ||
import { writeFile } from 'fs'; | ||
import { uniqueId } from 'lodash'; | ||
import path from 'path'; | ||
import log from 'electron-log'; | ||
import './styles.scss'; | ||
|
||
const ImageEditor : React.FC = () => { | ||
const imgPreview = useRef(null); | ||
const [loading, setLoading] = useState(true); | ||
useEffect(() => { | ||
ipcRenderer.on('DID_MOUNT', async (_, data) => { | ||
try { | ||
const image = await jimp.read(data.path); | ||
const { x, y } = remote.getCurrentWindow().getBounds(); | ||
const activeDisplay = remote.screen.getDisplayNearestPoint({ x, y }); | ||
const { width, height } = activeDisplay.bounds; | ||
image | ||
.resize(width, height) | ||
.crop(data.x, data.y, data.width, data.height); | ||
const base64 = await new Promise((resolve, reject) => { | ||
image.getBase64('image/png', (err, base64) => { | ||
if(err) { | ||
reject(err); | ||
} | ||
else { | ||
resolve(base64); | ||
} | ||
}); | ||
}); | ||
imgPreview.current.src = base64; | ||
setLoading(false); | ||
} catch(err) { | ||
log.warn(err.message); | ||
} | ||
}); | ||
}, [ | ||
setLoading | ||
]); | ||
|
||
const save = async () => { | ||
const { filePath } = await remote.dialog.showSaveDialog({ | ||
buttonLabel: 'Save', | ||
defaultPath: `Screenshot-${Date.now()}.png`, | ||
}); | ||
|
||
if(!filePath) { | ||
return; | ||
} | ||
|
||
const srcBase64 = imgPreview.current.src.replace(/^data:image\/png;base64,/, ""); | ||
|
||
writeFile(filePath, srcBase64, 'base64', (err) => { | ||
if(err) { | ||
return; | ||
} | ||
|
||
const id = uniqueId() | ||
const notification = { | ||
id, | ||
title: 'Screenshot saved', | ||
body: 'Click here to open', | ||
} | ||
|
||
ipcRenderer.send('NOTIFICATION', notification); | ||
ipcRenderer.on(`NOTIFICATION:click:${id}`, () => { | ||
shell.openItem(path.dirname(filePath)); | ||
}); | ||
}); | ||
} | ||
|
||
return ( | ||
<div className="image-editor-window"> | ||
<div className="editor"> | ||
<div className="panes"> | ||
</div> | ||
<div className="preview"> | ||
{ | ||
loading && | ||
<FontAwesomeIcon icon="spinner" size="3x" id="spinner" spin/> | ||
} | ||
<img ref={imgPreview} style={{ display: loading ? 'none' : 'block' }}/> | ||
</div> | ||
</div> | ||
<div className="toolbar"> | ||
<button | ||
onClick={save} | ||
className="button">Save</button> | ||
</div> | ||
</div> | ||
); | ||
} | ||
|
||
export default ImageEditor; |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
.image-editor-window { | ||
display: flex; | ||
flex-direction: column; | ||
|
||
.toolbar { | ||
height: 50px; | ||
width: 100%; | ||
background-color: #C8CCCE; | ||
justify-content: flex-end; | ||
align-items: center; | ||
display: flex; | ||
padding-right: 20px; | ||
} | ||
|
||
.editor { | ||
display: flex; | ||
flex-direction: row; | ||
|
||
.preview { | ||
display: flex; | ||
justify-content: center; | ||
align-items: center; | ||
width: 100%; | ||
height: calc(100vh - 50px); | ||
padding: 20px; | ||
|
||
img { | ||
height: auto; | ||
max-width: 100%; | ||
max-height: 100%; | ||
} | ||
} | ||
} | ||
} |
Oops, something went wrong.