Mercurial > projects > dwt-addons
diff dwtx/jface/internal/text/StickyHoverManager.d @ 129:eb30df5ca28b
Added JFace Text sources
author | Frank Benoit <benoit@tionex.de> |
---|---|
date | Sat, 23 Aug 2008 19:10:48 +0200 |
parents | |
children | c4fb132a086c |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/internal/text/StickyHoverManager.d Sat Aug 23 19:10:48 2008 +0200 @@ -0,0 +1,381 @@ +/******************************************************************************* + * Copyright (c) 2007, 2008 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: + * IBM Corporation - initial API and implementation + * Port to the D programming language: + * Frank Benoit <benoit@tionex.de> + *******************************************************************************/ +module dwtx.jface.internal.text.StickyHoverManager; + +import dwt.dwthelper.utils; + + + +import dwt.DWT; +import dwt.events.ControlEvent; +import dwt.events.ControlListener; +import dwt.events.FocusEvent; +import dwt.events.FocusListener; +import dwt.events.KeyEvent; +import dwt.events.KeyListener; +import dwt.events.MouseEvent; +import dwt.events.MouseListener; +import dwt.graphics.Point; +import dwt.graphics.Rectangle; +import dwt.widgets.Control; +import dwt.widgets.Display; +import dwt.widgets.Event; +import dwt.widgets.Listener; +import dwtx.jface.text.IInformationControl; +import dwtx.jface.text.IInformationControlExtension3; +import dwtx.jface.text.IInformationControlExtension5; +import dwtx.jface.text.IViewportListener; +import dwtx.jface.text.IWidgetTokenKeeper; +import dwtx.jface.text.IWidgetTokenKeeperExtension; +import dwtx.jface.text.IWidgetTokenOwner; +import dwtx.jface.text.TextViewer; +import dwtx.jface.util.Geometry; + + +/** + * Implements a sticky hover control, i.e. a control that replaces a hover + * with an enriched and focusable control. + * <p> + * The information control is made visible on request by calling + * {@link #showInformationControl(Rectangle)}. + * </p> + * <p> + * Clients usually instantiate and configure this class before using it. The configuration + * must be consistent: This means the used {@link dwtx.jface.text.IInformationControlCreator} + * must create an information control expecting information in the same format the configured + * {@link dwtx.jface.text.information.IInformationProvider}s use to encode the information they provide. + * </p> + * + * @since 3.4 + */ +public class StickyHoverManager : InformationControlReplacer , IWidgetTokenKeeper, IWidgetTokenKeeperExtension { + + /** + * Priority of the info controls managed by this sticky hover manager. + * <p> + * Note: Only applicable when info control does not have focus. + * -5 as value has been chosen in order to be beaten by the hovers of TextViewerHoverManager. + * </p> + */ + private static final int WIDGET_PRIORITY= -5; + + + /** + * Internal information control closer. Listens to several events issued by its subject control + * and closes the information control when necessary. + */ + class Closer : IInformationControlCloser, ControlListener, MouseListener, IViewportListener, KeyListener, FocusListener, Listener { + //TODO: Catch 'Esc' key in fInformationControlToClose: Don't dispose, just hideInformationControl(). + // This would allow to reuse the information control also when the user explicitly closes it. + + //TODO: if subject control is a Scrollable, should add selection listeners to both scroll bars + // (and remove the ViewPortListener, which only listens to vertical scrolling) + + /** The subject control. */ + private Control fSubjectControl; + /** Indicates whether this closer is active. */ + private bool fIsActive= false; + /** The display. */ + private Display fDisplay; + + /* + * @see IInformationControlCloser#setSubjectControl(Control) + */ + public void setSubjectControl(Control control) { + fSubjectControl= control; + } + + /* + * @see IInformationControlCloser#setInformationControl(IInformationControl) + */ + public void setInformationControl(IInformationControl control) { + // NOTE: we use getCurrentInformationControl2() from the outer class + } + + /* + * @see IInformationControlCloser#start(Rectangle) + */ + public void start(Rectangle informationArea) { + + if (fIsActive) + return; + fIsActive= true; + + if (fSubjectControl !is null && !fSubjectControl.isDisposed()) { + fSubjectControl.addControlListener(this); + fSubjectControl.addMouseListener(this); + fSubjectControl.addKeyListener(this); + } + + fTextViewer.addViewportListener(this); + + IInformationControl fInformationControlToClose= getCurrentInformationControl2(); + if (fInformationControlToClose !is null) + fInformationControlToClose.addFocusListener(this); + + fDisplay= fSubjectControl.getDisplay(); + if (!fDisplay.isDisposed()) { + fDisplay.addFilter(DWT.MouseMove, this); + fDisplay.addFilter(DWT.FocusOut, this); + } + } + + /* + * @see IInformationControlCloser#stop() + */ + public void stop() { + + if (!fIsActive) + return; + fIsActive= false; + + fTextViewer.removeViewportListener(this); + + if (fSubjectControl !is null && !fSubjectControl.isDisposed()) { + fSubjectControl.removeControlListener(this); + fSubjectControl.removeMouseListener(this); + fSubjectControl.removeKeyListener(this); + } + + IInformationControl fInformationControlToClose= getCurrentInformationControl2(); + if (fInformationControlToClose !is null) + fInformationControlToClose.removeFocusListener(this); + + if (fDisplay !is null && !fDisplay.isDisposed()) { + fDisplay.removeFilter(DWT.MouseMove, this); + fDisplay.removeFilter(DWT.FocusOut, this); + } + + fDisplay= null; + } + + /* + * @see ControlListener#controlResized(ControlEvent) + */ + public void controlResized(ControlEvent e) { + hideInformationControl(); + } + + /* + * @see ControlListener#controlMoved(ControlEvent) + */ + public void controlMoved(ControlEvent e) { + hideInformationControl(); + } + + /* + * @see MouseListener#mouseDown(MouseEvent) + */ + public void mouseDown(MouseEvent e) { + hideInformationControl(); + } + + /* + * @see MouseListener#mouseUp(MouseEvent) + */ + public void mouseUp(MouseEvent e) { + } + + /* + * @see MouseListener#mouseDoubleClick(MouseEvent) + */ + public void mouseDoubleClick(MouseEvent e) { + hideInformationControl(); + } + + /* + * @see IViewportListenerListener#viewportChanged(int) + */ + public void viewportChanged(int topIndex) { + hideInformationControl(); + } + + /* + * @see KeyListener#keyPressed(KeyEvent) + */ + public void keyPressed(KeyEvent e) { + hideInformationControl(); + } + + /* + * @see KeyListener#keyReleased(KeyEvent) + */ + public void keyReleased(KeyEvent e) { + } + + /* + * @see dwt.events.FocusListener#focusGained(dwt.events.FocusEvent) + */ + public void focusGained(FocusEvent e) { + } + + /* + * @see dwt.events.FocusListener#focusLost(dwt.events.FocusEvent) + */ + public void focusLost(FocusEvent e) { + if (DEBUG) System.out.println("StickyHoverManager.Closer.focusLost(): " + e); //$NON-NLS-1$ + Display d= fSubjectControl.getDisplay(); + d.asyncExec(new Runnable() { + // Without the asyncExec, mouse clicks to the workbench window are swallowed. + public void run() { + hideInformationControl(); + } + }); + } + + /* + * @see dwt.widgets.Listener#handleEvent(dwt.widgets.Event) + */ + public void handleEvent(Event event) { + if (event.type is DWT.MouseMove) { + if (!(event.widget instanceof Control) || event.widget.isDisposed()) + return; + + IInformationControl infoControl= getCurrentInformationControl2(); + if (infoControl !is null && !infoControl.isFocusControl() && infoControl instanceof IInformationControlExtension3) { +// if (DEBUG) System.out.println("StickyHoverManager.Closer.handleEvent(): activeShell= " + fDisplay.getActiveShell()); //$NON-NLS-1$ + IInformationControlExtension3 iControl3= (IInformationControlExtension3) infoControl; + Rectangle controlBounds= iControl3.getBounds(); + if (controlBounds !is null) { + Point mouseLoc= event.display.map((Control) event.widget, null, event.x, event.y); + int margin= getKeepUpMargin(); + Geometry.expand(controlBounds, margin, margin, margin, margin); + if (!controlBounds.contains(mouseLoc)) { + hideInformationControl(); + } + } + + } else { + /* + * TODO: need better understanding of why/if this is needed. + * Looks like the same panic code we have in dwtx.jface.text.AbstractHoverInformationControlManager.Closer.handleMouseMove(Event) + */ + if (fDisplay !is null && !fDisplay.isDisposed()) + fDisplay.removeFilter(DWT.MouseMove, this); + } + + } else if (event.type is DWT.FocusOut) { + if (DEBUG) System.out.println("StickyHoverManager.Closer.handleEvent(): focusOut: " + event); //$NON-NLS-1$ + IInformationControl iControl= getCurrentInformationControl2(); + if (iControl !is null && ! iControl.isFocusControl()) + hideInformationControl(); + } + } + } + + + private final TextViewer fTextViewer; + + + /** + * Creates a new sticky hover manager. + * + * @param textViewer the text viewer + */ + public StickyHoverManager(TextViewer textViewer) { + super(new DefaultInformationControlCreator()); + + fTextViewer= textViewer; + setCloser(new Closer()); + + install(fTextViewer.getTextWidget()); + } + + /* + * @see AbstractInformationControlManager#showInformationControl(Rectangle) + */ + protected void showInformationControl(Rectangle subjectArea) { + if (fTextViewer !is null && fTextViewer.requestWidgetToken(this, WIDGET_PRIORITY)) + super.showInformationControl(subjectArea); + else + if (DEBUG) + System.out.println("cancelled StickyHoverManager.showInformationControl(..): did not get widget token (with prio)"); //$NON-NLS-1$ + } + + /* + * @see AbstractInformationControlManager#hideInformationControl() + */ + public void hideInformationControl() { + try { + super.hideInformationControl(); + } finally { + if (fTextViewer !is null) + fTextViewer.releaseWidgetToken(this); + } + } + + /* + * @see AbstractInformationControlManager#handleInformationControlDisposed() + */ + protected void handleInformationControlDisposed() { + try { + super.handleInformationControlDisposed(); + } finally { + if (fTextViewer !is null) + fTextViewer.releaseWidgetToken(this); + } + } + + /* + * @see dwtx.jface.text.IWidgetTokenKeeper#requestWidgetToken(IWidgetTokenOwner) + */ + public bool requestWidgetToken(IWidgetTokenOwner owner) { + hideInformationControl(); + if (DEBUG) + System.out.println("StickyHoverManager gave up widget token (no prio)"); //$NON-NLS-1$ + return true; + } + + /* + * @see dwtx.jface.text.IWidgetTokenKeeperExtension#requestWidgetToken(dwtx.jface.text.IWidgetTokenOwner, int) + */ + public bool requestWidgetToken(IWidgetTokenOwner owner, int priority) { + if (getCurrentInformationControl2() !is null) { + if (getCurrentInformationControl2().isFocusControl()) { + if (DEBUG) + System.out.println("StickyHoverManager kept widget token (focused)"); //$NON-NLS-1$ + return false; + } else if (priority > WIDGET_PRIORITY) { + hideInformationControl(); + if (DEBUG) + System.out.println("StickyHoverManager gave up widget token (prio)"); //$NON-NLS-1$ + return true; + } else { + if (DEBUG) + System.out.println("StickyHoverManager kept widget token (prio)"); //$NON-NLS-1$ + return false; + } + } + if (DEBUG) + System.out.println("StickyHoverManager gave up widget token (no iControl)"); //$NON-NLS-1$ + return true; + } + + /* + * @see dwtx.jface.text.IWidgetTokenKeeperExtension#setFocus(dwtx.jface.text.IWidgetTokenOwner) + */ + public bool setFocus(IWidgetTokenOwner owner) { + IInformationControl iControl= getCurrentInformationControl2(); + if (iControl instanceof IInformationControlExtension5) { + IInformationControlExtension5 iControl5= (IInformationControlExtension5) iControl; + if (iControl5.isVisible()) { + iControl.setFocus(); + return iControl.isFocusControl(); + } + return false; + } + iControl.setFocus(); + return iControl.isFocusControl(); + } + +}