From 1128bb45241cf79e1b7688c65a4dd745bdf7ad61 Mon Sep 17 00:00:00 2001 From: abe33 Date: Tue, 16 Dec 2014 14:39:51 +0100 Subject: [PATCH] Implement minimap scroll indicator --- lib/minimap-element.coffee | 28 +++++++++++++++ spec/minimap-element-spec.coffee | 59 +++++++++++++++++++++++++++++++- stylesheets/minimap.less | 10 ++++++ 3 files changed, 96 insertions(+), 1 deletion(-) diff --git a/lib/minimap-element.coffee b/lib/minimap-element.coffee index e4e6da93..66e9d9d7 100644 --- a/lib/minimap-element.coffee +++ b/lib/minimap-element.coffee @@ -22,6 +22,12 @@ class MinimapElement extends HTMLElement @swapMinimapPosition() if swapPosition + @subscriptions.add atom.config.observe 'minimap.minimapScrollIndicator', (@minimapScrollIndicator) => + if @minimapScrollIndicator and not @scrollIndicator? + @initializeScrollIndicator() + else if @scrollIndicator? + @disposeScrollIndicator() + attachedCallback: -> @domPollingIntervalId = setInterval((=> @pollDOM()), @domPollingInterval) @measureHeightAndWidth() @@ -78,6 +84,15 @@ class MinimapElement extends HTMLElement @visibleArea.classList.add('minimap-visible-area') @shadowRoot.appendChild(@visibleArea) + initializeScrollIndicator: -> + @scrollIndicator = document.createElement('div') + @scrollIndicator.classList.add 'minimap-scroll-indicator' + @shadowRoot.appendChild(@scrollIndicator) + + disposeScrollIndicator: -> + @shadowRoot.removeChild(@scrollIndicator) + @scrollIndicator = undefined + pauseDOMPolling: -> @domPollingPaused = true @resumeDOMPollingAfterDelay ?= debounce(@resumeDOMPolling, 100) @@ -135,6 +150,19 @@ class MinimapElement extends HTMLElement @canvas.style.top = (@minimap.getFirstVisibleScreenRow() * @minimap.getLineHeight() - @minimap.getMinimapScrollTop()) + 'px' + if @minimapScrollIndicator and @minimap.canScroll() and not @scrollIndicator + @initializeScrollIndicator() + + if @scrollIndicator? + editorHeight = @getTextEditor().getHeight() + indicatorHeight = editorHeight * (editorHeight / @minimap.getHeight()) + indicatorScroll = (editorHeight - indicatorHeight) * @minimap.getTextEditorScrollRatio() + + @scrollIndicator.style.height = indicatorHeight + 'px' + @scrollIndicator.style.top = indicatorScroll + 'px' + + @disposeScrollIndicator() if not @minimap.canScroll() + @updateCanvas() # ######## ## ######## ## ## ######## ## ## ######## diff --git a/spec/minimap-element-spec.coffee b/spec/minimap-element-spec.coffee index 7305a0c7..aff0b759 100644 --- a/spec/minimap-element-spec.coffee +++ b/spec/minimap-element-spec.coffee @@ -8,7 +8,7 @@ stylesheetPath = path.resolve __dirname, '..', 'stylesheets', 'minimap.less' stylesheet = atom.themes.loadStylesheet(stylesheetPath) describe 'MinimapElement', -> - [editor, minimap, largeSample, smallSample, jasmineContent, editorElement, minimapElement] = [] + [editor, minimap, largeSample, mediumSample, smallSample, jasmineContent, editorElement, minimapElement] = [] beforeEach -> atom.config.set 'minimap.charHeight', 4 @@ -23,6 +23,7 @@ describe 'MinimapElement', -> minimap = new Minimap({textEditor: editor}) largeSample = fs.readFileSync(atom.project.resolve('large-file.coffee')).toString() + mediumSample = fs.readFileSync(atom.project.resolve('two-hundred.txt')).toString() smallSample = fs.readFileSync(atom.project.resolve('sample.coffee')).toString() editor.setText largeSample @@ -76,6 +77,10 @@ describe 'MinimapElement', -> background: rgba(255,0,0,0.3); } + atom-text-editor atom-text-editor-minimap::shadow .minimap-scroll-indicator, atom-text-editor::shadow atom-text-editor-minimap::shadow .minimap-scroll-indicator { + background: rgba(0,0,255,0.3); + } + atom-text-editor atom-text-editor-minimap::shadow .minimap-visible-area, atom-text-editor::shadow atom-text-editor-minimap::shadow .minimap-visible-area { background: rgba(0,255,0,0.3); } @@ -223,3 +228,55 @@ describe 'MinimapElement', -> it 'moves the attached minimap to the left', -> expect(Array::indexOf.call(editorElement.shadowRoot.children, minimapElement)).toEqual(0) nextAnimationFrame() + + describe 'when minimap.minimapScrollIndicator setting is true', -> + beforeEach -> + editor.setText(mediumSample) + editor.setScrollTop(50) + nextAnimationFrame() + + atom.config.set 'minimap.minimapScrollIndicator', true + + it 'adds a scroll indicator in the element', -> + expect(minimapElement.shadowRoot.querySelector('.minimap-scroll-indicator')).toExist() + + describe 'and then deactivated', -> + it 'removes the scroll indicator from the element', -> + atom.config.set 'minimap.minimapScrollIndicator', false + expect(minimapElement.shadowRoot.querySelector('.minimap-scroll-indicator')).not.toExist() + + describe 'on update', -> + beforeEach -> + height = editor.getHeight() + editorElement.style.height = '500px' + + waitsFor -> editor.getHeight() isnt height + + runs -> + advanceClock(150) + nextAnimationFrame() + + it 'adjusts the size and position of the indicator', -> + indicator = minimapElement.shadowRoot.querySelector('.minimap-scroll-indicator') + + height = editor.getHeight() * (editor.getHeight() / minimap.getHeight()) + scroll = (editor.getHeight() - height) * minimap.getTextEditorScrollRatio() + + expect(indicator.offsetHeight).toBeCloseTo(height, 0) + expect(indicator.offsetTop).toBeCloseTo(scroll, 0) + + describe 'when the minimap cannot scroll', -> + beforeEach -> + editor.setText(smallSample) + nextAnimationFrame() + + it 'removes the scroll indicator', -> + expect(minimapElement.shadowRoot.querySelector('.minimap-scroll-indicator')).not.toExist() + + describe 'and then can scroll again', -> + beforeEach -> + editor.setText(largeSample) + nextAnimationFrame() + + it 'attaches the scroll indicator', -> + expect(minimapElement.shadowRoot.querySelector('.minimap-scroll-indicator')).toExist() diff --git a/stylesheets/minimap.less b/stylesheets/minimap.less index f3e587e8..1749f455 100644 --- a/stylesheets/minimap.less +++ b/stylesheets/minimap.less @@ -23,6 +23,16 @@ atom-text-editor::shadow, atom-text-editorĀ { position: absolute; display: 'block'; } + + .minimap-scroll-indicator { + position: absolute; + display: block; + right: 0; + width: 2px; + min-height: 2px; + z-index: 10; + background: @background-color-selected; + } } } }