From 1de8c413c6cc5dd5d272c139c84bfcf9dcd722be Mon Sep 17 00:00:00 2001 From: nightwing Date: Fri, 25 May 2012 00:00:10 +0400 Subject: [PATCH] fix textinput and contextmenu --- lib/ace/css/editor.css | 20 +++--- lib/ace/keyboard/textinput.js | 115 +++++++++++++++++------------- lib/ace/mouse/default_handlers.js | 19 ++--- lib/ace/mouse/mouse_handler.js | 3 - lib/ace/virtual_renderer.js | 54 +++++++------- 5 files changed, 110 insertions(+), 101 deletions(-) diff --git a/lib/ace/css/editor.css b/lib/ace/css/editor.css index b38dc453..aad4d2bf 100644 --- a/lib/ace/css/editor.css +++ b/lib/ace/css/editor.css @@ -18,13 +18,6 @@ cursor: text; } -.ace_composition { - position: absolute; - background: #555; - color: #DDD; - z-index: 4; -} - .ace_gutter { position: absolute; overflow : hidden; @@ -95,8 +88,8 @@ height: 100%; } -.ace_editor textarea { - position: fixed; +.ace_editor > textarea { + position: absolute; z-index: 0; width: 0.5em; height: 1em; @@ -110,6 +103,15 @@ overflow: hidden; } +.ace_editor > textarea.ace_composition { + background: #fff; + color: #000; + z-index: 1000; + opacity: 1; + border: solid lightgray 1px; + margin: -1px +} + .ace_layer { z-index: 1; position: absolute; diff --git a/lib/ace/keyboard/textinput.js b/lib/ace/keyboard/textinput.js index e053e33f..d39b13e3 100644 --- a/lib/ace/keyboard/textinput.js +++ b/lib/ace/keyboard/textinput.js @@ -45,18 +45,22 @@ var useragent = require("../lib/useragent"); var dom = require("../lib/dom"); var TextInput = function(parentNode, host) { - var text = dom.createElement("textarea"); + /*/ debug + text.style.opacity = 1 + text.style.background = "rgba(0, 250, 0, 0.3)" + text.style.outline = "rgba(0, 250, 0, 0.8) solid 1px" + text.style.outlineOffset = "3px" + /**/ if (useragent.isTouchPad) text.setAttribute("x-palm-disable-auto-cap", true); text.setAttribute("wrap", "off"); - text.style.left = "-10000px"; - text.style.position = "fixed"; + text.style.top = "-2em"; parentNode.insertBefore(text, parentNode.firstChild); - var PLACEHOLDER = String.fromCharCode(0); + var PLACEHOLDER = useragent.isIE ? "\x01" : "\x01"; sendText(); var inCompostion = false; @@ -64,9 +68,14 @@ var TextInput = function(parentNode, host) { var pasted = false; var tempStyle = ''; - function select() { + function reset(full) { try { - text.select(); + if (full) { + text.value = PLACEHOLDER; + text.selectionStart = 0; + text.selectionEnd = 1; + } else + text.select(); } catch (e) {} } @@ -87,11 +96,6 @@ var TextInput = function(parentNode, host) { else host.onTextInput(value); } - - // If editor is no longer focused we quit immediately, since - // it means that something else is in charge now. - if (!isFocused()) - return false; } } @@ -99,19 +103,19 @@ var TextInput = function(parentNode, host) { pasted = false; // Safari doesn't fire copy events if no text is selected - text.value = PLACEHOLDER; - select(); + reset(true); } var onTextInput = function(e) { + if (!inCompostion) + sendText(e.data); setTimeout(function () { if (!inCompostion) - sendText(e.data); + reset(true); }, 0); }; - + var onPropertyChange = function(e) { - if (useragent.isOldIE && text.value.charCodeAt(0) > 128) return; setTimeout(function() { if (!inCompostion) sendText(); @@ -121,7 +125,7 @@ var TextInput = function(parentNode, host) { var onCompositionStart = function(e) { inCompostion = true; host.onCompositionStart(); - if (!useragent.isGecko) setTimeout(onCompositionUpdate, 0); + setTimeout(onCompositionUpdate, 0); }; var onCompositionUpdate = function() { @@ -141,12 +145,12 @@ var TextInput = function(parentNode, host) { text.value = copyText; else e.preventDefault(); - select(); + reset(); setTimeout(function () { sendText(); }, 0); }; - + var onCut = function(e) { copied = true; var copyText = host.getCopyText(); @@ -155,31 +159,15 @@ var TextInput = function(parentNode, host) { host.onCut(); } else e.preventDefault(); - select(); + reset(); setTimeout(function () { sendText(); }, 0); }; event.addCommandKeyListener(text, host.onCommandKey.bind(host)); + event.addListener(text, "input", useragent.isIE ? onPropertyChange : onTextInput); - if (useragent.isOldIE) { - var keytable = { 13:1, 27:1 }; - event.addListener(text, "keyup", function (e) { - if (inCompostion && (!text.value || keytable[e.keyCode])) - setTimeout(onCompositionEnd, 0); - if ((text.value.charCodeAt(0)|0) < 129) { - return; - } - inCompostion ? onCompositionUpdate() : onCompositionStart(); - }); - } - - if ("onpropertychange" in text && !("oninput" in text)) - event.addListener(text, "propertychange", onPropertyChange); - else - event.addListener(text, "input", onTextInput); - event.addListener(text, "paste", function(e) { // Mark that the next input text comes from past. pasted = true; @@ -198,6 +186,8 @@ var TextInput = function(parentNode, host) { if ("onbeforecopy" in text && typeof clipboardData !== "undefined") { event.addListener(text, "beforecopy", function(e) { + if (tempStyle) + return; // without this text is copied when contextmenu is shown var copyText = host.getCopyText(); if (copyText) clipboardData.setData("Text", copyText); @@ -214,6 +204,7 @@ var TextInput = function(parentNode, host) { event.preventDefault(e); } }); + event.addListener(text, "cut", onCut); // for ie9 context menu } else if (useragent.isOpera) { event.addListener(parentNode, "keydown", function(e) { @@ -251,12 +242,12 @@ var TextInput = function(parentNode, host) { event.addListener(text, "focus", function() { host.onFocus(); - select(); + reset(); }); this.focus = function() { host.onFocus(); - select(); + reset(); text.focus(); }; @@ -273,28 +264,50 @@ var TextInput = function(parentNode, host) { return text; }; - this.onContextMenu = function(mousePos, isEmpty){ - if (mousePos) { - if (!tempStyle) - tempStyle = text.style.cssText; - - text.style.cssText = - 'position:fixed; z-index:1000;' + - 'left:' + (mousePos.x - 2) + 'px; top:' + (mousePos.y - 2) + 'px;'; - } - if (isEmpty) - text.value=''; + this.onContextMenu = function(e) { + if (!tempStyle) + tempStyle = text.style.cssText; + + text.style.cssText = + "position:fixed; z-index:100000;" + //"background:rgba(250, 0, 0, 0.3); opacity:1;" + + "left:" + (e.clientX - 2) + "px; top:" + (e.clientY - 2) + "px;"; + + if (host.selection.isEmpty()) + text.value = ""; + + if (e.type != "mousedown") + return; + + if (host.renderer.$keepTextAreaAtCursor) + host.renderer.$keepTextAreaAtCursor = null; + + event.capture(host.container, function(e) { + text.style.left = e.clientX - 2 + "px"; + text.style.top = e.clientY - 2 + "px"; + }, onContextMenuClose); }; - this.onContextMenuClose = function(){ + function onContextMenuClose() { setTimeout(function () { if (tempStyle) { text.style.cssText = tempStyle; tempStyle = ''; } sendText(); + if (host.renderer.$keepTextAreaAtCursor == null) { + host.renderer.$keepTextAreaAtCursor = true; + host.renderer.$moveTextAreaToCursor(); + } }, 0); }; + this.onContextMenuClose = onContextMenuClose; + + // firefox fires contextmenu event after opening it + if (!useragent.isGecko) + event.addListener(text, "contextmenu", function(e) { + host.textInput.onContextMenu(e); + onContextMenuClose() + }); }; exports.TextInput = TextInput; diff --git a/lib/ace/mouse/default_handlers.js b/lib/ace/mouse/default_handlers.js index 48fe88d7..8c804f2b 100644 --- a/lib/ace/mouse/default_handlers.js +++ b/lib/ace/mouse/default_handlers.js @@ -80,26 +80,19 @@ function DefaultHandlers(mouseHandler) { var editor = this.editor; var _self = this; - this.ev = ev - var selectionRange = editor.getSelectionRange(); - var selectionEmpty = selectionRange.isEmpty(); - var button = ev.getButton(); if (button !== 0) { + var selectionRange = editor.getSelectionRange(); + var selectionEmpty = selectionRange.isEmpty(); + if (selectionEmpty) { editor.moveCursorToPosition(pos); editor.selection.clearSelection(); } - // 2: contextmenu, 1: linux paste - this.moveTextarea = function() { - editor.textInput.onContextMenu({x: _self.x, y: _self.y}); - }; - this.moveTextareaEnd = editor.textInput.onContextMenuClose; - - editor.textInput.onContextMenu({x: this.x, y: this.y}, selectionEmpty); - this.captureMouse(ev, "moveTextarea"); - return; + // 2: contextmenu, 1: linux paste + editor.textInput.onContextMenu(ev.domEvent); + return ev.stop(); } // if this click caused the editor to be focused should not clear the diff --git a/lib/ace/mouse/mouse_handler.js b/lib/ace/mouse/mouse_handler.js index a85e9672..f7dbee99 100644 --- a/lib/ace/mouse/mouse_handler.js +++ b/lib/ace/mouse/mouse_handler.js @@ -55,9 +55,6 @@ var MouseHandler = function(editor) { editor.focus(); return event.preventDefault(e); }); - event.addListener(editor.container, "selectstart", function(e) { - return event.preventDefault(e); - }); var mouseTarget = editor.renderer.getMouseEventTarget(); event.addListener(mouseTarget, "mousedown", this.onMouseEvent.bind(this, "mousedown")); diff --git a/lib/ace/virtual_renderer.js b/lib/ace/virtual_renderer.js index 363f3cd8..190471fa 100644 --- a/lib/ace/virtual_renderer.js +++ b/lib/ace/virtual_renderer.js @@ -533,13 +533,23 @@ var VirtualRenderer = function(container, theme) { var posLeft = this.$cursorLayer.$pixelPos.left; posTop -= this.layerConfig.offset; - if (posTop < 0 || posTop > this.layerConfig.height) + if (posTop < 0 || posTop > this.layerConfig.height - this.lineHeight) return; - posLeft += (this.showGutter ? this.$gutterLayer.gutterWidth : 0) - this.scrollLeft; - var bounds = this.container.getBoundingClientRect(); - this.textarea.style.left = (bounds.left + posLeft) + "px"; - this.textarea.style.top = (bounds.top + posTop) + "px"; + var w = this.characterWidth; + if (this.$composition) + w += this.textarea.scrollWidth; + posLeft -= this.scrollLeft; + if (posLeft > this.$size.scrollerWidth - w) + posLeft = this.$size.scrollerWidth - w; + + if (this.showGutter) + posLeft += this.$gutterLayer.gutterWidth; + + this.textarea.style.height = this.lineHeight + "px"; + this.textarea.style.width = w + "px"; + this.textarea.style.left = posLeft + "px"; + this.textarea.style.top = posTop - 1 + "px"; }; /** @@ -1211,21 +1221,16 @@ var VirtualRenderer = function(container, theme) { * **/ this.showComposition = function(position) { - if (!this.$composition) { - this.$composition = dom.createElement("div"); - this.$composition.className = "ace_composition"; - this.content.appendChild(this.$composition); - } + if (!this.$composition) + this.$composition = { + keepTextAreaAtCursor: this.$keepTextAreaAtCursor, + cssText: this.textarea.style.cssText + }; - this.$composition.innerHTML = " "; - - var pos = this.$cursorLayer.getPixelPosition(); - var style = this.$composition.style; - style.top = pos.top + "px"; - style.left = (pos.left + this.$padding) + "px"; - style.height = this.lineHeight + "px"; - - this.hideCursor(); + this.$keepTextAreaAtCursor = true; + dom.addCssClass(this.textarea, "ace_composition"); + this.textarea.style.cssText = ""; + this.$moveTextAreaToCursor(); }; /** @@ -1235,7 +1240,7 @@ var VirtualRenderer = function(container, theme) { * Sets the inner text of the current composition to `text`. **/ this.setCompositionText = function(text) { - dom.setInnerText(this.$composition, text); + this.$moveTextAreaToCursor(); }; /** @@ -1244,14 +1249,13 @@ var VirtualRenderer = function(container, theme) { * Hides the current composition. **/ this.hideComposition = function() { - this.showCursor(); - if (!this.$composition) return; - var style = this.$composition.style; - style.top = "-10000px"; - style.left = "-10000px"; + dom.removeCssClass(this.textarea, "ace_composition"); + this.$keepTextAreaAtCursor = this.$composition.keepTextAreaAtCursor; + this.textarea.style.cssText = this.$composition.cssText; + this.$composition = null; }; this._loadTheme = function(name, callback) {