package com.gwt.ui.client; import com.google.gwt.dom.client.Document; import com.google.gwt.dom.client.EventTarget; import com.google.gwt.dom.client.NativeEvent; import com.google.gwt.event.dom.client.MouseDownEvent; import com.google.gwt.event.dom.client.MouseDownHandler; import com.google.gwt.event.dom.client.MouseEvent; import com.google.gwt.event.dom.client.MouseMoveEvent; import com.google.gwt.event.dom.client.MouseMoveHandler; import com.google.gwt.event.dom.client.MouseUpEvent; import com.google.gwt.event.dom.client.MouseUpHandler; import com.google.gwt.event.logical.shared.ResizeEvent; import com.google.gwt.event.logical.shared.ResizeHandler; import com.google.gwt.event.shared.HandlerRegistration; import com.google.gwt.user.client.DOM; import com.google.gwt.user.client.Event; import com.google.gwt.user.client.Window; import com.google.gwt.user.client.Event.NativePreviewEvent; import com.google.gwt.user.client.ui.DockPanel; import com.google.gwt.user.client.ui.HTML; import com.google.gwt.user.client.ui.PopupPanel; import com.google.gwt.user.client.ui.ProvidesResize; import com.google.gwt.user.client.ui.RequiresResize; import com.google.gwt.user.client.ui.Widget; public class DialogPanel extends PopupPanel implements ProvidesResize, MouseMoveHandler, MouseUpHandler, MouseDownHandler { private static final int DRAG_ZONE_WIDTH = 5; enum DragZoneType { NONE("default"), MOVE("move"), RESIZE_E("e-resize"), RESIZE_W("w-resize"), RESIZE_S("s-resize"), RESIZE_N("n-resize"), RESIZE_NE("ne-resize"), RESIZE_NW("nw-resize"), RESIZE_SE("se-resize"), RESIZE_SW("sw-resize"); private String cursor; DragZoneType(String cursor) { this.cursor = cursor; } public String getCursor() { return cursor; } } private final DockPanel container; private Widget contentWidget; private int clientWindowLeft; private int clientWindowRight; private int clientWindowTop; private int clientWindowBottom; private HandlerRegistration resizeHandlerRegistration; private final CaptionPanel captionPanel; private boolean dragging = false; private DragZoneType dragZoneType; private int dragStartX; private int dragStartY; private int dragStartLeft; private int dragStartTop; private int dragStartWidth; private int dragStartHeight; public DialogPanel(boolean autoHide, boolean modal) { super(autoHide, modal); getElement().getStyle().setProperty("zIndex", "20"); getElement().getStyle().setProperty("padding", DRAG_ZONE_WIDTH + "px"); updateClientWindowPosition(); container = new DockPanel(); DOM.setStyleAttribute(container.getElement(), "cursor", "default"); captionPanel = new CaptionPanel(); container.add(captionPanel, DockPanel.NORTH); setWidget(container); addDomHandler(this, MouseMoveEvent.getType()); addDomHandler(this, MouseUpEvent.getType()); addDomHandler(this, MouseDownEvent.getType()); } public void setContentWidget(Widget widget) { if (contentWidget != null) { container.remove(contentWidget); } contentWidget = widget; container.add(contentWidget, DockPanel.CENTER); contentWidget.setSize("100%", "100%"); container.setCellHeight(contentWidget, "100%"); } public void setCaption(String caption) { captionPanel.setHTML(caption); } private void updateClientWindowPosition() { clientWindowLeft = Document.get().getScrollLeft(); clientWindowRight = clientWindowLeft + Window.getClientWidth(); clientWindowTop = Document.get().getScrollTop(); clientWindowBottom = clientWindowTop + Window.getClientHeight(); } @Override public void show() { updateClientWindowPosition(); if (resizeHandlerRegistration == null) { resizeHandlerRegistration = Window.addResizeHandler(new ResizeHandler() { public void onResize(ResizeEvent event) { updateClientWindowPosition(); } }); } super.show(); } @Override protected void onPreviewNativeEvent(NativePreviewEvent event) { // We need to preventDefault() on mouseDown events (outside of the // DialogBox content) to keep text from being selected when it // is dragged. NativeEvent nativeEvent = event.getNativeEvent(); EventTarget target = nativeEvent.getEventTarget(); if (target != null && target.equals(this) && !event.isCanceled() && (event.getTypeInt() == Event.ONMOUSEDOWN)) { nativeEvent.preventDefault(); } super.onPreviewNativeEvent(event); } protected void beginDragging(MouseDownEvent event) { dragging = true; dragZoneType = getDragZoneType(event); DOM.setCapture(getElement()); dragStartX = event.getX() + getAbsoluteLeft(); dragStartY = event.getY() + getAbsoluteTop(); dragStartLeft = getAbsoluteLeft(); dragStartTop = getAbsoluteTop(); dragStartWidth = getOffsetWidth(); dragStartHeight = getOffsetHeight(); } /** * That simple override cancels the inherited event previewing and simply allows all mouse * events to pass through to other widgets in the application. */ public boolean onEventPreview(Event event) { Event currentEvent = Event.getCurrentEvent(); EventTarget target = currentEvent.getEventTarget(); if (target != null && !target.equals(this)) { currentEvent.stopPropagation(); } return true; } protected void continueDragging(MouseMoveEvent event) { if (dragging) { int absX = event.getX() + getAbsoluteLeft(); int absY = event.getY() + getAbsoluteTop(); // if the mouse is off the screen to the left, right, or top, don't // move or resize the dialog box. if (absX < clientWindowLeft || absX >= clientWindowRight || absY < clientWindowTop || absY >= clientWindowBottom) { return; } int width = dragStartWidth; int height = dragStartHeight; int left = dragStartLeft; int top = dragStartTop; switch (dragZoneType) { case RESIZE_E: width = dragStartWidth - dragStartX + absX; break; case RESIZE_W: width = dragStartWidth + dragStartX - absX; left = dragStartLeft - dragStartX + absX; break; case RESIZE_S: height = dragStartHeight - dragStartY + absY; break; case RESIZE_N: height = dragStartHeight + dragStartY - absY; top = dragStartTop - dragStartY + absY; break; case RESIZE_SE: width = dragStartWidth - dragStartX + absX; height = dragStartHeight - dragStartY + absY; break; case RESIZE_SW: width = dragStartWidth + dragStartX - absX; height = dragStartHeight - dragStartY + absY; left = dragStartLeft - dragStartX + absX; break; case RESIZE_NE: width = dragStartWidth - dragStartX + absX; height = dragStartHeight + dragStartY - absY; top = dragStartTop - dragStartY + absY; break; case RESIZE_NW: width = dragStartWidth + dragStartX - absX; height = dragStartHeight + dragStartY - absY; left = dragStartLeft - dragStartX + absX; top = dragStartTop - dragStartY + absY; break; case MOVE: left = dragStartLeft - dragStartX + absX; top = dragStartTop - dragStartY + absY; break; default: break; } setPixelSize(width - 2 * DRAG_ZONE_WIDTH, height - 2 * DRAG_ZONE_WIDTH); setPopupPosition(left, top); if (contentWidget instanceof RequiresResize) { ((RequiresResize)contentWidget).onResize(); } } } protected void endDragging(MouseUpEvent event) { dragging = false; dragZoneType = DragZoneType.NONE; DOM.releaseCapture(getElement()); } @Override public void onMouseMove(MouseMoveEvent event) { if (dragging) { continueDragging(event); } else { DragZoneType dragZoneType = getDragZoneType(event); setCursor(dragZoneType); } } @Override public void onMouseDown(MouseDownEvent event) { if (!DragZoneType.NONE.equals(getDragZoneType(event))) { beginDragging(event); } } @Override public void onMouseUp(MouseUpEvent event) { if (dragging) { endDragging(event); } } private void setCursor(DragZoneType dragZoneType) { DOM.setStyleAttribute(this.getElement(), "cursor", dragZoneType.getCursor()); } private DragZoneType getDragZoneType(MouseEvent<?> mouseEvent) { if (captionPanel.equals(mouseEvent.getSource())) { return DragZoneType.MOVE; } else { int eventY = mouseEvent.getY() + getAbsoluteTop(); int boxY = this.getAbsoluteTop(); int height = this.getOffsetHeight(); int eventX = mouseEvent.getX() + getAbsoluteLeft(); int boxX = this.getAbsoluteLeft(); int width = this.getOffsetWidth(); int y = eventY - boxY; int x = eventX - boxX; if (y <= DRAG_ZONE_WIDTH) { if (x <= DRAG_ZONE_WIDTH) { return DragZoneType.RESIZE_NW; } else if (x >= width - DRAG_ZONE_WIDTH) { return DragZoneType.RESIZE_NE; } else { return DragZoneType.RESIZE_N; } } else if (y >= height - DRAG_ZONE_WIDTH) { if (x <= DRAG_ZONE_WIDTH) { return DragZoneType.RESIZE_SW; } else if (x >= width - DRAG_ZONE_WIDTH) { return DragZoneType.RESIZE_SE; } else { return DragZoneType.RESIZE_S; } } else { if (x <= DRAG_ZONE_WIDTH) { return DragZoneType.RESIZE_W; } else if (x >= width - DRAG_ZONE_WIDTH) { return DragZoneType.RESIZE_E; } else { return DragZoneType.NONE; } } } } class CaptionPanel extends HTML { public CaptionPanel() { setWordWrap(false); this.getStyleElement().addClassName("captionPanel-dialog"); setHeight("22px"); DOM.setStyleAttribute(this.getElement(), "cursor", "move"); addDomHandler(DialogPanel.this, MouseMoveEvent.getType()); addDomHandler(DialogPanel.this, MouseUpEvent.getType()); addDomHandler(DialogPanel.this, MouseDownEvent.getType()); } } }