Skip to content

Commit

Permalink
Adding Dialog general type (see #166), with a layout change to the Ab…
Browse files Browse the repository at this point in the history
…out dialog (see #71)
  • Loading branch information
jonathanolson committed Oct 8, 2014
1 parent fa5a352 commit 8e248c4
Show file tree
Hide file tree
Showing 5 changed files with 187 additions and 49 deletions.
41 changes: 17 additions & 24 deletions js/AboutDialog.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,12 @@ define( function( require ) {
var VBox = require( 'SCENERY/nodes/VBox' );
var Text = require( 'SCENERY/nodes/Text' );
var inherit = require( 'PHET_CORE/inherit' );
var ButtonListener = require( 'SCENERY/input/ButtonListener' );
var MultiLineText = require( 'SCENERY_PHET/MultiLineText' );
var ScreenView = require( 'JOIST/ScreenView' );
var Panel = require( 'SUN/Panel' );
var PhetFont = require( 'SCENERY_PHET/PhetFont' );
var StringUtils = require( 'PHETCOMMON/util/StringUtils' );
var VStrut = require( 'SUN/VStrut' );
var Dialog = require( 'JOIST/Dialog' );

// strings
var creditsTitleString = require( 'string!JOIST/credits.title' );
Expand All @@ -31,25 +31,18 @@ define( function( require ) {

/**
* @param {Sim} sim
* @param {Brand} Brand?
* @constructor
*/
function AboutDialog( sim, Brand ) {

var thisDialog = this;

/*
* Use ScreenView, to help center and scale content.
* Renderer must be specified here because the AboutDialog is added directly to the scene,
* instead of to some other node that already has svg renderer.
*/
ScreenView.call( this, {renderer: 'svg'} );
var dialog = this;

var children = [
new Text( Brand.name, { font: new PhetFont( 16 ) } ),
new Text( Brand.copyright, { font: new PhetFont( 12 ) } ),
new VStrut( 15 ),
new Text( sim.name, { font: new PhetFont( 28 ) } ),
new Text( 'version ' + sim.version, { font: new PhetFont( 20 ) } )
new Text( 'version ' + sim.version, { font: new PhetFont( 20 ) } ),
new VStrut( 15 ),
new Text( Brand.name, { font: new PhetFont( 16 ) } ),
new Text( Brand.copyright, { font: new PhetFont( 12 ) } )
];

if ( sim.credits ) {
Expand All @@ -67,15 +60,15 @@ define( function( require ) {

var content = new VBox( { align: 'left', spacing: 5, children: children } );

this.addChild( new Panel( content, {centerX: this.layoutBounds.centerX, centerY: this.layoutBounds.centerY, xMargin: 20, yMargin: 20 } ) );

function resize() {
thisDialog.layout( $( window ).width(), $( window ).height() );
}
Dialog.call( this, content, {
modal: true,
hasCloseButton: false
} );

//Fit to the window and render the initial scene
$( window ).resize( resize );
resize();
// close it on a click
this.addInputListener( new ButtonListener( {
fire: dialog.hide.bind( dialog )
} ) );
}

/**
Expand Down Expand Up @@ -143,7 +136,7 @@ define( function( require ) {
return new VBox( { align: 'left', spacing: 1, children: children } );
};

inherit( ScreenView, AboutDialog );
inherit( Dialog, AboutDialog );

return AboutDialog;
} );
146 changes: 146 additions & 0 deletions js/Dialog.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
// Copyright 2002-2013, University of Colorado Boulder

/**
* General dialog type
*
* @author Jonathan Olson <[email protected]>
* @author Sam Reid
*/
define( function( require ) {
'use strict';

// modules
var inherit = require( 'PHET_CORE/inherit' );
var Shape = require( 'KITE/Shape' );
var Node = require( 'SCENERY/nodes/Node' );
var Path = require( 'SCENERY/nodes/Path' );
var Panel = require( 'SUN/Panel' );
var RectangularPushButton = require( 'SUN/buttons/RectangularPushButton' );

/**
* @constructor
* @param {Node} content - The content to display inside the dialog (not including the title)
* @param {object} [options]
*/
function Dialog( content, options ) {
options = _.extend( {
// Dialog-specific options
modal: false, // {boolean} modal dialogs prevent interaction with the rest of the sim while open
title: null, // {Node} title to be displayed at top
titleAlign: 'center', // horizontal alignment of the title: {string} left, right or center
titleSpacing: 10, // {number} how far the title is placed above the content
hasCloseButton: true, // whether to put a close 'X' button is upper-right corner

// {function} which sets the dialog's position in global coordinates. called as
// layoutStrategy( dialog, simBounds, screenBounds, scale )
layoutStrategy: Dialog.DEFAULT_LAYOUT_STRATEGY,

// pass through to Panel options
cornerRadius: 10, // {number} radius of the dialog's corners
resize: true, // {boolean} whether to resize if content's size changes
fill: 'white', // {string|Color}
stroke: 'black', // {string|Color}
backgroundPickable: true,
xMargin: 20,
yMargin: 20
}, options );

var dialog = this;

this.isModal = options.modal;

var dialogContent = new Node( {
children: [content]
} );

if ( options.title ) {
var titleNode = options.title;

dialogContent.addChild( titleNode );

var updateTitlePosition = function() {
switch ( options.titleAlign ) {
case 'center':
titleNode.centerX = content.centerX;
break;
case 'left':
titleNode.left = content.left;
break;
case 'right':
titleNode.right = content.right;
break;
default:
throw new Error( 'unknown titleAlign for Dialog: ' + options.titleAlign );
}
titleNode.bottom = content.top - options.titleSpacing;
};

if ( options.resize ) {
content.addEventListener( 'bounds', updateTitlePosition );
titleNode.addEventListener( 'bounds', updateTitlePosition );
}
updateTitlePosition();
}

if ( options.hasCloseButton ) {
var crossSize = 10;
var crossNode = new Path( new Shape().moveTo( 0, 0 ).lineTo( crossSize, crossSize ).moveTo( 0, crossSize ).lineTo( crossSize, 0 ), {
stroke: '#fff',
lineWidth: 3
} );

var closeButton = new RectangularPushButton( {
content: crossNode,
baseColor: '#d00', // TODO: color dependent on scheme?
xMargin: 5,
yMargin: 5,
listener: function() {
dialog.hide();
}
} );
dialogContent.addChild( closeButton );

var updateClosePosition = function() {
closeButton.left = content.right + 10;
if ( options.title ) {
closeButton.top = options.title.top;
} else {
closeButton.top = content.top;
}
};

if ( options.resize ) {
content.addEventListener( 'bounds', updateClosePosition );
if ( options.title ) {
options.title.addEventListener( 'bounds', updateClosePosition );
}
}
updateClosePosition();
}

Panel.call( this, dialogContent, options );

var sim = window.phet.sim;

function updateLayout() {
options.layoutStrategy( dialog, sim.bounds, sim.screenBounds, sim.scale );
}
sim.on( 'resized', updateLayout );
updateLayout();
}

Dialog.DEFAULT_LAYOUT_STRATEGY = function( dialog, simBounds, screenBounds, scale ) {
dialog.setScaleMagnitude( scale );
dialog.center = simBounds.center;
};

return inherit( Panel, Dialog, {
show: function() {
window.phet.sim.showPopup( this, this.isModal );
},

hide: function() {
window.phet.sim.hidePopup( this, this.isModal );
}
} );
} );
4 changes: 2 additions & 2 deletions js/PhetButton.js
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ define( function( require ) {
showSaveAndLoad: sim.options.showSaveAndLoad,
closeCallback: function() {
// hides the popup and barrier background
sim.hidePopup( phetMenu );
sim.hidePopup( phetMenu, true );
}
} );
function onResize( bounds, screenBounds, scale ) {
Expand All @@ -85,7 +85,7 @@ define( function( require ) {
sim.on( 'resized', onResize );
onResize( sim.bounds, sim.screenBounds, sim.scale );

sim.showPopup( phetMenu );
sim.showPopup( phetMenu, true );
};
this.addListener( phetButtonPressed );

Expand Down
19 changes: 4 additions & 15 deletions js/PhetMenu.js
Original file line number Diff line number Diff line change
Expand Up @@ -110,19 +110,6 @@ define( function( require ) {
var thisMenu = this;
Node.call( thisMenu, {renderer: 'svg'} );

var showAboutDialog = function( aboutDialog ) {
var plane = new Plane( {fill: 'black', opacity: 0.3, renderer: 'svg'} );//Renderer must be specified here because the plane is added directly to the scene (instead of to some other node that already has svg renderer)
sim.showPopup( aboutDialog );
var aboutDialogListener = {up: function() {
aboutDialog.removeInputListener( aboutDialogListener );
plane.addInputListener( aboutDialogListener );
sim.hidePopup( aboutDialog );
plane.detach();
}};
aboutDialog.addInputListener( aboutDialogListener );
plane.addInputListener( aboutDialogListener );
};

/*
* Description of the items in the menu. Each descriptor has these properties:
* {string} text - the item's text
Expand Down Expand Up @@ -218,7 +205,9 @@ define( function( require ) {
text: aboutString,
present: isPhETBrand,
separatorBefore: true,
callback: function() { showAboutDialog( new AboutDialog( sim, Brand ) ); }
callback: function() {
new AboutDialog( sim, Brand ).show();
}
},

//About dialog for non-phet sims
Expand All @@ -227,7 +216,7 @@ define( function( require ) {
present: !isPhETBrand,
separatorBefore: false,
callback: function() {
showAboutDialog( new AboutDialog( sim, Brand ) );
new AboutDialog( sim, Brand ).show();
}
}
];
Expand Down
26 changes: 18 additions & 8 deletions js/Sim.js
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,11 @@ define( function( require ) {

this.destroyed = false;
var sim = this;
window.sim = sim;

// global namespace for accessing the sim
window.phet = window.phet || {};
assert && assert( !window.phet.sim, 'Only supports one sim at a time' );
window.phet.sim = sim;

sim.name = name;
sim.version = version();
Expand Down Expand Up @@ -390,9 +394,9 @@ define( function( require ) {
sim.barrierRectangle.visible = numBarriers > 0;
} );
this.barrierRectangle.addInputListener( new ButtonListener( {
fire: function() {
fire: function( event ) {
assert && assert( sim.barrierStack.length > 0 );
sim.hidePopup( sim.barrierStack.get( sim.barrierStack.length - 1 ) );
sim.hidePopup( sim.barrierStack.get( sim.barrierStack.length - 1 ), true );
}
} ) );

Expand All @@ -405,25 +409,31 @@ define( function( require ) {

return inherit( PropertySet, Sim, {
/*
* Adds a popup in the global coordinate frame, and displays a semi-transparent black input barrier behind it.
* Adds a popup in the global coordinate frame, and optionally displays a semi-transparent black input barrier behind it.
* Use hidePopup() to remove it.
* @param {Node} node
* @param {boolean} isModal - Whether to display the semi-transparent black input barrier behind it.
*/
showPopup: function( node ) {
showPopup: function( node, isModal ) {
assert && assert( node );

this.barrierStack.push( node );
if ( isModal ) {
this.barrierStack.push( node );
}
this.topLayer.addChild( node );
},

/*
* Hides a popup that was previously displayed with showPopup()
* @param {Node} node
* @param {boolean} isModal - Whether the previous popup was modal (or not)
*/
hidePopup: function( node ) {
hidePopup: function( node, isModal ) {
assert && assert( node && this.barrierStack.contains( node ) );

this.barrierStack.remove( node );
if ( isModal ) {
this.barrierStack.remove( node );
}
this.topLayer.removeChild( node );
},

Expand Down

0 comments on commit 8e248c4

Please sign in to comment.