X-Git-Url: https://gerrit.simantics.org/r/gitweb?a=blobdiff_plain;f=bundles%2Forg.simantics.utils.ui%2Fsrc%2Forg%2Fsimantics%2Futils%2Fui%2Finternal%2Fawt%2FSwtFocusHandler.java;fp=bundles%2Forg.simantics.utils.ui%2Fsrc%2Forg%2Fsimantics%2Futils%2Fui%2Finternal%2Fawt%2FSwtFocusHandler.java;h=9c73ab6f9669b12dfe5a1a4898152241de9d5171;hb=ac5f1da15cc639da880fea86a7b828c8fa2e1b7e;hp=0000000000000000000000000000000000000000;hpb=b8392422ec5b5961202d941ec447018526d26dd2;p=simantics%2Fplatform.git diff --git a/bundles/org.simantics.utils.ui/src/org/simantics/utils/ui/internal/awt/SwtFocusHandler.java b/bundles/org.simantics.utils.ui/src/org/simantics/utils/ui/internal/awt/SwtFocusHandler.java new file mode 100644 index 000000000..9c73ab6f9 --- /dev/null +++ b/bundles/org.simantics.utils.ui/src/org/simantics/utils/ui/internal/awt/SwtFocusHandler.java @@ -0,0 +1,162 @@ +/******************************************************************************* + * Copyright (c) 2007 SAS Institute. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * SAS Institute - initial API and implementation + *******************************************************************************/ +package org.simantics.utils.ui.internal.awt; + +import java.awt.EventQueue; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.ControlAdapter; +import org.eclipse.swt.events.ControlEvent; +import org.eclipse.swt.events.DisposeEvent; +import org.eclipse.swt.events.DisposeListener; +import org.eclipse.swt.events.FocusEvent; +import org.eclipse.swt.events.FocusListener; +import org.eclipse.swt.events.KeyEvent; +import org.eclipse.swt.events.KeyListener; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Shell; + +public class SwtFocusHandler implements FocusListener, KeyListener { + + private Composite composite; + private final Display display; + private AwtFocusHandler awtHandler; + + public SwtFocusHandler(Composite composite) { + assert composite != null; + assert Display.getCurrent() != null; // On SWT event thread + + this.composite = composite; + display = composite.getDisplay(); + composite.addFocusListener(this); + composite.addKeyListener(this); + } + + public void setAwtHandler(AwtFocusHandler handler) { + assert handler != null; + assert awtHandler == null; // this method is meant to be called once + assert composite != null; + assert Display.getCurrent() != null; // On SWT event thread + + awtHandler = handler; + + // Dismiss Swing popups when the main window is moved. (It would be + // better to dismiss popups whenever the titlebar is clicked, but + // there does not seem to be a way.) + final ControlAdapter controlAdapter = new ControlAdapter() { + public void controlMoved(ControlEvent e) { + assert awtHandler != null; + awtHandler.postHidePopups(); + } + }; + final Shell shell = composite.getShell(); + shell.addControlListener(controlAdapter); + + // Cleanup listeners on dispose + composite.addDisposeListener(new DisposeListener() { + public void widgetDisposed(DisposeEvent e) { + // Remove listener from shell before nullifying awtHandler + shell.removeControlListener(controlAdapter); + awtHandler.dispose(); + awtHandler = null; + composite = null; + } + }); + } + + void gainFocusNext() { + traverse(SWT.TRAVERSE_TAB_NEXT); + } + + void gainFocusPrevious() { + traverse(SWT.TRAVERSE_TAB_PREVIOUS); + } + + private void traverse(final int traversal) { + //assert composite != null; + if (composite == null) + return; + + // Tab from the containing SWT component while + // running on the SWT thread + Runnable r = new Runnable() { + public void run() { + composite.traverse(traversal); + } + }; + display.asyncExec(r); + } + +// boolean hasFocus() { +// assert composite != null; +// +// // This will return true if the composite has focus, or if any +// // foreign (e.g. AWT) child of the composite has focus. +// if (display.isDisposed()) { +// return false; +// } +// final boolean[] result = new boolean[1]; +// display.syncExec(new Runnable() { +// public void run() { +// result[0] = (!composite.isDisposed() && +// (display.getFocusControl() == composite)); +// } +// }); +// return result[0]; +// } + + // ..................... Listener implementations + + public void focusGained(FocusEvent e) { + assert awtHandler != null; + assert Display.getCurrent() != null; // On SWT event thread + + // System.out.println("Gained: " + e.toString() + " (" + e.widget.getClass().getName() + ")"); + EventQueue.invokeLater(new Runnable() { + public void run() { + // composite DisposeListener may have nullified this meanwhile! + // Not a bug. + if (awtHandler != null) + awtHandler.gainFocus(); + } + }); + } + + public void focusLost(FocusEvent e) { + // System.out.println("Lost: " + e.toString() + " (" + e.widget.getClass().getName() + ")"); + } + + public void keyPressed(KeyEvent e) { + assert Display.getCurrent() != null; // On SWT event thread + + // If the embedded swing root pane has no components to receive focus, + // then there will be cases where the parent SWT composite will keep + // focus. (For example, when tabbing into the root pane container). + // By default, in these cases, the focus is swallowed by the Composite + // and never escapes. This code allows tab and back-tab to do the + // proper traversal to other SWT components from the composite. + // TODO: other keys? + if (e.keyCode == SWT.TAB) { + // TODO: In some cases, this gobbles up all the tabs, even from AWT children. Find a more selective way. + /*if (e.stateMask == SWT.NONE) { + traverse(SWT.TRAVERSE_TAB_NEXT); + } else if (e.stateMask == SWT.SHIFT) { + traverse(SWT.TRAVERSE_TAB_PREVIOUS); + }*/ + } + } + + public void keyReleased(KeyEvent e) { + } + + +}