From c2cd67105de6778613522b1deb96d9a170bdd146 Mon Sep 17 00:00:00 2001 From: Joe Cheng Date: Thu, 10 Feb 2011 20:11:28 +0800 Subject: [PATCH] Add free text wrapping with constraints --- demo/demo.js | 5 ++++ editor.html | 1 + lib/ace/edit_session.js | 58 +++++++++++++++++++++++++++++++++---- lib/ace/editor.js | 10 ++++++- lib/ace/virtual_renderer.js | 7 +++++ 5 files changed, 75 insertions(+), 6 deletions(-) diff --git a/demo/demo.js b/demo/demo.js index 542b6087..2e3a8a2f 100644 --- a/demo/demo.js +++ b/demo/demo.js @@ -228,6 +228,11 @@ exports.launch = function(env) { session.setWrapLimit(80); renderer.setPrintMarginColumn(80); break; + case "free": + session.setUseWrapMode(true); + session.setWrapLimitRange(null, null); + renderer.setPrintMarginColumn(80); + break; } }); diff --git a/editor.html b/editor.html index c8d0ebec..d4557f3b 100644 --- a/editor.html +++ b/editor.html @@ -97,6 +97,7 @@ + diff --git a/lib/ace/edit_session.js b/lib/ace/edit_session.js index d06147ae..2e6246b8 100644 --- a/lib/ace/edit_session.js +++ b/lib/ace/edit_session.js @@ -642,6 +642,10 @@ var EditSession = function(text, mode) { // WRAPMODE this.$wrapLimit = 80; this.$useWrapMode = false; + this.$wrapLimitRange = { + min : null, + max : null + }; this.setUseWrapMode = function(useWrapMode) { if (useWrapMode != this.$useWrapMode) { @@ -667,20 +671,64 @@ var EditSession = function(text, mode) { }; this.setWrapLimit = function(wrapLimit) { - if (wrapLimit != this.$wrapLimit) { - this.$wrapLimit = wrapLimit; + this.setWrapLimitRange(wrapLimit, wrapLimit); + }; + + // Allow the wrap limit to move freely between min and max. Either + // parameter can be null to allow the wrap limit to be unconstrained + // in that direction. Or set both parameters to the same number to pin + // the limit to that value. + this.setWrapLimitRange = function(min, max) { + if (this.$wrapLimitRange.min !== min || this.$wrapLimitRange.max !== max) { + this.$wrapLimitRange.min = min; + this.$wrapLimitRange.max = max; this.$modified = true; - if (this.$useWrapMode) { - this.$updateWrapData(0, this.getLength() - 1); - } + // This will force a recalculation of the wrap limit this._dispatchEvent("changeWrapMode"); } }; + // This should generally only be called by the renderer when a resize + // is detected. + this.adjustWrapLimit = function(desiredLimit) { + var wrapLimit = this.$constrainWrapLimit(desiredLimit); + if (wrapLimit != this.$wrapLimit && wrapLimit > 0) { + this.$wrapLimit = wrapLimit; + this.$modified = true; + if (this.$useWrapMode) { + this.$updateWrapData(0, this.getLength() - 1); + this._dispatchEvent("changeWrapLimit"); + } + return true; + } + return false; + }; + + this.$constrainWrapLimit = function(wrapLimit) { + var min = this.$wrapLimitRange.min; + if (min) + wrapLimit = Math.max(min, wrapLimit); + + var max = this.$wrapLimitRange.max; + if (max) + wrapLimit = Math.min(max, wrapLimit); + + // What would a limit of 0 even mean? + return Math.max(1, wrapLimit); + }; + this.getWrapLimit = function() { return this.$wrapLimit; }; + this.getWrapLimitRange = function() { + // Avoid unexpected mutation by returning a copy + return { + min : this.$wrapLimitRange.min, + max : this.$wrapLimitRange.max + }; + }; + this.$updateWrapDataOnChange = function(e) { if (!this.$useWrapMode) { return; diff --git a/lib/ace/editor.js b/lib/ace/editor.js index a0669372..685ee10a 100644 --- a/lib/ace/editor.js +++ b/lib/ace/editor.js @@ -126,6 +126,7 @@ var Editor =function(renderer, session) { this.session.removeEventListener("change", this.$onDocumentChange); this.session.removeEventListener("changeMode", this.$onDocumentModeChange); this.session.removeEventListener("changeTabSize", this.$onDocumentChangeTabSize); + this.session.removeEventListener("changeWrapLimit", this.$onDocumentChangeWrapLimit); this.session.removeEventListener("changeWrapMode", this.$onDocumentChangeWrapMode); this.session.removeEventListener("changeBreakpoint", this.$onDocumentChangeBreakpoint); this.session.removeEventListener("changeAnnotation", this.$onDocumentChangeAnnotation); @@ -149,6 +150,9 @@ var Editor =function(renderer, session) { this.$onDocumentChangeTabSize = this.renderer.updateText.bind(this.renderer); session.addEventListener("changeTabSize", this.$onDocumentChangeTabSize); + this.$onDocumentChangeWrapLimit = this.onDocumentChangeWrapLimit.bind(this); + session.addEventListener("changeWrapLimit", this.$onDocumentChangeWrapLimit); + this.$onDocumentChangeWrapMode = this.onDocumentChangeWrapMode.bind(this); session.addEventListener("changeWrapMode", this.$onDocumentChangeWrapMode); @@ -348,11 +352,15 @@ var Editor =function(renderer, session) { this.renderer.setTokenizer(this.bgTokenizer); }; - this.onDocumentChangeWrapMode = function() { + this.onDocumentChangeWrapLimit = function() { this.renderer.updateCursor(this.getCursorPosition(), this.$overwrite); this.renderer.updateFull(); }; + this.onDocumentChangeWrapMode = function() { + this.renderer.onResize(true); + }; + this.getCopyText = function() { if (!this.selection.isEmpty()) { return this.session.getTextRange(this.getSelectionRange()); diff --git a/lib/ace/virtual_renderer.js b/lib/ace/virtual_renderer.js index a432bd3f..c7fa8d63 100644 --- a/lib/ace/virtual_renderer.js +++ b/lib/ace/virtual_renderer.js @@ -210,6 +210,13 @@ var VirtualRenderer = function(container, theme) { var gutterWidth = this.showGutter ? this.$gutter.offsetWidth : 0; this.scroller.style.left = gutterWidth + "px"; this.scroller.style.width = Math.max(0, width - gutterWidth - this.scrollBar.getWidth()) + "px"; + + if (this.session.getUseWrapMode()) { + var limit = Math.floor(this.scroller.clientWidth / this.characterWidth); + if (this.session.adjustWrapLimit(limit) || force) { + changes = changes | this.CHANGE_FULL; + } + } } this.$size.scrollerWidth = this.scroller.clientWidth;