Change-Id: I259c659987b5b15b354e16d0be1523f4ede809f0tags/8.0.0.alpha8
@@ -44,6 +44,7 @@ import com.google.gwt.event.dom.client.KeyDownEvent; | |||
import com.google.gwt.event.dom.client.KeyDownHandler; | |||
import com.google.gwt.event.dom.client.ScrollEvent; | |||
import com.google.gwt.event.dom.client.ScrollHandler; | |||
import com.google.gwt.event.shared.HandlerManager; | |||
import com.google.gwt.event.shared.HandlerRegistration; | |||
import com.google.gwt.user.client.Command; | |||
import com.google.gwt.user.client.DOM; | |||
@@ -64,6 +65,8 @@ import com.vaadin.client.ui.ShortcutActionHandler.ShortcutActionHandlerOwner; | |||
import com.vaadin.client.ui.aria.AriaHelper; | |||
import com.vaadin.client.ui.window.WindowMoveEvent; | |||
import com.vaadin.client.ui.window.WindowMoveHandler; | |||
import com.vaadin.client.ui.window.WindowOrderEvent; | |||
import com.vaadin.client.ui.window.WindowOrderHandler; | |||
import com.vaadin.shared.Connector; | |||
import com.vaadin.shared.EventId; | |||
import com.vaadin.shared.ui.window.WindowMode; | |||
@@ -79,6 +82,9 @@ public class VWindow extends VOverlay implements ShortcutActionHandlerOwner, | |||
private static ArrayList<VWindow> windowOrder = new ArrayList<>(); | |||
private static HandlerManager WINDOW_ORDER_HANDLER = new HandlerManager( | |||
VWindow.class); | |||
private static boolean orderingDefered; | |||
public static final String CLASSNAME = "v-window"; | |||
@@ -278,14 +284,37 @@ public class VWindow extends VOverlay implements ShortcutActionHandlerOwner, | |||
} | |||
public void bringToFront() { | |||
int curIndex = windowOrder.indexOf(this); | |||
bringToFront(true); | |||
} | |||
private void bringToFront(boolean notifyListeners) { | |||
int curIndex = getWindowOrder(); | |||
if (curIndex + 1 < windowOrder.size()) { | |||
windowOrder.remove(this); | |||
windowOrder.add(this); | |||
for (; curIndex < windowOrder.size(); curIndex++) { | |||
windowOrder.get(curIndex).setWindowOrder(curIndex); | |||
VWindow window = windowOrder.get(curIndex); | |||
window.setWindowOrder(curIndex); | |||
} | |||
} | |||
if (notifyListeners) { | |||
fireOrderEvent(); | |||
} | |||
} | |||
static void fireOrderEvent() { | |||
fireOrderEvent(windowOrder); | |||
} | |||
private void doFireOrderEvent() { | |||
ArrayList<VWindow> list = new ArrayList<>(); | |||
list.add(this); | |||
fireOrderEvent(list); | |||
} | |||
private static void fireOrderEvent(ArrayList<VWindow> windows) { | |||
WINDOW_ORDER_HANDLER | |||
.fireEvent(new WindowOrderEvent(new ArrayList<>(windows))); | |||
} | |||
/** | |||
@@ -317,13 +346,22 @@ public class VWindow extends VOverlay implements ShortcutActionHandlerOwner, | |||
windowOrder.add(this); | |||
setPopupPosition(order * STACKING_OFFSET_PIXELS, | |||
order * STACKING_OFFSET_PIXELS); | |||
doFireOrderEvent(); | |||
} | |||
private void setWindowOrder(int order) { | |||
setZIndex(order + Z_INDEX); | |||
} | |||
/** | |||
* Returns window position in list of opened and shown windows. | |||
* | |||
* @since 8.0.0 | |||
*/ | |||
public final int getWindowOrder() { | |||
return windowOrder.indexOf(this); | |||
} | |||
@Override | |||
protected void setZIndex(int zIndex) { | |||
super.setZIndex(zIndex); | |||
@@ -536,7 +574,7 @@ public class VWindow extends VOverlay implements ShortcutActionHandlerOwner, | |||
for (int i = 0; i < array.length; i++) { | |||
VWindow w = array[i]; | |||
if (w.bringToFrontSequence != -1 || w.vaadinModality) { | |||
w.bringToFront(); | |||
w.bringToFront(false); | |||
w.bringToFrontSequence = -1; | |||
} | |||
} | |||
@@ -548,6 +586,7 @@ public class VWindow extends VOverlay implements ShortcutActionHandlerOwner, | |||
if (topmost != null && topmost.vaadinModality) { | |||
topmost.focus(); | |||
} | |||
fireOrderEvent(); | |||
} | |||
@Override | |||
@@ -655,15 +694,21 @@ public class VWindow extends VOverlay implements ShortcutActionHandlerOwner, | |||
} | |||
super.hide(); | |||
int curIndex = windowOrder.indexOf(this); | |||
int curIndex = getWindowOrder(); | |||
// Remove window from windowOrder to avoid references being left | |||
// hanging. | |||
windowOrder.remove(curIndex); | |||
// Update the z-indices of any remaining windows | |||
ArrayList<VWindow> update = new ArrayList<>( | |||
windowOrder.size() - curIndex + 1); | |||
update.add(this); | |||
while (curIndex < windowOrder.size()) { | |||
windowOrder.get(curIndex).setWindowOrder(curIndex++); | |||
VWindow window = windowOrder.get(curIndex); | |||
window.setWindowOrder(curIndex++); | |||
update.add(window); | |||
} | |||
focusTopmostModalWindow(); | |||
fireOrderEvent(update); | |||
} | |||
/** For internal use only. May be removed or replaced in the future. */ | |||
@@ -689,8 +734,7 @@ public class VWindow extends VOverlay implements ShortcutActionHandlerOwner, | |||
} | |||
private void showModalityCurtain() { | |||
getModalityCurtain().getStyle() | |||
.setZIndex(windowOrder.indexOf(this) + Z_INDEX); | |||
getModalityCurtain().getStyle().setZIndex(getWindowOrder() + Z_INDEX); | |||
if (isShowing()) { | |||
getOverlayContainer().insertBefore(getModalityCurtain(), | |||
@@ -1463,4 +1507,16 @@ public class VWindow extends VOverlay implements ShortcutActionHandlerOwner, | |||
return addHandler(handler, WindowMoveEvent.getType()); | |||
} | |||
/** | |||
* Adds a Handler for window order change event. | |||
* | |||
* @since 8.0.0 | |||
* | |||
* @return registration object to deregister the handler | |||
*/ | |||
public static HandlerRegistration addWindowOrderHandler( | |||
WindowOrderHandler handler) { | |||
return WINDOW_ORDER_HANDLER.addHandler(WindowOrderEvent.getType(), | |||
handler); | |||
} | |||
} |
@@ -16,6 +16,11 @@ | |||
package com.vaadin.client.ui.ui; | |||
import java.util.ArrayList; | |||
import java.util.Arrays; | |||
import java.util.Collections; | |||
import java.util.Comparator; | |||
import java.util.HashMap; | |||
import java.util.HashSet; | |||
import java.util.Iterator; | |||
import java.util.List; | |||
import java.util.logging.Logger; | |||
@@ -75,14 +80,19 @@ import com.vaadin.client.ui.VUI; | |||
import com.vaadin.client.ui.VWindow; | |||
import com.vaadin.client.ui.layout.MayScrollChildren; | |||
import com.vaadin.client.ui.window.WindowConnector; | |||
import com.vaadin.client.ui.window.WindowOrderEvent; | |||
import com.vaadin.client.ui.window.WindowOrderHandler; | |||
import com.vaadin.server.Page.Styles; | |||
import com.vaadin.shared.ApplicationConstants; | |||
import com.vaadin.shared.Connector; | |||
import com.vaadin.shared.EventId; | |||
import com.vaadin.shared.MouseEventDetails; | |||
import com.vaadin.shared.Version; | |||
import com.vaadin.shared.communication.MethodInvocation; | |||
import com.vaadin.shared.ui.ComponentStateUtil; | |||
import com.vaadin.shared.ui.Connect; | |||
import com.vaadin.shared.ui.Connect.LoadStyle; | |||
import com.vaadin.shared.ui.WindowOrderRpc; | |||
import com.vaadin.shared.ui.ui.DebugWindowClientRpc; | |||
import com.vaadin.shared.ui.ui.DebugWindowServerRpc; | |||
import com.vaadin.shared.ui.ui.PageClientRpc; | |||
@@ -103,6 +113,8 @@ public class UIConnector extends AbstractSingleComponentContainerConnector | |||
private String activeTheme = null; | |||
private HandlerRegistration windowOrderRegistration; | |||
private final StateChangeHandler childStateChangeHandler = new StateChangeHandler() { | |||
@Override | |||
public void onStateChanged(StateChangeEvent stateChangeEvent) { | |||
@@ -112,9 +124,33 @@ public class UIConnector extends AbstractSingleComponentContainerConnector | |||
} | |||
}; | |||
private WindowOrderHandler windowOrderHandler = new WindowOrderHandler() { | |||
@Override | |||
public void onWindowOrderChange(WindowOrderEvent event) { | |||
VWindow[] windows = event.getWindows(); | |||
HashMap<Integer, Connector> orders = new HashMap<>(); | |||
boolean hasEventListener = hasEventListener(EventId.WINDOW_ORDER); | |||
for (VWindow window : windows) { | |||
Connector connector = Util.findConnectorFor(window); | |||
orders.put(window.getWindowOrder(), connector); | |||
if (connector instanceof AbstractConnector | |||
&& ((AbstractConnector) connector) | |||
.hasEventListener(EventId.WINDOW_ORDER)) { | |||
hasEventListener = true; | |||
} | |||
} | |||
if (hasEventListener) { | |||
getRpcProxy(WindowOrderRpc.class).windowOrderChanged(orders); | |||
} | |||
} | |||
}; | |||
@Override | |||
protected void init() { | |||
super.init(); | |||
windowOrderRegistration = VWindow | |||
.addWindowOrderHandler(windowOrderHandler); | |||
registerRpc(PageClientRpc.class, new PageClientRpc() { | |||
@Override | |||
@@ -703,6 +739,8 @@ public class UIConnector extends AbstractSingleComponentContainerConnector | |||
} | |||
} | |||
setWindowOrderAndPosition(); | |||
// Close removed sub windows | |||
for (ComponentConnector c : event.getOldChildren()) { | |||
if (c.getParent() != this && c instanceof WindowConnector) { | |||
@@ -1124,4 +1162,50 @@ public class UIConnector extends AbstractSingleComponentContainerConnector | |||
getRpcProxy(UIServerRpc.class).acknowledge(); | |||
} | |||
private void setWindowOrderAndPosition() { | |||
if (windowOrderRegistration != null) { | |||
windowOrderRegistration.removeHandler(); | |||
} | |||
WindowOrderCollector collector = new WindowOrderCollector(); | |||
HandlerRegistration registration = VWindow | |||
.addWindowOrderHandler(collector); | |||
for (ComponentConnector c : getChildComponents()) { | |||
if (c instanceof WindowConnector) { | |||
WindowConnector wc = (WindowConnector) c; | |||
wc.setWindowOrderAndPosition(); | |||
} | |||
} | |||
windowOrderHandler.onWindowOrderChange( | |||
new WindowOrderEvent(collector.getWindows())); | |||
registration.removeHandler(); | |||
windowOrderRegistration = VWindow | |||
.addWindowOrderHandler(windowOrderHandler); | |||
} | |||
private static class WindowOrderCollector | |||
implements WindowOrderHandler, Comparator<VWindow> { | |||
private HashSet<VWindow> windows = new HashSet<>(); | |||
@Override | |||
public void onWindowOrderChange(WindowOrderEvent event) { | |||
windows.addAll(Arrays.asList(event.getWindows())); | |||
} | |||
@Override | |||
public int compare(VWindow window1, VWindow window2) { | |||
if (window1.getWindowOrder() == window2.getWindowOrder()) { | |||
return 0; | |||
} | |||
return window1.getWindowOrder() > window2.getWindowOrder() ? 1 : -1; | |||
} | |||
ArrayList<VWindow> getWindows() { | |||
ArrayList<VWindow> result = new ArrayList<>(); | |||
result.addAll(windows); | |||
Collections.sort(result, this); | |||
return result; | |||
} | |||
}; | |||
} |
@@ -38,7 +38,6 @@ import com.vaadin.client.ConnectorHierarchyChangeEvent; | |||
import com.vaadin.client.LayoutManager; | |||
import com.vaadin.client.Paintable; | |||
import com.vaadin.client.UIDL; | |||
import com.vaadin.client.communication.RpcProxy; | |||
import com.vaadin.client.communication.StateChangeEvent; | |||
import com.vaadin.client.ui.AbstractSingleComponentContainerConnector; | |||
import com.vaadin.client.ui.ClickEventHandler; | |||
@@ -497,8 +496,7 @@ public class WindowConnector extends AbstractSingleComponentContainerConnector | |||
@Override | |||
public void onWindowMove(WindowMoveEvent event) { | |||
RpcProxy.create(WindowServerRpc.class, this) | |||
.windowMoved(event.getNewX(), event.getNewY()); | |||
getRpcProxy(WindowServerRpc.class).windowMoved(event.getNewX(), | |||
event.getNewY()); | |||
} | |||
} |
@@ -0,0 +1,74 @@ | |||
/* | |||
* Copyright 2000-2016 Vaadin Ltd. | |||
* | |||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not | |||
* use this file except in compliance with the License. You may obtain a copy of | |||
* the License at | |||
* | |||
* http://www.apache.org/licenses/LICENSE-2.0 | |||
* | |||
* Unless required by applicable law or agreed to in writing, software | |||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | |||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | |||
* License for the specific language governing permissions and limitations under | |||
* the License. | |||
*/ | |||
package com.vaadin.client.ui.window; | |||
import java.util.ArrayList; | |||
import com.google.gwt.event.shared.GwtEvent; | |||
import com.vaadin.client.ui.VWindow; | |||
/** | |||
* Event for window order position updates. | |||
* | |||
* @since 8.0.0 | |||
* | |||
* @author Vaadin Ltd | |||
*/ | |||
public class WindowOrderEvent extends GwtEvent<WindowOrderHandler> { | |||
private static final Type<WindowOrderHandler> TYPE = new Type<>(); | |||
private final ArrayList<VWindow> windows; | |||
/** | |||
* Creates a new event with the given order. | |||
* | |||
* @param windows | |||
* The new order position for the VWindow | |||
*/ | |||
public WindowOrderEvent(ArrayList<VWindow> windows) { | |||
this.windows = windows; | |||
} | |||
@Override | |||
public Type<WindowOrderHandler> getAssociatedType() { | |||
return TYPE; | |||
} | |||
/** | |||
* Returns windows in order. | |||
* | |||
* @return windows in the specific order | |||
*/ | |||
public VWindow[] getWindows() { | |||
return windows.toArray(new VWindow[windows.size()]); | |||
} | |||
@Override | |||
protected void dispatch(WindowOrderHandler handler) { | |||
handler.onWindowOrderChange(this); | |||
} | |||
/** | |||
* Gets the type of the event. | |||
* | |||
* @return the type of the event | |||
*/ | |||
public static Type<WindowOrderHandler> getType() { | |||
return TYPE; | |||
} | |||
} |
@@ -0,0 +1,36 @@ | |||
/* | |||
* Copyright 2000-2016 Vaadin Ltd. | |||
* | |||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not | |||
* use this file except in compliance with the License. You may obtain a copy of | |||
* the License at | |||
* | |||
* http://www.apache.org/licenses/LICENSE-2.0 | |||
* | |||
* Unless required by applicable law or agreed to in writing, software | |||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | |||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | |||
* License for the specific language governing permissions and limitations under | |||
* the License. | |||
*/ | |||
package com.vaadin.client.ui.window; | |||
import com.google.gwt.event.shared.EventHandler; | |||
/** | |||
* Handler for {@link WindowOrderEvent}s. | |||
* | |||
* @since 8.0.0 | |||
* | |||
* @author Vaadin Ltd | |||
*/ | |||
public interface WindowOrderHandler extends EventHandler { | |||
/** | |||
* Called when the VWindow instances changed their order position. | |||
* | |||
* @param event | |||
* Contains windows whose position has changed | |||
*/ | |||
public void onWindowOrderChange(WindowOrderEvent event); | |||
} |
@@ -18,14 +18,18 @@ package com.vaadin.ui; | |||
import java.io.ByteArrayOutputStream; | |||
import java.io.IOException; | |||
import java.lang.reflect.Method; | |||
import java.net.URI; | |||
import java.util.ArrayList; | |||
import java.util.Collection; | |||
import java.util.Collections; | |||
import java.util.HashMap; | |||
import java.util.Iterator; | |||
import java.util.LinkedHashMap; | |||
import java.util.LinkedHashSet; | |||
import java.util.List; | |||
import java.util.Map; | |||
import java.util.Map.Entry; | |||
import java.util.concurrent.Future; | |||
import java.util.logging.Level; | |||
import java.util.logging.Logger; | |||
@@ -34,6 +38,7 @@ import com.vaadin.annotations.PreserveOnRefresh; | |||
import com.vaadin.event.Action; | |||
import com.vaadin.event.Action.Handler; | |||
import com.vaadin.event.ActionManager; | |||
import com.vaadin.event.ConnectorEventListener; | |||
import com.vaadin.event.MouseEvents.ClickEvent; | |||
import com.vaadin.event.MouseEvents.ClickListener; | |||
import com.vaadin.event.UIEvents.PollEvent; | |||
@@ -62,6 +67,7 @@ import com.vaadin.shared.EventId; | |||
import com.vaadin.shared.MouseEventDetails; | |||
import com.vaadin.shared.Registration; | |||
import com.vaadin.shared.communication.PushMode; | |||
import com.vaadin.shared.ui.WindowOrderRpc; | |||
import com.vaadin.shared.ui.ui.DebugWindowClientRpc; | |||
import com.vaadin.shared.ui.ui.DebugWindowServerRpc; | |||
import com.vaadin.shared.ui.ui.ScrollClientRpc; | |||
@@ -70,9 +76,11 @@ import com.vaadin.shared.ui.ui.UIConstants; | |||
import com.vaadin.shared.ui.ui.UIServerRpc; | |||
import com.vaadin.shared.ui.ui.UIState; | |||
import com.vaadin.ui.Component.Focusable; | |||
import com.vaadin.ui.Window.WindowOrderChangeListener; | |||
import com.vaadin.ui.declarative.Design; | |||
import com.vaadin.util.ConnectorHelper; | |||
import com.vaadin.util.CurrentInstance; | |||
import com.vaadin.util.ReflectTools; | |||
/** | |||
* The topmost component in any component hierarchy. There is one UI for every | |||
@@ -245,6 +253,21 @@ public abstract class UI extends AbstractSingleComponentContainer | |||
} | |||
}; | |||
private WindowOrderRpc windowOrderRpc = new WindowOrderRpc() { | |||
@Override | |||
public void windowOrderChanged( | |||
HashMap<Integer, Connector> windowOrders) { | |||
Map<Integer, Window> orders = new LinkedHashMap<>(); | |||
for (Entry<Integer, Connector> entry : windowOrders.entrySet()) { | |||
if (entry.getValue() instanceof Window) { | |||
orders.put(entry.getKey(), (Window) entry.getValue()); | |||
} | |||
} | |||
fireWindowOrder(orders); | |||
} | |||
}; | |||
/** | |||
* Timestamp keeping track of the last heartbeat of this UI. Updated to the | |||
* current time whenever the application receives a heartbeat or UIDL | |||
@@ -290,6 +313,7 @@ public abstract class UI extends AbstractSingleComponentContainer | |||
public UI(Component content) { | |||
registerRpc(rpc); | |||
registerRpc(debugRpc); | |||
registerRpc(windowOrderRpc); | |||
setSizeFull(); | |||
setContent(content); | |||
} | |||
@@ -387,6 +411,19 @@ public abstract class UI extends AbstractSingleComponentContainer | |||
fireEvent(new ClickEvent(this, mouseDetails)); | |||
} | |||
/** | |||
* Fire a window order event. | |||
* | |||
* @param windows | |||
* The windows with their orders whose order has been updated. | |||
*/ | |||
private void fireWindowOrder(Map<Integer, Window> windows) { | |||
for (Entry<Integer, Window> entry : windows.entrySet()) { | |||
entry.getValue().fireWindowOrderChange(entry.getKey()); | |||
} | |||
fireEvent(new WindowOrderUpdateEvent(this, windows.values())); | |||
} | |||
@Override | |||
@SuppressWarnings("unchecked") | |||
public void changeVariables(Object source, Map<String, Object> variables) { | |||
@@ -569,6 +606,7 @@ public abstract class UI extends AbstractSingleComponentContainer | |||
markAsDirty(); | |||
window.fireClose(); | |||
fireComponentDetachEvent(window); | |||
fireWindowOrder(Collections.singletonMap(-1, window)); | |||
return true; | |||
} | |||
@@ -1728,4 +1766,88 @@ public abstract class UI extends AbstractSingleComponentContainer | |||
int lastProcessedClientToServerId) { | |||
this.lastProcessedClientToServerId = lastProcessedClientToServerId; | |||
} | |||
/** | |||
* Adds a WindowOrderUpdateListener to the UI. | |||
* <p> | |||
* The WindowOrderUpdateEvent is fired when the order positions of windows | |||
* are updated. It can happen when some window (this or other) is brought to | |||
* front or detached. | |||
* <p> | |||
* The other way to listen window position for specific window is | |||
* {@link Window#addWindowOrderChangeListener(WindowOrderChangeListener)} | |||
* | |||
* @see Window#addWindowOrderChangeListener(WindowOrderChangeListener) | |||
* | |||
* @param listener | |||
* the WindowModeChangeListener to add. | |||
* @since 8.0.0 | |||
* | |||
* @return a registration object for removing the listener | |||
*/ | |||
public Registration addWindowOrderUpdateListener( | |||
WindowOrderUpdateListener listener) { | |||
addListener(EventId.WINDOW_ORDER, WindowOrderUpdateEvent.class, | |||
listener, WindowOrderUpdateListener.windowOrderUpdateMethod); | |||
return () -> removeListener(EventId.WINDOW_ORDER, | |||
WindowOrderUpdateEvent.class, listener); | |||
} | |||
/** | |||
* Event which is fired when the ordering of the windows is updated. | |||
* <p> | |||
* The other way to listen window position for specific window is | |||
* {@link Window#addWindowOrderChangeListener(WindowOrderChangeListener)} | |||
* | |||
* @see Window.WindowOrderChangeEvent | |||
* | |||
* @author Vaadin Ltd | |||
* @since 8.0.0 | |||
* | |||
*/ | |||
public static class WindowOrderUpdateEvent extends Component.Event { | |||
private final Collection<Window> windows; | |||
public WindowOrderUpdateEvent(Component source, | |||
Collection<Window> windows) { | |||
super(source); | |||
this.windows = windows; | |||
} | |||
/** | |||
* Gets the windows in the order they appear in the UI: top most window | |||
* is first, bottom one last. | |||
* | |||
* @return the windows collection | |||
*/ | |||
public Collection<Window> getWindows() { | |||
return windows; | |||
} | |||
} | |||
/** | |||
* An interface used for listening to Windows order update events. | |||
* | |||
* @since 8.0.0 | |||
* | |||
* @see Window.WindowOrderChangeEvent | |||
*/ | |||
public interface WindowOrderUpdateListener extends ConnectorEventListener { | |||
public static final Method windowOrderUpdateMethod = ReflectTools | |||
.findMethod(WindowOrderUpdateListener.class, | |||
"windowOrderUpdated", WindowOrderUpdateEvent.class); | |||
/** | |||
* Called when the windows order positions are changed. Use | |||
* {@link WindowOrderUpdateEvent#getWindows()} to get a reference to the | |||
* {@link Window}s whose order positions are updated. Use | |||
* {@link Window#getOrderPosition()} to get window position for specific | |||
* window. | |||
* | |||
* @param event | |||
*/ | |||
public void windowOrderUpdated(WindowOrderUpdateEvent event); | |||
} | |||
} |
@@ -28,6 +28,7 @@ import java.util.Map; | |||
import org.jsoup.nodes.Element; | |||
import org.jsoup.select.Elements; | |||
import com.vaadin.event.ConnectorEventListener; | |||
import com.vaadin.event.FieldEvents.BlurEvent; | |||
import com.vaadin.event.FieldEvents.BlurListener; | |||
import com.vaadin.event.FieldEvents.BlurNotifier; | |||
@@ -42,6 +43,7 @@ import com.vaadin.event.ShortcutListener; | |||
import com.vaadin.server.PaintException; | |||
import com.vaadin.server.PaintTarget; | |||
import com.vaadin.shared.Connector; | |||
import com.vaadin.shared.EventId; | |||
import com.vaadin.shared.MouseEventDetails; | |||
import com.vaadin.shared.Registration; | |||
import com.vaadin.shared.ui.window.WindowMode; | |||
@@ -109,6 +111,15 @@ public class Window extends Panel | |||
*/ | |||
private List<CloseShortcut> closeShortcuts = new ArrayList<>(4); | |||
/** | |||
* Used to keep the window order position. Order position for unattached | |||
* window is {@code -1}. | |||
* <p> | |||
* Window with greatest order position value is on the top and window with 0 | |||
* position value is on the bottom. | |||
*/ | |||
private int orderPosition = -1; | |||
/** | |||
* Creates a new, empty window | |||
*/ | |||
@@ -317,6 +328,24 @@ public class Window extends Panel | |||
return getState(false).positionY; | |||
} | |||
/** | |||
* Returns the position of this window in the order of all open windows for | |||
* this UI. | |||
* <p> | |||
* Window with position 0 is on the bottom, and window with greatest | |||
* position is at the top. If window has no position (it's not yet attached | |||
* or hidden) then position is {@code -1}. | |||
* | |||
* @see UI#addWindowOrderUpdateListener(com.vaadin.ui.UI.WindowOrderUpdateListener) | |||
* | |||
* @since 8.0.0 | |||
* | |||
* @return window order position. | |||
*/ | |||
public int getOrderPosition() { | |||
return orderPosition; | |||
} | |||
/** | |||
* Sets the distance of Window top border in pixels from top border of the | |||
* containing (main window). Has effect only if in {@link WindowMode#NORMAL} | |||
@@ -365,6 +394,96 @@ public class Window extends Panel | |||
} | |||
} | |||
/** | |||
* Event which is fired when the window order position is changed. | |||
* | |||
* @see UI.WindowOrderUpdateEvent | |||
* | |||
* @author Vaadin Ltd | |||
* | |||
*/ | |||
public static class WindowOrderChangeEvent extends Component.Event { | |||
private final int order; | |||
public WindowOrderChangeEvent(Component source, int order) { | |||
super(source); | |||
this.order = order; | |||
} | |||
/** | |||
* Gets the Window. | |||
* | |||
* @return the window | |||
*/ | |||
public Window getWindow() { | |||
return (Window) getSource(); | |||
} | |||
/** | |||
* Gets the new window order position. | |||
* | |||
* @return the new order position | |||
*/ | |||
public int getOrder() { | |||
return order; | |||
} | |||
} | |||
/** | |||
* An interface used for listening to Window order change events. | |||
* | |||
* @see UI.WindowOrderUpdateListener | |||
*/ | |||
public interface WindowOrderChangeListener extends ConnectorEventListener { | |||
public static final Method windowOrderChangeMethod = ReflectTools | |||
.findMethod(WindowOrderChangeListener.class, | |||
"windowOrderChanged", WindowOrderChangeEvent.class); | |||
/** | |||
* Called when the window order position is changed. Use | |||
* {@link WindowOrderChangeEvent#getWindow()} to get a reference to the | |||
* {@link Window} whose order position is changed. Use | |||
* {@link WindowOrderChangeEvent#getOrder()} to get a new order | |||
* position. | |||
* | |||
* @param event | |||
*/ | |||
public void windowOrderChanged(WindowOrderChangeEvent event); | |||
} | |||
/** | |||
* Adds a WindowOrderChangeListener to the window. | |||
* <p> | |||
* The WindowOrderChangeEvent is fired when the order position is changed. | |||
* It can happen when some window (this or other) is brought to front or | |||
* detached. | |||
* <p> | |||
* The other way to listen positions of all windows in UI is | |||
* {@link UI#addWindowOrderUpdateListener(com.vaadin.ui.UI.WindowOrderUpdateListener)} | |||
* | |||
* @see UI#addWindowOrderUpdateListener(com.vaadin.ui.UI.WindowOrderUpdateListener) | |||
* | |||
* @param listener | |||
* the WindowModeChangeListener to add. | |||
*/ | |||
public Registration addWindowOrderChangeListener( | |||
WindowOrderChangeListener listener) { | |||
addListener(EventId.WINDOW_ORDER, WindowOrderChangeEvent.class, | |||
listener, WindowOrderChangeListener.windowOrderChangeMethod); | |||
return () -> removeListener(EventId.WINDOW_ORDER, | |||
WindowOrderChangeEvent.class, listener); | |||
} | |||
protected void fireWindowOrderChange(Integer order) { | |||
if (order == null || this.orderPosition != order) { | |||
this.orderPosition = (order == null) ? -1 : order; | |||
fireEvent(new Window.WindowOrderChangeEvent(this, | |||
getOrderPosition())); | |||
} | |||
} | |||
/** | |||
* An interface used for listening to Window close events. Add the | |||
* CloseListener to a window and |
@@ -25,4 +25,5 @@ public interface EventId extends Serializable { | |||
public static final String POLL = "poll"; | |||
public static final String CHANGE = "change"; | |||
public static final String CONTEXT_CLICK = "cClick"; | |||
public static final String WINDOW_ORDER = "windowOrder"; | |||
} |
@@ -0,0 +1,41 @@ | |||
/* | |||
* Copyright 2000-2016 Vaadin Ltd. | |||
* | |||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not | |||
* use this file except in compliance with the License. You may obtain a copy of | |||
* the License at | |||
* | |||
* http://www.apache.org/licenses/LICENSE-2.0 | |||
* | |||
* Unless required by applicable law or agreed to in writing, software | |||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | |||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | |||
* License for the specific language governing permissions and limitations under | |||
* the License. | |||
*/ | |||
package com.vaadin.shared.ui; | |||
import java.util.HashMap; | |||
import com.vaadin.shared.Connector; | |||
import com.vaadin.shared.communication.ServerRpc; | |||
/** | |||
* Window order RPC interface. | |||
* <p> | |||
* Notifies server when windows order is changed. | |||
* | |||
* @author Vaadin Ltd | |||
* @since 8.0.0 | |||
* | |||
*/ | |||
public interface WindowOrderRpc extends ServerRpc { | |||
/** | |||
* Sends RPC request about windows order change. | |||
* | |||
* @param windowOrders | |||
* new windows order | |||
*/ | |||
void windowOrderChanged(HashMap<Integer, Connector> windowOrders); | |||
} |
@@ -0,0 +1,175 @@ | |||
/* | |||
* Copyright 2000-2014 Vaadin Ltd. | |||
* | |||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not | |||
* use this file except in compliance with the License. You may obtain a copy of | |||
* the License at | |||
* | |||
* http://www.apache.org/licenses/LICENSE-2.0 | |||
* | |||
* Unless required by applicable law or agreed to in writing, software | |||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | |||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | |||
* License for the specific language governing permissions and limitations under | |||
* the License. | |||
*/ | |||
package com.vaadin.tests.components.window; | |||
import com.vaadin.server.VaadinRequest; | |||
import com.vaadin.tests.components.AbstractTestUI; | |||
import com.vaadin.ui.Alignment; | |||
import com.vaadin.ui.Button; | |||
import com.vaadin.ui.Button.ClickEvent; | |||
import com.vaadin.ui.Button.ClickListener; | |||
import com.vaadin.ui.Label; | |||
import com.vaadin.ui.Window; | |||
import com.vaadin.ui.Window.WindowOrderChangeEvent; | |||
import com.vaadin.ui.Window.WindowOrderChangeListener; | |||
/** | |||
* Test UI for accessing to window order position. | |||
* | |||
* @author Vaadin Ltd | |||
*/ | |||
public class WindowOrder extends AbstractTestUI { | |||
@Override | |||
protected void setup(VaadinRequest request) { | |||
w1 = new Window(); | |||
w1.setCaption("Window1"); | |||
w1.addStyleName("window1"); | |||
w2 = new Window(); | |||
w2.setCaption("Window2"); | |||
w2.addStyleName("window2"); | |||
w3 = new Window(); | |||
w3.setCaption("Window3"); | |||
w3.addStyleName("window3"); | |||
getUI().addWindow(w1); | |||
getUI().addWindow(w2); | |||
getUI().addWindow(w3); | |||
OrderListener listener = new OrderListener(); | |||
for (Window window : getUI().getWindows()) { | |||
window.addWindowOrderChangeListener(listener); | |||
} | |||
w4 = new Window(); | |||
w4.setCaption("Window4"); | |||
w4.addStyleName("window4"); | |||
w4.addWindowOrderChangeListener(listener); | |||
infoLabel = createLabel("info-label"); | |||
uiLabel = createLabel("ui-label"); | |||
getUI().addWindowOrderUpdateListener(new WindowOrderListener()); | |||
addComponent(infoLabel); | |||
addComponent(uiLabel); | |||
Button first = new Button("Bring first to front", new ClickListener() { | |||
@Override | |||
public void buttonClick(ClickEvent event) { | |||
w1.bringToFront(); | |||
} | |||
}); | |||
first.addStyleName("bring-to-front-first"); | |||
addComponent(first); | |||
getLayout().setComponentAlignment(first, Alignment.MIDDLE_RIGHT); | |||
Button all = new Button("Bring to front all windows", | |||
new ClickListener() { | |||
@Override | |||
public void buttonClick(ClickEvent event) { | |||
w3.bringToFront(); | |||
w1.bringToFront(); | |||
w2.bringToFront(); | |||
} | |||
}); | |||
all.addStyleName("bring-to-front-all"); | |||
addComponent(all); | |||
getLayout().setComponentAlignment(all, Alignment.MIDDLE_RIGHT); | |||
Button detach = new Button("Detach last window", new ClickListener() { | |||
@Override | |||
public void buttonClick(ClickEvent event) { | |||
getUI().removeWindow(w3); | |||
} | |||
}); | |||
detach.addStyleName("detach-window"); | |||
addComponent(detach); | |||
getLayout().setComponentAlignment(detach, Alignment.MIDDLE_RIGHT); | |||
Button add = new Button("Add new window", new ClickListener() { | |||
@Override | |||
public void buttonClick(ClickEvent event) { | |||
getUI().addWindow(w4); | |||
} | |||
}); | |||
add.addStyleName("add-window"); | |||
addComponent(add); | |||
getLayout().setComponentAlignment(add, Alignment.MIDDLE_RIGHT); | |||
} | |||
@Override | |||
protected String getTestDescription() { | |||
return "Window order position access and listeners for order change events."; | |||
} | |||
@Override | |||
protected Integer getTicketNumber() { | |||
return 14325; | |||
} | |||
private Label createLabel(String style) { | |||
Label label = new Label(); | |||
label.addStyleName(style); | |||
return label; | |||
} | |||
private class OrderListener implements WindowOrderChangeListener { | |||
@Override | |||
public void windowOrderChanged(WindowOrderChangeEvent event) { | |||
infoLabel.removeStyleName("w4--1"); | |||
infoLabel.addStyleName("w4-" + w4.getOrderPosition()); | |||
if (event.getWindow() == w3 && event.getOrder() == -1) { | |||
Label detached = new Label("Window 3 is detached"); | |||
detached.addStyleName("w3-detached"); | |||
detached.addStyleName("w3-" + w3.getOrderPosition()); | |||
addComponent(detached); | |||
} | |||
Window window = event.getWindow(); | |||
Label label = new Label(String.valueOf(window.getOrderPosition())); | |||
label.addStyleName("event-order" + event.getOrder()); | |||
window.setContent(label); | |||
} | |||
} | |||
private class WindowOrderListener implements WindowOrderUpdateListener { | |||
@Override | |||
public void windowOrderUpdated(WindowOrderUpdateEvent event) { | |||
uiLabel.removeStyleName(infoLabel.getStyleName()); | |||
for (Window window : event.getWindows()) { | |||
uiLabel.addStyleName( | |||
window.getStyleName() + "-" + window.getOrderPosition()); | |||
} | |||
} | |||
} | |||
private Window w1; | |||
private Window w2; | |||
private Window w3; | |||
private Window w4; | |||
private Label infoLabel; | |||
private Label uiLabel; | |||
} |
@@ -0,0 +1,182 @@ | |||
/* | |||
* Copyright 2000-2014 Vaadin Ltd. | |||
* | |||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not | |||
* use this file except in compliance with the License. You may obtain a copy of | |||
* the License at | |||
* | |||
* http://www.apache.org/licenses/LICENSE-2.0 | |||
* | |||
* Unless required by applicable law or agreed to in writing, software | |||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | |||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | |||
* License for the specific language governing permissions and limitations under | |||
* the License. | |||
*/ | |||
package com.vaadin.tests.components.window; | |||
import org.junit.Assert; | |||
import org.junit.Test; | |||
import org.openqa.selenium.By; | |||
import org.openqa.selenium.WebElement; | |||
import com.vaadin.tests.tb3.MultiBrowserTest; | |||
/** | |||
* Test for window order position access. | |||
* | |||
* @author Vaadin Ltd | |||
*/ | |||
public class WindowOrderTest extends MultiBrowserTest { | |||
@Test | |||
public void orderGetterTest() { | |||
openTestURL(); | |||
checkPositionsAfterFirstWindowActivation(); | |||
checkPositionsAfterActivationThirdFirstSecond(); | |||
checkPositionsAfterDetachingThirdWindow(); | |||
checkPositionsAfterNewWindowAttach(); | |||
} | |||
private void checkPositionsAfterFirstWindowActivation() { | |||
// Bring the first window to front and check order positions of the | |||
// windows | |||
findElement(By.className("bring-to-front-first")).click(); | |||
Assert.assertTrue( | |||
"The first window has wrong order position after bring first to front", | |||
hasOrder("window1", 2)); | |||
Assert.assertTrue( | |||
"The first window position is incorrectly updated via UI listener after bring first to front", | |||
hasOrderInUi("window1", 2)); | |||
Assert.assertTrue( | |||
"The second window has wrong order position after bring first to front", | |||
hasOrder("window2", 0)); | |||
Assert.assertTrue( | |||
"The second window position is incorrectly updated via UI after bring first to front", | |||
hasOrderInUi("window2", 0)); | |||
Assert.assertTrue( | |||
"The third window has wrong order position after bring first to front", | |||
hasOrder("window3", 1)); | |||
Assert.assertTrue( | |||
"The third window position is incorrectly updated via UI after bring first to front", | |||
hasOrderInUi("window3", 1)); | |||
Assert.assertTrue( | |||
"Last window is not attached and should have '-1' position, but hasn't.", | |||
lastWindowHasOrder(-1)); | |||
} | |||
private void checkPositionsAfterActivationThirdFirstSecond() { | |||
// Bring third, first and second window at once (exactly in this order) | |||
// to front and check order positions of the | |||
// windows | |||
findElement(By.className("bring-to-front-all")).click(); | |||
Assert.assertTrue( | |||
"The first window has wrong order position after bring all to front", | |||
hasOrder("window2", 2)); | |||
Assert.assertTrue( | |||
"The first window position is incorrectly updated via UI after bring all to front", | |||
hasOrderInUi("window2", 2)); | |||
Assert.assertTrue( | |||
"The second window has wrong order position after bring all to front", | |||
hasOrder("window1", 1)); | |||
Assert.assertTrue( | |||
"The second window position is incorrectly updated via UI after bring all to front", | |||
hasOrderInUi("window1", 1)); | |||
Assert.assertTrue( | |||
"The third window has wrong order position after bring all to front", | |||
hasOrder("window3", 0)); | |||
Assert.assertTrue( | |||
"The third window position is incorrectly updated via UI after bring all to front", | |||
hasOrderInUi("window3", 0)); | |||
Assert.assertTrue( | |||
"Last window is not attached and should have '-1' position, but hasn't.", | |||
lastWindowHasOrder(-1)); | |||
} | |||
private void checkPositionsAfterDetachingThirdWindow() { | |||
// Detach third window and check order positions of the | |||
// windows | |||
findElement(By.className("detach-window")).click(); | |||
Assert.assertTrue( | |||
"The first window has wrong order position after detach last window", | |||
hasOrder("window2", 1)); | |||
Assert.assertTrue( | |||
"The first window position is incorrectly updated after detach last window", | |||
hasOrderInUi("window2", 1)); | |||
Assert.assertTrue( | |||
"The second window has wrong order position after detach last window", | |||
hasOrder("window1", 0)); | |||
Assert.assertTrue( | |||
"The second window position is incorrectly updated after detach last window", | |||
hasOrderInUi("window1", 0)); | |||
WebElement thirdWindowInfo = findElement(By.className("w3-detached")); | |||
Assert.assertTrue("The third window has wrong order after detach", | |||
thirdWindowInfo.getAttribute("class").contains("w3--1")); | |||
Assert.assertTrue( | |||
"The third window position is incorrectly updated after detach last window", | |||
hasOrderInUi("window3", -1)); | |||
Assert.assertTrue( | |||
"Last window is not attached and should have '-1' position, but hasn't.", | |||
lastWindowHasOrder(-1)); | |||
} | |||
private void checkPositionsAfterNewWindowAttach() { | |||
// Attach new window and check order positions of the | |||
// windows | |||
findElement(By.className("add-window")).click(); | |||
Assert.assertTrue( | |||
"The first window has wrong order position after add new window", | |||
hasOrder("window2", 1)); | |||
Assert.assertTrue( | |||
"The first window position is incorrectly updated after add new window", | |||
hasOrderInUi("window2", 1)); | |||
Assert.assertTrue( | |||
"The second window has wrong order position after add new window", | |||
hasOrder("window1", 0)); | |||
Assert.assertTrue( | |||
"The second window position is incorrectly updated after add new window", | |||
hasOrderInUi("window1", 0)); | |||
Assert.assertTrue( | |||
"The last window has wrong order position after add new window", | |||
hasOrder("window4", 2)); | |||
Assert.assertTrue( | |||
"The last window position is incorrectly updated after add new window", | |||
hasOrderInUi("window4", 2)); | |||
} | |||
private WebElement findElement(String styleName) { | |||
return findElement(By.className(styleName)); | |||
} | |||
private boolean hasOrder(String window, int order) { | |||
WebElement win = findElement(window); | |||
WebElement content = win.findElement(By.className("v-label")); | |||
return content.getText().equals(String.valueOf(order)) && content | |||
.getAttribute("class").contains("event-order" + order); | |||
} | |||
private boolean hasOrderInUi(String window, int order) { | |||
WebElement uiLabel = findElement(By.className("ui-label")); | |||
return uiLabel.getAttribute("class").contains(window + '-' + order); | |||
} | |||
private boolean lastWindowHasOrder(int order) { | |||
WebElement info = findElement("info-label"); | |||
String clazz = info.getAttribute("class"); | |||
String style = "w4-" + order; | |||
boolean hasOrder = clazz.contains(style); | |||
if (!hasOrder) { | |||
return false; | |||
} | |||
clazz = clazz.replace(style, ""); | |||
return !clazz.contains("w4"); | |||
} | |||
} |