Bug 575995 - Can't type quotation marks in text fields within SWT_AWT context
Summary: Can't type quotation marks in text fields within SWT_AWT context
Status: NEW
Alias: None
Product: Platform
Classification: Eclipse Project
Component: SWT (show other bugs)
Version: 4.22   Edit
Hardware: Macintosh Mac OS X
: P3 normal (vote)
Target Milestone: ---   Edit
Assignee: Platform-SWT-Inbox CLA
QA Contact:
URL:
Whiteboard:
Keywords:
Depends on:
Blocks:
 
Reported: 2021-09-15 05:18 EDT by Carl Witt CLA
Modified: 2021-09-15 05:18 EDT (History)
0 users

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Carl Witt CLA 2021-09-15 05:18:54 EDT
The KNIME analytics platform is an open source application based on Eclipse. Since we updated Eclipse to 2021-03, our users on Mac OS have problems when using the international keyboard layout. This seems to be related to the SWT_AWT bridge.

To reproduce: 
Run the code example below on a Mac with U.S. international keyboard layout.
Try to type in the text fields
a) ' and then ␣ (space bar) 
 this used to give a single quotation mark
 now gives a blank character
b) ⇧ + ' and then ␣ (space bar)
 this used to result in a double quotation mark
 now also gives a blank character

Interestingly, other combinations still work: for instance typing ' and then a will still give á.

I tried this on Mac OS Big Sur using the swt-4.21RC2a-cocoa-macosx-x86_64 build.

When tracking the key events on a text field, the java.awt.KeyEvent sequences differ between the working and not working text fields:

Working sequence:
[KEY_PRESSED	keyCode=16	keyText=⇧	keyChar=ndf	modifiers=⇧	extModifiers=⇧	keyLocation=LEFT	extendedKeyCode=0x10]
[KEY_RELEASED	keyCode=222	keyText='	keyChar='"'	modifiers=⇧	extModifiers=⇧	keyLocation=STANDARD	extendedKeyCode=0x98]
[KEY_RELEASED	keyCode=16	keyText=⇧	keyChar=ndf	keyLocation=LEFT		extendedKeyCode=0x10]
[KEY_TYPED	keyCode=0	keyText=0x0	keyChar='"'	keyLocation=UNKNOWN		extendedKeyCode=0x0]
[KEY_RELEASED	keyCode=32	keyText=␣	keyChar=' '	keyLocation=STANDARD	extendedKeyCode=0x20]

Actually observed sequence, e.g., in a JTextField embedded into an SWT Composite via SWT_AWT.new_frame

√ [KEY_PRESSED	keyCode=16	keyText=⇧	keyChar=ndf	modifiers=⇧	extModifiers=⇧	keyLocation=LEFT	extendedKeyCode=0x10]
// extended key code should be 0x98?
X [KEY_RELEASED	keyCode=222	keyText='	keyChar=ndf	modifiers=⇧	extModifiers=⇧	keyLocation=STANDARD	extendedKeyCode=0xde]
√ [KEY_RELEASED	keyCode=16	keyText=⇧	keyChar=ndf	keyLocation=LEFT	extendedKeyCode=0x10]
// instead of this key pressed event, there should be a key typed event
X [KEY_PRESSED	keyCode=32	keyText=␣	keyChar=' '	keyLocation=STANDARD	extendedKeyCode=0x20]
// instead of keyChar=' ' the typed character should be '"'
X [KEY_TYPED	keyCode=0	keyText=0x0	keyChar=' '	keyLocation=UNKNOWN	extendedKeyCode=0x20]
√ [KEY_RELEASED	keyCode=32	keyText=␣	keyChar=' '	keyLocation=STANDARD	extendedKeyCode=0x20]

import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.FlowLayout;
import java.awt.Frame;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.WindowConstants;

import org.eclipse.swt.SWT;
import org.eclipse.swt.awt.SWT_AWT;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Text;

public class MixGuiToolkits {

	public static void main(final String[] args) {
		final var display = new Display();

		// problematic
		Shell mixToolkits = awtWithinSwt(display);
		mixToolkits.open();

		// works
		Shell pureSwt = MixGuiToolkits.pureSWT(display);
		pureSwt.open();

		// works
		pureSwing(display);

		while (!pureSwt.isDisposed()) {
			if (!display.readAndDispatch()) {
				display.sleep();
			}
		}
		display.dispose();
	}

	/** Create an SWT Composite with Swing and AWT text fields - does not work */
	private static Shell awtWithinSwt(final Display display) {
		final var shell = new Shell(display);

		final var swtComposite = new Composite(shell, SWT.EMBEDDED);
		swtComposite.setBounds(5, 5, 300, 300);

		// use the SWT_AWT bridge to embed AWT or Swing into an SWT composite
		java.awt.Frame awtFrame = SWT_AWT.new_Frame(swtComposite);

		java.awt.Panel awtPanel = new java.awt.Panel(new java.awt.BorderLayout());
		awtFrame.add(awtPanel);
		// these text fields have the bug
		awtPanel.add("North", new JTextField("Swing text field (has bug)"));
		awtPanel.add("South", new java.awt.TextField("AWT text fiel (has bug)"));
		return shell;
	}

	/** Create an SWT Composite with an SWT text field - works as expected */
	private static Shell pureSWT(final Display display) {
		var shell = new Shell(display);
		shell.setLayout(new GridLayout());
		shell.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));

		// The border gives the appearance of a single component
		final var baseComposite = new Composite(shell, SWT.BORDER);
		baseComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
		final var baseCompositeGridLayout = new GridLayout(2, false);
		baseComposite.setLayout(baseCompositeGridLayout);

		final var text = new Text(baseComposite, SWT.SINGLE | SWT.RIGHT);
		text.setText("SWT Text (works)");
		text.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
		return shell;
	}

	/** Create a Swing window with a Swing text field - works as expected */
	private static void pureSwing(final Display display) {
		EventQueue.invokeLater(() -> {
			final var mainFrame = new JFrame();
			mainFrame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
			final var mainPanel = new JPanel();
			mainPanel.setLayout(new FlowLayout());

			mainPanel.add(new JTextField("JTextField (works)"));
			mainFrame.getContentPane().add(mainPanel, BorderLayout.CENTER);
			mainFrame.pack();
			mainFrame.setVisible(true);
		});
		display.addListener(SWT.Close, event -> EventQueue.invokeLater(() -> {
			Frame[] frames = Frame.getFrames();
			for (Frame frame : frames) {
				frame.dispose();
			}
		}));
	}

}