Tooltips for GWT tree: adding mouseovers to nodes - gwt

I'm trying to add tooltips for the nodes of a Tree in GWT. As such, I'd like to add a mouseover listener for the nodes of a tree rather than on the tree itself.
The Treelistener interface seems to be what I want but this is now deprecated in lieu of the handler system. I don't quite understand how to get mouseover behaviour on the cell as I only seem to be able to add a MouseOverHandler to the tree itself.
Any help would be appreciated, thank you.

I'm going to stick my neck out a bit here since I haven't actually used a Tree in GWT yet, but I see that the TreeItem class is a subclass of UIObject. Any UIObject can have its setTitle() method called. Under the hood, this method sets the standard HTML title attribute to be whatever string you pass into setTitle().
This should give you the tooltip behavior you seek. As an added bonus, the browser does all of the mouse event handling for you:
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.Tree;
import com.google.gwt.user.client.ui.TreeItem;
public class TreeTest extends Composite {
public TreeTest() {
Tree root = new Tree();
initWidget(root);
TreeItem dogs = new TreeItem("canines");
dogs.addItem("Fido").setTitle("faithful");
dogs.addItem("Lassie").setTitle("starlet");
dogs.addItem("Touser").setTitle("ruthless killer");
root.addItem(dogs);
TreeItem cats = new TreeItem("felines");
cats.addItem("Boots").setTitle("needy");
cats.addItem("Fabio").setTitle("aloof");
cats.addItem("Mandu").setTitle("bob seger");
root.addItem(cats);
}
}
Edit: Now let's imagine that you don't want to use the browser's built-in tool-tip mechanism described above, and that you would like to handle the mouse events yourself.
TreeItem might look, on the surface, as a non-starter. After all, it inherits directly from UIObject and not from Widget. (The key difference that a Widget adds to UIObject is, after all, the ability to handle events. So one would think that we cannot add handlers to the TreeItem!)
While this is strictly true, notice that TreeItem gives us the following constructor:
public TreeItem(Widget widget)
When we make each instance, then, we can pass a real Widget into it (such as a Label, perhaps, or maybe your own class MyWidget extends Composite) and we can add event handlers directly to that:
import com.google.gwt.core.client.GWT;
import com.google.gwt.event.dom.client.MouseOutEvent;
import com.google.gwt.event.dom.client.MouseOutHandler;
import com.google.gwt.event.dom.client.MouseOverEvent;
import com.google.gwt.event.dom.client.MouseOverHandler;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.Tree;
import com.google.gwt.user.client.ui.TreeItem;
public class AnotherTreeTest extends Composite {
public AnotherTreeTest() {
Tree root = new Tree();
initWidget(root);
TreeItem dogs = new TreeItem("canines");
makeItem(dogs,"Fido","faithful");
makeItem(dogs,"Lassie","starlet");
makeItem(dogs,"Touser","ruthless killer");
root.addItem(dogs);
TreeItem cats = new TreeItem("felines");
makeItem(cats,"Boots","needy");
makeItem(cats,"Fabio","aloof");
makeItem(cats,"Mandu","bob seger");
root.addItem(cats);
}
private void makeItem(TreeItem parent, String name, final String tip) {
Label label = new Label(name);
TreeItem animal = new TreeItem(label);
label.addMouseOverHandler(new MouseOverHandler() {
#Override
public void onMouseOver(MouseOverEvent event) {
GWT.log("mouse over " + tip); // do something better here
}
});
label.addMouseOutHandler(new MouseOutHandler() {
#Override
public void onMouseOut(MouseOutEvent event) {
GWT.log("mouse out " + tip); // do something better here
}
});
parent.addItem(animal);
}
}
Note that there may be other ways to accomplish this that are less expensive. If you have an enormous tree, then creating a Widget for each node can get expensive. Then you might want to explore a more sophisticated way of dealing with your mouse events, perhaps by having one handler that checks to see which element it is in.

A TreeItem can contains a Widget object. So add a MouseOverHandler and a MouseOutHandler on a widget (i.e. a Label) and put the widget inside the TreeItem to add :
Label myItemContent = new Label("My content");
myItemContent.addMouseOverHandler(new MouseOverHandler() {
public void onMouseOver(MouseOverEvent event) {
// construct and/or open your tooltip
}
});
myItemContent.addMouseOutHandler(new MouseOutHandler() {
public void onMouseOut(MouseOutEvent event) {
// close your tooltip
}
});
//put your Label inside a TreeItem
TreeItem myItem = new TreeItem(myItemContent);
// let's assume that parentNode is an ItemTree
parentNode.addItem(myItem);
An other solution can be to use GwtQuery. GwtQuery allows to bind event handler to any DOM element :
import static com.google.gwt.query.client.GQuery.$;
...
TreeItem myItem = new TreeItem("My content");
$(myItem.getElement()).hover(new Function() {
//method called on mouse over
public void f(Element e) {
// construct and/or open your tooltip
}
}, new Function() {
//method called on mouse out
public void f(Element e) {
//close your tooltip
}
});
parentNode.addItem(myItem);
Julien

An alternative way that I settled upon is to make use of CSS.
/**
* #return a label with a CSS controlled popup
*/
public static Widget labelWithHTMLPopup(String text, String description)
{
FlowPanel p = new FlowPanel();
p.addStyleName("tooltipLabel");
p.add(new Label(text));
HTML contents = new HTML(description);
contents.setStyleName("tooltip");
p.add(contents);
return p;
}
With accompanying css:
/*************** Tooltip **************/
div.tooltip
{
display: none;
position: absolute;
border: 1px outset black;
left: 90%;
top: -20px;
background: white;
padding: 5px;
box-shadow: 3px 3px 2px 0px #555;
overflow-y: auto;
max-height: 150px;
font-size: 80%;
z-index: 99;
}
div.tooltipLabel
{
position: relative;
}
div.tooltipLabel:hover div.tooltip
{
display: block;
position: absolute;
}
Of course, you can change the style, add fades etc as you like.
Less javas/javascript and more css.

Related

Can't get GWT Popup panel glass effect to work

I have extended PopupPanel and am trying to get the glass/see through effect to work
Here is my class
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.ui.*;
public class MyPopup extends PopupPanel
{
private static Label label = new Label();
public MyPopup()
{
super(true);
setWidget(label);
}
public static void showToast(String message, int time)
{
final MyPopup popup = new MyPopup();
label.getElement().setId("mypopupID");
popup.setGlassEnabled(true);
label.setText(message);
popup.show();
Timer timer = new Timer()
{
#Override
public void run()
{
popup.hide();
}
};
timer.schedule(time);
}
}
I just call showToast and this works as expected but looks like a plain old panel with what ever colour back ground I have declared in my CSS.
Here is my CSS
#mypopupID
{
background-color: yellow;
position:absolute;
left:130px;
top:50px;
width: 300px;
}
Any ideas how to get this to work? The dream is to get a fade in/out animation working but little steps first.
The "glass" effect of setGlassEnabled(true) does not apply to the popup itself, but to whathever is behind it. If you want the popup itself to be transparent, maybe you can try playing with the "opacity" css property.

How to hide scrollbars in the JavaFX WebView

I'm trying to remove the scrollbars in a javafx webview.
Search on forums, the suggestion is to make them invisible as follows:
browser.getChildrenUnmodifiable().addListener(new ListChangeListener<Node>() {
#Override public void onChanged(Change<? extends Node> change) {
Set<Node> deadSeaScrolls = browser.lookupAll(".scroll-bar");
for (Node scroll : deadSeaScrolls) {
scroll.setVisible(false);
}
}
})
However, I receive the following error:
"trait ListChangeListener is abstract; cannot be instantiated"
I can understand why its failing, but then again, why are people using this code with success? I'm using Eclipse and the code is surrounded in Scala code.
Thanks!
S
I wrote the scroll bar hiding code you refer to and posted it to a forum.
I tried it again using WinXPsp3, JavaFX 2.2b13, JDK7u6b14ea and it still works for me.
I have never tried accessing the code from Scala, so you may have run into some Java<->Scala interoperability issue. Java does not have traits, so the error you receive would appear Scala related. I added a Scala tag to your question, so maybe somebody with Scala expertise could help.
Here is a short, compilable test application I used to recheck the functionality.
import java.util.Set;
import javafx.application.Application;
import javafx.collections.ListChangeListener;
import javafx.collections.ListChangeListener.Change;
import javafx.scene.*;
import javafx.scene.web.WebView;
import javafx.stage.Stage;
// demos showing a webview which does not visibly display scrollbars.
public class NoScrollWebView extends Application {
public static void main(String[] args) { launch(args); }
#Override public void start(Stage primaryStage) {
// show a doc in webview.
final WebView webView = new WebView();
webView.getEngine().load("http://docs.oracle.com/javafx/2/get_started/jfxpub-get_started.htm");
primaryStage.setScene(new Scene(webView));
primaryStage.show();
// hide webview scrollbars whenever they appear.
webView.getChildrenUnmodifiable().addListener(new ListChangeListener<Node>() {
#Override public void onChanged(Change<? extends Node> change) {
Set<Node> deadSeaScrolls = webView.lookupAll(".scroll-bar");
for (Node scroll : deadSeaScrolls) {
scroll.setVisible(false);
}
}
});
}
}
The best solution here, would probably be to provide a new WebView control skin which does not not have any controls in it - but that would likely be difficult until the WebView control is open sourced.
The simplest way to do this is to provide a NoOp Skin for .scroll-bar components in your scene CSS file.
So, in the CSS enter something like:
.scroll-bar {
-fx-skin: "org.acme.visual.NoOpScrollbarSkin";
}
and subclass SkinBase class:
public class NoOpScrollbarSkin extends SkinBase<ScrollBar,ScrollBarBehavior> {
public NoOpScrollbarSkin(ScrollBar scrollBar, ScrollBarBehavior scrollBarBehavior) {
super(scrollBar, scrollBarBehavior);
}
public NoOpScrollbarSkin(ScrollBar scrollBar) {
super(scrollBar, new ScrollBarBehavior(scrollBar));
}
}
EDIT: this example doesn't appear to work for WebView because of a cast to com.sun.javafx.scene.control.skin.ScrollBarSkin. Solution is not better than previous checking for node changes, one has to subclass ScrollBarSkin to override behavior.
When setting up your WebView add this code:
engine.getLoadWorker().stateProperty().addListener(new ChangeListener<State>()
{
public void changed(ObservableValue<? extends State> o, State old, final State state)
{
if (state == State.RUNNING || state == State.SUCCEEDED)
{
// System.out.println("Page: " + state + ": " + engine.getLocation());
engine.executeScript("document.body.style.overflow = 'hidden';");
}
}
});
This code will remove the scrollbars of any web-page that is loaded in the WebView.
Unfortunately while the page is loading there might be a scrollbar for a short moment (if the webpage specified one in its markup).
I was able to remove the scrollbars by adding the following
css block to my global html stylesheet
body {
overflow-x: hidden;
overflow-y: hidden;
}
In JavaFx put newCascadeStyleSheet.css in resource folder :
body {
overflow-x: hidden;
overflow-y: hidden;
}
And then in Webview :
webView.getEngine().setUserStyleSheetLocation(getClass().getResource("/newCascadeStyleSheet.css").toExternalForm());
Thats All.

