Merge "Outline controls"

This commit is contained in:
jenkins-bot 2013-06-14 21:47:36 +00:00 committed by Gerrit Code Review
commit 86406d5e48
15 changed files with 305 additions and 16 deletions

View file

@ -99,6 +99,8 @@ $messages['en'] = array(
'visualeditor-notification-created' => '$1 has been created.', 'visualeditor-notification-created' => '$1 has been created.',
'visualeditor-notification-restored' => '$1 has been restored.', 'visualeditor-notification-restored' => '$1 has been restored.',
'visualeditor-notification-saved' => 'Your changes to $1 have been saved.', 'visualeditor-notification-saved' => 'Your changes to $1 have been saved.',
'visualeditor-outline-control-move-up' => 'Move item up',
'visualeditor-outline-control-move-down' => 'Move item down',
'visualeditor-preference-enable' => 'Enable VisualEditor (only in the [[{{MediaWiki:Visualeditor-mainnamespacepagelink}}|main]] and [[{{MediaWiki:Visualeditor-usernamespacepagelink}}|user]] namespaces)', 'visualeditor-preference-enable' => 'Enable VisualEditor (only in the [[{{MediaWiki:Visualeditor-mainnamespacepagelink}}|main]] and [[{{MediaWiki:Visualeditor-usernamespacepagelink}}|user]] namespaces)',
'visualeditor-preference-nosectionedit' => 'Use the wikitext editor for editing sections while VisualEditor is in beta', 'visualeditor-preference-nosectionedit' => 'Use the wikitext editor for editing sections while VisualEditor is in beta',
'visualeditor-savedialog-label-create' => 'Create page', 'visualeditor-savedialog-label-create' => 'Create page',
@ -279,6 +281,8 @@ Parameters:
'visualeditor-notification-restored' => 'Shown after a user restores a page to a previous revision. Parameters: 'visualeditor-notification-restored' => 'Shown after a user restores a page to a previous revision. Parameters:
* $1 is a page name.', * $1 is a page name.',
'visualeditor-notification-saved' => 'Shown after a user saves a page, $1 is a page name.', 'visualeditor-notification-saved' => 'Shown after a user saves a page, $1 is a page name.',
'visualeditor-outline-control-move-up' => 'Tool tip for a button that moves items in a list up one place',
'visualeditor-outline-control-move-down' => 'Tool tip for a button that moves items in a list down one place',
'visualeditor-preference-enable' => 'Label for the user preference to enable the VisualEditor. 'visualeditor-preference-enable' => 'Label for the user preference to enable the VisualEditor.
Links are in {{msg-mw|Visualeditor-mainnamespacepagelink}} and {{msg-mw|visualeditor-usernamespacepagelink}}.', Links are in {{msg-mw|Visualeditor-mainnamespacepagelink}} and {{msg-mw|visualeditor-usernamespacepagelink}}.',
'visualeditor-preference-nosectionedit' => 'Label for the user preference to make section edit links go to the old editor instead of VisualEditor.', 'visualeditor-preference-nosectionedit' => 'Label for the user preference to make section edit links go to the old editor instead of VisualEditor.',

View file

@ -391,6 +391,7 @@ $wgResourceModules += array(
've/ui/widgets/ve.ui.TextInputWidget.js', 've/ui/widgets/ve.ui.TextInputWidget.js',
've/ui/widgets/ve.ui.OutlineItemWidget.js', 've/ui/widgets/ve.ui.OutlineItemWidget.js',
've/ui/widgets/ve.ui.OutlineWidget.js', 've/ui/widgets/ve.ui.OutlineWidget.js',
've/ui/widgets/ve.ui.OutlineControlsWidget.js',
've/ui/widgets/ve.ui.MenuItemWidget.js', 've/ui/widgets/ve.ui.MenuItemWidget.js',
've/ui/widgets/ve.ui.MenuSectionItemWidget.js', 've/ui/widgets/ve.ui.MenuSectionItemWidget.js',
've/ui/widgets/ve.ui.MenuWidget.js', 've/ui/widgets/ve.ui.MenuWidget.js',
@ -535,6 +536,8 @@ $wgResourceModules += array(
'visualeditor-notification-created', 'visualeditor-notification-created',
'visualeditor-notification-restored', 'visualeditor-notification-restored',
'visualeditor-notification-saved', 'visualeditor-notification-saved',
'visualeditor-outline-control-move-up',
'visualeditor-outline-control-move-down',
'visualeditor-savedialog-label-create', 'visualeditor-savedialog-label-create',
'visualeditor-savedialog-label-report', 'visualeditor-savedialog-label-report',
'visualeditor-savedialog-label-resolve-conflict', 'visualeditor-savedialog-label-resolve-conflict',

View file

@ -268,6 +268,7 @@ $html = file_get_contents( $page );
<script src="../../modules/ve/ui/widgets/ve.ui.TextInputWidget.js"></script> <script src="../../modules/ve/ui/widgets/ve.ui.TextInputWidget.js"></script>
<script src="../../modules/ve/ui/widgets/ve.ui.OutlineItemWidget.js"></script> <script src="../../modules/ve/ui/widgets/ve.ui.OutlineItemWidget.js"></script>
<script src="../../modules/ve/ui/widgets/ve.ui.OutlineWidget.js"></script> <script src="../../modules/ve/ui/widgets/ve.ui.OutlineWidget.js"></script>
<script src="../../modules/ve/ui/widgets/ve.ui.OutlineControlsWidget.js"></script>
<script src="../../modules/ve/ui/widgets/ve.ui.MenuItemWidget.js"></script> <script src="../../modules/ve/ui/widgets/ve.ui.MenuItemWidget.js"></script>
<script src="../../modules/ve/ui/widgets/ve.ui.MenuSectionItemWidget.js"></script> <script src="../../modules/ve/ui/widgets/ve.ui.MenuSectionItemWidget.js"></script>
<script src="../../modules/ve/ui/widgets/ve.ui.MenuWidget.js"></script> <script src="../../modules/ve/ui/widgets/ve.ui.MenuWidget.js"></script>

View file

@ -205,8 +205,7 @@ ve.dm.MWTransclusionModel.prototype.getUniquePartId = function () {
*/ */
ve.dm.MWTransclusionModel.prototype.addContent = function ( value, index ) { ve.dm.MWTransclusionModel.prototype.addContent = function ( value, index ) {
var part = new ve.dm.MWTransclusionContentModel( this, value ); var part = new ve.dm.MWTransclusionContentModel( this, value );
this.parts.splice( index === undefined ? this.parts.length : index, 0, part ); this.addPart( part, index );
this.emit( 'add', part );
return part; return part;
}; };
@ -224,16 +223,28 @@ ve.dm.MWTransclusionModel.prototype.addTemplate = function ( title, index ) {
if ( this.specs.hasOwnProperty( title ) ) { if ( this.specs.hasOwnProperty( title ) ) {
part.getSpec().extend( this.specs[title] ); part.getSpec().extend( this.specs[title] );
} }
this.addPart( part, index );
return part;
};
/**
* Add part.
*
* @method
* @param {ve.dm.MWTransclusionPartModel} part Part to add
* @param {number} [index] Specific index to add content at
* @emits add
*/
ve.dm.MWTransclusionModel.prototype.addPart = function ( part, index ) {
this.parts.splice( index === undefined ? this.parts.length : index, 0, part ); this.parts.splice( index === undefined ? this.parts.length : index, 0, part );
this.emit( 'add', part ); this.emit( 'add', part );
return part;
}; };
/** /**
* Remove a part. * Remove a part.
* *
* @method * @method
* @param {ve.dm.MWTransclusionPartModel} part Template part * @param {ve.dm.MWTransclusionPartModel} part Part to remove
* @emits remove * @emits remove
*/ */
ve.dm.MWTransclusionModel.prototype.removePart = function ( part ) { ve.dm.MWTransclusionModel.prototype.removePart = function ( part ) {
@ -254,6 +265,24 @@ ve.dm.MWTransclusionModel.prototype.getParts = function () {
return this.parts; return this.parts;
}; };
/**
* Get part by its ID.
*
* @method
* @param {string} id Part ID
* @returns {ve.dm.MWTransclusionPartModel|null} Part with matching ID, if found
*/
ve.dm.MWTransclusionModel.prototype.getPartFromId = function ( id ) {
var i, len;
for ( i = 0, len = this.parts.length; i < len; i++ ) {
if ( this.parts[i].getId() === id ) {
return this.parts[i];
}
}
return null;
};
/** /**
* Get a template specification. * Get a template specification.
* *

View file

@ -221,6 +221,7 @@
<script src="../../ve/ui/widgets/ve.ui.TextInputWidget.js"></script> <script src="../../ve/ui/widgets/ve.ui.TextInputWidget.js"></script>
<script src="../../ve/ui/widgets/ve.ui.OutlineItemWidget.js"></script> <script src="../../ve/ui/widgets/ve.ui.OutlineItemWidget.js"></script>
<script src="../../ve/ui/widgets/ve.ui.OutlineWidget.js"></script> <script src="../../ve/ui/widgets/ve.ui.OutlineWidget.js"></script>
<script src="../../ve/ui/widgets/ve.ui.OutlineControlsWidget.js"></script>
<script src="../../ve/ui/widgets/ve.ui.MenuItemWidget.js"></script> <script src="../../ve/ui/widgets/ve.ui.MenuItemWidget.js"></script>
<script src="../../ve/ui/widgets/ve.ui.MenuSectionItemWidget.js"></script> <script src="../../ve/ui/widgets/ve.ui.MenuSectionItemWidget.js"></script>
<script src="../../ve/ui/widgets/ve.ui.MenuWidget.js"></script> <script src="../../ve/ui/widgets/ve.ui.MenuWidget.js"></script>

View file

@ -19,6 +19,9 @@
* @param {Object} [config] Config options * @param {Object} [config] Config options
*/ */
ve.ui.MWTransclusionDialog = function VeUiMWTransclusionDialog( surface, config ) { ve.ui.MWTransclusionDialog = function VeUiMWTransclusionDialog( surface, config ) {
// Configuration initialization
config = ve.extendObject( {}, config, { 'editable': true } );
// Parent constructor // Parent constructor
ve.ui.PagedDialog.call( this, surface, config ); ve.ui.PagedDialog.call( this, surface, config );
@ -41,6 +44,18 @@ ve.ui.MWTransclusionDialog.static.modelClasses = [ ve.dm.MWTransclusionNode ];
/* Methods */ /* Methods */
/**
* Handle frame ready events.
*
* @method
*/
ve.ui.MWTransclusionDialog.prototype.initialize = function () {
// Call parent method
ve.ui.PagedDialog.prototype.initialize.call( this );
this.outlineControlsWidget.connect( this, { 'move': 'onOutlineControlsMove' } );
};
/** /**
* Handle frame open events. * Handle frame open events.
* *
@ -106,17 +121,27 @@ ve.ui.MWTransclusionDialog.prototype.onClose = function ( action ) {
* @param {ve.dm.MWTransclusionPartModel} part Added part * @param {ve.dm.MWTransclusionPartModel} part Added part
*/ */
ve.ui.MWTransclusionDialog.prototype.onAddPart = function ( part ) { ve.ui.MWTransclusionDialog.prototype.onAddPart = function ( part ) {
var page; var i, len, page, params, param, names;
if ( part instanceof ve.dm.MWTemplateModel ) { if ( part instanceof ve.dm.MWTemplateModel ) {
page = this.getTemplatePage( part ); page = this.getTemplatePage( part );
part.connect( this, { 'add': 'onAddParameter', 'remove': 'onRemoveParameter' } );
} else if ( part instanceof ve.dm.MWTransclusionContentModel ) { } else if ( part instanceof ve.dm.MWTransclusionContentModel ) {
page = this.getContentPage( part ); page = this.getContentPage( part );
} }
page.index = this.getPageIndex( part ) + 1; page.index = this.getPageIndex( part );
if ( page ) { if ( page ) {
this.addPage( part.getId(), page ); this.addPage( part.getId(), page );
if ( part instanceof ve.dm.MWTemplateModel ) {
names = part.getParameterNames();
params = part.getParameters();
for ( i = 0, len = names.length; i < len; i++ ) {
param = params[names[i]];
page = this.getParameterPage( param );
page.index = this.getPageIndex( param );
this.addPage( param.getId(), page );
}
part.connect( this, { 'add': 'onAddParameter', 'remove': 'onRemoveParameter' } );
}
} }
}; };
@ -147,7 +172,7 @@ ve.ui.MWTransclusionDialog.prototype.onRemovePart = function ( part ) {
*/ */
ve.ui.MWTransclusionDialog.prototype.onAddParameter = function ( param ) { ve.ui.MWTransclusionDialog.prototype.onAddParameter = function ( param ) {
var page = this.getParameterPage( param ); var page = this.getParameterPage( param );
page.index = this.getPageIndex( param ) + 1; page.index = this.getPageIndex( param );
this.addPage( param.getId(), page ); this.addPage( param.getId(), page );
}; };
@ -163,6 +188,27 @@ ve.ui.MWTransclusionDialog.prototype.onRemoveParameter = function ( param ) {
this.setPageByName( param.getTemplate().getId() ); this.setPageByName( param.getTemplate().getId() );
}; };
/**
* Handle outline controls move events.
*
* @method
* @param {number} places Number of places to move the selected item.
*/
ve.ui.MWTransclusionDialog.prototype.onOutlineControlsMove = function ( places ) {
var part, index, name,
parts = this.transclusion.getParts(),
item = this.outlineWidget.getSelectedItem();
if ( item ) {
name = item.getData();
part = this.transclusion.getPartFromId( name );
index = ve.indexOf( part, parts );
this.transclusion.removePart( part );
this.transclusion.addPart( part, index + places );
this.setPageByName( name );
}
};
/** /**
* Set the page by name. * Set the page by name.
* *
@ -194,6 +240,7 @@ ve.ui.MWTransclusionDialog.prototype.getPageIndex = function ( item ) {
if ( part === item ) { if ( part === item ) {
return index; return index;
} }
index++;
if ( part instanceof ve.dm.MWTemplateModel ) { if ( part instanceof ve.dm.MWTemplateModel ) {
names = part.getParameterNames(); names = part.getParameterNames();
for ( j = 0, jLen = names.length; j < jLen; j++ ) { for ( j = 0, jLen = names.length; j < jLen; j++ ) {
@ -203,7 +250,6 @@ ve.ui.MWTransclusionDialog.prototype.getPageIndex = function ( item ) {
index++; index++;
} }
} }
index++;
} }
return -1; return -1;
}; };
@ -284,7 +330,8 @@ ve.ui.MWTransclusionDialog.prototype.getContentPage = function ( content ) {
return { return {
'label': ve.msg( 'visualeditor-dialog-transclusion-content' ), 'label': ve.msg( 'visualeditor-dialog-transclusion-content' ),
'icon': 'source', 'icon': 'source',
'$content': valueFieldset.$.add( optionsFieldset.$ ) '$content': valueFieldset.$.add( optionsFieldset.$ ),
'moveable': true
}; };
}; };
@ -360,7 +407,8 @@ ve.ui.MWTransclusionDialog.prototype.getTemplatePage = function ( template ) {
return { return {
'label': label, 'label': label,
'icon': 'template', 'icon': 'template',
'$content': infoFieldset.$.add( addParameterFieldset.$ ).add( optionsFieldset.$ ) '$content': infoFieldset.$.add( addParameterFieldset.$ ).add( optionsFieldset.$ ),
'moveable': true
}; };
}; };

View file

@ -19,12 +19,17 @@
* @constructor * @constructor
* @param {ve.ui.Surface} surface * @param {ve.ui.Surface} surface
* @param {Object} [config] Config options * @param {Object} [config] Config options
* @cfg {boolean} [editable] Show controls for adding, removing and reordering items in the outline
*/ */
ve.ui.PagedDialog = function VeUiPagedDialog( surface, config ) { ve.ui.PagedDialog = function VeUiPagedDialog( surface, config ) {
// Configuration initialization
config = config || {};
// Parent constructor // Parent constructor
ve.ui.Dialog.call( this, surface, config ); ve.ui.Dialog.call( this, surface, config );
// Properties // Properties
this.editable = !!config.editable;
this.pages = {}; this.pages = {};
this.currentPageName = null; this.currentPageName = null;
}; };
@ -51,12 +56,24 @@ ve.ui.PagedDialog.prototype.initialize = function () {
[this.outlinePanel, this.pagesPanel], { '$$': this.frame.$$, 'widths': [1, 2] } [this.outlinePanel, this.pagesPanel], { '$$': this.frame.$$, 'widths': [1, 2] }
); );
this.outlineWidget = new ve.ui.OutlineWidget( { '$$': this.frame.$$ } ); this.outlineWidget = new ve.ui.OutlineWidget( { '$$': this.frame.$$ } );
if ( this.editable ) {
this.outlineControlsWidget = new ve.ui.OutlineControlsWidget(
this.outlineWidget, { '$$': this.frame.$$ }
);
}
// Events // Events
this.outlineWidget.connect( this, { 'select': 'onOutlineSelect' } ); this.outlineWidget.connect( this, { 'select': 'onOutlineSelect' } );
// Initialization // Initialization
this.outlinePanel.$.append( this.outlineWidget.$ ).addClass( 've-ui-pagedDialog-outlinePanel' ); this.outlinePanel.$
.addClass( 've-ui-pagedDialog-outlinePanel' )
.append( this.outlineWidget.$ );
if ( this.editable ) {
this.outlinePanel.$
.addClass( 've-ui-pagedDialog-outlinePanel-editable' )
.append( this.outlineControlsWidget.$ );
}
this.pagesPanel.$.addClass( 've-ui-pagedDialog-pagesPanel' ); this.pagesPanel.$.addClass( 've-ui-pagedDialog-pagesPanel' );
this.$body.append( this.layout.$ ); this.$body.append( this.layout.$ );
}; };
@ -84,6 +101,7 @@ ve.ui.PagedDialog.prototype.onOutlineSelect = function ( item ) {
* @param {number} [config.level=0] Indentation level * @param {number} [config.level=0] Indentation level
* @param {number} [config.index] Specific index to insert page at * @param {number} [config.index] Specific index to insert page at
* @param {jQuery} [config.$content] Page content * @param {jQuery} [config.$content] Page content
* @param {jQuery} [config.moveable] Allow page to be moved in the outline
* @chainable * @chainable
*/ */
ve.ui.PagedDialog.prototype.addPage = function ( name, config ) { ve.ui.PagedDialog.prototype.addPage = function ( name, config ) {
@ -99,7 +117,8 @@ ve.ui.PagedDialog.prototype.addPage = function ( name, config ) {
'$$': this.frame.$$, '$$': this.frame.$$,
'label': config.label || name, 'label': config.label || name,
'level': config.level || 0, 'level': config.level || 0,
'icon': config.icon 'icon': config.icon,
'moveable': config.moveable
} ) } )
], ],
config.index config.index
@ -169,7 +188,7 @@ ve.ui.PagedDialog.prototype.getPage = function ( name ) {
* @param {string} name Symbolic name of page * @param {string} name Symbolic name of page
*/ */
ve.ui.PagedDialog.prototype.setPage = function ( name ) { ve.ui.PagedDialog.prototype.setPage = function ( name ) {
if ( name in this.pages ) { if ( this.pages[name] ) {
this.currentPageName = name; this.currentPageName = name;
this.pagesPanel.showItem( this.pages[name] ); this.pagesPanel.showItem( this.pages[name] );
this.pages[name].$.find( ':input:first' ).focus(); this.pages[name].$.find( ':input:first' ).focus();

View file

@ -42,15 +42,20 @@ ve.ui.GroupElement.prototype.getItems = function () {
* @chainable * @chainable
*/ */
ve.ui.GroupElement.prototype.addItems = function ( items, index ) { ve.ui.GroupElement.prototype.addItems = function ( items, index ) {
var i, len, item, var i, len, item, currentIndex,
$items = $( [] ); $items = $( [] );
for ( i = 0, len = items.length; i < len; i++ ) { for ( i = 0, len = items.length; i < len; i++ ) {
item = items[i]; item = items[i];
// Check if item exists then remove it first, effectively "moving" it // Check if item exists then remove it first, effectively "moving" it
if ( this.items.indexOf( item ) !== -1 ) { currentIndex = this.items.indexOf( item );
if ( currentIndex >= 0 ) {
this.removeItems( [ item ] ); this.removeItems( [ item ] );
// Adjust index to compensate for removal
if ( currentIndex < index ) {
index--;
}
} }
// Add the item // Add the item
$items = $items.add( item.$ ); $items = $items.add( item.$ );

View file

@ -120,6 +120,23 @@
border-right: solid 1px #ddd; border-right: solid 1px #ddd;
} }
.ve-ui-pagedDialog-outlinePanel-editable .ve-ui-outlineWidget {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 3em;
overflow-y: auto;
}
.ve-ui-pagedDialog-outlinePanel .ve-ui-outlineControlsWidget {
position: absolute;
bottom: 0;
left: 0;
right: 0;
box-shadow: 0 0 0.25em rgba(0,0,0,0.25);
}
.ve-ui-pagedDialog-pagesPanel .ve-ui-panelLayout { .ve-ui-pagedDialog-pagesPanel .ve-ui-panelLayout {
padding: 1.5em; padding: 1.5em;
width: 100%; width: 100%;

View file

@ -239,3 +239,13 @@
/* @embed */ /* @embed */
background-image: url(images/icons/search-big.png); background-image: url(images/icons/search-big.png);
} }
.ve-ui-icon-expand {
/* @embed */
background-image: url(images/icons/expand.png);
}
.ve-ui-icon-collapse {
/* @embed */
background-image: url(images/icons/collapse.png);
}

View file

@ -239,3 +239,13 @@
/* @embed */ /* @embed */
background-image: url(images/icons/search-big.svg); background-image: url(images/icons/search-big.svg);
} }
.ve-ui-icon-expand {
/* @embed */
background-image: url(images/icons/expand.svg);
}
.ve-ui-icon-collapse {
/* @embed */
background-image: url(images/icons/collapse.svg);
}

View file

@ -25,6 +25,10 @@
opacity: 1; opacity: 1;
} }
.ve-ui-iconButtonWidget.ve-ui-widget-disabled {
opacity: 0.2;
}
/* ve.ui.ButtonWidget */ /* ve.ui.ButtonWidget */
.ve-ui-buttonWidget { .ve-ui-buttonWidget {
@ -202,6 +206,26 @@
text-shadow: 0 1px 1px rgba(255,255,255,0.5); text-shadow: 0 1px 1px rgba(255,255,255,0.5);
} }
/* ve.ui.OutlineControlsWidget */
.ve-ui-outlineControlsWidget {
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
height: 3em;
padding: 0.5em;
background-color: #fff;
}
.ve-ui-outlineControlsWidget-addButton {
float: left;
}
.ve-ui-outlineControlsWidget-upButton,
.ve-ui-outlineControlsWidget-downButton {
float: right;
}
/* ve.ui.InputLabelWidget */ /* ve.ui.InputLabelWidget */
.ve-ui-inputLabelWidget { .ve-ui-inputLabelWidget {

View file

@ -0,0 +1,85 @@
/*!
* VisualEditor UserInterface OutlineControlsWidget class.
*
* @copyright 2011-2013 VisualEditor Team and others; see AUTHORS.txt
* @license The MIT License (MIT); see LICENSE.txt
*/
/**
* Creates an ve.ui.OutlineControlsWidget object.
*
* @class
*
* @constructor
* @param {ve.ui.OutlineWidget} outline Outline to control
* @param {Object} [config] Config options
*/
ve.ui.OutlineControlsWidget = function VeUiOutlineControlsWidget( outline, config ) {
// Parent constructor
ve.ui.Widget.call( this, config );
// Properties
this.outline = outline;
this.upButton = new ve.ui.IconButtonWidget( {
'$$': this.$$, 'icon': 'collapse', 'title': ve.msg( 'visualeditor-outline-control-move-up' )
} );
this.downButton = new ve.ui.IconButtonWidget( {
'$$': this.$$, 'icon': 'expand', 'title': ve.msg( 'visualeditor-outline-control-move-down' )
} );
// Events
outline.connect( this, {
'select': 'onOutlineChange',
'add': 'onOutlineChange',
'remove': 'onOutlineChange'
} );
this.upButton.connect( this, { 'click': ['emit', 'move', -1] } );
this.downButton.connect( this, { 'click': ['emit', 'move', 1] } );
// Initialization
this.$.addClass( 've-ui-outlineControlsWidget' );
this.upButton.$.addClass( 've-ui-outlineControlsWidget-upButton' );
this.downButton.$.addClass( 've-ui-outlineControlsWidget-downButton' );
this.$.append( this.upButton.$, this.downButton.$ );
};
/* Inheritance */
ve.inheritClass( ve.ui.OutlineControlsWidget, ve.ui.Widget );
/* Events */
/**
* @event move
* @param {number} places Number of places to move
*/
/* Methods */
ve.ui.OutlineControlsWidget.prototype.onOutlineChange = function () {
var i, len, firstMoveable, lastMoveable,
moveable = false,
items = this.outline.getItems(),
selectedItem = this.outline.getSelectedItem();
if ( selectedItem && selectedItem.isMoveable() ) {
moveable = true;
i = -1;
len = items.length;
while ( ++i < len ) {
if ( items[i].isMoveable() ) {
firstMoveable = items[i];
break;
}
}
i = len;
while ( i-- ) {
if ( items[i].isMoveable() ) {
lastMoveable = items[i];
break;
}
}
}
this.upButton.setDisabled( !moveable || selectedItem === firstMoveable );
this.downButton.setDisabled( !moveable || selectedItem === lastMoveable );
};

View file

@ -16,6 +16,7 @@
* @param {Object} [config] Config options * @param {Object} [config] Config options
* @cfg {string} [icon] Symbolic name of icon * @cfg {string} [icon] Symbolic name of icon
* @cfg {number} [level] Indentation level * @cfg {number} [level] Indentation level
* @cfg {boolean} [moveable] Allow modification from outline controls
*/ */
ve.ui.OutlineItemWidget = function VeUiOutlineItemWidget( data, config ) { ve.ui.OutlineItemWidget = function VeUiOutlineItemWidget( data, config ) {
// Config intialization // Config intialization
@ -26,6 +27,7 @@ ve.ui.OutlineItemWidget = function VeUiOutlineItemWidget( data, config ) {
// Properties // Properties
this.level = 0; this.level = 0;
this.moveable = !!config.moveable;
// Initialization // Initialization
this.$.addClass( 've-ui-outlineItemWidget' ); this.$.addClass( 've-ui-outlineItemWidget' );
@ -49,6 +51,17 @@ ve.ui.OutlineItemWidget.static.levels = 3;
/* Methods */ /* Methods */
/**
* Check if item is moveable.
*
* Moveablilty is used by outline controls.
*
* @returns {boolean} Item is moveable
*/
ve.ui.OutlineItemWidget.prototype.isMoveable = function () {
return this.moveable;
};
/** /**
* Get indentation level. * Get indentation level.
* *

View file

@ -56,6 +56,17 @@ ve.mixinClass( ve.ui.SelectWidget, ve.ui.GroupElement );
* @param {ve.ui.OptionWidget|null} item Selected item or null if no item is selected * @param {ve.ui.OptionWidget|null} item Selected item or null if no item is selected
*/ */
/**
* @event add
* @param {ve.ui.OptionWidget[]} items Added items
* @param {number} index Index items were added at
*/
/**
* @event remove
* @param {ve.ui.OptionWidget[]} items Removed items
*/
/* Static Properties */ /* Static Properties */
ve.ui.SelectWidget.static.tagName = 'ul'; ve.ui.SelectWidget.static.tagName = 'ul';
@ -347,6 +358,9 @@ ve.ui.SelectWidget.prototype.addItems = function ( items, index ) {
} }
ve.ui.GroupElement.prototype.addItems.call( this, items, index ); ve.ui.GroupElement.prototype.addItems.call( this, items, index );
// Always provide an index, even if it was omitted
this.emit( 'add', items, index === undefined ? this.items.length - items.length - 1 : index );
return this; return this;
}; };
@ -372,6 +386,8 @@ ve.ui.SelectWidget.prototype.removeItems = function ( items ) {
} }
ve.ui.GroupElement.prototype.removeItems.call( this, items ); ve.ui.GroupElement.prototype.removeItems.call( this, items );
this.emit( 'remove', items );
return this; return this;
}; };
@ -384,9 +400,13 @@ ve.ui.SelectWidget.prototype.removeItems = function ( items ) {
* @chainable * @chainable
*/ */
ve.ui.SelectWidget.prototype.clearItems = function () { ve.ui.SelectWidget.prototype.clearItems = function () {
var items = this.items.slice();
// Clear all items // Clear all items
this.hashes = {}; this.hashes = {};
ve.ui.GroupElement.prototype.clearItems.call( this ); ve.ui.GroupElement.prototype.clearItems.call( this );
this.emit( 'remove', items );
return this; return this;
}; };