I work on a Desktop-based application, the window handling awaits always Window references to add and remove the windows on a central point. So I have to override all x-tool-close listener and also I have to set new listeners on every button of the dialogs.
The simplest way is to use Dialog instead of MessageBox, because it is a Window subclass and I can remove the tools listener and override onButtonPressed() to generate events on my EventBus. That was the background of my question.
I would like to use both, the icon on the head of the Dialog and the MessageBox icon next to the message.
Yesterday I created a sub-class of MessageBox, which contains:
Code:
public abstract class AbstractMessageBox extends MessageBox {
public void initButtons() {
getDialog().setHideOnButtonClick(false);
ButtonBar bar = getDialog().getButtonBar();
for (Component c : bar.getItems()) {
removeSelectionListener(c);
c.addListener(Events.Select, getCloseButtonListener());
}
if (!isClosable()) {
return;
}
getDialog().addListener(Events.Attach, getAttachListener());
}
}
The removeSelection() method removes all listeners with class names starting with "com.extjs.gxt". These listeners are not defined by my own subclasses and hide the window automatically or do something other things. Then I install my own listener. The listener for the x-tool-close I have to remove later, because at creation time of the Dialog the tools are not initiated. So I use an Events.Attach listener.
Code:
private void removeSelectionListener(Component c) {
List<Listener<? extends BaseEvent>> listenerList = new ArrayList<Listener<? extends BaseEvent>>(c.getListeners(Events.Select));
for (Listener<? extends BaseEvent> listener : listenerList) {
if (listener.getClass().getName().startsWith("com.extjs.gxt")) {
c.removeListener(Events.Select, listener);
}
}
}
private Listener<ComponentEvent> getCloseButtonListener() {
return new Listener<ComponentEvent>() {
@Override
public void handleEvent(ComponentEvent ce) {
beforeHide();
}
};
}
public abstract void beforeHide();
private Listener<BaseEvent> getAttachListener() {
return new Listener<BaseEvent>() {
@Override
public void handleEvent(BaseEvent event) {
for (Component tool : getDialog().getHeader().getTools()) {
if (tool.el().hasStyleName("x-tool-close")) {
removeSelectionListener(tool);
tool.addListener(Events.Select, getCloseButtonListener());
}
}
}
};
}
beforeHide() must be overriden to send events on my EventBus. It is now possible to create a view class, which handles the MessageBox subclass as Window.
Code:
public abstract class AbstractMessageBoxView implements WindowView {
private AbstractMessageBox box;
public final Window getWindow() {
if (box == null) {
box = new AbstractMessageBox() {
@Override
public void beforeHide() {
getPresenter().beforeHide();
}
};
initMessageBox(box);
box.initButtons();
box.getDialog().addWindowListener(getShowWindowListener());
}
return box.getDialog();
}
private WindowListener getShowWindowListener() {
return new WindowListener() {
@Override
public void windowShow(WindowEvent we) {
getPresenter().afterShow();
}
};
}
protected Button getButtonById(String id) {
return ((Dialog) getWindow()).getButtonById(id);
}
protected abstract WindowPresenter getPresenter();
protected abstract void initMessageBox(MessageBox box);
}
initMessageBox() can be used to define options for the MessageBox. This is very tricky, because some options must be set on the Dialog, some other on the MessageBox, to get an effect.
Code:
public class ConfirmDeleteView extends AbstractMessageBoxView {
@Override
protected void initMessageBox(MessageBox box) {
box.setClosable(true);
box.getDialog().setHeading(constants.confirmDeleteTitle());
box.getDialog().setIcon(IconHelper.create(resources.menuitemIcon()));
box.setIcon(MessageBox.QUESTION);
box.getDialog().setButtons(Dialog.YESNO);
box.getDialog().yesText = constants.confirmDeleteYes();
box.getDialog().noText = constants.confirmDeleteNo();
box.getDialog().setButtonAlign(HorizontalAlignment.RIGHT);
box.getDialog().setModal(false);
box.setMessage("ho-ho-ho");
getButtonById(Dialog.YES).addSelectionListener(getYesButtonListener());
}
}
The selection listener for Dialog.YES doesn't handle window actions, it is only there to get information about the pressed button and initiated the business logic for the button on the presenter class.
The WindowView interface defines getWindow() and so it is possible to call the method on a central point (after a specifc event on the EventBus). It is similar to your DesktopApp example, I have a class, which handles desktop specific things like start menu, shortcuts and also the windows.
Code:
public void addApplication(WindowView view) {
Window window = view.getWindow();
if (!getDesktop().getWindows().contains(window)) {
getDesktop().addWindow(window);
}
if ((window != null) && (!window.isVisible())) {
window.show();
} else {
window.toFront();
}
}
public void removeApplication(WindowView view) {
Window window = view.getWindow();
if (getDesktop().getWindows().contains(window)) {
window.hide();
getDesktop().removeWindow(window);
}
}
It is not possible to override the Dialog within the MessageBox with an own class. This would simplify some parts of the code (i.e. I could override Dialog.initTools()). I could also copy the whole code of MessageBox.getDialog() and rewrite it on a subclass, but the member field "dialog" is private, so I can not replace the right dialog.
Last but not least, I could use Dialog instead of MessageBox, but (and this was my first idea) I have to recreate the code, which generates the MessageBox icon within a simple Dialog to mimic the MessageBox layout. And this was my initial question
.
Andre