-
-
Notifications
You must be signed in to change notification settings - Fork 1.4k
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 IME support #4614
base: develop
Are you sure you want to change the base?
Add IME support #4614
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for working on this really complex task.
I have some suggestions inline.
@@ -75,6 +75,14 @@ type Focusable interface { | |||
TypedKey(*KeyEvent) | |||
} | |||
|
|||
// Preeditable describes any CanvasObject that can respond to IME inputs. | |||
// Originally, it should be a method of Focusable, but only a few widgets need PreeditChanged, | |||
// and adding a method to Focusable will have a big impact on others, so defined as a new interface. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree with this sentiment - adding it to Focusable would be a breaking change.
However can it be worded less like a workaround.
i.e. "Widgets that implement Focusable
may also want to implement PreeditChanged
to handle IME input?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That comment certainly makes the relationship with Focusable clearer. People who see this code for the first time may not understand the deep relationship between Focusable and Preeditable.
@@ -191,6 +191,15 @@ func (c *Canvas) Focused() fyne.Focusable { | |||
return mgr.Focused() | |||
} | |||
|
|||
// Preeditable return the current preeditable object only when focused |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This will crash if the input is not Preeditable. It should not be needed as it will always be the same object returned by Focused()
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This will crash if the input is not Preeditable.
I didn't notice it when running fyne_demo. Thank you for pointing that out. I had a chance to learn the correct casting method.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would only happen if you started to interact with an input that had not implemented that interface.
Something like the checkbox I would think for example.
@@ -677,6 +678,32 @@ func (w *window) charInput(viewport *glfw.Window, char rune) { | |||
w.processCharInput(char) | |||
} | |||
|
|||
var counterIme int | |||
|
|||
// IME |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A comment should not be needed here. Where comments are added it usually implies naming is insufficient.
focusedBlock int, | ||
caret int, | ||
) { | ||
fmt.Println( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please don't leave in debug
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@andydotxyz ok, removed.
"focusedBlock", focusedBlock, | ||
"caret", caret, | ||
) | ||
w.processPreedit(preeditString) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks like it would be easier to just inline processPreedit here as it is barely a single line and only called from this location
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@andydotxyz Unfortunately processPreedit and Preeditable.PreeditChanged are temporary interfaces. This is because the original Preedit response requires a more complex response. It is necessary to additionally display the conversion candidates for input content provided by the IME and the IME's own caret.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK. Should we mark this as a draft PR then instead of one ready for review?
widget/entry.go
Outdated
@@ -102,6 +105,7 @@ type Entry struct { | |||
|
|||
// NewEntry creates a new single line entry widget. | |||
func NewEntry() *Entry { | |||
// e := &Entry{Wrapping: fyne.TextTruncate, PreeditText: "(日本語)"} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please don't leave dead code in comments
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@andydotxyz OK, removed
widget/entry.go
Outdated
Text string | ||
shortcut fyne.ShortcutHandler | ||
Text string | ||
PreeditText string |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is this a public field? it feels like it should be an internal implementation if required at all?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@andydotxyz OK, privated
// Convert column, row into x,y | ||
getCoordinates := func(column int, row int) (float32, float32) { | ||
sz := provider.lineSizeToColumn(column, row) | ||
return sz.Width, sz.Height*float32(row) - theme.InputBorderSize() + theme.InnerPadding() | ||
szpe := preedit.lineSizeToColumn(column, row) | ||
return float32(math.Max(float64(sz.Width), float64(r.cursor.Position().X+szpe.Width))), sz.Height*float32(row) - theme.InputBorderSize() + theme.InnerPadding() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are you sure this is correct? Isn't the pre-edit only on the current cursor line? This seems to add the "szpe" irrespective of the row being measured...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@andydotxyz OK, Removed.
@kanryu If you are experiencing a bug on develop, report it as an issue instead of as a comment on a PR please. Thanks. |
@qizhanchan This issue is not caused by this PR's fix, but also occurs with the original Fyne Entry. So it's a Fyne problem. |
@Jacalz Which problem in this PR are you pointing out? This PR involves a very large source code modification. |
Ah, sorry @kanryu. I meant to be tagging @qizhanchan in my message. I assumed that he was referring to a bug on the develop branch. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry it has taken so long to get back on this.
I can't figure out how to test this - is there info about the versions of GLFW etc missing form go.mod?
@@ -765,11 +807,15 @@ func (w *window) create() { | |||
win.SetRefreshCallback(w.refresh) | |||
win.SetContentScaleCallback(w.scaled) | |||
win.SetCursorPosCallback(w.mouseMoved) | |||
win.SetPreeditCallback(w.preedit) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This won't compile for me. I guess the go.mod
is missing some alterations?
@@ -11,8 +11,11 @@ import ( | |||
"github.com/go-gl/glfw/v3.3/glfw" | |||
) | |||
|
|||
const GLFW_X11_ONTHESPOT glfw.Hint = 0x00052002 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Won't this be declared in the GLFW project?
I guess it relates to my other comment about not being sure of how to compile or which GLFW version is needed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@andydotxyz Thanks for trying. As I wrote at the beginning of this PR, in order to check the operation of this PR, you need to clone both the modified fyne and glfw and change go.mod.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@kanryu If this is under active development but not completed, please mark the PR as draft.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Jacalz It's difficult to explain in a few words because the work spanned multiple projects, but achieving this PR required major modifications to go-gl/glfw, and they effectively refused to merge. Because of this, we are currently unable to proceed with this PR any further.
Other than that, I think this PR is of a quality worthy of being tested by many people.
It's up to the people at the Fyne project to decide whether or not this PR should be marked as draft. Not that I would complain.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@kanryu I am entirely aware of the situation. My personal opinion is that if a PR can't be tested as it is, i.e. isn't ready to be merged, it should be a draft. Those that are interested in testing it can do so but we maintainers don't have to remember that it isn't ready. It makes it easier to grasp which pull requests are waiting for reviews.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@andydotxyz Thanks for trying. As I wrote at the beginning of this PR, in order to check the operation of this PR, you need to clone both the modified fyne and glfw and change go.mod.
This can be done with a replace directive in the go.mod so that this PR can simply be checked out and tested without pre-requisites. That also makes it clear of the requirements to compile.
Converting to draft as this is not in a state where we would be reviewing for merge. |
Description:
As everyone knows, fyne does not currently support IME. This is fundamentally caused by the fact that glfw does not support IME, and cannot be resolved by fyne's efforts alone. So I solved this problem by adding a hack to both go-gl/glfw and fyne. Please check it on your PC.
Fixes #618
Supported environments
How to try this
git clone https://github.com/kanryu/fyne git clone https://github.com/kanryu/glfw cd fyne go mod edit -replace github.com/go-gl/glfw/v3.3/glfw=../glfw/v3.4/glfw go run cmd/fyne_demo/main.go
Fyne.Demo.2024-02-06.19-48-28.mp4
Checklist:
Where applicable: