jenkins-bot has submitted this change and it was merged.
Change subject: FocusedNode Selection
......................................................................
FocusedNode Selection
Introduction of fake selection for single focused nodes. This
change specifically makes the selection much nicer in appearance
for Chrome users selecting floated FocusableNodes (block images)
for example.
Added ve-ce-surface-highlights DOM element to contain styled
highlight elements.
Made adjustments to getSelectionRect to return fake selection
bounds if necessary.
Replaced old uses of showSelection with model.change().
Change-Id: I96e66567cdce6455ef3eb77568e72f23140448ff
---
M modules/ve/ce/styles/ve.ce.Node.css
M modules/ve/ce/styles/ve.ce.Surface.css
M modules/ve/ce/ve.ce.FocusableNode.js
M modules/ve/ce/ve.ce.ProtectedNode.js
M modules/ve/ce/ve.ce.ResizableNode.js
M modules/ve/ce/ve.ce.Surface.js
M modules/ve/init/mw/targets/ve.init.mw.ViewPageTarget.js
M modules/ve/ui/actions/ve.ui.FormatAction.js
M modules/ve/ui/actions/ve.ui.HistoryAction.js
9 files changed, 137 insertions(+), 29 deletions(-)
Approvals:
Catrope: Looks good to me, approved
jenkins-bot: Verified
diff --git a/modules/ve/ce/styles/ve.ce.Node.css
b/modules/ve/ce/styles/ve.ce.Node.css
index 68233f1..f0f1a00 100644
--- a/modules/ve/ce/styles/ve.ce.Node.css
+++ b/modules/ve/ce/styles/ve.ce.Node.css
@@ -61,6 +61,13 @@
cursor: default;
}
+/* ve.ce.FocusableNode */
+
+.ve-ce-focusableNode-highlight {
+ background: #6da9f7;
+ position: absolute;
+}
+
/* ve.ce.ResizableNode */
.ve-ce-resizableNode-transitioning {
diff --git a/modules/ve/ce/styles/ve.ce.Surface.css
b/modules/ve/ce/styles/ve.ce.Surface.css
index 1324d2b..b4c4c64 100644
--- a/modules/ve/ce/styles/ve.ce.Surface.css
+++ b/modules/ve/ce/styles/ve.ce.Surface.css
@@ -36,10 +36,19 @@
/* @noflip */
.ve-ce-surface-phantoms {
- position: absolute;
- top: 0;
left: 0;
opacity: 0.75;
+ position: absolute;
+ top: 0;
+}
+
+/* @noflip */
+.ve-ce-surface-highlights {
+ left: 0;
+ opacity: 0.5;
+ pointer-events: none;
+ position: absolute;
+ top: 0;
}
.ve-ce-surface-paste {
diff --git a/modules/ve/ce/ve.ce.FocusableNode.js
b/modules/ve/ce/ve.ce.FocusableNode.js
index 8175be9..2659a74 100644
--- a/modules/ve/ce/ve.ce.FocusableNode.js
+++ b/modules/ve/ce/ve.ce.FocusableNode.js
@@ -15,6 +15,9 @@
* element, but in some cases it may need to be configured to be a specific
child element within the
* node's DOM rendering.
*
+ * If your focusable node changes size and the highlight must be redrawn, call
redrawHighlight().
+ * ResizableNode 'resize' event is already bound.
+ *
* @class
* @abstract
*
@@ -25,6 +28,14 @@
// Properties
this.focused = false;
this.$focusable = $focusable || this.$;
+ this.$highlights = $( [] );
+ this.surface = null;
+
+ // Events
+ this.connect( this, {
+ 'setup': 'onFocusableSetup',
+ 'resize': 'redrawHighlight'
+ } );
};
/* Events */
@@ -38,6 +49,25 @@
*/
/* Methods */
+
+/**
+ * Handle setup event.
+ *
+ * @method
+ */
+ve.ce.FocusableNode.prototype.onFocusableSetup = function () {
+ this.surface = this.root.getSurface();
+};
+
+/**
+ * Notification of dimension change
+ *
+ * @method
+ */
+ve.ce.FocusableNode.prototype.redrawHighlight = function () {
+ this.clearHighlight();
+ this.createHighlight();
+};
/**
* Check if node is focused.
@@ -64,9 +94,48 @@
if ( this.focused ) {
this.emit( 'focus' );
this.$.addClass( 've-ce-node-focused' );
+ this.createHighlight();
} else {
this.emit( 'blur' );
this.$.removeClass( 've-ce-node-focused' );
+ this.clearHighlight();
}
}
};
+
+/**
+ * Creates highlight
+ *
+ * @method
+ */
+ve.ce.FocusableNode.prototype.createHighlight = function () {
+ var elementOffset;
+
+ this.$.find( '*' ).add( this.$ ).each(
+ ve.bind( function( i, element ) {
+ elementOffset = $(element).offset();
+ this.$highlights = this.$highlights.add(
+ $( '<div>' )
+ .css( {
+ height: $( element ).height(),
+ width: $( element ).width(),
+ top: elementOffset.top,
+ left: elementOffset.left
+ } )
+ .addClass(
've-ce-focusableNode-highlight' )
+ );
+ }, this )
+ );
+
+ this.surface.replaceHighlight( this.$highlights );
+};
+
+/**
+ * Clears highlight
+ *
+ * @method
+ */
+ve.ce.FocusableNode.prototype.clearHighlight = function () {
+ this.$highlights = $( [] );
+ this.surface.replaceHighlight( null );
+};
diff --git a/modules/ve/ce/ve.ce.ProtectedNode.js
b/modules/ve/ce/ve.ce.ProtectedNode.js
index 4f7f923..be561d3 100644
--- a/modules/ve/ce/ve.ce.ProtectedNode.js
+++ b/modules/ve/ce/ve.ce.ProtectedNode.js
@@ -125,12 +125,12 @@
};
/**
- * Handle phantom click events.
+ * Handle phantom mouse down events.
*
* @method
- * @param {jQuery.Event} e Mouse click event
+ * @param {jQuery.Event} e Mouse down event
*/
-ve.ce.ProtectedNode.prototype.onPhantomClick = function ( e ) {
+ve.ce.ProtectedNode.prototype.onPhantomMouseDown = function ( e ) {
var surfaceModel = this.getRoot().getSurface().getModel(),
selectionRange = surfaceModel.getSelection(),
nodeRange = this.model.getOuterRange();
@@ -142,6 +142,8 @@
) :
nodeRange
).select();
+
+ e.preventDefault();
};
/**
@@ -206,7 +208,7 @@
this.$.find( '.ve-ce-protectedNode-shield' ).each(
ve.bind( function () {
this.$phantoms = this.$phantoms.add(
- $phantomTemplate.clone().on( 'click', ve.bind(
this.onPhantomClick, this ) )
+ $phantomTemplate.clone().on( 'mousedown',
ve.bind( this.onPhantomMouseDown, this ) )
);
}, this )
);
diff --git a/modules/ve/ce/ve.ce.ResizableNode.js
b/modules/ve/ce/ve.ce.ResizableNode.js
index 0337312..771b4ad 100644
--- a/modules/ve/ce/ve.ce.ResizableNode.js
+++ b/modules/ve/ce/ve.ce.ResizableNode.js
@@ -27,7 +27,8 @@
this.connect( this, {
'focus': 'onResizableFocus',
'blur': 'onResizableBlur',
- 'live': 'onResizableLive'
+ 'live': 'onResizableLive',
+ 'resize': 'onResizableFocus'
} );
// Initialization
@@ -280,7 +281,7 @@
surfaceModel.change( txs, selection );
}
- // HACK: Update bounding box
- this.onResizableFocus();
+ this.emit( 'resize' );
+
}, this ), transition ? 200 : 0 );
};
diff --git a/modules/ve/ce/ve.ce.Surface.js b/modules/ve/ce/ve.ce.Surface.js
index e42abae..0434acf 100644
--- a/modules/ve/ce/ve.ce.Surface.js
+++ b/modules/ve/ce/ve.ce.Surface.js
@@ -40,6 +40,7 @@
this.relocating = false;
this.selecting = false;
this.$phantoms = this.$$( '<div>' );
+ this.$highlights = this.$$( '<div>' );
this.$pasteTarget = this.$$( '<div>' );
this.pasting = false;
this.clickHistory = [];
@@ -68,11 +69,12 @@
// Initialization
this.$.addClass( 've-ce-surface' );
this.$phantoms.addClass( 've-ce-surface-phantoms' );
+ this.$highlights.addClass( 've-ce-surface-highlights' );
this.$pasteTarget.addClass( 've-ce-surface-paste' ).prop(
'contenteditable', true );
// Add elements to the DOM
this.$.append( this.documentView.getDocumentNode().$, this.$pasteTarget
);
- this.surface.$localOverlay.append( this.$phantoms );
+ this.surface.$localOverlay.append( this.$phantoms, this.$highlights );
};
/* Inheritance */
@@ -121,18 +123,29 @@
* Get the coordinates of the selection anchor.
*
* @method
- * @static
- * @param {HTMLDocument} domDocument Document of selection
*/
ve.ce.Surface.prototype.getSelectionRect = function () {
- var sel, rect, $span, lineHeight, startRange, startOffset, endRange,
endOffset,
- domDocument = this.getElementDocument();
+ var sel, rect, $span, lineHeight, startRange, startOffset, endRange,
endOffset, focusedOffset;
+
+ if ( this.focusedNode ) {
+ focusedOffset = this.focusedNode.$.offset();
+ return {
+ 'start': {
+ 'x': focusedOffset.left,
+ 'y': focusedOffset.top
+ },
+ 'end': {
+ 'x': focusedOffset.left +
this.focusedNode.$.width(),
+ 'y': focusedOffset.top +
this.focusedNode.$.height()
+ }
+ };
+ }
if ( !rangy.initialized ) {
rangy.init();
}
- sel = rangy.getSelection( domDocument );
+ sel = rangy.getSelection( this.getElementDocument() );
// We can't do anything if there's no selection
if ( sel.rangeCount === 0 ) {
@@ -670,9 +683,6 @@
previous = this.focusedNode;
if ( selection ) {
- if ( this.isRenderingEnabled() ) {
- this.showSelection( selection );
- }
// Detect when only a single inline element is selected
if ( !selection.isCollapsed() ) {
start =
this.documentView.getDocumentNode().getNodeFromOffset( selection.start + 1 );
@@ -692,7 +702,12 @@
if ( next ) {
next.setFocused( true );
this.focusedNode = start;
+ rangy.getSelection( this.getElementDocument()
).removeAllRanges();
}
+ }
+ // If there is no focused node, use native selection
+ if ( !this.focusedNode && this.isRenderingEnabled() ) {
+ this.showSelection( selection );
}
}
};
@@ -1331,12 +1346,10 @@
rangySel = rangy.getSelection( this.$document[0] ),
rangyRange = rangy.createRange( this.$document[0] );
- // Ensure the range we are asking to select is from and to correct
offsets - failure to do so
- // may cause getNodeAndOffset to throw an exception
- range = new ve.Range(
- this.getNearestCorrectOffset( range.from, -1 ),
- this.getNearestCorrectOffset( range.to, 1 )
- );
+ range = new ve.Range(
+ this.getNearestCorrectOffset( range.from, -1 ),
+ this.getNearestCorrectOffset( range.to, 1 )
+ );
if ( !range.isCollapsed() ) {
start = this.documentView.getNodeAndOffset( range.start );
@@ -1362,6 +1375,16 @@
this.$phantoms.empty().append( $phantoms );
};
+/**
+ * Append passed highlights to highlight container after emptying it first.
+ *
+ * @method
+ * @param {jQuery} $highlights Highlights to append
+ */
+ve.ce.Surface.prototype.replaceHighlight = function ( $highlights ) {
+ this.$highlights.empty().append( $highlights );
+};
+
/*! Helpers */
/**
diff --git a/modules/ve/init/mw/targets/ve.init.mw.ViewPageTarget.js
b/modules/ve/init/mw/targets/ve.init.mw.ViewPageTarget.js
index adc8be9..9c9ebfc 100644
--- a/modules/ve/init/mw/targets/ve.init.mw.ViewPageTarget.js
+++ b/modules/ve/init/mw/targets/ve.init.mw.ViewPageTarget.js
@@ -1673,7 +1673,6 @@
headingNode.getModel().getOffset()
);
surfaceModel.change( null, new ve.Range(
offset, offset ) );
- surfaceView.showSelection(
surfaceModel.getSelection() );
}
} );
this.section = null;
diff --git a/modules/ve/ui/actions/ve.ui.FormatAction.js
b/modules/ve/ui/actions/ve.ui.FormatAction.js
index 7f0cd54..0d5c6b7 100644
--- a/modules/ve/ui/actions/ve.ui.FormatAction.js
+++ b/modules/ve/ui/actions/ve.ui.FormatAction.js
@@ -46,7 +46,6 @@
*/
ve.ui.FormatAction.prototype.convert = function ( type, attributes ) {
var selected, i, length, contentBranch, txs,
- surfaceView = this.surface.getView(),
surfaceModel = this.surface.getModel(),
selection = surfaceModel.getSelection(),
fragmentForSelection = surfaceModel.getFragment( selection,
true ),
@@ -71,7 +70,6 @@
txs = ve.dm.Transaction.newFromContentBranchConversion( doc, selection,
type, attributes );
surfaceModel.change( txs, selection );
- surfaceView.showSelection( selection );
};
/* Registration */
diff --git a/modules/ve/ui/actions/ve.ui.HistoryAction.js
b/modules/ve/ui/actions/ve.ui.HistoryAction.js
index a949066..9d2f22b 100644
--- a/modules/ve/ui/actions/ve.ui.HistoryAction.js
+++ b/modules/ve/ui/actions/ve.ui.HistoryAction.js
@@ -42,7 +42,7 @@
ve.ui.HistoryAction.prototype.undo = function () {
var range = this.surface.getModel().undo();
if ( range ) {
- this.surface.getView().showSelection( range );
+ this.surface.getModel().change( null, range );
}
};
@@ -54,7 +54,7 @@
ve.ui.HistoryAction.prototype.redo = function () {
var range = this.surface.getModel().redo();
if ( range ) {
- this.surface.getView().showSelection( range );
+ this.surface.getModel().change( null, range );
}
};
--
To view, visit https://gerrit.wikimedia.org/r/67153
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: merged
Gerrit-Change-Id: I96e66567cdce6455ef3eb77568e72f23140448ff
Gerrit-PatchSet: 8
Gerrit-Project: mediawiki/extensions/VisualEditor
Gerrit-Branch: master
Gerrit-Owner: Christian <[email protected]>
Gerrit-Reviewer: Catrope <[email protected]>
Gerrit-Reviewer: Trevor Parscal <[email protected]>
Gerrit-Reviewer: jenkins-bot
_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits