/******************************************************************************* * Copyright (c) 2007, 2014 compeople AG and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * compeople AG - initial API and implementation *******************************************************************************/ package org.eclipse.riena.ui.swt.facades.internal; import java.util.concurrent.CountDownLatch; import org.pushingpixels.trident.Timeline; import org.pushingpixels.trident.Timeline.TimelineState; import org.pushingpixels.trident.callback.TimelineCallbackAdapter; import org.pushingpixels.trident.ease.Sine; import org.eclipse.core.runtime.Assert; import org.eclipse.jface.layout.GridDataFactory; import org.eclipse.swt.SWT; import org.eclipse.swt.events.ControlEvent; import org.eclipse.swt.events.ControlListener; import org.eclipse.swt.events.PaintEvent; import org.eclipse.swt.events.PaintListener; import org.eclipse.swt.graphics.Color; import org.eclipse.swt.graphics.GC; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Shell; import org.eclipse.riena.ui.core.resource.IconSize; import org.eclipse.riena.ui.swt.InfoFlyout; import org.eclipse.riena.ui.swt.layout.DpiGridLayoutFactory; import org.eclipse.riena.ui.swt.lnf.LnfKeyConstants; import org.eclipse.riena.ui.swt.lnf.LnfManager; import org.eclipse.riena.ui.swt.utils.ImageStore; import org.eclipse.riena.ui.swt.utils.SwtUtilities; import org.eclipse.riena.ui.swt.utils.UIControlsFactory; /** * Platform specific {@link InfoFlyout} for RCP. * * @see InfoFlyout */ public class InfoFlyoutRCP extends InfoFlyout { private static final Integer PAUSE_ANIMATION_TIME = LnfManager.getLnf().getIntegerSetting(LnfKeyConstants.INFO_FLYOUT_PAUSE_ANIMATION_TIME); private static final Integer WAIT_ANIMATION_TIME = LnfManager.getLnf().getIntegerSetting(LnfKeyConstants.INFO_FLYOUT_WAIT_ANIMATION_TIME); private static final Integer SHOW_HIDE_ANIMATION_TIME = LnfManager.getLnf().getIntegerSetting(LnfKeyConstants.INFO_FLYOUT_SHOW_AND_HIDE_ANIMATION_TIME); private static final int WIDTH = getLnfSettingAndConvertX(LnfKeyConstants.INFO_FLYOUT_WIDTH); private static final int HEIGHT = getLnfSettingAndConvertY(LnfKeyConstants.INFO_FLYOUT_HEIGHT); private static final int SHELL_RIGHT_INDENT = getLnfSettingAndConvertX(LnfKeyConstants.INFO_FLYOUT_RIGHT_INDENT); private static final int ICON_LEFT_MARGIN = getLnfSettingAndConvertX(LnfKeyConstants.INFO_FLYOUT_LEFT_MARGIN); private static final int TEXT_LEFT_MARGIN = getLnfSettingAndConvertX(LnfKeyConstants.INFO_FLYOUT_ICON_TEXT_GAP); private static final int INFO_FLYOUT_RIGHT_MARGIN = getLnfSettingAndConvertX(LnfKeyConstants.INFO_FLYOUT_RIGHT_MARGIN); // private static final int RIGHT_LABEL_WIDTH = WIDTH - INFO_FLYOUT_RIGHT_MARGIN - TEXT_LEFT_MARGIN - ICON_LEFT_MARGIN - SwtUtilities.convertXToDpi(30); private static final int RIGHT_LABEL_WIDTH = getLnfSetting(LnfKeyConstants.INFO_FLYOUT_WIDTH) - getLnfSetting(LnfKeyConstants.INFO_FLYOUT_RIGHT_MARGIN) - getLnfSetting(LnfKeyConstants.INFO_FLYOUT_LEFT_MARGIN) - getLnfSetting(LnfKeyConstants.INFO_FLYOUT_LEFT_MARGIN) - 30; private String message; private String icon; private IconSize iconSize; private Shell shell; private final Composite parent; private Timeline tShow; private Timeline tWait; private Timeline tHide; private Timeline tWaitAtEnd; private Label rightLabel; private Label leftLabel; private Rectangle topLevelShellBounds; private int xPosition; private int startY; private int endY; private CountDownLatch latch; private final Color bgColor; public InfoFlyoutRCP(final Composite parent) { this.parent = parent; message = ""; //$NON-NLS-1$ icon = null; bgColor = LnfManager.getLnf().getColor(LnfKeyConstants.INFO_FLYOUT_BACKGROUND_COLOR); latch = new CountDownLatch(0); initializeLayout(); } @Override public void openFlyout() { if (!isAnimationGoingOn() && !shell.isDisposed()) { latch = new CountDownLatch(1); updateIconAndMessage(); updateLocation(); updateLayoutData(); initializeTimelines(); shell.setVisible(true); tShow.play(); } } @Override public void setMessage(final String message) { this.message = message; } @Override public void setIcon(final String icon) { this.icon = icon; this.iconSize = IconSize.A16; } /* * (non-Javadoc) * * @see org.eclipse.riena.ui.swt.InfoFlyout#setIcon(java.lang.String, org.eclipse.riena.ui.core.resource.IconSize) */ @Override public void setIcon(final String icon, final IconSize iconSize) { this.icon = icon; this.iconSize = iconSize; } @Override public void waitForClosing() { try { latch.await(); } catch (final InterruptedException e) { Thread.currentThread().interrupt(); } } // helping methods ////////////////// private static int getLnfSetting(final String lnfKey) { return LnfManager.getLnf().getIntegerSetting(lnfKey); } private static int getLnfSettingAndConvertX(final String lnfKey) { final int setting = getLnfSetting(lnfKey); return SwtUtilities.convertXToDpi(setting); } private static int getLnfSettingAndConvertY(final String lnfKey) { final int setting = getLnfSetting(lnfKey); return SwtUtilities.convertYToDpi(setting); } private void initializeLayout() { Assert.isTrue(shell == null); // only call once final Shell parentShell = parent.getShell(); parentShell.addControlListener(new CloseOnParentMove()); shell = new Shell(parentShell, SWT.MODELESS | SWT.NO_TRIM); shell.addPaintListener(new BorderPainter()); DpiGridLayoutFactory.fillDefaults().numColumns(2).applyTo(shell); shell.setBackground(bgColor); leftLabel = UIControlsFactory.createLabel(shell, ""); //$NON-NLS-1$ leftLabel.setImage(ImageStore.getInstance().getImage("arrowRight")); //$NON-NLS-1$ leftLabel.setBackground(bgColor); rightLabel = UIControlsFactory.createLabel(shell, message, SWT.WRAP); rightLabel.setBackground(bgColor); rightLabel.setFont(LnfManager.getLnf().getFont(LnfKeyConstants.INFO_FLYOUT_FONT)); updateLocation(); updateLayoutData(); } private void updateIconAndMessage() { rightLabel.setText(message); leftLabel.setImage(ImageStore.getInstance().getImage(icon, iconSize)); shell.layout(true); } private void initializeTimelines() { tShow = new Timeline(shell); tWait = new Timeline(shell); tHide = new Timeline(shell); tWaitAtEnd = new Timeline(shell); tShow.addPropertyToInterpolate(Timeline.<Point> property("location").fromCurrent().to(new Point(xPosition, endY))); //$NON-NLS-1$ tShow.addPropertyToInterpolate(Timeline.<Point> property("size").fromCurrent() //$NON-NLS-1$ .to(new Point(WIDTH, HEIGHT))); tHide.addPropertyToInterpolate(Timeline.<Point> property("location").fromCurrent().to(new Point(xPosition, startY))); //$NON-NLS-1$ tHide.addPropertyToInterpolate(Timeline.<Point> property("size").fromCurrent() //$NON-NLS-1$ .to(new Point(WIDTH, 0))); tShow.addCallback(new TimelineCallbackAdapter() { @Override public void onTimelineStateChanged(final TimelineState oldState, final TimelineState newState, final float durationFraction, final float timelinePosition) { if (newState == TimelineState.DONE) { tWait.play(); } } }); tWait.addCallback(new TimelineCallbackAdapter() { @Override public void onTimelineStateChanged(final TimelineState oldState, final TimelineState newState, final float durationFraction, final float timelinePosition) { if (newState == TimelineState.DONE) { tHide.play(); } } }); tHide.addCallback(new TimelineCallbackAdapter() { @Override public void onTimelineStateChanged(final TimelineState oldState, final TimelineState newState, final float durationFraction, final float timelinePosition) { if (newState == TimelineState.DONE) { tWaitAtEnd.play(); } } }); tWaitAtEnd.addCallback(new TimelineCallbackAdapter() { @Override public void onTimelineStateChanged(final TimelineState oldState, final TimelineState newState, final float durationFraction, final float timelinePosition) { if (newState == TimelineState.DONE) { synchronized (this) { latch.countDown(); } } } }); tShow.setDuration(SHOW_HIDE_ANIMATION_TIME); tWait.setDuration(WAIT_ANIMATION_TIME); tHide.setDuration(SHOW_HIDE_ANIMATION_TIME); tWaitAtEnd.setDuration(PAUSE_ANIMATION_TIME); tHide.setEase(new Sine()); tShow.setEase(new Sine()); } private void updateLayoutData() { int topIndent = (HEIGHT - rightLabel.getBounds().height) / 2; // Note: The DpiGridLayout used for the parent shell already scales all margins, identations, etc. // according to the currently set scaling factor of the os. Thus, we need to specify the identation // at 100% scaling. final float[] dpiFactors = SwtUtilities.getDpiFactors(); topIndent = (int) (topIndent / dpiFactors[1] + 0.5); GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER).indent(ICON_LEFT_MARGIN, topIndent).applyTo(leftLabel); GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER).indent(TEXT_LEFT_MARGIN, topIndent).hint(RIGHT_LABEL_WIDTH, SWT.DEFAULT).applyTo(rightLabel); } private void updateLocation() { final Shell topLevelShell = parent.getShell(); topLevelShellBounds = topLevelShell.getBounds(); xPosition = topLevelShellBounds.x + topLevelShellBounds.width - WIDTH - SHELL_RIGHT_INDENT; startY = parent.getDisplay().map(parent.getParent(), null, parent.getBounds()).y + positionCorrectionY; endY = startY - HEIGHT; shell.setSize(WIDTH, 0); shell.setLocation(xPosition, startY); } private boolean isAnimationGoingOn() { if (tShow == null || tWait == null || tHide == null) { return false; } boolean isPlaying = tShow.getState() == TimelineState.PLAYING_FORWARD; isPlaying |= tWait.getState() == TimelineState.PLAYING_FORWARD; isPlaying |= tHide.getState() == TimelineState.PLAYING_FORWARD; return isPlaying; } // helping classes ////////////////// private static final class BorderPainter implements PaintListener { public void paintControl(final PaintEvent e) { final Control control = (Control) e.widget; final GC gc = e.gc; final Color oldFg = gc.getForeground(); final int oldWidth = gc.getLineWidth(); gc.setForeground(LnfManager.getLnf().getColor(LnfKeyConstants.INFO_FLYOUT_BORDER_COLOR)); gc.setLineWidth(1); final Rectangle bounds = control.getBounds(); gc.drawLine(0, 0, bounds.width - 1, 0); gc.drawLine(0, 0, 0, bounds.height - 1); gc.drawLine(bounds.width - 1, 0, bounds.width - 1, bounds.height); gc.setForeground(oldFg); gc.setLineWidth(oldWidth); } } /** * Closes the InfoFlyout, if the shell is moved or resized. */ private final class CloseOnParentMove implements ControlListener { public void controlMoved(final ControlEvent e) { close(); } public void controlResized(final ControlEvent e) { close(); } // helping methods ////////////////// private void close() { if (!shell.isDisposed() && shell.isVisible()) { shell.setVisible(false); tShow.abort(); tWait.abort(); tHide.abort(); latch.countDown(); } } } }