How to auto scroll GWT SuggestBox with max-height and overflow-y: scroll?

How can I auto scroll the GWT SuggestBox with max-height set on the PopupPanel holding the SuggestBox? Currently when the user presses keyboard up keys and down keys styles changes on the suggested items and pressing enter will select the currently selected item on the list.
When the item is located in lower than the max-height scroll bars doesn't scroll.
I tried extending the SuggestBox and inner class DefaultSuggestionDisplay to override moveSelectionDown() and moveSelectionUp() to explicitly call popup.setScrollTop().
In order to do this I need access to the absolute top of the currently selected MenuItem therefore need access to SuggestionMenu which is also an inner class of SuggestBox which is private and declared as a private member within DefaultSuggestionDisplay without getter. Since GWT is a JavaScript we can't use reflection to access it.... Does anyone have a workaround for this issue?
Thanks.
I've been searching around and couldn't find a proper solution (apart from reimplementing SuggestBox). The following avoids reimplementing SuggestBox:
private static class ScrollableDefaultSuggestionDisplay extends SuggestBox.DefaultSuggestionDisplay {
private Widget suggestionMenu;
#Override
protected Widget decorateSuggestionList(Widget suggestionList) {
suggestionMenu = suggestionList;
return suggestionList;
}
#Override
protected void moveSelectionDown() {
super.moveSelectionDown();
scrollSelectedItemIntoView();
}
#Override
protected void moveSelectionUp() {
super.moveSelectionUp();
scrollSelectedItemIntoView();
}
private void scrollSelectedItemIntoView() {
// DIV TABLE TBODY TR's
NodeList<Node> trList = suggestionMenu.getElement().getChild(1).getChild(0).getChildNodes();
for (int trIndex = 0; trIndex < trList.getLength(); ++trIndex) {
Element trElement = (Element)trList.getItem(trIndex);
if (((Element)trElement.getChild(0)).getClassName().contains("selected")) {
trElement.scrollIntoView();
break;
}
}
}
}
Following this discussion on Google groups, I implemented a similar solution which is a bit more concise due to the use of JSNI:
private class ScrollableDefaultSuggestionDisplay extends DefaultSuggestionDisplay {
#Override
protected void moveSelectionDown() {
super.moveSelectionDown();
scrollSelectedItemIntoView();
}
#Override
protected void moveSelectionUp() {
super.moveSelectionUp();
scrollSelectedItemIntoView();
}
private void scrollSelectedItemIntoView() {
getSelectedMenuItem().getElement().scrollIntoView();
}
private native MenuItem getSelectedMenuItem() /*-{
var menu = this.#com.google.gwt.user.client.ui.SuggestBox.DefaultSuggestionDisplay::suggestionMenu;
return menu.#com.google.gwt.user.client.ui.MenuBar::selectedItem;
}-*/;
}
Ok, I finally found the solution. I had to create my own suggest box based on GWT SuggestBox implementations. But follow below in custom implementaion:
-Place ScrollPanel to PopupPanel then place MenuBar to ScrollPanel
-In moveSelectionUp() and moveSelectionDown() of your new internal SuggestionDisplat implementation add the code below:
panel.ensureVisible( menu.getSelectedItem( ) );
This is not achievable by extending the SuggestBox since we won't have access to selected
MenuItem unless overriding protected getSelectionItem() method as public method.
Finally add CSS:
max-height: 250px;
To the popupPanel in your display implementations.

GWT Image button

I am creating a Image button from PNG icons, I need to make a feel of button (but not push button effect) just a when user clicks the image a dotted square (like a selection) will appear so to have the effect that the button is clicked. Any ideas?
Something like this in GWT:
Hyperlink link = new Hyperlink();
Image image = new Image(imageUrl);
...
link.getElement().appendChild(image.getElement());
link.setStyleName("imgBtnLink");
Add/set CSS style:
.imgBtnLink:active {
border-style:dashed;
}
Old JSFiddle example: http://jsfiddle.net/Wsaf5/
ToggleButton with CSS-styling is suitable.
ToggleButton button = new ToggleButton();
button.setStyleName("toggle-button");
...
CSS:
.toggle-button {
outline: none;
border: none;
margin: 3px; /* border size */
background: transparent url(../img/toggle-button.jpg);
}
.toggle-button-down, .toogle-button-down-hovering {
margin: 0;
border: 3px #469 dashed; /* margin size */
}
You can create your own Widget, i have this one
public class ImageButton extends Button {
public ImageButton() {
super();
}
public void setImgSrc(String imgSrc) {
Image img = new Image(imgSrc);
String definedStyles = img.getElement().getAttribute("style");
img.getElement().setAttribute("style",definedStyles);
img.getElement().getStyle().setVerticalAlign(VerticalAlign.MIDDLE);
DOM.insertBefore(getElement(), img.getElement(), DOM.getFirstChild(getElement()));
}
#Override
public void setText(String text) {
Element span = DOM.createElement("span");
span.setInnerText(text);
span.getStyle().setPaddingLeft(5, Unit.PX);
span.getStyle().setPaddingRight(3, Unit.PX);
span.getStyle().setVerticalAlign(VerticalAlign.MIDDLE);
span.getStyle().setColor("black");
span.setAttribute("class", "arial12R6D6D6D");
DOM.insertChild(getElement(), span, 0);
}
}
Could you add a stylesheet to the element in a ClickEvent handler? (addStyleDependentName).
Or, maybe PushButton could be useful?
I created a simple custom widget class that uses CSS and Events to display custom buttons:
Activity:
view.getContent().add(new ImageButton(resource.comment(), resource.search(), resource.chechCircle(), new ClickCallback() {
public void onClick() {
Window.alert("Clicked!");
}
}));
I use a UiBinder/Composite to control the layout and css and events. The "ClickCallback" is just a custom interface with one onClick method
the UiBinder files are here:
http://www.acrinta.com/ImageButton.zip
The Css shows a border on hover and the MouseOverMouseOut can swap images same with onClick where it changes the image for a split second. may give some ideas

GWT Close button in title bar of DialogBox

