changeset 6196:facbab6abf0b

RT-153 [Stage] Option to keep Stages always on top Reviewed by: anthony, snorthov
author Martin Sladecek <martin.sladecek@oracle.com>
date Mon, 27 Jan 2014 08:46:12 +0100
parents 7fe2dcba784d
children ca535e7912ef
files apps/toys/Hello/src/main/java/hello/HelloStageOnTop.java modules/graphics/src/main/java/com/sun/glass/ui/Window.java modules/graphics/src/main/java/com/sun/javafx/tk/TKStage.java modules/graphics/src/main/java/com/sun/javafx/tk/quantum/EmbeddedStage.java modules/graphics/src/main/java/com/sun/javafx/tk/quantum/WindowStage.java modules/graphics/src/main/java/javafx/stage/Stage.java modules/graphics/src/main/native-glass/gtk/glass_window.cpp modules/graphics/src/main/native-glass/gtk/glass_window.h modules/graphics/src/test/java/com/sun/javafx/pgstub/StubStage.java
diffstat 9 files changed, 243 insertions(+), 21 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/apps/toys/Hello/src/main/java/hello/HelloStageOnTop.java	Mon Jan 27 08:46:12 2014 +0100
@@ -0,0 +1,115 @@
+package hello;
+/*
+ * Copyright (c) 2011, 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import javafx.application.Application;
+import javafx.beans.value.ChangeListener;
+import javafx.beans.value.ObservableValue;
+import javafx.event.ActionEvent;
+import javafx.event.EventHandler;
+import javafx.geometry.Insets;
+import javafx.scene.Scene;
+import javafx.scene.control.Button;
+import javafx.scene.control.CheckBox;
+import javafx.scene.control.ToggleButton;
+import javafx.scene.layout.VBox;
+import javafx.stage.Stage;
+
+public class HelloStageOnTop extends Application{
+
+    public static final String ENABLE_ON_TOP = "Enable on top";
+    public static final String DISABLE_ON_TOP = "Disable on top";
+
+    @Override
+    public void start(Stage primaryStage) throws Exception {
+        Button button = new Button("Open Root stage with child");
+        CheckBox box = new CheckBox("Root is always on top");
+        box.setSelected(true);
+        VBox root = new VBox(15, box, button);
+        root.setPadding(new Insets(20));
+        Scene scene = new Scene(root);
+
+
+        button.setOnAction(new EventHandler<ActionEvent>() {
+            @Override
+            public void handle(ActionEvent event) {
+                createNewStage(0, null, box.isSelected());
+            }
+        });
+
+        primaryStage.setScene(scene);
+        primaryStage.show();
+    }
+
+    private void createNewStage(int level, Stage owner, boolean onTop) {
+        Stage stage = new Stage();
+        stage.initOwner(owner);
+        stage.setTitle(level == 0 ? "Root" : "Child " + level);
+        stage.setAlwaysOnTop(onTop);
+
+        VBox root = new VBox(15);
+        root.setPadding(new Insets(20));
+        Scene scene = new Scene(root);
+        stage.setScene(scene);
+
+        ToggleButton onTopButton = new ToggleButton(onTop ? DISABLE_ON_TOP : ENABLE_ON_TOP);
+        onTopButton.setSelected(onTop);
+
+        stage.alwaysOnTopProperty().addListener(new ChangeListener<Boolean>() {
+            @Override
+            public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
+                onTopButton.setSelected(newValue);
+                onTopButton.setText(newValue ? DISABLE_ON_TOP : ENABLE_ON_TOP);
+            }
+        });
+
+        onTopButton.setOnAction(new EventHandler<ActionEvent>() {
+            @Override
+            public void handle(ActionEvent event) {
+                stage.setAlwaysOnTop(!stage.isAlwaysOnTop());
+            }
+        });
+
+        CheckBox box = new CheckBox("Child stage always on top");
+        box.setSelected(true);
+        Button newStageButton = new Button("Open child stage");
+
+        newStageButton.setOnAction(new EventHandler<ActionEvent>() {
+            @Override
+            public void handle(ActionEvent event) {
+                createNewStage(level + 1, stage, box.isSelected());
+            }
+        });
+
+        root.getChildren().addAll(onTopButton, box, newStageButton);
+
+        stage.show();
+    }
+
+
+    public static void main(String[] args) {
+        launch(args);
+    }
+}
--- a/modules/graphics/src/main/java/com/sun/glass/ui/Window.java	Mon Jan 27 16:17:44 2014 +1300
+++ b/modules/graphics/src/main/java/com/sun/glass/ui/Window.java	Mon Jan 27 08:46:12 2014 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -38,7 +38,7 @@
     public static class EventHandler {
         public void handleWindowEvent(Window window, long time, int type) {
         }
-
+        
         /**
          * Notifies a listener that the screen object for this Window instance
          * has been updated.
@@ -816,10 +816,10 @@
         if (level < Level._MIN || level > Level._MAX) {
             throw new IllegalArgumentException("Level should be in the range [" + Level._MIN + ".." + Level._MAX + "]");
         }
-
-        _setLevel(this.ptr, level);
-
-        this.level = level;
+        if (this.level != level) {
+            _setLevel(this.ptr, level);
+            this.level = level;
+        }
     }
 
     public int getLevel() {
@@ -1226,7 +1226,7 @@
     protected void notifyInitAccessibility() {
         handleWindowEvent(System.nanoTime(), WindowEvent.INIT_ACCESSIBILITY);
     }
-
+    
     // *****************************************************
     // window event handlers
     // *****************************************************
--- a/modules/graphics/src/main/java/com/sun/javafx/tk/TKStage.java	Mon Jan 27 16:17:44 2014 +1300
+++ b/modules/graphics/src/main/java/com/sun/javafx/tk/TKStage.java	Mon Jan 27 08:46:12 2014 +0100
@@ -105,6 +105,8 @@
     public void setIconified(boolean iconified);
 
     public void setMaximized(boolean maximized);
+    
+    public void setAlwaysOnTop(boolean alwaysOnTop);
 
     public void setResizable(boolean resizable);
 
--- a/modules/graphics/src/main/java/com/sun/javafx/tk/quantum/EmbeddedStage.java	Mon Jan 27 16:17:44 2014 +1300
+++ b/modules/graphics/src/main/java/com/sun/javafx/tk/quantum/EmbeddedStage.java	Mon Jan 27 08:46:12 2014 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -134,6 +134,13 @@
     }
 
     @Override
+    public void setAlwaysOnTop(boolean alwaysOnTop) {
+        if (QuantumToolkit.verbose) {
+            System.err.println("EmbeddedScene.setAlwaysOnTop " + alwaysOnTop);
+        }
+    }
+
+    @Override
     public void setResizable(boolean resizable) {
         if (QuantumToolkit.verbose) {
             System.err.println("EmbeddedStage.setResizable " + resizable);
--- a/modules/graphics/src/main/java/com/sun/javafx/tk/quantum/WindowStage.java	Mon Jan 27 16:17:44 2014 +1300
+++ b/modules/graphics/src/main/java/com/sun/javafx/tk/quantum/WindowStage.java	Mon Jan 27 08:46:12 2014 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2008, 2014, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -44,6 +44,7 @@
 
 import com.sun.glass.events.WindowEvent;
 import com.sun.glass.ui.*;
+import com.sun.glass.ui.Window.Level;
 import com.sun.glass.ui.accessible.AccessibleBaseProvider;
 import com.sun.glass.ui.accessible.AccessibleRoot;
 import com.sun.javafx.PlatformUtil;
@@ -494,6 +495,18 @@
         platformWindow.maximize(maximized);
     }
 
+    @Override
+    public void setAlwaysOnTop(boolean alwaysOnTop) {
+        if (alwaysOnTop) {
+            if (hasPermission(alwaysOnTopPermission)) {
+                platformWindow.setLevel(Level.FLOATING);
+            }
+        } else {
+            platformWindow.setLevel(Level.NORMAL);
+        }
+        
+    }
+
     @Override public void setResizable(boolean resizable) {
         platformWindow.setResizable(resizable);
         // note: for child windows this is ignored and we fail silently
@@ -530,6 +543,7 @@
     // now AllPermission is good enough to do the job we need, such
     // as fullscreen support for signed/unsigned application.
     private static final Permission fullScreenPermission = new AllPermission();
+    private static final Permission alwaysOnTopPermission = new AllPermission();
 
     private boolean fullScreenFromUserEvent = false;
 
--- a/modules/graphics/src/main/java/javafx/stage/Stage.java	Mon Jan 27 16:17:44 2014 +1300
+++ b/modules/graphics/src/main/java/javafx/stage/Stage.java	Mon Jan 27 08:46:12 2014 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -778,6 +778,52 @@
         }
         return maximized;
     }
+    
+    /**
+     * Defines whether this {@code Stage} is kept on top of other windows.
+     * <p>
+     * If some other window is already always-on-top then the
+     * relative order between these windows is unspecified (depends on
+     * platform).
+     * </p>
+     * <p>
+     * There are differences in behavior between applications if a security 
+     * manager is present. Applications with permissions are allowed to set
+     * "always on top" flag on a Stage. In applications without the proper 
+     * permissions, an attempt to set the flag will be ignored and the property
+     * value will be restored to "false".
+     * </p>
+     * <p>
+     * The property is read only because it can be changed externally
+     * by the underlying platform and therefore must not be bindable.
+     * </p>
+     * 
+     * @defaultValue false
+     * @since JavaFX 8u20
+     */
+    private ReadOnlyBooleanWrapper alwaysOnTop;
+    
+    public final void setAlwaysOnTop(boolean value) {
+        alwaysOnTopPropertyImpl().set(value);
+        if (impl_peer != null) {
+            impl_peer.setAlwaysOnTop(value);
+        }
+    }
+
+    public final boolean isAlwaysOnTop() {
+        return alwaysOnTop == null ? false : alwaysOnTop.get();
+    }
+
+    public final ReadOnlyBooleanProperty alwaysOnTopProperty() {
+        return alwaysOnTopPropertyImpl().getReadOnlyProperty();
+    }
+
+    private ReadOnlyBooleanWrapper alwaysOnTopPropertyImpl() {
+        if (alwaysOnTop == null) {
+            alwaysOnTop = new ReadOnlyBooleanWrapper(Stage.this, "alwaysOnTop");
+        }
+        return alwaysOnTop;
+    }
 
     /**
      * Defines whether the {@code Stage} is resizable or not by the user.
@@ -1082,6 +1128,7 @@
             impl_peer.setImportant(isImportant());
             impl_peer.setResizable(isResizable());
             impl_peer.setFullScreen(isFullScreen());
+            impl_peer.setAlwaysOnTop(isAlwaysOnTop());
             impl_peer.setIconified(isIconified());
             impl_peer.setMaximized(isMaximized());
             impl_peer.setTitle(getTitle());
--- a/modules/graphics/src/main/native-glass/gtk/glass_window.cpp	Mon Jan 27 16:17:44 2014 +1300
+++ b/modules/graphics/src/main/native-glass/gtk/glass_window.cpp	Mon Jan 27 08:46:12 2014 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2014, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -120,7 +120,7 @@
         }
 
         notify_state(stateChangeEvent);
-    }
+    } 
 }
 
 void WindowContextBase::process_focus(GdkEventFocus* event) {
@@ -622,7 +622,8 @@
             frame_extents_initialized(),
             map_received(false),
             location_assigned(false),
-            size_assigned(false)
+            size_assigned(false),
+            on_top(false)
 {
     jwindow = mainEnv->NewGlobalRef(_jwindow);
 
@@ -630,6 +631,9 @@
 
     if (owner) {
         owner->add_child(this);
+        if (on_top_inherited()) {
+            gtk_window_set_keep_above(GTK_WINDOW(gtk_widget), TRUE);
+        }
     }
 
     if (type == UTILITY) {
@@ -839,9 +843,9 @@
         if (is_iconified != is_hidden) {
             is_iconified = is_hidden;
 
-            notify_state((is_hidden) 
-                            ? com_sun_glass_events_WindowEvent_MINIMIZE 
-                            : com_sun_glass_events_WindowEvent_RESTORE);
+            notify_state((is_hidden)
+                    ? com_sun_glass_events_WindowEvent_MINIMIZE
+                    : com_sun_glass_events_WindowEvent_RESTORE);
         }
     }
 }
@@ -1317,12 +1321,38 @@
     geometry_set_window_y(&geometry, oldY);
 }
 
+void WindowContextTop::update_ontop_tree(bool on_top) {
+    bool effective_on_top = on_top || this->on_top;
+    gtk_window_set_keep_above(GTK_WINDOW(gtk_widget), effective_on_top ? TRUE : FALSE);
+    for (std::set<WindowContextTop*>::iterator it = children.begin(); it != children.end(); ++it) {
+        (*it)->update_ontop_tree(effective_on_top);
+    }
+}
+
+bool WindowContextTop::on_top_inherited() {
+    WindowContext* o = owner;
+    while (o) {
+        WindowContextTop* topO = dynamic_cast<WindowContextTop*>(o);
+        if (!topO) break;
+        if (topO->on_top) {
+            return true;
+        }
+        o = topO->owner;
+    }
+    return false;
+}
+
 void WindowContextTop::set_level(int level) {
     if (level == com_sun_glass_ui_Window_Level_NORMAL) {
-        gtk_window_set_keep_above(GTK_WINDOW(gtk_widget), FALSE);
+        on_top = false;
     } else if (level == com_sun_glass_ui_Window_Level_FLOATING
             || level == com_sun_glass_ui_Window_Level_TOPMOST) {
-        gtk_window_set_keep_above(GTK_WINDOW(gtk_widget), TRUE);
+        on_top = true;
+    }
+    // We need to emulate always on top behaviour on child windows
+
+    if (!on_top_inherited()) {
+        update_ontop_tree(on_top);
     }
 }
 
--- a/modules/graphics/src/main/native-glass/gtk/glass_window.h	Mon Jan 27 16:17:44 2014 +1300
+++ b/modules/graphics/src/main/native-glass/gtk/glass_window.h	Mon Jan 27 08:46:12 2014 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2014, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -176,7 +176,6 @@
 };
 
 class WindowContextBase: public WindowContext {
-    std::set<WindowContextTop*> children;
 
     struct _XIM{
         XIM im;
@@ -187,6 +186,7 @@
     size_t events_processing_cnt;
     bool can_be_deleted;
 protected:
+    std::set<WindowContextTop*> children;
     jobject jwindow;
     jobject jview;
     GtkWidget* gtk_widget;
@@ -373,6 +373,7 @@
     bool map_received;
     bool location_assigned;
     bool size_assigned;
+    bool on_top;
 public:
     WindowContextTop(jobject, WindowContext*, long, WindowFrameType, WindowType);
     void process_map();
@@ -417,6 +418,8 @@
     void window_configure(XWindowChanges *, unsigned int);
     void update_window_constraints();
     void set_window_resizable(bool, bool);
+    void update_ontop_tree(bool);
+    bool on_top_inherited();
     WindowContextTop(WindowContextTop&);
     WindowContextTop& operator= (const WindowContextTop&);
 };
--- a/modules/graphics/src/test/java/com/sun/javafx/pgstub/StubStage.java	Mon Jan 27 16:17:44 2014 +1300
+++ b/modules/graphics/src/test/java/com/sun/javafx/pgstub/StubStage.java	Mon Jan 27 08:46:12 2014 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2014, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -179,6 +179,10 @@
     }
 
     @Override
+    public void setAlwaysOnTop(boolean alwaysOnTop) {
+    }
+
+    @Override
     public void setResizable(boolean resizable) {
         notificationSender.changedResizable(resizable);
     }