使用Popup扩展JavaFX Stage内容(?)

时间:2016-01-11 17:52:04

标签: javafx popup stage always-on-top

这是我在这里的第一篇文章 - 请保持友好:)

我要做的是控制/窗口:

  1. 属于JavaFX Stage
  2. 点击
  3. 时不会从拥有阶段窃取焦点
  4. 可以超越拥有舞台的边界
  5. 我已经能够以两种方式实现这一点;然而,两者都有缺陷。首先,我在舞台的内容中制作了可拖动的控件;但是,他们不能被拖到舞台区域之外。其次,我创建了它们作为可拖动的Popups锚定到舞台;但是,即使其他窗口移动到拥有舞台之上,它们仍然在一切之上。

    我要问的是:有没有办法将控件拖出它拥有舞台的范围之外;或者让Popup并不总是出现在所有内容之上?

    我在这里找到了一个类似的问题,解释了Popup在顶部的问题(Javafx popup won't hide behind other application upon losing focus)。但那里没有可接受的解决方案(当舞台或应用程序失去焦点时,我不希望Popup“隐藏”。)

    感谢阅读。如果你能提出任何建议,我们将不胜感激。

    下面是我尝试使用MoveablePopup的MCV示例。它工作正常,除了你在顶部带来额外的窗口。

    package moveablepopuptest;
    
    import javafx.application.Application;
    import javafx.beans.property.DoubleProperty;
    import javafx.beans.property.SimpleDoubleProperty;
    import javafx.beans.value.ChangeListener;
    import javafx.beans.value.ObservableValue;
    import javafx.geometry.Insets;
    import javafx.geometry.Point2D;
    import javafx.scene.Cursor;
    import javafx.scene.Node;
    import javafx.scene.Scene;
    import javafx.scene.control.Label;
    import javafx.scene.input.MouseEvent;
    import javafx.scene.layout.Background;
    import javafx.scene.layout.BackgroundFill;
    import javafx.scene.layout.Border;
    import javafx.scene.layout.BorderPane;
    import javafx.scene.layout.BorderStroke;
    import javafx.scene.layout.BorderStrokeStyle;
    import javafx.scene.layout.BorderWidths;
    import javafx.scene.layout.CornerRadii;
    import javafx.scene.layout.Pane;
    import javafx.scene.layout.StackPane;
    import javafx.scene.paint.Color;
    import javafx.stage.Popup;
    import javafx.stage.Stage;
    
    public class MoveablePopupTest extends Application
    {    
    
        @Override
        public void start(Stage primaryStage)
        {        
            // Set up Primary Stage:
            StackPane root = new StackPane();        
            Scene scene = new Scene(root, 400, 400);        
            primaryStage.setTitle("MoveablePopup Test");
            primaryStage.setScene(scene);
            primaryStage.show();
    
    
            // Show a MoveablePopup for an associated node in the Stage:
            MoveablePopup popup = new MoveablePopup();
            popup.setTitle("Extension");
            Pane popupContent = new Pane();
            popupContent.setBackground(new Background(new BackgroundFill(Color.WHITE, CornerRadii.EMPTY, Insets.EMPTY)));
            popupContent.setMinSize(200, 200);
            popup.setContent(popupContent);
            popup.show(root, 0, 0);        
    
            // When Primary Stage is moved, re-anchor the popup:
            ChangeListener<Number> windowMoveListener = (ObservableValue<? extends Number> observable, Number oldValue, Number newValue) ->
            {
                 popup.anchorToOwner();
            };       
            primaryStage.xProperty().addListener(windowMoveListener);
            primaryStage.yProperty().addListener(windowMoveListener);
        }
    
        public static void main(String[] args)
        {
            launch(args);
        }
    
        public class MoveablePopup extends Popup
        {    
            private final BorderPane bgPane = new BorderPane(); 
            private final DoubleProperty ownerXAnchorProperty = new SimpleDoubleProperty(0);
            private final DoubleProperty ownerYAnchorProperty = new SimpleDoubleProperty(0);        
            private final Label titleLabel = new Label("Title");
            private Point2D lastMouse = null;      
            private Node content = null;
    
            public MoveablePopup()    
            {   
                // Don't want Esc to close the Popup:
                setHideOnEscape(false);
    
                // Create a border:
                bgPane.setBorder(new Border(new BorderStroke(Color.DARKORANGE, BorderStrokeStyle.SOLID, CornerRadii.EMPTY, new BorderWidths(2))));
    
                // Create a title bar for the top, which will also function as a handle to move the Popup:
                StackPane barPane = new StackPane();  
                titleLabel.setTextFill(Color.WHITE);
                barPane.getChildren().add(titleLabel);            
                barPane.setCursor(Cursor.MOVE);
                barPane.setBackground(new Background(new BackgroundFill(Color.DARKORANGE, CornerRadii.EMPTY, Insets.EMPTY)));
                barPane.setMaxSize(Double.MAX_VALUE, 24);
                bgPane.setTop(barPane);                         
    
                // Add drag/anchoring functionality to the Popup
                barPane.setOnMousePressed((MouseEvent event) ->
                {
                    lastMouse = new Point2D(event.getScreenX(), event.getScreenY());
                });
                barPane.setOnMouseDragged((MouseEvent event) ->
                {
                    double moveX = event.getScreenX() - lastMouse.getX();
                    double moveY = event.getScreenY() - lastMouse.getY();
                    ownerXAnchorProperty.set(ownerXAnchorProperty.doubleValue()+moveX);
                    ownerYAnchorProperty.set(ownerYAnchorProperty.doubleValue()+moveY);
                    lastMouse = new Point2D(event.getScreenX(), event.getScreenY());
                    anchorToOwner();
                });
                getContent().add(bgPane);        
    
            }
    
            @Override
            protected void show()
            {
                super.show();
                anchorToOwner();
            }
    
            public void setContent(Node content)
            {
                this.content = content;
                bgPane.setCenter(content);
            }
    
            public void setTitle(String title)
            {
                titleLabel.setText(title);
            }
    
            // Repositions the MoveablePopup so that it's relationship to
            // the owning Node's location is maintained:
            public final void anchorToOwner()
            {
                if (getOwnerNode() != null)
                {
                    Point2D screenPoint = getOwnerNode().localToScreen(0, 0);
                    setX(screenPoint.getX() + ownerXAnchorProperty.doubleValue());
                    setY(screenPoint.getY() + ownerYAnchorProperty.doubleValue());
                }
            }
    
        }   
    }
    

2 个答案:

答案 0 :(得分:0)

创建一个新的舞台,它将是'popup':

Stage childStage = new Stage();

childStage.initModality(Modality.WINDOW_MODAL);
childStage.setTitle("Title of popup");

childStage.setScene(YOUR_SCENE);
childStage.sizeToScene();
childStage.setResizable(false);

childStage.show();

答案 1 :(得分:-1)

正如你已经发现的那样,舞台上发生的一切都停留在舞台之内(请原谅双关语)。因此,如果您需要在舞台外显示某些内容,我们可以排除除Stage之外的所有内容。您可以创建一个新阶段并为所有者分配初始阶段。 Stage,作为原生窗口的代表,将专注于点击。但是,有可能here回到初始阶段。这应该勾选你的需求框。

我不确定您要尝试实现的用例,但request focus提供了对现有JavaFX API的一些扩展,包括通知和JFXtras窗口。看看这对你有什么帮助。