{"id":310,"date":"2009-05-12T17:10:09","date_gmt":"2009-05-12T21:10:09","guid":{"rendered":"http:\/\/www.outofwhatbox.com\/blog\/?p=310"},"modified":"2009-07-25T12:37:16","modified_gmt":"2009-07-25T16:37:16","slug":"eclipse-rich-hovers-redux","status":"publish","type":"post","link":"https:\/\/www.outofwhatbox.com\/blog\/2009\/05\/eclipse-rich-hovers-redux\/","title":{"rendered":"Eclipse: Rich Hovers Redux"},"content":{"rendered":"<p>In <a href=\"http:\/\/www.outofwhatbox.com\/blog\/2009\/04\/using-the-new-rich-text-hovers-in-eclipse-34\/\">Using the new Rich Text Hovers in Eclipse 3.4<\/a>, I, uh, suggested that perhaps, um, <em>maybe<\/em>, an implementation of <a href=\"http:\/\/help.eclipse.org\/stable\/topic\/org.eclipse.platform.doc.isv\/reference\/api\/org\/eclipse\/jface\/text\/ITextHoverExtension2.html#getHoverInfo2(org.eclipse.jface.text.ITextViewer,%20org.eclipse.jface.text.IRegion)\"><code>ITextHoverExtension2#getHoverInfo2<\/code><\/a> (from the package <code>org.eclipse.jface.text<\/code>) could return either a String or &#8220;An implementation of <code>IInformationControlExtension2<\/code>&#8220;.<\/p>\n<p>That was a m- mi- mis- <a href=\"http:\/\/www.outofwhatbox.com\/blog\/2009\/04\/how_to_make_mistakes\/\"><em>mistake<\/em><\/a>. What I <a href=\"http:\/\/help.eclipse.org\/stable\/nftopic\/org.eclipse.platform.doc.isv\/reference\/api\/org\/eclipse\/jface\/text\/IInformationControlExtension.html\">meant to say<\/a> was:<\/p>\n<blockquote><p>\n [It] is the responsibility of the implementer of <a href=\"http:\/\/help.eclipse.org\/stable\/nftopic\/org.eclipse.platform.doc.isv\/reference\/api\/org\/eclipse\/jface\/text\/IInformationControl.html\"><code>IInformationControl<\/code><\/a> and <a href=\"http:\/\/help.eclipse.org\/stable\/nftopic\/org.eclipse.platform.doc.isv\/reference\/api\/org\/eclipse\/jface\/text\/IInformationControlExtension2.html\"><code>IInformationControlExtension2<\/code><\/a> to specify the concrete nature of the information control&#8217;s input&#8230;<\/p><\/blockquote>\n<p>In plain(er) English: If you write the hover control, you get to specify the hover information. The implementation of <code>getHoverInfo2<\/code> must be aware of the hover control&#8217;s implementation, and must conform to its specification.<\/p>\n<p>So as a plug-in author, how can you create hovers with rich display formats?  I&#8217;ve found two options. The first of these doesn&#8217;t even require any of the extensions that I&#8217;ve already touched upon.<\/p>\n<h2>Using the <code>DefaultHoverControl<\/code><\/h2>\n<p>Straight out of the box (as it were), the JavaEditor example plug-in is configured to use the default version of the <code>org.eclipse.jface.text.DefaultHoverControl<\/code> class. In this configuration, the <code>DefaultHoverControl<\/code> treats its text input as raw text (without markup), as shown in the screen shot below.<\/p>\n<p><em>(Note: The example Java editor plug-in simply copies the current text selection into the hover. In these screen shots, the selection is a Java comment that contains HTML markup.)<\/em><\/p>\n<p><img src=\"http:\/\/www.outofwhatbox.com\/images\/RichTextRedux\/StandardPoorHover.jpg\" alt=\"Screen shot of Java editor plug-in example, showing HTML markup passed through as text.\" \/><\/p>\n<p>Some of the constructors for <code>DefaultHoverControl<\/code> enable the control to process a subset of HTML. To use one of these, we need to add the following method to <code>JavaSourceViewerConfiguration<\/code> in the sample editor; this overrides the base class implementation in <code>org.eclipse.jface.text.source.SourceViewerConfiguration<\/code> :<\/p>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\r\n\/**\r\n * Returns the information control creator. The creator is a \r\n * factory creating information controls for the given source\r\n * viewer. This implementation always returns a creator for\r\n * &lt;code&gt;JavaInformationControl&lt;\/code&gt; instances.\r\n *\r\n * @param sourceViewer the source viewer to be configured\r\n *        by this configuration\r\n * @return the information control creator, or &lt;code&gt;null&lt;\/code&gt; \r\n *         if no information support should be installed\r\n * @since 2.0\r\n *\/\r\npublic IInformationControlCreator \r\ngetInformationControlCreator(ISourceViewer sourceViewer) {\r\n   return new IInformationControlCreator() {\r\n      public IInformationControl createInformationControl(Shell parent) {\r\n          return new JavaHoverInformationControl(parent);\r\n      }\r\n   };\r\n}\r\n<\/pre>\n<p>Unlike the base class implementation, this version calls the two-argument constructor <code>DefaultInformationControl(parent, false)<\/code>. Internally, this constructor will create an <code>HTMLTextPresenter<\/code> object, which supports some simple HTML markup.<\/p>\n<p>The emphasis here is on <em>simple<\/em> HTML. Unfortunately, while <code>HTMLTextPresenter<\/code> happily <strong>reads<\/strong> the HTML markup, it doesn&#8217;t <strong>implement<\/strong> very much of it. As this screenshot illustrates, both <code>&lt;code><\/code> and <code>&lt;em><\/code> markup are ignored. (There&#8217;s no example hyperlink here, but I can confirm that they&#8217;re ignored as well.)<\/p>\n<p><img src=\"http:\/\/www.outofwhatbox.com\/images\/RichTextRedux\/UsingHTMLTextPresenter.jpg\" alt=\"Another Java editor plug-in example, showing partially supported HTML markup.\" \/><\/p>\n<h2>Creating a new hover control<\/h2>\n<p><img src=\"http:\/\/www.outofwhatbox.com\/images\/RichTextRedux\/UFO.jpg\" alt=\"Hovering\" class=\"oowbleft\"\/>If you need to support richer content than <code>DefaultInformationControl<\/code> can handle, then it&#8217;s likely that you&#8217;ll need to create your own hover control. This may require some experimentation, as the definitive guide to writing hover controls hasn&#8217;t been written yet (or if it has been, its visibility flag is set to <code>false<\/code>.)<\/p>\n<p>I&#8217;ve put together a demo of a hover control that uses the SWT Browser widget. The <code>JavaHoverInformationControl<\/code> class implementation requires only about 150 lines of code, plus a few changes in two other files in the JavaEditor demo. It&#8217;s still rough around the edges; you may not be replacing your desktop browser with it anytime soon, but it should help to illustrate how a plug-in author can take control over hover controls.<\/p>\n<p>In this screenshot, you can see that the example comment is now displayed in all of its HTML glory in the hover:<\/p>\n<p><img src=\"http:\/\/www.outofwhatbox.com\/images\/RichTextRedux\/JavaHoverControl.jpg\" alt=\"The JavaHoverInformationControl, showing HTML displayed in a small Browser widget.\" \/><\/p>\n<p>For additional demo value\u00e2\u20ac\u201dand to show how this all ties in with <code>ITextHoverExtension2#getHoverInfo2<\/code>\u00e2\u20ac\u201dthe <code>JavaHoverInformationControl<\/code> also displays something useful when no text is currently selected:<\/p>\n<p><img src=\"http:\/\/www.outofwhatbox.com\/images\/RichTextRedux\/JavaHoverControlNoSelection.jpg\" alt=\"It isn't all that useful, really.\" \/><br \/>\n<em>(Well, it&#8217;s useful to <\/em>me<em>, anyway!)<\/em><\/p>\n<p>A few aspects of the demo are worth describing here. The tie-in to <code>ITextHoverExtension2#getHoverInfo2<\/code> is through an interface nested within the new <code>JavaHoverInformationControl<\/code> class:<\/p>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\r\npublic interface IHTMLHoverInfo {\r\n    \/**\r\n     * @return true if the String returned by getHTMLString()\r\n     *  represents a URL;  false if the String contains marked-up text.\r\n     *\/\r\n    public boolean isURL();\r\n\r\n    \/**\r\n     * @return The input string to be displayed in the Browser widget\r\n     *  (either as marked-up text, or as a URL.)\r\n     *\/\r\n    public String getHTMLString();\r\n}\r\n<\/pre>\n<p>This is reflected in the <code>JavaTextHover<\/code> class, which I&#8217;ve modified to implement<code>ITextHoverExtension2#getHoverInfo2<\/code>. This implementation returns an <code>Object<\/code> that conforms to the <code>IHTMLHoverInfo<\/code> interface:<\/p>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\r\npublic class JavaTextHover \r\n                implements ITextHover, ITextHoverExtension2 {\r\n\/\/... other methods are not shown here...\r\npublic Object getHoverInfo2(ITextViewer textViewer, \r\n                                      IRegion hoverRegion) {\r\n\r\n    \/\/ Start with the string returned by the older getHoverInfo()\r\n    final String selection = getHoverInfo(textViewer, hoverRegion);\r\n\r\n   \/\/ If text is selected in the editor window, it's returned as the\r\n   \/\/ hover string. If no text is selected, then the returned hover is\r\n   \/\/ a URL pointing to www.outofwhatbox.com\/blog.\r\n    return new JavaHoverInformationControl.IHTMLHoverInfo() {\r\n        public boolean isURL() {return selection.length() == 0;}\r\n        public String getHTMLString() {\r\n            if (isURL()){\r\n                return &quot;http:\/\/www.outofwhatbox.com\/blog&quot;;\r\n            }\r\n            return selection;\r\n        }\r\n    };\r\n}\r\n\r\n<\/pre>\n<p>Similarly, the <code>JavaHoverInformationControl#setObject<\/code> method will invoke this object&#8217;s <code>isURL<\/code> method, indicating whether to handle the input as marked-up text or as a URL:<\/p>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\r\n    public void setInput(Object input) {\r\n        \/\/ Assume that the input is marked-up text, not a URL\r\n        fIsURL = false;\r\n        final String inputString;\r\n\r\n        if (input instanceof IHTMLHoverInfo) {\r\n            \/\/ Get the input string, then see whether it's a URL\r\n            IHTMLHoverInfo inputInfo = (IHTMLHoverInfo) input;\r\n            inputString = inputInfo.getHTMLString();\r\n            fIsURL= inputInfo.isURL();\r\n\r\n\/\/... rest of the code not shown here...\r\n<\/pre>\n<p>I&#8217;ve packaged the <code>JavaHoverInformationControl<\/code> implementation with the modified source from the sample <code>org.eclipse.ui.examples.javaeditor<\/code> package. It&#8217;s available for download as a <a href=\"http:\/\/www.outofwhatbox.com\/downloads\/RichTextRedux\/javaeditor.zip\">ZIP file<\/a> file and as a <a href=\"http:\/\/www.outofwhatbox.com\/downloads\/RichTextRedux\/javaeditor.tgz\">gzip&#8217;d tar file<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>In my last post, I misstated how to use the new <code>ITextHoverExtension2#getHoverInfo2<\/code> interface. Herein, I attempt to set things right(er).<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[17,4,9],"tags":[6,48,19,18],"_links":{"self":[{"href":"https:\/\/www.outofwhatbox.com\/blog\/wp-json\/wp\/v2\/posts\/310"}],"collection":[{"href":"https:\/\/www.outofwhatbox.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.outofwhatbox.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.outofwhatbox.com\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/www.outofwhatbox.com\/blog\/wp-json\/wp\/v2\/comments?post=310"}],"version-history":[{"count":50,"href":"https:\/\/www.outofwhatbox.com\/blog\/wp-json\/wp\/v2\/posts\/310\/revisions"}],"predecessor-version":[{"id":530,"href":"https:\/\/www.outofwhatbox.com\/blog\/wp-json\/wp\/v2\/posts\/310\/revisions\/530"}],"wp:attachment":[{"href":"https:\/\/www.outofwhatbox.com\/blog\/wp-json\/wp\/v2\/media?parent=310"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.outofwhatbox.com\/blog\/wp-json\/wp\/v2\/categories?post=310"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.outofwhatbox.com\/blog\/wp-json\/wp\/v2\/tags?post=310"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}