Mooeypoo has uploaded a new change for review. https://gerrit.wikimedia.org/r/84014
Change subject: [WIP] StickeredNode mixin and StickerWidget ...................................................................... [WIP] StickeredNode mixin and StickerWidget This is the UI piece of the StickerNode, a node mixin that allows for static menus on block nodes for edit purposes. The widget will be populated by inspectors for the node they are associated with. The main use of this functionality is the Language Block node, but the functionality will be useful for other block nodes like tables. Change-Id: Iaef3afdf6ae8e18457146fbc03b6877494bcf650 --- M VisualEditor.php A modules/ve/ce/ve.ce.StickeredNode.js M modules/ve/ui/styles/ve.ui.Widget.css A modules/ve/ui/ve.ui.Sticker.js A modules/ve/ui/widgets/ve.ui.StickerWidget.js 5 files changed, 441 insertions(+), 0 deletions(-) git pull ssh://gerrit.wikimedia.org:29418/mediawiki/extensions/VisualEditor refs/changes/14/84014/1 diff --git a/VisualEditor.php b/VisualEditor.php index 4920ed4..878ce47 100644 --- a/VisualEditor.php +++ b/VisualEditor.php @@ -705,6 +705,9 @@ 've/ui/tools/ve.ui.BlockInspectorTool.js', 've/ui/actions/ve.ui.BlockInspectorAction.js', 've/ui/ve.ui.StickerToolbar.js', + 've/ce/ve.ce.StickeredNode.js', + 've/ui/ve.ui.Sticker.js', + 've/ui/widgets/ve.ui.StickerWidget.js', 've/ui/tools/ve.ui.ExperimentalTool.js', 've-mw/ui/tools/ve.ui.MWExperimentalTool.js', ), diff --git a/modules/ve/ce/ve.ce.StickeredNode.js b/modules/ve/ce/ve.ce.StickeredNode.js new file mode 100644 index 0000000..ecd7b9d --- /dev/null +++ b/modules/ve/ce/ve.ce.StickeredNode.js @@ -0,0 +1,80 @@ +/*! + * VisualEditor ContentEditable FocusableNode class. + * + * @copyright 2011-2013 VisualEditor Team and others; see AUTHORS.txt + * @license The MIT License (MIT); see LICENSE.txt + */ + +/** + * ContentEditable sticker node. + * + * Node that pops a sticky menu at the top. + * + * @param {jQuery} [$stickered=this.$] node jquery object + */ +ve.ce.StickeredNode = function VeCeStickeredNode( $stickered ) { + this.$stickered = $stickered || this.$; + + this.$stickered.addClass( 've-ce-stickeredNode' ); + + this.$stickered.on( 'mouseenter', ve.bind( this.onMouseEnter, this ) ); + this.$stickered.on( 'mouseleave', ve.bind( this.onMouseLeave, this ) ); + + this.dmNode = this.model; // <-- how do I get Dm of the current node?? + + this.surface = null; + this.sticker = null; + + this.focused = false; + // Events + this.connect( this, { + 'setup': 'onStickeredSetup', + 'resize': 'onStickeredResize', + 'rerender': 'onStickeredRerendered', + } ); +} + +ve.ce.StickeredNode.prototype.onStickeredSetup = function () { + this.surface = this.root.getSurface(); + // Create Sticker: + this.sticker = new ve.ui.Sticker( this.surface.getSurface(), { + '$$': this.$$, + '$stickeredNode': this.$, + 'nodeView': this, + 'nodeModel': this.dmNode + } ); + this.sticker.show( true ); + + // Repositioning Events: + this.surface.getModel() + .connect( this, { 'change': 'onStickeredModelChange' } ); + this.surface.getSurface() + .connect( this, { 'position': 'onStickeredResize' } ); + +} + +ve.ce.StickeredNode.prototype.onStickeredRerendered = function () { + this.updatePosition(); +} + +ve.ce.StickeredNode.prototype.onStickeredResize = function () { + this.updatePosition(); + +} + +ve.ce.StickeredNode.prototype.onStickeredModelChange = function () { + this.updatePosition(); + this.sticker.updateMenu(); +} + +ve.ce.StickeredNode.prototype.updatePosition = function() { + var $stickeredNode = this.$; + this.sticker.updateDimensions( true, $stickeredNode.position() ); +}; + +ve.ce.StickeredNode.prototype.onMouseEnter = function () { + this.focused = true; +}; +ve.ce.StickeredNode.prototype.onMouseLeave = function () { + this.focused = false; +}; diff --git a/modules/ve/ui/styles/ve.ui.Widget.css b/modules/ve/ui/styles/ve.ui.Widget.css index 2bf8ef3..aa002f9 100644 --- a/modules/ve/ui/styles/ve.ui.Widget.css +++ b/modules/ve/ui/styles/ve.ui.Widget.css @@ -575,3 +575,15 @@ border-bottom-left-radius: 0; border-bottom-width: 0; } + +/* ve.ui.StickerWidget.js */ + +.ve-ui-stickerWidget { + position: absolute; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + border: 1px solid #F2DCA0; + background: #FFFAC7; + padding: 2px; +} diff --git a/modules/ve/ui/ve.ui.Sticker.js b/modules/ve/ui/ve.ui.Sticker.js new file mode 100644 index 0000000..80c3b7a --- /dev/null +++ b/modules/ve/ui/ve.ui.Sticker.js @@ -0,0 +1,242 @@ +/*! + * VisualEditor UserInterface Context class. + * + * @copyright 2011-2013 VisualEditor Team and others; see AUTHORS.txt + * @license The MIT License (MIT); see LICENSE.txt + */ + +/** + * UserInterface . + * + * @class + * @extends ve.Element + * + * @constructor + * @param {ve.ui.Surface} surface + * @param {Object} [config] Config options + */ +ve.ui.Sticker = function VeUiSticker( surface, config ) { + // Parent constructor + ve.Element.call( this, config ); + + // Properties + this.$stickeredNode = config.$stickeredNode; + this.nodeModel = config.nodeModel; + this.nodeView = config.nodeView; + this.surface = surface; + this.inspectors = {}; + this.visible = false; + this.showing = false; + this.selecting = false; + this.relocating = false; + this.embedded = false; + this.selection = null; + this.toolbar = null; + this.popup = new ve.ui.StickerWidget( { + '$$': this.$$, + '$container': this.surface.$, + '$wrapper': this.$, + '$stickeredNode': this.$stickeredNode + } ); + this.$menu = this.$$( '<div>' ); + this.inspectors = new ve.ui.WindowSet( surface, ve.ui.inspectorFactory, { 'isBlock': true } ); + + // Initialization + this.$.addClass( 've-ui-sticker' ).append( this.popup.$ ); + this.inspectors.$.addClass( 've-ui-sticker-inspectors' ); + + this.popup.$body.append( + this.$menu.addClass( 've-ui-sticker-menu' ), + this.inspectors.$.addClass( 've-ui-sticker-inspectors' ) + ); + + this.$.append( this.popup.$ ); + this.surface.$localOverlay.append( this.$ ); + + + // Events + this.inspectors.connect( this, { + 'setup': 'onInspectorSetup', + 'open': 'onInspectorOpen', + 'close': 'onInspectorClose' + } ); + this.$.add( this.$menu ) + .on( 'mousedown', false ); + + this.$$( this.getElementWindow() ).on( { + 'resize': ve.bind( this.updateMenu, this ) + } ); + + +}; + + +/* Inheritance */ + +ve.inheritClass( ve.ui.Sticker, ve.Element ); + +/* Methods */ + +/** + * Handle an inspector being setup. + * + * @method + * @param {ve.ui.Inspector} inspector Inspector that's been setup + */ +ve.ui.Sticker.prototype.onInspectorSetup = function () { +// this.selection = this.surface.getModel().getSelection(); +}; + +/** + * Handle an inspector being opened. + * + * @method + * @param {ve.ui.Inspector} inspector Inspector that's been opened + */ +ve.ui.Sticker.prototype.onInspectorOpen = function () { + // Transition between menu and inspector + this.show( true ); +}; + +/** + * Handle an inspector being closed. + * + * @method + * @param {ve.ui.Inspector} inspector Inspector that's been opened + * @param {boolean} accept Changes have been accepted + */ +ve.ui.Sticker.prototype.onInspectorClose = function () { +// this.updateDimensions(); +}; + +/** + * Gets the surface the context is being used in. + * + * @method + * @returns {ve.ui.Surface} Surface of context + */ +ve.ui.Sticker.prototype.getSurface = function () { + return this.surface; +}; + +/** + * Destroy the context, removing all DOM elements. + * + * @method + * @returns {ve.ui.Context} Context UserInterface + * @chainable + */ +ve.ui.Sticker.prototype.destroy = function () { + this.$.remove(); + return this; +}; + +/** + * Shows the context menu. + * + * @method + * @chainable + */ +ve.ui.Sticker.prototype.show = function ( transition ) { + var inspector = this.inspectors.getCurrent(); + this.updateDimensions( true ); + this.popup.show(); + this.$.show(); + this.visible = true; + + return this; +}; + +/** + * Updates the context menu. + * + * @method + * @chainable + */ +ve.ui.Sticker.prototype.updateMenu = function () { + var i, nodes, tools, + inspector = this.inspectors.getCurrent(), + tool = ve.ui.toolFactory.getToolForNode( this.nodeModel ); + + // This is hard-coded for testing. until I figure out what's wrong with the getToolForNode() + tool = "languageblock"; + + // This should only take whatever the tool is for whatever stickerNode we're in + if ( tool ) { + // There's at least one inspectable annotation, build a menu and show it + this.$menu.empty(); + if ( this.toolbar ) { + this.toolbar.destroy(); + } + this.toolbar = new ve.ui.StickerToolbar( this.surface, { 'nodeView': this.nodeView, 'nodeModel': this.nodeModel } ); + this.toolbar.setup( [ { 'include' : [ tool ] } ] ); + this.$menu.append( this.toolbar.$ ); + this.show(); + this.toolbar.initialize(); + } else if ( this.visible ) { + // Nothing to inspect (this shouldn't happen) + this.hide(); + } + + return this; +}; + +/** + * Updates the position and size of the sticker on top of the node. + * + * @method + * @chainable + */ +ve.ui.Sticker.prototype.updateDimensions = function ( transition, nodePosition ) { + var $container, focusableOffset, focusableWidth, +// inspector = this.inspectors.getCurrent(), + position = nodePosition || this.$stickeredNode.position(); + + this.popup.display( + position.left, + position.top, + 200, + 35, + transition + ); + + + return this; +}; + +/** + * Hides the context menu. + * + * @method + * @chainable + */ +ve.ui.Sticker.prototype.hide = function () { + var inspector = this.inspectors.getCurrent(); + + if ( inspector ) { + // This will recurse, but inspector will be undefined next time + inspector.close( 'hide' ); + return this; + } + + this.popup.hide(); + this.$.hide(); + this.visible = false; + + return this; +}; + + +/** + * Opens a given inspector. + * + * @method + * @param {string} name Symbolic name of inspector + * @chainable + */ +ve.ui.Sticker.prototype.openInspector = function ( name ) { + if ( !this.inspectors.currentWindow ) { + this.inspectors.open( name ); + } + return this; +}; diff --git a/modules/ve/ui/widgets/ve.ui.StickerWidget.js b/modules/ve/ui/widgets/ve.ui.StickerWidget.js new file mode 100644 index 0000000..e20a666 --- /dev/null +++ b/modules/ve/ui/widgets/ve.ui.StickerWidget.js @@ -0,0 +1,104 @@ +/*! + * VisualEditor UserInterface PopupWidget class. + * + * @copyright 2011-2013 VisualEditor Team and others; see AUTHORS.txt + * @license The MIT License (MIT); see LICENSE.txt + */ + +/** + * Creates an ve.ui.StickerWidget object. + * + * @class + * @extends ve.ui.Widget + * + * @constructor + * @param {Object} [config] Config options + * @cfg {jQuery} [$container] Container to make sticker positioned relative to + */ +ve.ui.StickerWidget = function VeUiStickerWidget( config ) { + // Config intialization + config = config || {}; + + // Parent constructor + ve.ui.Widget.call( this, config ); + + // Properties + this.visible = false; + this.$body = this.$$( '<div>' ); + this.transitionTimeout = null; + this.align = config.align || 'left'; + + this.$stickeredNode = config.$stickeredNode; + this.$wrapper = config.$wrapper || this.$; + + // Events + this.$.add( this.$ ) + .on( 'mousedown', function ( e ) { + // Cancel only local mousedown events + return e.target !== this; + } ); + + // Initialization + this.$.addClass( 've-ui-stickerWidget' ); + this.$.append( this.$body ); + +}; + +/* Inheritance */ + +ve.inheritClass( ve.ui.StickerWidget, ve.ui.Widget ); + + +/** + * Check if the sticker is visible. + * + * @method + * @returns {boolean} Popup is visible + */ +ve.ui.StickerWidget.prototype.isVisible = function () { + return this.visible; +}; + +/** + * Show the sticker. + * + * @method + * @chainable + */ +ve.ui.StickerWidget.prototype.show = function () { + this.$.show(); + this.visible = true; + return this; +}; + +/** + * Show the sticker. + * + * @method + * @chainable + */ +ve.ui.StickerWidget.prototype.hide = function () { + this.$.hide(); + this.visible = false; + this.emit( 'hide' ); + return this; +}; + + +/** + * Updates the position and size. + * + * @method + * @chainable + */ +ve.ui.StickerWidget.prototype.display = function ( x, y, width, height, transition ) { + this.$.css( { + 'position': 'absolute', + 'left': x, + 'top': y - (height/2), + 'width': width, + 'height': height === undefined ? 'auto' : height + } ); + + return this; +}; -- To view, visit https://gerrit.wikimedia.org/r/84014 To unsubscribe, visit https://gerrit.wikimedia.org/r/settings Gerrit-MessageType: newchange Gerrit-Change-Id: Iaef3afdf6ae8e18457146fbc03b6877494bcf650 Gerrit-PatchSet: 1 Gerrit-Project: mediawiki/extensions/VisualEditor Gerrit-Branch: master Gerrit-Owner: Mooeypoo <mor...@gmail.com> _______________________________________________ MediaWiki-commits mailing list MediaWiki-commits@lists.wikimedia.org https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits