Source code file content

Revision: 13

Code cleanup and Javadocs
» Project Revision History

» Checkout URL

onyx / src / org / pushingpixels / onyx / details / DetailsWindow.java

Size: 9046 bytes, 1 line
/*
 * Copyright (c) 2009 Onyx Kirill Grouchnikov. All Rights Reserved.
 *
 * Redistribution and use in source and binary forms, with or without 
 * modification, are permitted provided that the following conditions are met:
 * 
 *  o Redistributions of source code must retain the above copyright notice, 
 *    this list of conditions and the following disclaimer. 
 *     
 *  o Redistributions in binary form must reproduce the above copyright notice, 
 *    this list of conditions and the following disclaimer in the documentation 
 *    and/or other materials provided with the distribution. 
 *     
 *  o Neither the name of Onyx Kirill Grouchnikov nor the names of 
 *    its contributors may be used to endorse or promote products derived 
 *    from this software without specific prior written permission. 
 *     
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 
 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 
 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
 */
package org.pushingpixels.onyx.details;

import java.awt.*;
import java.awt.image.BufferedImage;
import java.net.URL;

import javax.imageio.ImageIO;
import javax.swing.JWindow;

import org.pushingpixels.trident.*;
import org.pushingpixels.trident.Timeline.TimelineState;
import org.pushingpixels.trident.callback.Repaint;
import org.pushingpixels.trident.callback.UIThreadTimelineCallbackAdapter;

import com.ECS.client.jax.Item;

/**
 * Shows the details of the selected album, including bigger album art and a
 * scrollable list of album tracks.
 * 
 * @author Kirill Grouchnikov
 */
public class DetailsWindow extends JWindow {
	/**
	 * Component that shows the album art.
	 */
	private BigAlbumArt albumArt;

	/**
	 * Component that shows the scrollable list of album tracks.
	 */
	private TrackListingScroller trackListingScroller;

	/**
	 * The scenario that is transitioning to the last selected album item.
	 */
	private TimelineScenario currentShowAlbumDetailsScenario;

	/**
	 * 0.0f - the album art and track listing are completely overlayed, 1.0f -
	 * the album art and track listing are completely separate. Is updated in
	 * the {@link #currentShowAlbumDetailsScenario}.
	 */
	private float overlayPosition;

	/**
	 * Creates a new details window.
	 */
	public DetailsWindow() {
		this.setSize(2 * BigAlbumArt.TOTAL_DIM, BigAlbumArt.TOTAL_DIM);
		this.overlayPosition = 0.0f;

		this.albumArt = new BigAlbumArt();
		this.trackListingScroller = new TrackListingScroller();

		Container contentPane = this.getContentPane();
		contentPane.setLayout(new LayoutManager() {
			@Override
			public void addLayoutComponent(String name, Component comp) {
			}

			@Override
			public void removeLayoutComponent(Component comp) {
			}

			@Override
			public Dimension minimumLayoutSize(Container parent) {
				return null;
			}

			@Override
			public Dimension preferredLayoutSize(Container parent) {
				return null;
			}

			@Override
			public void layoutContainer(Container parent) {
				int w = parent.getWidth();
				int h = parent.getHeight();

				// respect the current overlay position to implement the sliding
				// effect in steps 1 and 6 of currentShowAlbumDetailsScenario
				int dim = BigAlbumArt.TOTAL_DIM;
				int dx = (int) (overlayPosition * dim / 2);
				albumArt.setBounds((w - dim) / 2 - dx, (h - dim) / 2, dim, dim);
				trackListingScroller.setBounds((w - dim) / 2 + dx,
						(h - dim) / 2 + 2, dim, dim - 4);
			}
		});
		contentPane.add(albumArt);
		contentPane.add(trackListingScroller);

		contentPane.setComponentZOrder(trackListingScroller, 1);
		contentPane.setComponentZOrder(albumArt, 0);
	}

	/**
	 * Signals that details of the specified album item should be displayed in
	 * this window. Note that this window can already display another album item
	 * when this method is called.
	 * 
	 * @param albumItem
	 *            New album item to show in this window.
	 */
	public void setAlbumItem(Item albumItem) {
		if (this.currentShowAlbumDetailsScenario != null)
			this.currentShowAlbumDetailsScenario.cancel();

		this.currentShowAlbumDetailsScenario = this
				.getShowAlbumDetailsScenario(albumItem);
		this.currentShowAlbumDetailsScenario.play();
	}

	/**
	 * Sets the new overlay position of the album art and track listing. This
	 * method will also cause revalidation of the main window content pane.
	 * 
	 * @param overlayPosition
	 *            The new overlay position of the album art and track listing.
	 */
	public void setOverlayPosition(float overlayPosition) {
		this.overlayPosition = overlayPosition;
		this.getContentPane().invalidate();
		this.getContentPane().validate();
	}

	/**
	 * Returns the timeline scenario that implements a transition from the
	 * currently shown album item (which may be <code>null</code>) to the
	 * specified album item.
	 * 
	 * @param albumItem
	 *            The new album item to be shown in this window.
	 * @return The timeline scenario that implements a transition from the
	 *         currently shown album item (which may be <code>null</code>) to
	 *         the specified album item.
	 */
	private TimelineScenario getShowAlbumDetailsScenario(final Item albumItem) {
		TimelineScenario.RendezvousSequence scenario = new TimelineScenario.RendezvousSequence();

		// step 1 - move album art and track listing to the same location
		Timeline collapseArtAndTracks = new Timeline(this);
		collapseArtAndTracks.addPropertyToInterpolate("overlayPosition",
				this.overlayPosition, 0.0f);
		collapseArtAndTracks.setDuration((int) (500 * this.overlayPosition));
		scenario.addScenarioActor(collapseArtAndTracks);

		// step 2 (in parallel) - load the new album art
		final BufferedImage[] albumArtHolder = new BufferedImage[1];
		TimelineSwingWorker<Void, Void> loadNewAlbumArt = new TimelineSwingWorker<Void, Void>() {
			@Override
			protected Void doInBackground() throws Exception {
				URL url = new URL(albumItem.getLargeImage().getURL());
				albumArtHolder[0] = ImageIO.read(url);
				return null;
			}
		};
		scenario.addScenarioActor(loadNewAlbumArt);
		scenario.rendezvous();

		// step 3 (wait for steps 1 and 2) - replace album art
		TimelineRunnable replaceAlbumArt = new TimelineRunnable() {
			@Override
			public void run() {
				albumArt.setAlbumArtImage(albumArtHolder[0]);
			}
		};
		scenario.addScenarioActor(replaceAlbumArt);

		// step 4 (in parallel) - replace the track listing
		TimelineRunnable replaceTrackListing = new TimelineRunnable() {
			@Override
			public void run() {
				trackListingScroller.setAlbumItem(albumItem);
			}
		};
		scenario.addScenarioActor(replaceTrackListing);
		scenario.rendezvous();

		// step 5 (wait for steps 3 and 4) - cross fade album art from old to
		// new
		Timeline albumArtCrossfadeTimeline = new Timeline(albumArt);
		albumArtCrossfadeTimeline.addPropertyToInterpolate("oldImageAlpha",
				1.0f, 0.0f);
		albumArtCrossfadeTimeline.addPropertyToInterpolate("imageAlpha", 0.0f,
				1.0f);
		albumArtCrossfadeTimeline.addCallback(new Repaint(albumArt));
		albumArtCrossfadeTimeline.setDuration(400);

		scenario.addScenarioActor(albumArtCrossfadeTimeline);
		scenario.rendezvous();

		// step 6 (wait for step 5) - move new album art and track listing to
		// be side by side.
		Timeline separateArtAndTracks = new Timeline(this);
		separateArtAndTracks.addPropertyToInterpolate("overlayPosition", 0.0f,
				1.0f);
		separateArtAndTracks.setDuration(500);
		scenario.addScenarioActor(separateArtAndTracks);

		return scenario;
	}

	/**
	 * Calls the {@link Window#dispose()} method.
	 */
	private void superDispose() {
		super.dispose();
	}

	@Override
	public void dispose() {
		// dispose this window after a fade-out lasting 0.5 seconds.
		Timeline dispose = new Timeline(this);
		dispose.addPropertyToInterpolate("opacity", 1.0f, 0.0f);
		dispose.addCallback(new UIThreadTimelineCallbackAdapter() {
			@Override
			public void onTimelineStateChanged(TimelineState oldState,
					TimelineState newState, float durationFraction,
					float timelinePosition) {
				if (newState == TimelineState.DONE) {
					superDispose();
				}
			}
		});
		dispose.setDuration(500);
		dispose.play();
	}
}
  • Mysql
  • Glassfish
  • Jruby
  • Rails
  • Nblogo
Terms of Use; Privacy Policy;
© 2010, Oracle Corporation and/or its affiliates
(revision 20100521.d19488a)
 
 
loading
Please Confirm