org.eclipse.jface.window.ToolTip Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of org.eclipse.jface Show documentation
Show all versions of org.eclipse.jface Show documentation
This is org.eclipse.jface jar used by Scout SDK
The newest version!
/*******************************************************************************
* Copyright (c) 2006, 2015 IBM Corporation 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:
* Tom Schindl - initial API and implementation
* bugfix in: 195137, 198089, 225190
*******************************************************************************/
package org.eclipse.jface.window;
import java.util.HashMap;
import org.eclipse.jface.viewers.ColumnViewer;
import org.eclipse.jface.viewers.ViewerCell;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Monitor;
import org.eclipse.swt.widgets.Shell;
/**
* This class gives implementors to provide customized tooltips for any control.
*
* @since 3.3
*/
public abstract class ToolTip {
private Control control;
private int xShift = 3;
private int yShift = 0;
private int popupDelay = 0;
private int hideDelay = 0;
private ToolTipOwnerControlListener listener;
private HashMap data;
// Ensure that only one tooltip is active in time
private static Shell CURRENT_TOOLTIP;
/**
* Recreate the tooltip on every mouse move
*/
public static final int RECREATE = 1;
/**
* Don't recreate the tooltip as long the mouse doesn't leave the area
* triggering the tooltip creation
*/
public static final int NO_RECREATE = 1 << 1;
private TooltipHideListener hideListener = new TooltipHideListener();
private Listener shellListener;
private boolean hideOnMouseDown = true;
private boolean respectDisplayBounds = true;
private boolean respectMonitorBounds = true;
private int style;
private Object currentArea;
/**
* Create new instance which add TooltipSupport to the widget
*
* @param control
* the control on whose action the tooltip is shown
*/
public ToolTip(Control control) {
this(control, RECREATE, false);
}
/**
* @param control
* the control to which the tooltip is bound
* @param style
* style passed to control tooltip behavior
*
* @param manualActivation
* true
if the activation is done manually using
* {@link #show(Point)}
* @see #RECREATE
* @see #NO_RECREATE
*/
public ToolTip(Control control, int style, boolean manualActivation) {
this.control = control;
this.style = style;
this.listener = new ToolTipOwnerControlListener();
this.shellListener = event -> {
if (ToolTip.this.control != null
&& !ToolTip.this.control.isDisposed()) {
ToolTip.this.control.getDisplay().asyncExec(() -> {
// Check if the new active shell is the tooltip
// itself
if (ToolTip.this.control != null && !ToolTip.this.control.isDisposed()
&& ToolTip.this.control.getDisplay().getActiveShell() != CURRENT_TOOLTIP) {
toolTipHide(CURRENT_TOOLTIP, event);
}
});
}
};
if (!manualActivation) {
activate();
}
}
/**
* Restore arbitrary data under the given key
*
* @param key
* the key
* @param value
* the value
*/
public void setData(String key, Object value) {
if (data == null) {
data = new HashMap<>();
}
data.put(key, value);
}
/**
* Get the data restored under the key
*
* @param key
* the key
* @return data or null
if no entry is restored under the key
*/
public Object getData(String key) {
if (data != null) {
return data.get(key);
}
return null;
}
/**
* Set the shift (from the mouse position triggered the event) used to
* display the tooltip.
*
* By default the tooltip is shifted 3 pixels to the right.
*
*
* @param p
* the new shift
*/
public void setShift(Point p) {
xShift = p.x;
yShift = p.y;
}
/**
* Activate tooltip support for this control
*/
public void activate() {
deactivate();
control.addListener(SWT.Dispose, listener);
control.addListener(SWT.MouseHover, listener);
control.addListener(SWT.MouseMove, listener);
control.addListener(SWT.MouseExit, listener);
control.addListener(SWT.MouseDown, listener);
control.addListener(SWT.MouseWheel, listener);
}
/**
* Deactivate tooltip support for the underlying control
*/
public void deactivate() {
control.removeListener(SWT.Dispose, listener);
control.removeListener(SWT.MouseHover, listener);
control.removeListener(SWT.MouseMove, listener);
control.removeListener(SWT.MouseExit, listener);
control.removeListener(SWT.MouseDown, listener);
control.removeListener(SWT.MouseWheel, listener);
}
/**
* Return whether the tooltip respects bounds of the display.
*
* @return true
if the tooltip respects bounds of the display
*/
public boolean isRespectDisplayBounds() {
return respectDisplayBounds;
}
/**
* Set to false
if display bounds should not be respected or
* to true
if the tooltip is should repositioned to not
* overlap the display bounds.
*
* Default is true
*
*
* @param respectDisplayBounds
*/
public void setRespectDisplayBounds(boolean respectDisplayBounds) {
this.respectDisplayBounds = respectDisplayBounds;
}
/**
* Return whether the tooltip respects bounds of the monitor.
*
* @return true
if tooltip respects the bounds of the monitor
*/
public boolean isRespectMonitorBounds() {
return respectMonitorBounds;
}
/**
* Set to false
if monitor bounds should not be respected or
* to true
if the tooltip is should repositioned to not
* overlap the monitors bounds. The monitor the tooltip belongs to is the
* same is control's monitor the tooltip is shown for.
*
* Default is true
*
*
* @param respectMonitorBounds
*/
public void setRespectMonitorBounds(boolean respectMonitorBounds) {
this.respectMonitorBounds = respectMonitorBounds;
}
/**
* Should the tooltip displayed because of the given event.
*
* Subclasses may overwrite this to get custom behavior
*
*
* @param event
* the event
* @return true
if tooltip should be displayed
*/
protected boolean shouldCreateToolTip(Event event) {
if ((style & NO_RECREATE) != 0) {
Object tmp = getToolTipArea(event);
// No new area close the current tooltip
if (tmp == null) {
hide();
return false;
}
boolean rv = !tmp.equals(currentArea);
return rv;
}
return true;
}
/**
* This method is called before the tooltip is hidden
*
* @param event
* the event trying to hide the tooltip
* @return true
if the tooltip should be hidden
*/
private boolean shouldHideToolTip(Event event) {
if (event != null && event.type == SWT.MouseMove
&& (style & NO_RECREATE) != 0) {
Object tmp = getToolTipArea(event);
// No new area close the current tooltip
if (tmp == null) {
hide();
return false;
}
boolean rv = !tmp.equals(currentArea);
return rv;
}
return true;
}
/**
* This method is called to check for which area the tooltip is
* created/hidden for. In case of {@link #NO_RECREATE} this is used to
* decide if the tooltip is hidden recreated.
*
* By the default it is the widget the tooltip is created for but could be any object. To decide if
* the area changed the {@link Object#equals(Object)} method is used.
*
* @param event
* the event
* @return the area responsible for the tooltip creation or
* null
this could be any object describing the area
* (e.g. the {@link Control} onto which the tooltip is bound to, a
* part of this area e.g. for {@link ColumnViewer} this could be a
* {@link ViewerCell})
*/
protected Object getToolTipArea(Event event) {
return control;
}
/**
* Start up the tooltip programmatically
*
* @param location
* the location relative to the control the tooltip is shown
*/
public void show(Point location) {
Event event = new Event();
event.x = location.x;
event.y = location.y;
event.widget = control;
toolTipCreate(event);
}
private Shell toolTipCreate(final Event event) {
if (shouldCreateToolTip(event)) {
Shell shell = new Shell(control.getShell(), SWT.ON_TOP | SWT.TOOL
| SWT.NO_FOCUS);
shell.setLayout(new FillLayout());
toolTipOpen(shell, event);
return shell;
}
return null;
}
private void toolTipShow(Shell tip, Event event) {
if (!tip.isDisposed()) {
currentArea = getToolTipArea(event);
createToolTipContentArea(event, tip);
if (isHideOnMouseDown()) {
toolTipHookBothRecursively(tip);
} else {
toolTipHookByTypeRecursively(tip, true, SWT.MouseExit);
}
tip.pack();
Point size = tip.getSize();
Point location = fixupDisplayBounds(size, getLocation(size, event));
// Need to adjust a bit more if the mouse cursor.y == tip.y and
// the cursor.x is inside the tip
Point cursorLocation = tip.getDisplay().getCursorLocation();
if (cursorLocation.y == location.y && location.x < cursorLocation.x
&& location.x + size.x > cursorLocation.x) {
location.y -= 2;
}
tip.setLocation(location);
tip.setVisible(true);
}
}
private Point fixupDisplayBounds(Point tipSize, Point location) {
if (respectDisplayBounds || respectMonitorBounds) {
Rectangle bounds;
Point rightBounds = new Point(tipSize.x + location.x, tipSize.y
+ location.y);
Monitor[] ms = control.getDisplay().getMonitors();
if (respectMonitorBounds && ms.length > 1) {
// By default present in the monitor of the control
bounds = control.getMonitor().getBounds();
Point p = new Point(location.x, location.y);
// Search on which monitor the event occurred
Rectangle tmp;
for (int i = 0; i < ms.length; i++) {
tmp = ms[i].getBounds();
if (tmp.contains(p)) {
bounds = tmp;
break;
}
}
} else {
bounds = control.getDisplay().getBounds();
}
if (!(bounds.contains(location) && bounds.contains(rightBounds))) {
if (rightBounds.x > bounds.x + bounds.width) {
location.x -= rightBounds.x - (bounds.x + bounds.width);
}
if (rightBounds.y > bounds.y + bounds.height) {
location.y -= rightBounds.y - (bounds.y + bounds.height);
}
if (location.x < bounds.x) {
location.x = bounds.x;
}
if (location.y < bounds.y) {
location.y = bounds.y;
}
}
}
return location;
}
/**
* Get the display relative location where the tooltip is displayed.
* Subclasses may overwrite to implement custom positioning.
*
* @param tipSize
* the size of the tooltip to be shown
* @param event
* the event triggered showing the tooltip
* @return the absolute position on the display
*/
public Point getLocation(Point tipSize, Event event) {
return control.toDisplay(event.x + xShift, event.y + yShift);
}
private void toolTipHide(Shell tip, Event event) {
if (tip != null && !tip.isDisposed() && shouldHideToolTip(event)) {
control.getShell().removeListener(SWT.Deactivate, shellListener);
currentArea = null;
passOnEvent(tip, event);
tip.dispose();
CURRENT_TOOLTIP = null;
afterHideToolTip(event);
}
if (event != null && event.type == SWT.Dispose) {
deactivate();
data = null;
}
}
private void passOnEvent(Shell tip, Event event) {
if (control != null && !control.isDisposed() && event != null
&& event.widget != control && event.type == SWT.MouseDown) {
// the following was left in order to fix bug 298770 with minimal change. In 3.7, the complete method should be removed.
tip.close();
}
}
private void toolTipOpen(final Shell shell, final Event event) {
// Ensure that only one Tooltip is shown in time
if (CURRENT_TOOLTIP != null) {
toolTipHide(CURRENT_TOOLTIP, null);
}
CURRENT_TOOLTIP = shell;
control.getShell().addListener(SWT.Deactivate, shellListener);
if (popupDelay > 0) {
control.getDisplay().timerExec(popupDelay, () -> toolTipShow(shell, event));
} else {
toolTipShow(CURRENT_TOOLTIP, event);
}
if (hideDelay > 0) {
control.getDisplay().timerExec(popupDelay + hideDelay,
() -> toolTipHide(shell, null));
}
}
private void toolTipHookByTypeRecursively(Control c, boolean add, int type) {
if (add) {
c.addListener(type, hideListener);
} else {
c.removeListener(type, hideListener);
}
if (c instanceof Composite) {
Control[] children = ((Composite) c).getChildren();
for (int i = 0; i < children.length; i++) {
toolTipHookByTypeRecursively(children[i], add, type);
}
}
}
private void toolTipHookBothRecursively(Control c) {
c.addListener(SWT.MouseDown, hideListener);
c.addListener(SWT.MouseExit, hideListener);
if (c instanceof Composite) {
Control[] children = ((Composite) c).getChildren();
for (int i = 0; i < children.length; i++) {
toolTipHookBothRecursively(children[i]);
}
}
}
/**
* Creates the content area of the the tooltip.
*
* @param event
* the event that triggered the activation of the tooltip
* @param parent
* the parent of the content area
* @return the content area created
*/
protected abstract Composite createToolTipContentArea(Event event,
Composite parent);
/**
* This method is called after a tooltip is hidden.
*
* Subclasses may override to clean up requested system resources
*
*
* @param event
* event triggered the hiding action (may be null
* if event wasn't triggered by user actions directly)
*/
protected void afterHideToolTip(Event event) {
}
/**
* Set the hide delay.
*
* @param hideDelay
* the delay before the tooltip is hidden. If 0
* the tooltip is shown until user moves to other item
*/
public void setHideDelay(int hideDelay) {
this.hideDelay = hideDelay;
}
/**
* Set the popup delay.
*
* @param popupDelay
* the delay before the tooltip is shown to the user. If
* 0
the tooltip is shown immediately
*/
public void setPopupDelay(int popupDelay) {
this.popupDelay = popupDelay;
}
/**
* Return if hiding on mouse down is set.
*
* @return true
if hiding on mouse down in the tool tip is on
*/
public boolean isHideOnMouseDown() {
return hideOnMouseDown;
}
/**
* If you don't want the tool tip to be hidden when the user clicks inside
* the tool tip set this to false
. You maybe also need to
* hide the tool tip yourself depending on what you do after clicking in the
* tooltip (e.g. if you open a new {@link Shell})
*
* @param hideOnMouseDown
* flag to indicate of tooltip is hidden automatically on mouse
* down inside the tool tip
*/
public void setHideOnMouseDown(final boolean hideOnMouseDown) {
// Only needed if there's currently a tooltip active
if (CURRENT_TOOLTIP != null && !CURRENT_TOOLTIP.isDisposed()) {
// Only change if value really changed
if (hideOnMouseDown != this.hideOnMouseDown) {
control.getDisplay().syncExec(() -> {
if (CURRENT_TOOLTIP != null
&& CURRENT_TOOLTIP.isDisposed()) {
toolTipHookByTypeRecursively(CURRENT_TOOLTIP,
hideOnMouseDown, SWT.MouseDown);
}
});
}
}
this.hideOnMouseDown = hideOnMouseDown;
}
/**
* Hide the currently active tool tip
*/
public void hide() {
toolTipHide(CURRENT_TOOLTIP, null);
}
private class ToolTipOwnerControlListener implements Listener {
@Override
public void handleEvent(Event event) {
switch (event.type) {
case SWT.Dispose:
case SWT.KeyDown:
case SWT.MouseDown:
case SWT.MouseMove:
case SWT.MouseWheel:
toolTipHide(CURRENT_TOOLTIP, event);
break;
case SWT.MouseHover:
toolTipCreate(event);
break;
case SWT.MouseExit:
/*
* Check if the mouse exit happened because we move over the
* tooltip
*/
if (CURRENT_TOOLTIP != null && !CURRENT_TOOLTIP.isDisposed()) {
if (CURRENT_TOOLTIP.getBounds().contains(
control.toDisplay(event.x, event.y))) {
break;
}
}
toolTipHide(CURRENT_TOOLTIP, event);
break;
}
}
}
private class TooltipHideListener implements Listener {
@Override
public void handleEvent(Event event) {
if (event.widget instanceof Control) {
Control c = (Control) event.widget;
Shell shell = c.getShell();
switch (event.type) {
case SWT.MouseDown:
if (isHideOnMouseDown()) {
toolTipHide(shell, event);
}
break;
case SWT.MouseExit:
/*
* Give some insets to ensure we get exit informations from
* a wider area ;-)
*/
Rectangle rect = shell.getBounds();
rect.x += 5;
rect.y += 5;
rect.width -= 10;
rect.height -= 10;
if (!rect.contains(c.getDisplay().getCursorLocation())) {
toolTipHide(shell, event);
}
break;
}
}
}
}
}