From 9b562231a4e11f122dade579f28aa3de6efe315a Mon Sep 17 00:00:00 2001 From: Fabian Jakobs Date: Tue, 20 Apr 2010 16:48:08 +0200 Subject: [PATCH] Add vertical scroll bar --- css/editor.css | 13 +++++++++++++ demo/editor.html | 1 + src/Editor.js | 16 ++++++++++------ src/ScrollBar.js | 40 ++++++++++++++++++++++++++++++++++++++++ src/VirtualRenderer.js | 33 ++++++++++++++++++++++++++------- src/ace.js | 10 +++++----- 6 files changed, 95 insertions(+), 18 deletions(-) create mode 100644 src/ScrollBar.js diff --git a/css/editor.css b/css/editor.css index 3676c327..f38c7316 100644 --- a/css/editor.css +++ b/css/editor.css @@ -15,6 +15,19 @@ overflow-y: hidden; } +.editor .scrollbar { + position: absolute; + overflow-x: hidden; + overflow-y: scroll; + right: 0; +} + +.editor .scrollbar div { + position: absolute; + width: 1px; + left: -10px; +} + .layer { position: absolute; overflow: hidden; diff --git a/demo/editor.html b/demo/editor.html index 5c3c6cfc..5a63f936 100644 --- a/demo/editor.html +++ b/demo/editor.html @@ -56,6 +56,7 @@ + diff --git a/src/Editor.js b/src/Editor.js index 4854cfa0..a15e7de6 100644 --- a/src/Editor.js +++ b/src/Editor.js @@ -7,11 +7,17 @@ ace.Editor = function(renderer, doc) { this.textInput = new ace.TextInput(container, this); new ace.KeyBinding(container, this); + var self = this; + ace.addListener(container, "mousedown", function(e) { + self.focus(); + return ace.stopEvent(e); + }); - ace.addListener(container, "mousedown", ace.bind(this.onMouseDown, this)); - ace.addListener(container, "dblclick", ace.bind(this.onMouseDoubleClick, this)); - ace.addTripleClickListener(container, ace.bind(this.onMouseTripleClick, this)); - ace.addMouseWheelListener(container, ace.bind(this.onMouseWheel, this)); + var mouseTarget = renderer.getMouseEventTarget(); + ace.addListener(mouseTarget, "mousedown", ace.bind(this.onMouseDown, this)); + ace.addListener(mouseTarget, "dblclick", ace.bind(this.onMouseDoubleClick, this)); + ace.addTripleClickListener(mouseTarget, ace.bind(this.onMouseTripleClick, this)); + ace.addMouseWheelListener(mouseTarget, ace.bind(this.onMouseWheel, this)); this._selectionMarker = null; this._highlightLineMarker = null; @@ -196,8 +202,6 @@ ace.Editor.prototype.onDocumentModeChange = function() { ace.Editor.prototype.onMouseDown = function(e) { - this.focus(); - var pageX = ace.getDocumentX(e); var pageY = ace.getDocumentY(e); diff --git a/src/ScrollBar.js b/src/ScrollBar.js new file mode 100644 index 00000000..4f99abef --- /dev/null +++ b/src/ScrollBar.js @@ -0,0 +1,40 @@ +ace.provide("ace.ScrollBar"); + +ace.ScrollBar = function(parent) { + this.$initEvents(); + + this.element = document.createElement("div"); + this.element.className = "scrollbar"; + + this.inner = document.createElement("div"); + this.element.appendChild(this.inner); + + parent.appendChild(this.element); + + this.width = ace.scrollbarWidth(); + this.element.style.width = this.width; + + ace.addListener(this.element, "scroll", ace.bind(this.onScroll, this)); +}; + +ace.mixin(ace.ScrollBar.prototype, ace.MEventEmitter); + +ace.ScrollBar.prototype.onScroll = function() { + this.$dispatchEvent("scroll", {data: this.element.scrollTop}); +}; + +ace.ScrollBar.prototype.getWidth = function() { + return this.width; +}; + +ace.ScrollBar.prototype.setHeight = function(height) { + this.element.style.height = (height - this.width) + "px"; +}; + +ace.ScrollBar.prototype.setInnerHeight = function(height) { + this.inner.style.height = height + "px"; +}; + +ace.ScrollBar.prototype.setScrollTop = function(scrollTop) { + this.element.scrollTop = scrollTop; +}; \ No newline at end of file diff --git a/src/VirtualRenderer.js b/src/VirtualRenderer.js index 85b4564d..18ee8451 100644 --- a/src/VirtualRenderer.js +++ b/src/VirtualRenderer.js @@ -25,6 +25,9 @@ ace.VirtualRenderer = function(container) { this.layers = [ this.markerLayer, textLayer, this.cursorLayer ]; + this.scrollBar = new ace.ScrollBar(container); + this.scrollBar.addEventListener("scroll", ace.bind(this.onScroll, this)); + this.scrollTop = 0; this.cursorPos = { @@ -50,6 +53,10 @@ ace.VirtualRenderer.prototype.getContainerElement = function() { return this.container; }; +ace.VirtualRenderer.prototype.getMouseEventTarget = function() { + return this.scroller; +}; + ace.VirtualRenderer.prototype.getFirstVisibleRow = function() { return this.layerConfig.firstRow || 0; }; @@ -63,24 +70,36 @@ ace.VirtualRenderer.prototype.onResize = function() var height = ace.getInnerHeight(this.container); this.gutter.style.height = height + "px"; this.scroller.style.height = height + "px"; + this.scrollBar.setHeight(height); var width = ace.getInnerWidth(this.container); var gutterWidth = this.gutter.offsetWidth; this.scroller.style.left = gutterWidth + "px"; - this.scroller.style.width = Math.max(0, width - gutterWidth) + "px"; + this.scroller.style.width = Math.max(0, width - gutterWidth - this.scrollBar.getWidth()) + "px"; if (this.doc) { + this._updateScrollBar(); this.scrollToY(this.getScrollTop()); + this.draw(); } }; +ace.VirtualRenderer.prototype.onScroll = function(e) { + this.scrollToY(e.data); +}; + +ace.VirtualRenderer.prototype._updateScrollBar = function() { + this.scrollBar.setInnerHeight(this.doc.getLength() * this.lineHeight); + this.scrollBar.setScrollTop(this.scrollTop); +}; + ace.VirtualRenderer.prototype.updateLines = function(firstRow, lastRow) { var layerConfig = this.layerConfig; if (firstRow > layerConfig.lastRow + 1) { return; } if (lastRow < layerConfig.firstRow) { return; } - // if the last row is unknow -> redraw everything + // if the last row is unknown -> redraw everything if (lastRow === undefined) { this.draw(); return; @@ -96,9 +115,7 @@ ace.VirtualRenderer.prototype.draw = function() { var offset = this.scrollTop % this.lineHeight; var minHeight = this.scroller.clientHeight + offset; - var longestLine = Math.max(this.scroller.clientWidth, Math.round(this.doc - .getWidth() - * this.characterWidth)); + var longestLine = Math.max(this.scroller.clientWidth, Math.round(this.doc.getWidth() * this.characterWidth)); var lineCount = Math.ceil(minHeight / this.lineHeight); var firstRow = Math.round((this.scrollTop - offset) / this.lineHeight); @@ -121,12 +138,13 @@ ace.VirtualRenderer.prototype.draw = function() { style.width = longestLine + "px"; layer.update(layerConfig); - } - ; + }; this.gutterLayer.element.style.marginTop = (-offset) + "px"; this.gutterLayer.element.style.height = minHeight + "px"; this.gutterLayer.update(layerConfig); + + this._updateScrollBar(); }; ace.VirtualRenderer.prototype.addMarker = function(range, clazz, type) { @@ -191,6 +209,7 @@ ace.VirtualRenderer.prototype.scrollToY = function(scrollTop) { if (this.scrollTop !== scrollTop) { this.scrollTop = scrollTop; + this._updateScrollBar(); this.draw(); } }; diff --git a/src/ace.js b/src/ace.js index 080725f4..fb0ce76b 100644 --- a/src/ace.js +++ b/src/ace.js @@ -97,20 +97,20 @@ ace.computedStyle = function(element, style) { } }; -ace.scrollbarHeight = function() { +ace.scrollbarWidth = function(parent) { var el = document.createElement("div"); var style = el.style; style.position = "absolute"; style.left = "-10000px"; style.overflow = "scroll"; - style.height = "100px"; + style.width = "100px"; - document.body.appendChild(el); - var height = el.offsetHeight - el.clientHeight; + (parent || document.body).appendChild(el); + var width = el.offsetWidth - el.clientWidth; document.body.removeChild(el); - return height; + return width; }; ace.stringReverse = function(string) {