Bug 852088 - Add drag caret to select text support. r-fryn

This commit is contained in:
Jim Mathies 2013-03-21 08:55:16 -05:00
parent d958db6681
commit e88bfad5c0
5 changed files with 136 additions and 45 deletions

View File

@ -10,9 +10,9 @@
<html:div flex="1" class="selection-overlay-inner window-width window-height" anonid="selection-overlay-inner">
<xul:stack>
<html:div anonid="selection-overlay-debug" class="window-width window-height"/>
<xul:toolbarbutton id="selectionhandle-start" label="^" left="10" top="10" hidden="true"/>
<xul:toolbarbutton id="selectionhandle-end" label="^" left="10" top="10" hidden="true"/>
<xul:toolbarbutton id="selectionhandle-caret" label="^" left="10" top="10" hidden="true"/>
<xul:toolbarbutton id="selectionhandle-mark1" label="^" left="10" top="10" hidden="true"/>
<xul:toolbarbutton id="selectionhandle-mark2" label="^" left="10" top="10" hidden="true"/>
<xul:toolbarbutton id="selectionhandle-mark3" label="^" left="10" top="10" hidden="true"/>
</xul:stack>
</html:div>
</content>

View File

@ -411,8 +411,15 @@ var SelectionHandler = {
}
// Updates this._cache content selection position data which we send over
// to SelectionHelperUI.
this._updateUIMarkerRects(selection);
// to SelectionHelperUI. Note updateUIMarkerRects will fail if there isn't
// any selection in the page. This can happen when we start a monocle drag
// but haven't dragged enough to create selection. Just return.
try {
this._updateUIMarkerRects(selection);
} catch (ex) {
Util.dumpLn(ex.message);
return;
}
this._cache.updateStart = aUpdateStart;
this._cache.updateEnd = aUpdateEnd;

View File

