diff options
Diffstat (limited to 'eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/GestureToolTip.java')
-rw-r--r-- | eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/GestureToolTip.java | 217 |
1 files changed, 217 insertions, 0 deletions
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/GestureToolTip.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/GestureToolTip.java new file mode 100644 index 000000000..a49e79cbf --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/GestureToolTip.java @@ -0,0 +1,217 @@ +/* + * 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; + } +} |