/**
* (c) 2011, Alejandro Serrano
* Released under the terms of the EPL.
*/
package net.sf.eclipsefp.haskell.ui.internal.editors.haskell;
import net.sf.eclipsefp.haskell.ui.util.SWTUtil;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.jface.text.AbstractInformationControl;
import org.eclipse.jface.text.IInformationControl;
import org.eclipse.jface.text.IInformationControlCreator;
import org.eclipse.jface.util.Geometry;
import org.eclipse.jface.util.IPropertyChangeListener;
import org.eclipse.jface.util.PropertyChangeEvent;
import org.eclipse.swt.SWT;
import org.eclipse.swt.browser.Browser;
import org.eclipse.swt.browser.ProgressEvent;
import org.eclipse.swt.browser.ProgressListener;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.FontData;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.themes.ITheme;
/**
* Control showing hover or autocomplete info
* @author Alejandro Serrano & Martijn Schrage
*
*/
public class HaskellInformationControl extends AbstractInformationControl {
//private static final int HOVER_WRAPWIDTH = 600; // bounds for the tooltip with type info and errors/warnings
private static final int HOVER_MAXWIDTH = 1000;
private static final int HOVER_MINHEIGHT = 36;
private static final int HOVER_MAXHEIGHT = 700;
private Browser doc;
private boolean hasContents = false;
//private boolean hasHR = false;
public HaskellInformationControl( final Shell parent ) {
super( parent, true );
create();
}
@Override
public void setInformation( final String content ) {
// If we need more information than just a string, implement IInformationControlExtension2 and method setInput,
// which will be called with the object returned by getHoverInfo2 in HaskellTextHover.
// TODO: with the method above, encode the type of the content (type info vs. error/warning)
// and use a smaller width for errors/warnings.
setDocumentation( content );
}
@Override
protected void createContent( final Composite parent ) {
doc = new Browser( parent, SWT.TOP | SWT.RESIZE );
// These have no effect, so we set the colors with css.
// Unfortunately, the background remains white, which is sometimes visible when scrolling
doc.setForeground( parent.getDisplay().getSystemColor( SWT.COLOR_INFO_FOREGROUND ) );
doc.setBackground( parent.getDisplay().getSystemColor( SWT.COLOR_YELLOW ) );
doc.setFont( JFaceResources.getTextFont() );
// The only way to get the height of the browser to depend on the height of its
// rendered contents seems to be through javascript:
// http://www.eclipse.org/forums/index.php/t/257935/
doc.addProgressListener(new ProgressListener() {
@Override
public void completed(final ProgressEvent event) {
// wait for page to load before evaluating any javascript
int contentHeight = ((Double)doc.evaluate("return document.body.scrollHeight")).intValue();
int contentWidth = ((Double)doc.evaluate("return document.body.scrollWidth")).intValue();
// JP: I don't understand that code...
// so I just keep setting the hover to the best size
// if somebody write all documentation in one big line, they'll get a big hover
// but so what? Long lines are hard to read in code anyway
// contentWidth may exceed HOVER_WRAPWIDTH if there are wide lines that cannot
// be broken.
// if (contentWidth <= HOVER_WRAPWIDTH) {
// for content within the width, simply set the content height and use
// HOVER_WRAPWIDTH for the width. (cannot use contentWidth, since it sometimes
// assumes an unnecessary vertical scrollbar and gets too small)
// reserve space for scroll bar
if (contentWidth>HOVER_MAXWIDTH){
contentHeight+=25;
}
Rectangle r=getShell().computeTrim( 0, 0, contentWidth, contentHeight );
HaskellInformationControl.this.setSize(r.width ,r.height);
if (contentHeight<HOVER_MAXHEIGHT){
doc.evaluate( "document.body.style.overflowY='hidden';" );
}
// Unfortunately, once the width is set, it seems impossible to obtain
// the actual rendered width. Therefore, the tooltip always has at least a
// width of HOVER_WRAPWIDTH, even if the contents are not wrapped and less wide.
}
@Override
public void changed(final ProgressEvent event) {
// no need for changed events since content is only set once
}
});
}
/* (non-Javadoc)
* @see org.eclipse.jface.text.AbstractInformationControl#dispose()
*/
@Override
public void dispose() {
if (doc!=null){
doc.dispose();
doc=null;
}
super.dispose();
}
/*
* @see IInformationControl#setSize(int, int)
*/
@Override
public void setSize(final int width, final int height) {
// Clip according to max width and min and max height. (min width is not necessary)
super.setSize(Math.min(width,HOVER_MAXWIDTH), Math.max(HOVER_MINHEIGHT, Math.min(height,HOVER_MAXHEIGHT)));
}
/**
* cached style
*/
private static String style;
static {
/**
* reset cached style on change
*/
PlatformUI.getWorkbench().getThemeManager().getCurrentTheme().addPropertyChangeListener( new IPropertyChangeListener() {
@Override
public void propertyChange( final PropertyChangeEvent arg0 ) {
style=null;
}
} );
PlatformUI.getWorkbench().getThemeManager().addPropertyChangeListener( new IPropertyChangeListener() {
@Override
public void propertyChange( final PropertyChangeEvent arg0 ) {
style=null;
}
} );
}
/**
* get the style for tooltip
* @return the style
*/
public static String getStyle(){
if (style==null){
ITheme t=PlatformUI.getWorkbench().getThemeManager().getCurrentTheme();
Color fgcd=t.getColorRegistry().get( "net.sf.eclipsefp.haskell.themeElementCategory.tooltip.fgColor" );
Color bgcd=t.getColorRegistry().get( "net.sf.eclipsefp.haskell.themeElementCategory.tooltip.bgColor" );
String bg="background-color: "+SWTUtil.colorToHTML( bgcd )+"; ";
String fg="color: "+SWTUtil.colorToHTML( fgcd )+"; ";
Font f=t.getFontRegistry().get( "net.sf.eclipsefp.haskell.themeElementCategory.tooltip.font" );
FontData fd=f.getFontData()[0];
String n=fd.getName();
int h=fd.getHeight();
String fstyle="";
String weight="";
if ((fd.getStyle() & SWT.ITALIC)>0){
fstyle= "font-style:italic; ";
}
if ((fd.getStyle() & SWT.BOLD)>0){
weight="font-weight:bold; ";
}
style=bg+fg+"margin:0; padding:0; font-family: "+n+"; font-size: "+h+"pt;"+fstyle+weight;
}
return style;
}
public void setDocumentation( final String content ) {
hasContents = content.length() > 0;
//hasHR=content.contains("<hr>");
doc.setText( "<html><body style=\""+getStyle()+"\">"+content+"</body></html>" );
}
/*
* @see IInformationControl#setVisible(boolean)
*/
@Override
public void setVisible( final boolean visible ) {
super.setVisible( visible );
doc.setVisible( visible );
}
/*
* @see IInformationControl#computeSizeHint()
*/
/*
@Override
public Point computeSizeHint() {
// see: https://bugs.eclipse.org/bugs/show_bug.cgi?id=117602
int widthHint = SWT.DEFAULT;
Point constraints = getSizeConstraints();
if( constraints != null ) {
widthHint = constraints.x;
}
return getShell().computeSize( widthHint, SWT.DEFAULT, true );
}
For some reason, the method above causes a very large tooltip to be generated, which is
briefly visible before it shrinks to its normal size. Unfortunately, this doesn't happen
on test runs, but only when the plug-in is deployed, making it extremely difficult to debug.
The problem doesn't seem to occur in Eclipse Juno.
For some other reason, everything works fine if we specify the size with computeSizeConstraints.
*/
@Override
public Point computeSizeConstraints(final int widthInChars, final int heightInChars) {
return new Point(widthInChars*4,heightInChars*12); // final width/height is set by ProgressListener when page is loaded
}
/* (non-Javadoc)
* @see org.eclipse.jface.text.AbstractInformationControl#computeSizeHint()
*/
@Override
public Point computeSizeHint() {
return new Point(0,0);
}
/*
* @see org.eclipse.jface.text.AbstractInformationControl#computeTrim()
*/
@Override
public Rectangle computeTrim() {
Rectangle r = doc.computeTrim( 0, 0, 0, 0 );
return Geometry.add( super.computeTrim(), r );
}
/*
* @see IInformationControl#setForegroundColor(Color)
*/
@Override
public void setForegroundColor( final Color foreground ) {
super.setForegroundColor( foreground );
doc.setForeground( foreground );
}
/*
* @see IInformationControl#setBackgroundColor(Color)
*/
@Override
public void setBackgroundColor( final Color background ) {
super.setBackgroundColor( background );
doc.setBackground( background );
}
/*
* @see IInformationControlExtension#hasContents()
*/
@Override
public boolean hasContents() {
return this.hasContents;
}
@Override
public void setFocus() {
// super.setFocus();
doc.setFocus();
}
// Without this, the hover text disappears immediately on a mouse move.
/*
* @see org.eclipse.jface.text.IInformationControlExtension5#getInformationPresenterControlCreator()
* @since 3.4
*/
@Override
public IInformationControlCreator getInformationPresenterControlCreator() {
return new IInformationControlCreator() {
/*
* @see org.eclipse.jface.text.IInformationControlCreator#createInformationControl(org.eclipse.swt.widgets.Shell)
*/
@Override
public IInformationControl createInformationControl(final Shell parent) {
return new HaskellInformationControl(parent);
}
};
}
/* (non-Javadoc)
* @see org.eclipse.jface.text.AbstractInformationControl#setLocation(org.eclipse.swt.graphics.Point)
*/
@Override
public void setLocation( final Point location ) {
// move control slightly to the right so we can still right click on the icon
super.setLocation( new Point(location.x+20,location.y) );
}
}