@ -76,6 +76,7 @@ function Marker(aParent, aTag, aElementId, xPos, yPos) {
this._yPos = yPos;
this._selectionHelperUI = aParent;
this._element = document.getElementById(aElementId);
this._elementId = aElementId;
// These get picked in input.js and receives drag input
this._element.customDragger = new MarkerDragger(this);
this.tag = aTag;
@ -83,6 +84,7 @@ function Marker(aParent, aTag, aElementId, xPos, yPos) {
Marker.prototype = {
_element: null,
_elementId: "",
_selectionHelperUI: null,
_xPos: 0,
_yPos: 0,
@ -171,8 +173,12 @@ Marker.prototype = {
moveBy: function moveBy(aDx, aDy, aClientX, aClientY) {
this._xPos -= aDx;
this._yPos -= aDy;
this._selectionHelperUI.markerDragMove(this);
this._setPosition();
let direction = (aDx < 0 || aDy < 0 ? "end" : "start");
// We may swap markers in markerDragMove. If markerDragMove
// returns true keep processing, otherwise get out of here.
if (this._selectionHelperUI.markerDragMove(this, direction)) {
this._setPosition();
}
},
hitTest: function hitTest(aX, aY) {
@ -187,6 +193,23 @@ Marker.prototype = {
return true;
return false;
},
swapMonocle: function swapMonocle(aCaret) {
let targetElement = aCaret._element;
let targetElementId = aCaret._elementId;
aCaret._element = this._element;
aCaret._element.customDragger._marker = aCaret;
aCaret._elementId = this._elementId;
this._xPos = aCaret._xPos;
this._yPos = aCaret._yPos;
this._element = targetElement;
this._element.customDragger._marker = this;
this._elementId = targetElementId;
this._element.visible = true;
},
};
/*
@ -203,24 +226,29 @@ var SelectionHelperUI = {
_movement: { active: false, x:0, y: 0 },
_activeSelectionRect: null,
_selectionHandlerActive: false,
_selectionMarkIds: [],
/*
* Properties
*/
get startMark() {
if (this._startMark == null) {
this._startMark = new Marker(this, "start", "selectionhandle-start", 0, 0);
this._startMark = new Marker(this, "start", this._selectionMarkIds.pop(), 0, 0);
}
return this._startMark;
},
get endMark() {
if (this._endMark == null) {
this._endMark = new Marker(this, "end", "selectionhandle-end", 0, 0);
this._endMark = new Marker(this, "end", this._selectionMarkIds.pop(), 0, 0);
}
return this._endMark;
},
get caretMark() {
if (this._caretMark == null) {
this._caretMark = new Marker(this, "caret", "selectionhandle-caret", 0, 0);
this._caretMark = new Marker(this, "caret", this._selectionMarkIds.pop(), 0, 0);
}
return this._caretMark;
},
@ -229,6 +257,20 @@ var SelectionHelperUI = {
return document.getElementById("selection-overlay");
},
/*
* isActive (prop)
*
* Determines if an edit session is currently active.
*/
get isActive() {
return (this._msgTarget &&
this._selectionHandlerActive);
},
/*
* Public apis
*/
/*
* openEditSession
*
@ -241,10 +283,6 @@ var SelectionHelperUI = {
this._init(aContent);
this._setupDebugOptions();
// Set the track bounds for each marker NIY
this.startMark.setTrackBounds(aClientX, aClientY);
this.endMark.setTrackBounds(aClientX, aClientY);
// Send this over to SelectionHandler in content, they'll message us
// back with information on the current selection. SelectionStart
// takes client coordinates.
@ -266,10 +304,6 @@ var SelectionHelperUI = {
this._init(aContent);
this._setupDebugOptions();
// Set the track bounds for each marker NIY
this.startMark.setTrackBounds(aClientX, aClientY);
this.endMark.setTrackBounds(aClientX, aClientY);
// Send this over to SelectionHandler in content, they'll message us
// back with information on the current selection. SelectionAttach
// takes client coordinates.
@ -289,7 +323,7 @@ var SelectionHelperUI = {
* Note the caret marker is pretty limited in functionality. The
* only thing is can do is be displayed at the caret position.
* Once the user starts a drag, the caret marker is hidden, and
* the start and end markers take over. (TBD)
* the start and end markers take over.
*
* @param aClientX, aClientY client coordiates of the tap that
* initiated the session.
@ -319,16 +353,6 @@ var SelectionHelperUI = {
return false;
},
/*
* isActive (prop)
*
* Determines if an edit session is currently active.
*/
get isActive() {
return (this._msgTarget &&
this._selectionHandlerActive);
},
/*
* closeEditSession
*
@ -359,6 +383,11 @@ var SelectionHelperUI = {
// store the target message manager
this._msgTarget = aMsgTarget;
// Init our list of available monocle ids
this._selectionMarkIds = ["selectionhandle-mark1",
"selectionhandle-mark2",
"selectionhandle-mark3"];
// SelectionHandler messages
messageManager.addMessageListener("Content:SelectionRange", this);
messageManager.addMessageListener("Content:SelectionCopied", this);
@ -419,10 +448,11 @@ var SelectionHelperUI = {
if (this._caretMark != null)
this._caretMark.shutdown();
delete this._startMark;
delete this._endMark;
delete this._caretMark;
this._startMark = null;
this._endMark = null;
this._caretMark = null;
this._selectionMarkIds = [];
this._msgTarget = null;
this._activeSelectionRect = null;
this._selectionHandlerActive = false;
@ -435,6 +465,54 @@ var SelectionHelperUI = {
* Utilities
*/
/*
* _swapCaretMarker
*
* Swap two drag markers - used when transitioning from caret mode
* to selection mode. We take the current caret marker (which is in a
* drag state) and swap it out with one of the selection markers.
*/
_swapCaretMarker: function _swapCaretMarker(aDirection) {
let targetMark = null;
if (aDirection == "start")
targetMark = this.startMark;
else
targetMark = this.endMark;
let caret = this.caretMark;
targetMark.swapMonocle(caret);
let id = caret._elementId;
caret.shutdown();
this._caretMark = null;
this._selectionMarkIds.push(id);
},
/*
* _transitionFromCaretToSelection
*
* Transitions from caret mode to text selection mode.
*/
_transitionFromCaretToSelection: function _transitionFromCaretToSelection(aDirection) {
// Get selection markers initialized if they aren't already
{ let mark = this.startMark; mark = this.endMark; }
// Swap the caret marker out for the start or end marker depending
// on direction.
this._swapCaretMarker(aDirection);
let targetMark = null;
if (aDirection == "start")
targetMark = this.startMark;
else
targetMark = this.endMark;
// Start the selection monocle drag. SelectionHandler relies on this
// for getting initialized. This will also trigger a message back for
// monocle positioning. Note, markerDragMove is still on the stack in
// this call!
targetMark._setPosition();
this.markerDragStart(targetMark);
this.markerDragMove(targetMark, aDirection);
},
/*
* _setupDebugOptions
*
@ -731,13 +809,17 @@ var SelectionHelperUI = {
this._sendAsyncMessage("Browser:SelectionMoveEnd", json);
},
markerDragMove: function markerDragMove(aMarker) {
markerDragMove: function markerDragMove(aMarker, aDirection) {
let json = this._getMarkerBaseMessage();
json.change = aMarker.tag;
if (aMarker.tag == "caret") {
this._sendAsyncMessage("Browser:CaretMove", json);
return;
// We are going to transition from caret browsing mode to selection mode
// on a drag. So swap the caret monocle for a start or end monocle
// depending on the direction of the drag, and start selecting text.
this._transitionFromCaretToSelection(aDirection);
return false;
}
this._sendAsyncMessage("Browser:SelectionMove", json);
return true;
},
};

View File

@ -146,10 +146,12 @@ var TouchModule = {
// once we get omtc and the apzc. Currently though dblclick is delivered to
// content and triggers selection of text, so fire up the SelectionHelperUI
// once selection is present.
setTimeout(function () {
SelectionHelperUI.attachEditSession(Browser.selectedTab.browser,
aEvent.clientX, aEvent.clientY);
}, 50);
if (!SelectionHelperUI.isActive) {
setTimeout(function () {
SelectionHelperUI.attachEditSession(Browser.selectedTab.browser,
aEvent.clientX, aEvent.clientY);
}, 50);
}
break;
}
}

View File

@ -934,18 +934,18 @@ setting[type="directory"] > .preferences-alignment {
/* Text selection handles */
#selectionhandle-start,
#selectionhandle-end,
#selectionhandle-caret {
#selectionhandle-mark1,
#selectionhandle-mark2,
#selectionhandle-mark3 {
border: 0px solid gray;
padding: 0px;
margin-top: -30px;
margin-left: -18px;
}
#selectionhandle-start,
#selectionhandle-end,
#selectionhandle-caret {
#selectionhandle-mark1,
#selectionhandle-mark2,
#selectionhandle-mark3 {
list-style-image: url("chrome://browser/skin/images/selection-monocle.png");
}