Is there a non JSNI way to add a close button to the title bar area of a DialogBox?
We used GWT-ext from the begining in our project. It was a bad idea. They have lots of cool widgets, but they are not GWT widgets AND they have no compatibility with GWT widgets. Once you choose GWT-Ext, everything, even the event mechanism, must be in the GWT-Ext way, not in the GWT way. This library will not be updated for the newest version of GWT, because the javascript library Ext is no more free. We are removing GWT-Ext from our project now.
It´s not possible to add a different widget int the GWT DialogBox caption, but you can extend "DecoratedPanel" (it is the DialogBox parent). Look at the DialogBox source to learn the techniques, specially how it adds the Caption object to the panel and how the window drag is implemented.
That´s what we have done here, and it works very well. We´ve made our own Caption class that extends FocusablePanel (a SimplePanel that captures all mouse events) and we added a HorizontalPanel to it, with buttons and text. We had to override onAttach() and onDetach() just by calling the super method (they are protected).
I believe I am not allowed to put our source code in here, so I just can give you these tips.
You can do it by adding a button to the center panel of the DialogBox:
Image closeButton = new Image("");
closeButton.addClickHandler(new ClickHandler() {
public void onClick(ClickEvent event) {
registerBox.hide();
}
});
closeButton.setStyleName("TopRight");
Then position it with CSS:
.TopRight {
float:right;
margin-top:-22px;
width:16px;
height:16px;
display:block;
background-image: url(images/cancel_16.png);
}
I created this caption class:
public class DialogBoxCaptionWithCancel extends Composite
implements Caption, HasClickHandlers {
#UiField
HTMLPanel mainPanel;
#UiField
HTML captionLabel;
#UiField
PushButton cancelButton;
private HandlerManager handlerManager = null;
private static final Binder binder = GWT.create(Binder.class);
interface Binder extends UiBinder<Widget, DialogBoxCaptionWithCancel> {
}
public DialogBoxCaptionWithCancel() {
initWidget(binder.createAndBindUi(this));
mainPanel.setStyleName("Caption");
Image upImage = new Image("images/closeWindow.png");
Image hoverImage = new Image("images/closeWindowFocus.png");
cancelButton.getUpFace().setImage(upImage);
cancelButton.getUpHoveringFace().setImage(hoverImage);
cancelButton.setStylePrimaryName("none");
}
/*
* (non-Javadoc)
*
* #see com.google.gwt.user.client.ui.Widget#onLoad()
*/
#Override
protected void onLoad() {
super.onLoad();
handlerManager = new HandlerManager(this);
}
#UiHandler("cancelButton")
public void cancelButtonOnClick(ClickEvent event) {
handlerManager.fireEvent(event);
}
#Override
public HandlerRegistration addMouseDownHandler(MouseDownHandler handler) {
return handlerManager.addHandler(MouseDownEvent.getType(), handler);
}
#Override
public HandlerRegistration addMouseUpHandler(MouseUpHandler handler) {
return handlerManager.addHandler(MouseUpEvent.getType(), handler);
}
#Override
public HandlerRegistration addMouseOutHandler(MouseOutHandler handler) {
return handlerManager.addHandler(MouseOutEvent.getType(), handler);
}
#Override
public HandlerRegistration addMouseOverHandler(MouseOverHandler handler) {
return handlerManager.addHandler(MouseOverEvent.getType(), handler);
}
#Override
public HandlerRegistration addMouseMoveHandler(MouseMoveHandler handler) {
return handlerManager.addHandler(MouseMoveEvent.getType(), handler);
}
#Override
public HandlerRegistration addMouseWheelHandler(MouseWheelHandler handler) {
return handlerManager.addHandler(MouseWheelEvent.getType(), handler);
}
#Override
public String getHTML() {
return "";
}
#Override
public void setHTML(String html) {
}
#Override
public String getText() {
return this.captionLabel.getText();
}
#Override
public void setText(String text) {
this.captionLabel.setText(text);
}
#Override
public void setHTML(SafeHtml html) {
}
#Override
public HandlerRegistration addClickHandler(ClickHandler handler) {
return handlerManager.addHandler(ClickEvent.getType(), handler);
}
}
The images are just captured from the behavior of IE8 when you mouse over the cancel button.
Here is the UiBinder code:
<!DOCTYPE ui:UiBinder SYSTEM "http://dl.google.com/gwt/DTD/xhtml.ent">
<ui:UiBinder
xmlns:ui='urn:ui:com.google.gwt.uibinder'
xmlns:g='urn:import:com.google.gwt.user.client.ui'>
<ui:style>
.htmlField {
width: 100%;
}
.pushButton {
border: none;
padding: 0px;
width: 49px;
height: 21px;
}
</ui:style>
<g:HTMLPanel ui:field="mainPanel">
<table border="0" cellpadding="0" cellspacing="0" width="100%">
<tr>
<td width="100%">
<g:HTML ui:field="captionLabel" addStyleNames="{style.htmlField}"></g:HTML>
</td>
<td>
<g:PushButton ui:field="cancelButton" addStyleNames="{style.pushButton}"></g:PushButton>
</td>
</tr>
</table>
</g:HTMLPanel>
</ui:UiBinder>
Then my class that extends DialogBox has the following:
public class MyDialogBox extends DialogBox implements ClickHandler {
...
// instantiate the caption with the cancel button
private static DialogBoxCaptionWithCancel caption = new DialogBoxCaptionWithCancel();
...
public MyDialogBox() {
// construct the dialog box with the custom caption
super(false, false, caption);
setWidget(binder.createAndBindUi(this));
// set the caption's text
caption.setText("My Caption");
}
....
protected void onLoad() {
super.onLoad();
// let us react to the captions cancel button
caption.addClickHandler(this);
}
...
#Override
public void onClick(ClickEvent event) {
// the caption's cancel button was clicked
this.hide();
}
A more simplier solution is to use gwt-ext (http://code.google.com/p/gwt-ext/). It is free and easy to use and integrate.
You can see their showcase http://www.gwt-ext.com/demo/.
I think that what you want is the MessageBox or Layout Window (they are on the Windows category of the showcase).
Regards.
You can try this out, slightly improved solution by fungus1487:
import com.google.gwt.dom.client.EventTarget;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.i18n.client.HasDirection;
import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.ui.*;
/**
* #author Andrey Talnikov
*/
public class ClosablePopup extends DialogBox {
private Anchor closeAnchor;
/**
* Instantiates new closable popup.
*
* #param title the title
* #param defaultClose it {#code true}, hide popup on 'x' click
*/
public ClosablePopup(String title, boolean defaultClose) {
super(true);
closeAnchor = new Anchor("x");
FlexTable captionLayoutTable = new FlexTable();
captionLayoutTable.setWidth("100%");
captionLayoutTable.setText(0, 0, title);
captionLayoutTable.setWidget(0, 1, closeAnchor);
captionLayoutTable.getCellFormatter().setHorizontalAlignment(0, 1,
HasHorizontalAlignment.HorizontalAlignmentConstant.endOf(HasDirection.Direction.LTR));
HTML caption = (HTML) getCaption();
caption.getElement().appendChild(captionLayoutTable.getElement());
caption.addClickHandler(new ClickHandler() {
#Override
public void onClick(ClickEvent event) {
EventTarget target = event.getNativeEvent().getEventTarget();
Element targetElement = (Element) target.cast();
if (targetElement == closeAnchor.getElement()) {
closeAnchor.fireEvent(event);
}
}
});
if (defaultClose) {
addCloseHandler(new ClickHandler() {
#Override
public void onClick(ClickEvent event) {
hide();
}
});
}
}
public void addCloseHandler(ClickHandler handler) {
closeAnchor.addClickHandler(handler);
}
}
Yes there is
No there isn't - at least not without fiddling with GWT's DialogBox class itself or by recreating the DialogBox using common widgets. This is a known issue in GWT, aka issue 1405 (Star it to show your interest).
However; DialogBox doesn't give us the tools to do this so we need to extend it - Edit: this doesn't work.
If you want to make a drop-in replacement for DialogBox you can name your class DialogBox and import it instead of the one that's included in GWT. This thread on the GWT forum gives better details on how this can be done (outdated, uses listeners) Outdated, the internals of DialogBox have been changed a lot since this thread - it doesn't work.
Here's some code I hacked to get the same results (used the linked thread for guidance). This doesn't work:
MyDialogBox:
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.dom.client.MouseOutEvent;
import com.google.gwt.event.dom.client.MouseOutHandler;
import com.google.gwt.event.dom.client.MouseOverEvent;
import com.google.gwt.event.dom.client.MouseOverHandler;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.DialogBox;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.Widget;
public class MyDialogBox extends DialogBox {
private class crossHandler implements ClickHandler, MouseOverHandler, MouseOutHandler
{
#Override
public void onClick(ClickEvent event) {
hide();
Window.alert("Click!");
}
#Override
public void onMouseOver(MouseOverEvent event) {
DOM.setStyleAttribute(cross.getElement(), "font-weight", "bold");
}
#Override
public void onMouseOut(MouseOutEvent event) {
DOM.setStyleAttribute(cross.getElement(), "font-weight", "normal");
}
}
Label cross = new Label("X"); // The close button
crossHandler crosshandler = new crossHandler();
HTML caption = new HTML(); // The caption aka title
HorizontalPanel captionPanel = new HorizontalPanel(); // Contains caption and cross
/**
* Creates an empty dialog box. It should not be shown until its child widget
* has been added using {#link #add(Widget)}.
*/
public MyDialogBox()
{
this(false);
}
/**
* Creates an empty dialog box specifying its "auto-hide" property. It should
* not be shown until its child widget has been added using
* {#link #add(Widget)}.
*
* #param autoHide <code>true</code> if the dialog should be automatically
* hidden when the user clicks outside of it
*/
public MyDialogBox(boolean autoHide) {
this(autoHide, true);
}
/**
* Creates an empty dialog box specifying its "auto-hide" property. It should
* not be shown until its child widget has been added using
* {#link #add(Widget)}.
*
* #param autoHide <code>true</code> if the dialog should be automatically
* hidden when the user clicks outside of it
* #param modal <code>true</code> if keyboard and mouse events for widgets not
* contained by the dialog should be ignored
*/
public MyDialogBox(boolean autoHide, boolean modal)
{
super(autoHide, modal);
cross.addClickHandler(crosshandler);
cross.addMouseOutHandler(crosshandler);
cross.addMouseOverHandler(crosshandler);
captionPanel.add(caption);
captionPanel.add(cross);
captionPanel.setStyleName("caption");
Element td = getCellElement(0, 1); // Get the cell element that holds the caption
td.setInnerHTML(""); // Remove the old caption
td.appendChild(captionPanel.getElement());
}
#Override
public void setText(String text)
{
caption.setText(text);
}
public String getText()
{
return caption.getText();
}
public void setHtml(String html)
{
caption.setHTML(html);
}
public String getHtml()
{
return caption.getHTML();
}
Note: This code doesn't work. The ClickEvent isn't sent from cross but instead from MyDialogBox regardless of whether you add ClickHandlers to the cross or not, IOW the MyDialogBox is the sender/source and therefor not possible to check against cross. When cross is clicked it doesn't fire the ClickEvent for some reasons.
Edit:
It appears this cannot be done without hacks unless you either write your own DialogBox (almost) from scratch or fix issue 1405. Of course there are number of existing libraries that have already solved this problem, i.e. SmartGWT and GWT-Ext, but their implementation is made mostly from scratch.
So to answer your question in one sentence: Yes there is a way, but you're not gonna like it :)
I guess a simple answer to this is to instantiate a widget to replace the standard Caption widget from DialogBox.
I created a caption that has a button at right and you can pick a reference to it.
Then you can add any click event you desire.
In GWT 2.4 I used the following solution:
import com.google.gwt.event.dom.client.MouseDownHandler;
import com.google.gwt.event.dom.client.MouseMoveHandler;
import com.google.gwt.event.dom.client.MouseOutHandler;
import com.google.gwt.event.dom.client.MouseOverHandler;
import com.google.gwt.event.dom.client.MouseUpHandler;
import com.google.gwt.event.dom.client.MouseWheelHandler;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.safehtml.shared.SafeHtml;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.InlineLabel;
import com.google.gwt.user.client.ui.PushButton;
import com.google.gwt.user.client.ui.DialogBox.Caption;
/**
* #author Cristiano Sumariva
*/
public class ButtonCaption extends HorizontalPanel implements Caption
{
protected InlineLabel text;
protected PushButton closeDialog;
/**
* #return the button at caption
*/
public PushButton getCloseButton()
{
return closeDialog;
}
public ButtonCaption( String label )
{
super();
setWidth( "100%" );
setStyleName( "Caption" ); // so you have same styling as standard caption widget
closeDialog = new PushButton();
add( text = new InlineLabel( label ) );
add( closeDialog );
setCellWidth( closeDialog, "1px" ); // to make button cell minimal enough to it
}
/* (non-Javadoc)
* #see com.google.gwt.event.dom.client.HasMouseDownHandlers#addMouseDownHandler(com.google.gwt.event.dom.client.MouseDownHandler)
*/
#Override
public HandlerRegistration addMouseDownHandler( MouseDownHandler handler )
{
return addMouseDownHandler( handler );
}
/* (non-Javadoc)
* #see com.google.gwt.event.dom.client.HasMouseUpHandlers#addMouseUpHandler(com.google.gwt.event.dom.client.MouseUpHandler)
*/
#Override
public HandlerRegistration addMouseUpHandler( MouseUpHandler handler )
{
return addMouseUpHandler( handler );
}
/* (non-Javadoc)
* #see com.google.gwt.event.dom.client.HasMouseOutHandlers#addMouseOutHandler(com.google.gwt.event.dom.client.MouseOutHandler)
*/
#Override
public HandlerRegistration addMouseOutHandler( MouseOutHandler handler )
{
return addMouseOutHandler( handler );
}
/* (non-Javadoc)
* #see com.google.gwt.event.dom.client.HasMouseOverHandlers#addMouseOverHandler(com.google.gwt.event.dom.client.MouseOverHandler)
*/
#Override
public HandlerRegistration addMouseOverHandler( MouseOverHandler handler )
{
return addMouseOverHandler( handler );
}
/* (non-Javadoc)
* #see com.google.gwt.event.dom.client.HasMouseMoveHandlers#addMouseMoveHandler(com.google.gwt.event.dom.client.MouseMoveHandler)
*/
#Override
public HandlerRegistration addMouseMoveHandler( MouseMoveHandler handler )
{
return addMouseMoveHandler( handler );
}
/* (non-Javadoc)
* #see com.google.gwt.event.dom.client.HasMouseWheelHandlers#addMouseWheelHandler(com.google.gwt.event.dom.client.MouseWheelHandler)
*/
#Override
public HandlerRegistration addMouseWheelHandler( MouseWheelHandler handler )
{
return addMouseWheelHandler( handler );
}
/* (non-Javadoc)
* #see com.google.gwt.user.client.ui.HasHTML#getHTML()
*/
#Override
public String getHTML()
{
return getElement().getInnerHTML();
}
/* (non-Javadoc)
* #see com.google.gwt.user.client.ui.HasHTML#setHTML(java.lang.String)
*/
#Override
public void setHTML( String html )
{
remove( text );
insert( text, 1 );
}
/* (non-Javadoc)
* #see com.google.gwt.user.client.ui.HasText#getText()
*/
#Override
public String getText()
{
return text.getText();
}
/* (non-Javadoc)
* #see com.google.gwt.user.client.ui.HasText#setText(java.lang.String)
*/
#Override
public void setText( String text )
{
this.text.setText( text );
}
/* (non-Javadoc)
* #see com.google.gwt.safehtml.client.HasSafeHtml#setHTML(com.google.gwt.safehtml.shared.SafeHtml)
*/
#Override
public void setHTML( SafeHtml html )
{
setHTML( html.asString() );
}
}
Extends the DialogBox to use the new ButtonCaption available
class CaptionCloseableDialogBox extends DialogBox
{
public CaptionCloseableDialogBox()
{
super( new ButtonCaption( "dialog box title" ) );
setAutoHideEnabled( false );
ButtonCaption ref = (ButtonCaption) this.getCaption();
PushButton closeButton = ref.getCloseButton();
// apply button face here closeButton;
closeButton.addClickHandler( /* attach any click handler here like close this dialog */ );
}
}
Hope it helps any.
Check out the active project:
http://code.google.com/p/gwt-mosaic/
Their noble goal is, as mentioned on their page:
The goal is to provide a complete widget set by keeping the API as close as possible to the GWT's standard widgets API.
Have been trapped in the GXT vortex. Not at all a fan of how they require users to use entirely different API for listeners, etc. On their part this makes sense. After all, GXT is just a port of their existing javascript libraries. But I've been looking for this MOSAIC project for too long...
Just using GWT and no external libraries you can intercept the click events on the caption element and perform a hit test to see if the x,y mouse coord is within the bounds of the anchor element (or other element your using as a ClickHandler).
// Create anchor we want to accept click events
final Anchor myAnchor = new Anchor("My Anchor");
// Add handler to anchor
myAnchor.addClickHandler(new ClickHandler() {
#Override
public void onClick(ClickEvent event) {
Window.alert("Anchor was clicked");
}
});
// Create dialog
final DialogBox myDialog = new DialogBox();
myDialog.setText("My Dialog");
// Get caption element
final HTML caption = ((HTML)myDialog.getCaption());
// Add anchor to caption
caption.getElement().appendChild(myAnchor.getElement());
// Add click handler to caption
caption.addClickHandler(new ClickHandler() {
#Override
public void onClick(ClickEvent event) {
// Get x,y caption click relative to the anchor
final int x = event.getRelativeX(myAnchor.getElement());
final int y = event.getRelativeY(myAnchor.getElement());
// Check click was within bounds of anchor
if(x >= 0 && y >= 0 &&
x <= myAnchor.getOffsetWidth() &&
y <= myAnchor.getOffsetHeight()) {
// Raise event on anchor
myAnchor.fireEvent(event);
}
}
});
// Show the dialog
myDialog.show();
I realize this is ridiculously old, but you can just use absolute positioning with top and right of 0 to get a widget in the upper right. The dialog box is itself absolutely positioned, so the positioning of your widget will be against it.
This works if you just wan't a simple solution for the question asked:
Image button = new Image("images/cancel.png");
button.addClickHandler(new ClickHandler(){
public void onClick(ClickEvent event) {
hide();
}
});
button.setStyleName("dialog-close");
HorizontalPanel header = new HorizontalPanel();
header.add(new HTML("Example Tool"));
header.add(button);
setHTML(header.getElement().getInnerHTML());
You can find the closeable dialogbox in google code under the project synthfuljava.
It is actually called scrollable dialog box with a close X button at the caption.
The following blog explains the impediments that had to be overcome in order for thecaption X button to be able to listen to the click event to let it work:
http://h2g2java.blessedgeek.com/2009/07/gwt-useable-closeable-scrollable.html
I think the ButtonCaption of cavila is the best solution, but there is a bug in the implementation of the caption. The call of one of the overidden methods causes a infinitive loop because the method calls itself recursively.
To prevent this you you can call the method on the InlineLabel text instead:
#Override
public HandlerRegistration addMouseDownHandler( MouseDownHandler handler ) {
return text.addMouseDownHandler( handler );
}
The GWT dialog box's top level DIV has absolute positioning, so you can do the same with your close button. This allows you to put it in the body of the dialog as far as the DOM is concerned, but make it physically appear in the caption.
In my example below, I place it in the exact upper right of the dialog, and center it on the caption using padding.
<ui:style>
.close {
position: absolute;
top: 0;
right: 0;
padding: 3px 3px 1px 3px !important;
border-radius: 4px;
margin: 5px;
}
</ui:style>
<g:PushButton ui:field="closeButton" addStyleNames="{style.close}">
<g:upFace image='{closeIcon}'/>
<g:downFace image='{closeIcon}'/>
<g:upHoveringFace image='{closeIcon}'/>
<g:downHoveringFace image='{closeIcon}'/>
<g:upDisabledFace image='{closeIcon}'/>
<g:downDisabledFace image='{closeIcon}'/>
</g:PushButton>