【发布时间】:2018-05-08 18:26:12
【问题描述】:
这个问题涉及跨操作系统的鼠标行为;具体来说,我的代码适用于 Windows 和 Mac OS X,但不适用于 Ubuntu。
最终我要做的是创建一个特殊的窗格子类(“ConvertiblePane”),它存在于主舞台/场景的父窗格中,但在拖动时神奇地转移到自己的临时舞台/场景中,从而成为独立并且能够放置在屏幕上任何地方。当用户释放鼠标按钮时,ConvertiblePane 应返回到其原始父窗格并失去临时阶段。 (在我的完整程序中,原始父 Stage 会自行调整大小/重新定位以适应 ConvertiblePane 被丢弃的位置。)
这让我想到了我的问题。当我在 ConvertiblePane 上按下鼠标时,它会按预期在主场景中触发 MousePress,此时 ConvertiblePane 被移动到临时舞台。当我拖动鼠标时,它会在临时场景中触发 MouseDrag 并移动临时舞台。好的,太好了。
但是,当我释放鼠标按钮时,我在不同的操作系统上会遇到不同的行为。在 Windows (7) 和 Mac OS X (10.12.6) 上,MouseRelease 发生在临时场景中,按预期将窗格发送回主舞台中的原始父级。然而,在 Ubuntu 上,无论是主场景还是临时场景,似乎都没有生成 MouseRelease。
以下是作为 MCV 示例的相关代码:
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Point2D;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.Background;
import javafx.scene.layout.BackgroundFill;
import javafx.scene.layout.CornerRadii;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
public class ConvertibleTest extends Application {
@Override
public void start(Stage primaryStage) {
// Set up the main stage and scene with a
// Pane as root and a ConvertiblePane child:
primaryStage.initStyle(StageStyle.TRANSPARENT);
Pane root = new Pane();
ConvertiblePane conv = new ConvertiblePane();
root.getChildren().add(conv);
Scene scene = new Scene(root, 400, 400, Color.PINK);
primaryStage.setTitle("Convertible Test");
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
class ConvertiblePane extends Pane
{
private final Group TEMP_ROOT = new Group();
private final Stage TEMP_STAGE = new Stage(StageStyle.TRANSPARENT);
private final Scene TEMP_SCENE = new Scene(TEMP_ROOT);
private Pane originalParent = null;
private double deltaX = 0.0;
private double deltaY = 0.0;
private String name = null;
public void onMousePress(MouseEvent event)
{
// Save deltaX/Y for later:
Point2D delta = this.sceneToLocal(event.getX(), event.getY());
deltaX = delta.getX();
deltaY = delta.getY();
if (!isIndependent())
{
makeIndependent();
}
}
public void onMouseDrag(MouseEvent event)
{
// Keep the TEMP_STAGE relative to the original click point:
TEMP_STAGE.setX(event.getScreenX()-deltaX);
TEMP_STAGE.setY(event.getScreenY()-deltaY);
}
public void onMouseRelease(MouseEvent event)
{
if (isIndependent())
{
returnToParent();
}
}
public ConvertiblePane()
{
this.setPrefSize(100, 100);
this.setBackground(new Background(new BackgroundFill(Color.GREEN, new CornerRadii(10), Insets.EMPTY)));
this.setVisible(true);
// Attach event code and report to System.out what is happening:
this.setOnMousePressed((MouseEvent event) -> {
if (this.getScene() == TEMP_SCENE)
System.out.println("Pressed as Independent");
else
System.out.println("Pressed as Child");
onMousePress(event);
});
this.setOnMouseDragged((MouseEvent event) -> {
if (this.getScene() == TEMP_SCENE)
System.out.println("Dragged as Independent");
else
System.out.println("Dragged as Child");
onMouseDrag(event);
});
this.setOnMouseReleased((MouseEvent event) -> {
if (this.getScene() == TEMP_SCENE)
System.out.println("Released as Independent");
else
System.out.println("Released as Child");
onMouseRelease(event);
});
}
public boolean isIndependent()
{
// Return whether this ConvertiblePane is "independent" (exists in its own temp scene)
return this.getScene() == TEMP_SCENE;
}
public void makeIndependent()
{
// Get the point where this ConvertiblePane appears on screen:
Point2D screenPt = this.localToScreen(0, 0);
// Save the originaParent of this ConvertiblePane; we will return to it later:
originalParent = (Pane)getParent();
// Remove this ConvertiblePane from its originalParent:
originalParent.getChildren().remove(this);
// Set this ConvertiblePane as the root of the TEMP_SCENE on the TEMP_STAGE:
TEMP_SCENE.setRoot(this);
TEMP_STAGE.setScene(TEMP_SCENE);
System.out.println("Transferred to TEMP.");
this.relocate(0, 0);
// Show the TEMP_STAGE in the same location on screen where this ConvertiblePane originally was:
TEMP_STAGE.setX(screenPt.getX());
TEMP_STAGE.setY(screenPt.getY());
TEMP_STAGE.show();
}
public void returnToParent()
{
// Reset deltas:
deltaX = 0;
deltaY = 0;
// Get the location of this ConvertiblePane on screen:
Point2D screenPt = this.localToScreen(0, 0);
// Set TEMP_ROOT as the root of TEMP_SCENE; this will allow us to detach
// this ConvertiblePane from being the scene root (since root cannot == null).
TEMP_SCENE.setRoot(TEMP_ROOT);
// Hide the TEMP_STAGE:
TEMP_STAGE.hide();
// Add this ConvertiblePane back to the originalParent:
originalParent.getChildren().add(this);
System.out.println("Transferred to MAIN.");
// Relocate this ConvertiblePane within the originalParent to maintain its position on screen
Point2D parentPt = originalParent.screenToLocal(screenPt);
this.relocate(parentPt.getX(), parentPt.getY());
}
}
如您所见,事件处理方法中有一些基本的报告; makeIndependent() 和 returnToParent() 方法输出“转移到 TEMP”。和“转移到主要”。分别。
如果我在 ConvertiblePane 上单击鼠标,拖动几个像素,然后松开,输出如下:
(on Windows or Mac OS X)
Pressed as Child
Transferred to TEMP.
Dragged as Independent
Dragged as Independent
Dragged as Independent
Released as Independent
Transferred to MAIN.
(on Ubuntu)
Pressed as Child
Transferred to TEMP.
Dragged as Independent
Dragged as Independent
Dragged as Independent
我也尝试在两个场景中添加事件过滤器;但结果是一样的:MouseRelease 发生在 Win/Mac,但不是 Ubuntu。
如果有人能解释这种行为,或者提出一些建议,那就太好了。或者......我可以捕捉到 MouseEvents 的任何“全局”(场景前)创建吗?我的意思是,我并不真正关心鼠标释放的细节;我只是想要一个事件来告诉我何时将 ConvertiblePane 添加回主舞台。
谢谢!
【问题讨论】:
-
仅供参考,我在 Ubuntu 上使用 Oracle JDK 1.8,而不是 Open JDK。不确定这是否会有所作为。
标签: java ubuntu javafx mouseevent