2013年2月28日 星期四

在 eclipse swt 的介面上放 tool tip

實作 tool tip 時,其實就是在介面上面放一個滑鼠停留的事件
當事件發生時,要求程式畫出一個文字欄位,把 tool tip 要顯示的東西放在欄位上。
範例的滑鼠停留事件可以參考 [1]。

以下先節錄 [1] 定義的 tool tip 事件的 class。
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.HelpEvent;
import org.eclipse.swt.events.HelpListener;
import org.eclipse.swt.events.MouseAdapter;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.MouseTrackAdapter;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.ToolBar;
import org.eclipse.swt.widgets.Tree;
import org.eclipse.swt.widgets.Widget;

/**
 * Emulated tooltip handler Notice that we could display anything in a tooltip
 * besides text and images. For instance, it might make sense to embed large
 * tables of data or buttons linking data under inspection to material
 * elsewhere, or perform dynamic lookup for creating tooltip text on the fly.
 * @author http://www.java2s.com/Code/Java/SWT-JFace-Eclipse/HowtoimplementhoverhelpfeedbackusingtheMouseTrackListener.htm
 */
public class ToolTipHandler {
  
  private Shell parentShell;

  private Shell tipShell;

  private Label tipLabelImage, tipLabelText;

  private Widget tipWidget; // widget this tooltip is hovering over

  private Point tipPosition; // the position being hovered over

  /**
   * Creates a new tooltip handler
   * @param parent
   *            the parent Shell
   */
  public ToolTipHandler(Shell parent) {
    final Display display = parent.getDisplay();
    this.parentShell = parent;

    tipShell = new Shell(parent, SWT.ON_TOP | SWT.TOOL);
    GridLayout gridLayout = new GridLayout();
    gridLayout.numColumns = 2;
    gridLayout.marginWidth = 2;
    gridLayout.marginHeight = 2;
    tipShell.setLayout(gridLayout);

    tipShell.setBackground(display.getSystemColor(SWT.COLOR_INFO_BACKGROUND));

    tipLabelImage = new Label(tipShell, SWT.NONE);
    tipLabelImage.setForeground(display.getSystemColor(SWT.COLOR_INFO_FOREGROUND));
    tipLabelImage.setBackground(display.getSystemColor(SWT.COLOR_INFO_BACKGROUND));
    tipLabelImage.setLayoutData(
        new GridData(GridData.FILL_HORIZONTAL | GridData.VERTICAL_ALIGN_CENTER));

    tipLabelText = new Label(tipShell, SWT.NONE);
    tipLabelText.setForeground(display.getSystemColor(SWT.COLOR_INFO_FOREGROUND));
    tipLabelText.setBackground(display.getSystemColor(SWT.COLOR_INFO_BACKGROUND));
    tipLabelText.setLayoutData(
        new GridData(GridData.FILL_HORIZONTAL | GridData.VERTICAL_ALIGN_CENTER));
  }

  /**
   * Enables customized hover help for a specified control
   * 
   * @control the control on which to enable hoverhelp
   */
  public void activateHoverHelp(final Control control) {
    // Get out of the way if we attempt to activate the control underneath the tooltip
    control.addMouseListener(new MouseAdapter() {
      public void mouseDown(MouseEvent e) {
        if (tipShell.isVisible())
          tipShell.setVisible(false);
      }
    });

    // Trap hover events to pop-up tooltip
    control.addMouseTrackListener(new MouseTrackAdapter() {
      public void mouseExit(MouseEvent e) {
        if (tipShell.isVisible())
          tipShell.setVisible(false);
        tipWidget = null;
      }

      public void mouseHover(MouseEvent event) {
        Point pt = new Point(event.x, event.y);
        Widget widget = event.widget;
        if (widget instanceof ToolBar) {
          ToolBar w = (ToolBar) widget;
          widget = w.getItem(pt);
        }
        if (widget instanceof Table) {
          Table w = (Table) widget;
          widget = w.getItem(pt);
        }
        if (widget instanceof Tree) {
          Tree w = (Tree) widget;
          widget = w.getItem(pt);
        }
        if (widget == null) {
          tipShell.setVisible(false);
          tipWidget = null;
          return;
        }
        if (widget == tipWidget)
          return;
        tipWidget = widget;
        tipPosition = control.toDisplay(pt);
        String text = (String) widget.getData("TIP_TEXT");
        Image image = (Image) widget.getData("TIP_IMAGE");
        tipLabelText.setText(text != null ? text : "");
        tipLabelImage.setImage(image); // accepts null
        tipShell.pack();
        setHoverLocation(tipShell, tipPosition);
        tipShell.setVisible(true);
      }
    });

    // Trap F1 Help to pop up a custom help box
    control.addHelpListener(new HelpListener() {
      public void helpRequested(HelpEvent event) {
        if (tipWidget == null)
          return;
        ToolTipHelpTextHandler handler = (ToolTipHelpTextHandler) tipWidget
            .getData("TIP_HELPTEXTHANDLER");
        if (handler == null)
          return;
        String text = handler.getHelpText(tipWidget);
        if (text == null)
          return;

        if (tipShell.isVisible()) {
          tipShell.setVisible(false);
          Shell helpShell = new Shell(parentShell, SWT.SHELL_TRIM);
          helpShell.setLayout(new FillLayout());
          Label label = new Label(helpShell, SWT.NONE);
          label.setText(text);
          helpShell.pack();
          setHoverLocation(helpShell, tipPosition);
          helpShell.open();
        }
      }
    });
  }

  /**
   * Sets the location for a hovering shell
   * 
   * @param shell
   *            the object that is to hover
   * @param position
   *            the position of a widget to hover over
   * @return the top-left location for a hovering box
   */
  private void setHoverLocation(Shell shell, Point position) {
    Rectangle displayBounds = shell.getDisplay().getBounds();
    Rectangle shellBounds = shell.getBounds();
    shellBounds.x = Math.max(
        Math.min(position.x, displayBounds.width - shellBounds.width),
        0);
    shellBounds.y = Math.max(
        Math.min(position.y + 16, displayBounds.height
            - shellBounds.height), 0);
    shell.setBounds(shellBounds);
  }

  /**
   * ToolTip help handler
   */
  public interface ToolTipHelpTextHandler {
    /**
     * Get help text
     * @param widget
     *            the widget that is under help
     * @return a help text string
     */
    public String getHelpText(Widget widget);
  }
}

接著當需要放 tool tip 時,可以加入以下的程式碼。
Shell shell = new Shell();
ToolTipHandler tooltipHandler = new ToolTipHandler(shell.getShell());

// Initial table and table item.
Table table = new Table(shell, SWT.BORDER);
TableItem item = new TableItem(table, SWT.PUSH);

// Set properties about tool tip.
item.setData("TIP_TEXT", "tool tip");
item.setData("TIP_HELPTEXTHANDLER", new ToolTipHelpTextHandler() {
  public String getHelpText(Widget widget) {
    Item item = (Item) widget;
    return "tool tip"
  }
});

// Activate the tool tip on the table. (Bind events for the Control object)
tooltipHandler.activateHoverHelp(table);

不過詳細的原理我還沒仔細看,所以暫時先不加註語法的解釋了。
需要其他的範例的話,可以直接參考 [1] 的原始碼,上面有好幾種介面的使用範例
並且在 [1] 的範例中,都是有在 tool tip 上放圖的。

PS. 不過要直接執行 [1] 的範例程式碼時,記得自己放 notification.gif 和 warning.gif 兩個圖檔!

參考資料:
1、How to implement hover help feedback using the MouseTrackListener

沒有留言: