Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for Input Methods #146

Closed
JordanMartinez opened this issue May 16, 2015 · 11 comments · Fixed by #985
Closed

Add support for Input Methods #146

JordanMartinez opened this issue May 16, 2015 · 11 comments · Fixed by #985

Comments

@JordanMartinez
Copy link
Contributor

Hello Tomas,
What would be the best way to incorporate multiple language inputs? What would be the best way to handle this?

I think languages like Spanish or Greek wouldn't be too hard to do. One could remap the KeyTyped events to insert the correct letter in their respective alphabets.

For other languages (like Chinese using PinYin or some other input method), this wouldn't work. For my purposes, I don't need support for a more complex language, just the simpler ones. However, finding a way to incorporate the system's input method would prevent each developer from having to create 1) a separate KeyTyped events remapping for each language used and 2) a method to switch from one keyboard layout to another, depending on what the user needs.

@TomasMikula
Copy link
Member

Hi Jordan,
different keyboard layouts, like Spanish or Greek, should just work. Is that not your experience? JavaFX's KeyTyped event already contains a keyboard layout-dependent character. Input methods and right-to-left (or bidirectional) text is not supported.

@JordanMartinez
Copy link
Contributor Author

Sorry, I only meant input methods. I'm using a US keyboard layout, but if I wanted to quickly hop over to a Spanish/Greek/Chinese input method, it won't register.

@TomasMikula
Copy link
Member

I don't have any experience using input methods. A good place to start would be to look at what JavaFX's TextArea does.

@JordanMartinez
Copy link
Contributor Author

I looked into TextArea and input methods in general. Here's what I found.

First, there's a specific Event for this: InputMethodEvent.
Second, the API says that the event is delivered to the Node that extends TextInputControl, which TextArea extends and which RichTextFX does not extend (it extends Control). I'm not sure what the underlying code is in TextInputControl.

@JordanMartinez
Copy link
Contributor Author

The problem is that an area's InputMethodRequests value is null. Any InputMethodEvent won't be registered until this gets taken care of. My workaround was to "steal" a TextArea's InputMethodRequests (an instance of TextInputControlSkin). After trying a few things, I just decided to try a blank object that implemented InputMethodRequests. Turns out it works.

It doesn't insert the text that a user is composing before replacing it with the actual text inserted, but it does allow a user to change Input Methods and insert whatever text they desire (basic functionality). Here's the code below:

class InputMethodRequestsObject implements InputMethodRequests {
    @Override
    String getSelectedText() {
        return ""
    }
    @Override
    int getLocationOffset(int x, int y) {
        return 0
    }
    @Override
    void cancelLatestCommittedText() {

    }
    @Override
    Point2D getTextLocation(int offset) {
        return new Point2D(0, 0)
    }
}

import static groovyx.javafx.GroovyFX.start
start {
    stage(id: "stage", title: "Hello", primary: true, visible: true) {
        scene(width: 500, height: 500) {
            vbox(id: "vbox")
        }
    }
    def richArea = new InlineCssTextArea()
    richArea.setInputMethodRequests(new InputMethodRequestsObject())
    richArea.setOnInputMethodTextChanged({ e ->
        if (e.committed != "") {
            richArea.insertText(richArea.caretPosition, e.committed)
        }
    })
    vbox.getChildren().add(richArea)
}

@JordanMartinez JordanMartinez changed the title Multiple Language Input Feature How to insert text using an Input Method May 19, 2015
@TomasMikula
Copy link
Member

Thanks for posting back your workaround!

@JordanMartinez
Copy link
Contributor Author

No problem!

@JordanMartinez JordanMartinez changed the title How to insert text using an Input Method Add support for Input Methods Mar 17, 2017
@JordanMartinez
Copy link
Contributor Author

I'm reopening this to add official support for this out-of-box.

@clementinelove
Copy link

As a Chinese user, I guess people who use the most commonly used input method of China, Pinyin, would like to see the candidate window (for picking possible words) just below the caret.

Below is a sample input of Chinese characters “你好” (Greetings) using Pinyin in macOS Pages, the candidate window appears just below the caret and before the first input character (which is also the initial caret position).

chinese_input_using_pages

The workaround above works pretty well! But it doesn't show the candidate window in the place where the candidate window should be. To fix this, @JordanMartinez points out that Caret#getCaretBounds() can be used to get the caret's position on screen in #753. By passing the rich text area to the InputMethodRequestsObject, the InputMethodRequestsObject#getTextLocation(int) can be modified to show the effect presented above:

    @Override
    public Point2D getTextLocation(int offset) {
        // a very rough example, only tested under macOS
        Optional<Bounds> caretPositionBounds = area.getCaretBounds();
        if (caretPositionBounds.isPresent()) {
            Bounds bounds = caretPositionBounds.get();
            return new Point2D(bounds.getMaxX() - 5, bounds.getMaxY());
        }

        throw new NullPointerException();
    }

chinse_input_using_generic_styled_area

@xulihang
Copy link
Contributor

xulihang commented Aug 8, 2020

Hi there,

I tested the workaround and it works. Great thanks.

I modified the code a bit to replace selected text with text commited

richArea.setOnInputMethodTextChanged({ e ->
        if (e.committed != "") {
            richArea.replaceSelection("")
            richArea.insertText(richArea.caretPosition, e.committed)
        }
    })

As for the candidate window, I found that only macOS can display it correctly. Native TextArea has this problem as well. The candidate window is at the lower-right under Windows 10 and is at the lower-left area of the application under Linux. I tested on JRE 1.8.

@xulihang
Copy link
Contributor

xulihang commented Aug 8, 2020

Another problem is that the composed text is not showing correctly.

The correct example:
image

I then changed the TextChanged Event (code is in B4X) to show the composed text. But it is not underlined as above.

Sub InputMethodTextChanged_Event(MethodName As String,Args() As Object) As Object							'ignore
	Dim e As JavaObject=Args(0)
	JO.RunMethod("replaceSelection",Array(""))
	Dim startIndex,endIndex As Int
	startIndex=JO.RunMethod("getCaretPosition",Null)-previousComposedText.Length
	endIndex=JO.RunMethod("getCaretPosition",Null)
	JO.RunMethod("deleteText",Array(startIndex, endIndex))
	If e.RunMethod("getCommitted",Null)<>"" Then
		JO.RunMethod("insertText",Array(JO.RunMethod("getCaretPosition",Null), e.RunMethod("getCommitted",Null)))
		previousComposedText=""
	Else
		Dim sb As StringBuilder
		sb.Initialize
		Dim composed As List=e.RunMethod("getComposed",Null)
		For Each run As JavaObject In composed
			sb.Append(run.RunMethod("getText",Null))
		Next
		previousComposedText=sb.ToString
		JO.RunMethod("insertText",Array(JO.RunMethod("getCaretPosition",Null), sb.ToString))
	End If
End Sub

image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants