来自 AWT JFrame 的真正应用程序模态 SWT shell
True application modal SWT shell from AWT JFrame
我有一个 Java Swing 应用程序,我正在向它嵌入一个 SWT 小部件。我试图从我的 AWT JFrame
中显示一个 SWT Shell
,但无法使其成为应用程序模式。 JFrame
仍然可以聚焦,按钮点击将注册到 EDT。要使 Shell
表现得像模态 AWT 对话框,我需要采取哪些步骤?
我已阅读 this outdated tutorial,但它仅解释了如果应用程序在 SWT 事件线程上 运行 如何执行此操作。我也尝试用模态 JDialog
破解它,但这种行为充其量是丑陋的。从最小工作示例中删除注释以对其进行演示。 SWT.ON_TOP
本身就很麻烦,因为 shell
将停留在每个 window.
之上
This question 没有帮助。
SSCE
public class ModalDialogExample extends JFrame {
public ModalDialogExample()
{
this.setSize(500, 500);
this.setDefaultCloseOperation(DISPOSE_ON_CLOSE);
}
public static void main(String[] args)
{
JFrame frame = new ModalDialogExample();
JButton button = new JButton("CLick me");
button.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
// JDialog modalDialog = new JDialog();
// modalDialog.setSize(new Dimension(0,0));
// modalDialog.setModalityType(ModalityType.APPLICATION_MODAL);
Display display = new Display();
Shell shell = new Shell(display, SWT.CLOSE | SWT.TITLE | SWT.BORDER | SWT.OK | SWT.ON_TOP | SWT.APPLICATION_MODAL);
shell.setSize(200,200);
shell.open();
shell.forceActive();
// modalDialog.setVisible(true);
while (!shell.isDisposed()) {
if (!display.readAndDispatch())
display.sleep();
}
display.dispose();
}
});
frame.getContentPane().add(button);
frame.setVisible(true);
}
}
这是我想出的。它并不完美,但可以完成工作。回想一下,我有一个 JFrame
在其上我需要一个模态 SWT Shell
表现得像一个模态 JDialog
.
启动的Shell
不能在AWT事件派发线程中存活。我们需要访问线程以稍后拦截鼠标和 window 事件。所以在新的 Thread
中启动 Shell
我在 JFrame
上放置了一个 GlassPane
,因为 Shell
打开了。目的是阻止框架中的其他交互式 Swing 组件及其容器,并拦截所有鼠标事件。我需要鼠标事件,所以我不能简单地做 Frame.setEnabled(false)
.
我在框架上使用了 WindowListener
,在 GlassPane
上使用了 MouseListener
我在 Shell
上使用了 DisposeListener
来检测 JFrame
必须丢失 GlassPane
的时刻以及为Shell
的直播时间。
为了使用我的 Swing 侦听器控制 Shell
的可见性,我需要访问 SWT 事件线程。 这是通过执行 Runnable
与 shell.getDisplay().syncExec(...)
这就是我启动 Shell
、激活 GlassPane
、附加 "modality listeners" 并在 Shell
关闭时删除它们的方式。 Shell
在新线程中启动。
new Thread(new Runnable() {
@Override
public void run() {
final Shell shell = myWidget.getShell();
final EditorWindowListener ewl = new EditorWindowListener(shell);
myFrame.addWindowListener(ewl);
final EditorClickListener ecl = new EditorClickListener(shell);
myFrame.getGlassPane().addMouseListener(ecl);
myFrame.getGlassPane().setVisible(true);
shell.addDisposeListener(new DisposeListener() {
//Remove the disabled status
@Override
public void widgetDisposed(DisposeEvent arg0) {
myFrame.removeWindowListener(ewl);
myFrame.getGlassPane().removeMouseListener(ecl);
myFrame.getGlassPane().setVisible(false);
}
});
//The method that starts the shell
myWidget.show();
}
}).start();
这就是 myWidget.show()
中发生的事情(标准 SWT 内容,没有修改)
shell.open();
shell.forceActive();
while (!shell.isDisposed()) {
if (!display.readAndDispatch())
display.sleep();
}
display.dispose();
下面是我添加到 frame 和 glassPane 的两个监听器。首先,MouseListener
检测玻璃窗格上的点击,如果捕获到任何事件,则将 Shell
带到顶部。我知道在完美的模态对话框中不需要它,而且在这种情况下也不需要,但是一些双显示器设置导致的问题已通过此侦听器解决。
class EditorClickListener extends MouseAdapter
{
private Shell shell;
public EditorClickListener(Shell s)
{
this.shell = s;
}
@Override
public void mouseClicked(MouseEvent e) {
shellToFront(shell);
}
}
然后WindowListener
附在框架上。它确保在框架处于活动状态时,shell 跳到顶部。
class EditorWindowListener extends WindowAdapter
{
Shell shell;
public EditorWindowListener(Shell s)
{
this.shell = s;
}
@Override
public void windowOpened(WindowEvent e) {
shellToFront(shell);
}
@Override
public void windowDeiconified(WindowEvent e) {
shellToFront(shell);
}
@Override
public void windowActivated(WindowEvent e) {
//Set the shell on top of the frame
//Fixes some problems with dual monitor setups.
final java.awt.Point framePoint = myFrame.getLocation();
shellToFront(shell);
shell.getDisplay().syncExec(new Runnable() {
@Override
public void run() {
shell.setMinimized(false);
shell.setActive();
org.eclipse.swt.graphics.Point shellPoint = aikataulu.getLocation();
shellPoint.x = (int) framePoint.getX();
shellPoint.y = (int) framePoint.getY();
shell.setLocation(shellPoint);
}
});
}
}
最后是将 shell 弹出到您的脸上以创建模态感觉的方法。请注意,所有事件都需要分配才能在 SWT 事件线程中发生。我在将最小化状态设置为 false
时遇到了一些麻烦。所以我不得不添加一个人为的最小化,以防 Shell
没有最小化并且确实落后于其他 windows。这在某些用例中会导致愚蠢的不必要的动画,但现在它会做
private void shellToFront(final Shell shell)
{
shell.getDisplay().syncExec(new Runnable() {
@Override
public void run() {
if (!shell.getMinimized())
{
shell.setMinimized(true);
}
shell.setMinimized(false);
shell.setActive();
}
});
}
我有一个 Java Swing 应用程序,我正在向它嵌入一个 SWT 小部件。我试图从我的 AWT JFrame
中显示一个 SWT Shell
,但无法使其成为应用程序模式。 JFrame
仍然可以聚焦,按钮点击将注册到 EDT。要使 Shell
表现得像模态 AWT 对话框,我需要采取哪些步骤?
我已阅读 this outdated tutorial,但它仅解释了如果应用程序在 SWT 事件线程上 运行 如何执行此操作。我也尝试用模态 JDialog
破解它,但这种行为充其量是丑陋的。从最小工作示例中删除注释以对其进行演示。 SWT.ON_TOP
本身就很麻烦,因为 shell
将停留在每个 window.
This question 没有帮助。
SSCE
public class ModalDialogExample extends JFrame {
public ModalDialogExample()
{
this.setSize(500, 500);
this.setDefaultCloseOperation(DISPOSE_ON_CLOSE);
}
public static void main(String[] args)
{
JFrame frame = new ModalDialogExample();
JButton button = new JButton("CLick me");
button.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
// JDialog modalDialog = new JDialog();
// modalDialog.setSize(new Dimension(0,0));
// modalDialog.setModalityType(ModalityType.APPLICATION_MODAL);
Display display = new Display();
Shell shell = new Shell(display, SWT.CLOSE | SWT.TITLE | SWT.BORDER | SWT.OK | SWT.ON_TOP | SWT.APPLICATION_MODAL);
shell.setSize(200,200);
shell.open();
shell.forceActive();
// modalDialog.setVisible(true);
while (!shell.isDisposed()) {
if (!display.readAndDispatch())
display.sleep();
}
display.dispose();
}
});
frame.getContentPane().add(button);
frame.setVisible(true);
}
}
这是我想出的。它并不完美,但可以完成工作。回想一下,我有一个 JFrame
在其上我需要一个模态 SWT Shell
表现得像一个模态 JDialog
.
启动的
Shell
不能在AWT事件派发线程中存活。我们需要访问线程以稍后拦截鼠标和 window 事件。所以在新的Thread
中启动 我在
JFrame
上放置了一个GlassPane
,因为Shell
打开了。目的是阻止框架中的其他交互式 Swing 组件及其容器,并拦截所有鼠标事件。我需要鼠标事件,所以我不能简单地做Frame.setEnabled(false)
.我在框架上使用了
WindowListener
,在GlassPane
上使用了 我在
Shell
上使用了DisposeListener
来检测JFrame
必须丢失GlassPane
的时刻以及为Shell
的直播时间。为了使用我的 Swing 侦听器控制
Shell
的可见性,我需要访问 SWT 事件线程。 这是通过执行Runnable
与shell.getDisplay().syncExec(...)
Shell
MouseListener
这就是我启动 Shell
、激活 GlassPane
、附加 "modality listeners" 并在 Shell
关闭时删除它们的方式。 Shell
在新线程中启动。
new Thread(new Runnable() {
@Override
public void run() {
final Shell shell = myWidget.getShell();
final EditorWindowListener ewl = new EditorWindowListener(shell);
myFrame.addWindowListener(ewl);
final EditorClickListener ecl = new EditorClickListener(shell);
myFrame.getGlassPane().addMouseListener(ecl);
myFrame.getGlassPane().setVisible(true);
shell.addDisposeListener(new DisposeListener() {
//Remove the disabled status
@Override
public void widgetDisposed(DisposeEvent arg0) {
myFrame.removeWindowListener(ewl);
myFrame.getGlassPane().removeMouseListener(ecl);
myFrame.getGlassPane().setVisible(false);
}
});
//The method that starts the shell
myWidget.show();
}
}).start();
这就是 myWidget.show()
中发生的事情(标准 SWT 内容,没有修改)
shell.open();
shell.forceActive();
while (!shell.isDisposed()) {
if (!display.readAndDispatch())
display.sleep();
}
display.dispose();
下面是我添加到 frame 和 glassPane 的两个监听器。首先,MouseListener
检测玻璃窗格上的点击,如果捕获到任何事件,则将 Shell
带到顶部。我知道在完美的模态对话框中不需要它,而且在这种情况下也不需要,但是一些双显示器设置导致的问题已通过此侦听器解决。
class EditorClickListener extends MouseAdapter
{
private Shell shell;
public EditorClickListener(Shell s)
{
this.shell = s;
}
@Override
public void mouseClicked(MouseEvent e) {
shellToFront(shell);
}
}
然后WindowListener
附在框架上。它确保在框架处于活动状态时,shell 跳到顶部。
class EditorWindowListener extends WindowAdapter
{
Shell shell;
public EditorWindowListener(Shell s)
{
this.shell = s;
}
@Override
public void windowOpened(WindowEvent e) {
shellToFront(shell);
}
@Override
public void windowDeiconified(WindowEvent e) {
shellToFront(shell);
}
@Override
public void windowActivated(WindowEvent e) {
//Set the shell on top of the frame
//Fixes some problems with dual monitor setups.
final java.awt.Point framePoint = myFrame.getLocation();
shellToFront(shell);
shell.getDisplay().syncExec(new Runnable() {
@Override
public void run() {
shell.setMinimized(false);
shell.setActive();
org.eclipse.swt.graphics.Point shellPoint = aikataulu.getLocation();
shellPoint.x = (int) framePoint.getX();
shellPoint.y = (int) framePoint.getY();
shell.setLocation(shellPoint);
}
});
}
}
最后是将 shell 弹出到您的脸上以创建模态感觉的方法。请注意,所有事件都需要分配才能在 SWT 事件线程中发生。我在将最小化状态设置为 false
时遇到了一些麻烦。所以我不得不添加一个人为的最小化,以防 Shell
没有最小化并且确实落后于其他 windows。这在某些用例中会导致愚蠢的不必要的动画,但现在它会做
private void shellToFront(final Shell shell)
{
shell.getDisplay().syncExec(new Runnable() {
@Override
public void run() {
if (!shell.getMinimized())
{
shell.setMinimized(true);
}
shell.setMinimized(false);
shell.setActive();
}
});
}