summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDenis Anisimov <denis@vaadin.com>2016-11-23 20:11:33 +0300
committerDenis Anisimov <denis@vaadin.com>2016-11-29 16:03:31 +0300
commit8ba529503b6caee2d7755f292fb1f9f985943e78 (patch)
treea84eae038a6f6944947835ac5e121decaef0927b
parentd2ce5362fffe4d85b95762a02f5e68a980af7789 (diff)
downloadvaadin-framework-8ba529503b6caee2d7755f292fb1f9f985943e78.tar.gz
vaadin-framework-8ba529503b6caee2d7755f292fb1f9f985943e78.zip
Provide access to window order position in windows stack (#14325).
Change-Id: I259c659987b5b15b354e16d0be1523f4ede809f0
-rw-r--r--client/src/main/java/com/vaadin/client/ui/VWindow.java72
-rw-r--r--client/src/main/java/com/vaadin/client/ui/ui/UIConnector.java84
-rw-r--r--client/src/main/java/com/vaadin/client/ui/window/WindowConnector.java6
-rw-r--r--client/src/main/java/com/vaadin/client/ui/window/WindowOrderEvent.java74
-rw-r--r--client/src/main/java/com/vaadin/client/ui/window/WindowOrderHandler.java36
-rw-r--r--server/src/main/java/com/vaadin/ui/UI.java122
-rw-r--r--server/src/main/java/com/vaadin/ui/Window.java119
-rw-r--r--shared/src/main/java/com/vaadin/shared/EventId.java1
-rw-r--r--shared/src/main/java/com/vaadin/shared/ui/WindowOrderRpc.java41
-rw-r--r--uitest/src/main/java/com/vaadin/tests/components/window/WindowOrder.java175
-rw-r--r--uitest/src/test/java/com/vaadin/tests/components/window/WindowOrderTest.java182
11 files changed, 900 insertions, 12 deletions
diff --git a/client/src/main/java/com/vaadin/client/ui/VWindow.java b/client/src/main/java/com/vaadin/client/ui/VWindow.java
index 8f1292ca1d..5a316933ff 100644
--- a/client/src/main/java/com/vaadin/client/ui/VWindow.java
+++ b/client/src/main/java/com/vaadin/client/ui/VWindow.java
@@ -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);
+ }
}
diff --git a/client/src/main/java/com/vaadin/client/ui/ui/UIConnector.java b/client/src/main/java/com/vaadin/client/ui/ui/UIConnector.java
index af212c16f4..677abbbecb 100644
--- a/client/src/main/java/com/vaadin/client/ui/ui/UIConnector.java
+++ b/client/src/main/java/com/vaadin/client/ui/ui/UIConnector.java
@@ -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;
+ }
+ };
}
diff --git a/client/src/main/java/com/vaadin/client/ui/window/WindowConnector.java b/client/src/main/java/com/vaadin/client/ui/window/WindowConnector.java
index 3d5d7bbcfd..4e28fb6349 100644
--- a/client/src/main/java/com/vaadin/client/ui/window/WindowConnector.java
+++ b/client/src/main/java/com/vaadin/client/ui/window/WindowConnector.java
@@ -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());
}
}
diff --git a/client/src/main/java/com/vaadin/client/ui/window/WindowOrderEvent.java b/client/src/main/java/com/vaadin/client/ui/window/WindowOrderEvent.java
new file mode 100644
index 0000000000..33517462ea
--- /dev/null
+++ b/client/src/main/java/com/vaadin/client/ui/window/WindowOrderEvent.java
@@ -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;
+ }
+
+}
diff --git a/client/src/main/java/com/vaadin/client/ui/window/WindowOrderHandler.java b/client/src/main/java/com/vaadin/client/ui/window/WindowOrderHandler.java
new file mode 100644
index 0000000000..3a492ef27e
--- /dev/null
+++ b/client/src/main/java/com/vaadin/client/ui/window/WindowOrderHandler.java
@@ -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);
+} \ No newline at end of file
diff --git a/server/src/main/java/com/vaadin/ui/UI.java b/server/src/main/java/com/vaadin/ui/UI.java
index 22dcb437f5..dbca873cc1 100644
--- a/server/src/main/java/com/vaadin/ui/UI.java
+++ b/server/src/main/java/com/vaadin/ui/UI.java
@@ -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);
+ }
}
diff --git a/server/src/main/java/com/vaadin/ui/Window.java b/server/src/main/java/com/vaadin/ui/Window.java
index 84e7ca7c7c..028c585863 100644
--- a/server/src/main/java/com/vaadin/ui/Window.java
+++ b/server/src/main/java/com/vaadin/ui/Window.java
@@ -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;
@@ -110,6 +112,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
*/
public Window() {
@@ -318,6 +329,24 @@ public class Window extends Panel
}
/**
+ * 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}
* mode.
@@ -366,6 +395,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
* {@link CloseListener#windowClose(CloseEvent)} will be called whenever the
diff --git a/shared/src/main/java/com/vaadin/shared/EventId.java b/shared/src/main/java/com/vaadin/shared/EventId.java
index c3a785a988..78580abc41 100644
--- a/shared/src/main/java/com/vaadin/shared/EventId.java
+++ b/shared/src/main/java/com/vaadin/shared/EventId.java
@@ -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";
}
diff --git a/shared/src/main/java/com/vaadin/shared/ui/WindowOrderRpc.java b/shared/src/main/java/com/vaadin/shared/ui/WindowOrderRpc.java
new file mode 100644
index 0000000000..e3cd8290a7
--- /dev/null
+++ b/shared/src/main/java/com/vaadin/shared/ui/WindowOrderRpc.java
@@ -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);
+} \ No newline at end of file
diff --git a/uitest/src/main/java/com/vaadin/tests/components/window/WindowOrder.java b/uitest/src/main/java/com/vaadin/tests/components/window/WindowOrder.java
new file mode 100644
index 0000000000..d3a17006ba
--- /dev/null
+++ b/uitest/src/main/java/com/vaadin/tests/components/window/WindowOrder.java
@@ -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;
+} \ No newline at end of file
diff --git a/uitest/src/test/java/com/vaadin/tests/components/window/WindowOrderTest.java b/uitest/src/test/java/com/vaadin/tests/components/window/WindowOrderTest.java
new file mode 100644
index 0000000000..c3faea3d39
--- /dev/null
+++ b/uitest/src/test/java/com/vaadin/tests/components/window/WindowOrderTest.java
@@ -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");
+ }
+
+}