diff --git a/src/editor/contentState/paragraphCtrl.js b/src/editor/contentState/paragraphCtrl.js index 8519f71c..c39bd9f6 100644 --- a/src/editor/contentState/paragraphCtrl.js +++ b/src/editor/contentState/paragraphCtrl.js @@ -16,6 +16,7 @@ const getCurrentLevel = type => { const paragraphCtrl = ContentState => { ContentState.prototype.selectionChange = function () { const { start, end } = selection.getCursorRange() + const cursorCoords = selection.getCursorCoords() const startBlock = this.getBlock(start.key) const endBlock = this.getBlock(end.key) const startParents = this.getParents(startBlock) @@ -32,7 +33,8 @@ const paragraphCtrl = ContentState => { return { start, end, - affiliation + affiliation, + cursorCoords } } diff --git a/src/editor/index.css b/src/editor/index.css index dad4791c..7ca1cbcc 100644 --- a/src/editor/index.css +++ b/src/editor/index.css @@ -35,6 +35,7 @@ h6.ag-active::before { *::selection, .ag-selection { background: #E4E7ED; + color: #303133; } .ag-highlight { @@ -262,7 +263,7 @@ pre.ag-active .ag-language-input { caret-color: #303133; } .ag-gray { - color: #C0C4CC; + color: #E4E7ED; text-decoration: none; } diff --git a/src/editor/selection.js b/src/editor/selection.js index d9205fb9..fe3644e5 100644 --- a/src/editor/selection.js +++ b/src/editor/selection.js @@ -815,6 +815,26 @@ class Selection { } } + getCursorCoords () { + const sel = this.doc.getSelection() + let range + let x = 0 + let y = 0 + + if (sel.rangeCount) { + range = sel.getRangeAt(0).cloneRange() + if (range.getClientRects) { + range.collapse(true) + const rects = range.getClientRects() + if (rects.length) { + ({ x, y } = rects[0]) + } + } + } + + return { x, y } + } + getSelectionEnd () { const node = this.doc.getSelection().focusNode const endNode = (node && node.nodeType === 3 ? node.parentNode : node) diff --git a/src/editor/themes/light.css b/src/editor/themes/light.css index 1ca961b2..c2153dea 100644 --- a/src/editor/themes/light.css +++ b/src/editor/themes/light.css @@ -34,13 +34,13 @@ } html, body { - font-size: 16px; + font-size: 15px; background: rgb(252, 252, 252); } body { font-family: "Open Sans", "Clear Sans", "Helvetica Neue", Helvetica, Arial, sans-serif; - color: #303133; + color: #606266; line-height: 1.6; } diff --git a/src/editor/utils/index.js b/src/editor/utils/index.js index 8cb8d133..1f08dfe3 100644 --- a/src/editor/utils/index.js +++ b/src/editor/utils/index.js @@ -6,6 +6,13 @@ const getId = () => { return `${prefix}${Math.random().toString(32).slice(2)}` } +const easeInOutQuad = function (t, b, c, d) { + t /= d / 2 + if (t < 1) return c / 2 * t * t + b + t-- + return -c / 2 * (t * (t - 2) - 1) + b +} + /** * get unique id base on a set. */ @@ -184,6 +191,42 @@ export const getImageSrc = src => { } } +export const animatedScrollTo = function (element, to, duration, callback) { + let start = element.scrollTop + let change = to - start + let animationStart = +new Date() + let animating = true + let lastpos = null + + const animateScroll = function () { + if (!animating) { + return + } + requestAnimationFrame(animateScroll) + const now = +new Date() + const val = Math.floor(easeInOutQuad(now - animationStart, start, change, duration)) + if (lastpos) { + if (lastpos === element.scrollTop) { + lastpos = val + element.scrollTop = val + } else { + animating = false + } + } else { + lastpos = val + element.scrollTop = val + } + if (now > animationStart + duration) { + element.scrollTop = to + animating = false + if (callback) { + callback() + } + } + } + requestAnimationFrame(animateScroll) +} + /** * [genUpper2LowerKeyHash generate constants map hash, the value is lowercase of the key, * also translate `_` to `-`] diff --git a/src/main/actions/view.js b/src/main/actions/view.js new file mode 100644 index 00000000..5bb230a1 --- /dev/null +++ b/src/main/actions/view.js @@ -0,0 +1,4 @@ +export const view = (win, item, type) => { + const { checked } = item + win.webContents.send('AGANI::view', { type, checked }) +} diff --git a/src/main/menus/view.js b/src/main/menus/view.js index 2e1c0fa4..48a4441a 100755 --- a/src/main/menus/view.js +++ b/src/main/menus/view.js @@ -1,3 +1,5 @@ +import * as actions from '../actions/view' + let viewMenu = { label: 'View', submenu: [{ @@ -14,6 +16,31 @@ let viewMenu = { focusedWindow.setFullScreen(!focusedWindow.isFullScreen()) } } + }, { + type: 'separator' + }, { + label: 'Source Code Mode', + accelerator: 'Alt+CmdOrCtrl+S', + type: 'checkbox', + click (item, browserWindow) { + actions.view(browserWindow, item, 'sourceCode') + } + }, { + label: 'Typewriter Mode', + accelerator: 'Alt+CmdOrCtrl+T', + type: 'checkbox', + click (item, browserWindow) { + actions.view(browserWindow, item, 'typewriter') + } + }, { + label: 'Focus Mode', + accelerator: 'Alt+CmdOrCtrl+M', + type: 'checkbox', + click (item, browserWindow) { + actions.view(browserWindow, item, 'focus') + } + }, { + type: 'separator' }] } diff --git a/src/renderer/App.vue b/src/renderer/App.vue index c7ac7f7a..b83ea5e1 100644 --- a/src/renderer/App.vue +++ b/src/renderer/App.vue @@ -5,7 +5,11 @@ :active="windowActive" :word-count="wordCount" > - + @@ -27,7 +31,7 @@ return {} }, computed: { - ...mapState(['filename', 'windowActive', 'wordCount']) + ...mapState(['filename', 'windowActive', 'wordCount', 'typewriter', 'focus', 'sourceCode']) }, created () { const { dispatch } = this.$store @@ -40,6 +44,7 @@ dispatch('LISTEN_FOR_FILE_LOAD') dispatch('LISTEN_FOR_FILE_CHANGE') dispatch('LISTEN_FOR_EDIT') + dispatch('LISTEN_FOR_VIEW') dispatch('LISTEN_FOR_EXPORT') dispatch('LISTEN_FOR_PARAGRAPH_INLINE_STYLE') } @@ -49,6 +54,5 @@ diff --git a/src/renderer/components/Editor.vue b/src/renderer/components/Editor.vue index 5ce6bf3c..2665e0e7 100644 --- a/src/renderer/components/Editor.vue +++ b/src/renderer/components/Editor.vue @@ -1,6 +1,12 @@