| /* |
| * Copyright (C) 2011 The Android Open Source Project |
| * |
| * Licensed under the Eclipse Public License, Version 1.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.eclipse.org/org/documents/epl-v10.php |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.ide.eclipse.adt.internal.editors.layout.gle2; |
| |
| import org.eclipse.swt.SWT; |
| import org.eclipse.swt.custom.CLabel; |
| import org.eclipse.swt.graphics.Font; |
| import org.eclipse.swt.graphics.FontData; |
| import org.eclipse.swt.graphics.Point; |
| import org.eclipse.swt.layout.FillLayout; |
| import org.eclipse.swt.widgets.Composite; |
| import org.eclipse.swt.widgets.Display; |
| import org.eclipse.swt.widgets.Shell; |
| |
| /** |
| * A dedicated tooltip used during gestures, for example to show the resize dimensions. |
| * <p> |
| * This is necessary because {@link org.eclipse.jface.window.ToolTip} causes flicker when |
| * used to dynamically update the position and text of the tip, and it does not seem to |
| * have setter methods to update the text or position without recreating the tip. |
| */ |
| public class GestureToolTip { |
| /** Minimum number of milliseconds to wait between alignment changes */ |
| private static final int TIMEOUT_MS = 750; |
| |
| /** |
| * The alpha to use for the tooltip window (which sadly will apply to the tooltip text |
| * as well.) |
| */ |
| private static final int SHELL_TRANSPARENCY = 220; |
| |
| /** The size of the font displayed in the tooltip */ |
| private static final int FONT_SIZE = 9; |
| |
| /** Horizontal delta from the mouse cursor to shift the tooltip by */ |
| private static final int OFFSET_X = 20; |
| |
| /** Vertical delta from the mouse cursor to shift the tooltip by */ |
| private static final int OFFSET_Y = 20; |
| |
| /** The label which displays the tooltip */ |
| private CLabel mLabel; |
| |
| /** The shell holding the tooltip */ |
| private Shell mShell; |
| |
| /** The font shown in the label; held here such that it can be disposed of after use */ |
| private Font mFont; |
| |
| /** Is the tooltip positioned below the given anchor? */ |
| private boolean mBelow; |
| |
| /** Is the tooltip positioned to the right of the given anchor? */ |
| private boolean mToRightOf; |
| |
| /** Is an alignment change pending? */ |
| private boolean mTimerPending; |
| |
| /** The new value for {@link #mBelow} when the timer expires */ |
| private boolean mPendingBelow; |
| |
| /** The new value for {@link #mToRightOf} when the timer expires */ |
| private boolean mPendingRight; |
| |
| /** The time stamp (from {@link System#currentTimeMillis()} of the last alignment change */ |
| private long mLastAlignmentTime; |
| |
| /** |
| * Creates a new tooltip over the given parent with the given relative position. |
| * |
| * @param parent the parent control |
| * @param below if true, display the tooltip below the mouse cursor otherwise above |
| * @param toRightOf if true, display the tooltip to the right of the mouse cursor, |
| * otherwise to the left |
| */ |
| public GestureToolTip(Composite parent, boolean below, boolean toRightOf) { |
| mBelow = below; |
| mToRightOf = toRightOf; |
| mLastAlignmentTime = System.currentTimeMillis(); |
| |
| mShell = new Shell(parent.getShell(), SWT.ON_TOP | SWT.TOOL | SWT.NO_FOCUS); |
| mShell.setLayout(new FillLayout()); |
| mShell.setAlpha(SHELL_TRANSPARENCY); |
| |
| Display display = parent.getDisplay(); |
| mLabel = new CLabel(mShell, SWT.SHADOW_NONE); |
| mLabel.setBackground(display.getSystemColor(SWT.COLOR_INFO_BACKGROUND)); |
| mLabel.setForeground(display.getSystemColor(SWT.COLOR_INFO_FOREGROUND)); |
| |
| Font systemFont = display.getSystemFont(); |
| FontData[] fd = systemFont.getFontData(); |
| for (int i = 0; i < fd.length; i++) { |
| fd[i].setHeight(FONT_SIZE); |
| } |
| mFont = new Font(display, fd); |
| mLabel.setFont(mFont); |
| |
| mShell.setVisible(false); |
| } |
| |
| /** |
| * Show the tooltip at the given position and with the given text. Note that the |
| * position may not be applied immediately; to prevent flicker alignment changes |
| * are queued up with a timer (unless it's been a while since the last change, in |
| * which case the update is applied immediately.) |
| * |
| * @param text the new text to be displayed |
| * @param below if true, display the tooltip below the mouse cursor otherwise above |
| * @param toRightOf if true, display the tooltip to the right of the mouse cursor, |
| * otherwise to the left |
| */ |
| public void update(final String text, boolean below, boolean toRightOf) { |
| // If the alignment has not changed recently, just apply the change immediately |
| // instead of within a delay |
| if (!mTimerPending && (below != mBelow || toRightOf != mToRightOf) |
| && (System.currentTimeMillis() - mLastAlignmentTime >= TIMEOUT_MS)) { |
| mBelow = below; |
| mToRightOf = toRightOf; |
| mLastAlignmentTime = System.currentTimeMillis(); |
| } |
| |
| Point location = mShell.getDisplay().getCursorLocation(); |
| |
| mLabel.setText(text); |
| |
| // Pack the label to its minimum size -- unless we are positioning the tooltip |
| // on the left. Because of the way SWT works (at least on the OSX) this sometimes |
| // creates flicker, because when we switch to a longer string (such as when |
| // switching from "52dp" to "wrap_content" during a resize) the window size will |
| // change first, and then the location will update later - so there will be a |
| // brief flash of the longer label before it is moved to the right position on the |
| // left. To work around this, we simply pass false to pack such that it will reuse |
| // its cached size, which in practice means that for labels on the right, the |
| // label will grow but not shrink. |
| // This workaround is disabled because it doesn't work well in Eclipse 3.5; the |
| // labels don't grow when they should. Re-enable when we drop 3.5 support. |
| //boolean changed = mToRightOf; |
| boolean changed = true; |
| |
| mShell.pack(changed); |
| Point size = mShell.getSize(); |
| |
| // Position the tooltip to the left or right, and above or below, according |
| // to the saved state of these flags, not the current parameters. We don't want |
| // to flicker, instead we react on a timer to changes in alignment below. |
| if (mBelow) { |
| location.y += OFFSET_Y; |
| } else { |
| location.y -= OFFSET_Y; |
| location.y -= size.y; |
| } |
| |
| if (mToRightOf) { |
| location.x += OFFSET_X; |
| } else { |
| location.x -= OFFSET_X; |
| location.x -= size.x; |
| } |
| |
| mShell.setLocation(location); |
| |
| if (!mShell.isVisible()) { |
| mShell.setVisible(true); |
| } |
| |
| // Has the orientation changed? |
| mPendingBelow = below; |
| mPendingRight = toRightOf; |
| if (below != mBelow || toRightOf != mToRightOf) { |
| // Yes, so schedule a timer (unless one is already scheduled) |
| if (!mTimerPending) { |
| mTimerPending = true; |
| final Runnable timer = new Runnable() { |
| @Override |
| public void run() { |
| mTimerPending = false; |
| // Check whether the alignment is still different than the target |
| // (since we may change back and forth repeatedly during the timeout) |
| if (mBelow != mPendingBelow || mToRightOf != mPendingRight) { |
| mBelow = mPendingBelow; |
| mToRightOf = mPendingRight; |
| mLastAlignmentTime = System.currentTimeMillis(); |
| if (mShell != null && mShell.isVisible()) { |
| update(text, mBelow, mToRightOf); |
| } |
| } |
| } |
| }; |
| mShell.getDisplay().timerExec(TIMEOUT_MS, timer); |
| } |
| } |
| } |
| |
| /** Hide the tooltip and dispose of any associated resources */ |
| public void dispose() { |
| mShell.dispose(); |
| mFont.dispose(); |
| |
| mShell = null; |
| mFont = null; |
| mLabel = null; |
| } |
| } |