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

Introduce inputType 'deleteCompositionText' and 'insertFromComposition', fired right before 'compositionend' #34

Open
chong-z opened this issue Sep 21, 2016 · 12 comments

Comments

@chong-z
Copy link
Contributor

chong-z commented Sep 21, 2016

Original Discussion
w3c/editing#118 (comment) We want 'deleteComposedCharacter' during composition because of it's special non-cancelable attribute.

Issues

  1. 'insertText' during composition is also non-cancelable, do we want 'insertCompositionText'?
  2. 'deleteComposedCharacter' is not so helpful as Chrome (and maybe WebKit) always replace the entire composition text

Proposal

  1. Text replacement should only be split into deletion and insertion at 1. the start and 2. the end of the composition
    • During composition both deletion and insertion are non-cancelable, so there is no reason to split
  2. Have 4 composition related events
    1. Single or none 'deleteByComposition', fired before 'compositionstart', cancelable
    2. Multiple 'insertCompositionText', fired during composition, non-cancelable
    3. Single 'deleteCompositionText', fired before 'compositionend', non-cancelable
    4. Single or none 'insertFromComposition', fired between iii. and 'compositionend', cancelable

Default Action

InputType Default preventDefault()
'deleteByComposition' Delete original text Similar to collapse selection forward and start composition, but next 'insertCompositionText' will pre-insert selected text in certain cases
'insertCompositionText' Insert/replace marked text (with marked text) N/A
'deleteCompositionText' Clear marked text N/A
'insertFromComposition' Insert final confirmed text Only cancels text insertion and won't affect IME (IME terminates as usual)

Example Order

(1. beforeinput - 'deleteByComposition')
(2. input - 'deleteByComposition')
3. 'compositionstart'

4. beforeinput - 'insertCompositionText'
5. 'compositionupdate'
6. input - 'insertCompositionText'
7. beforeinput - 'insertCompositionText'
8. 'compositionupdate'
9. input - 'insertCompositionText'
(...)

10. beforeinput - 'deleteCompositionText'
11. 'compositionupdate'
12. input - 'deleteCompositionText'

(13. beforeinput - 'insertFromComposition')
(14. input - 'insertFromComposition')
15. 'compositionend'

Example of Recomposition on Android

# User Action Event Type inputType DOM event.data
1 Start 'Wo'
2 Tap 'Wo' to start composition 'beforeinput' 'deleteByComposition' 'Wo'
3 'input' 'deleteByComposition' ''
4 'compositionstart' '' 'Wo'
5 'beforeinput' 'insertCompositionText' '' 'Wo'
6 'compositionupdate' '' 'Wo'
7 'input' 'insertCompositionText' Underlined 'Wo'
8 Press key 'r' 'beforeinput' 'insertCompositionText' Underlined 'Wo' 'Wor'
9 'compositionupdate' Underlined 'Wor' 'Wor'
10 'input' 'insertCompositionText' Underlined 'Wor'
11 Tap suggested word 'Work' to commit 'beforeinput' 'deleteCompositionText' Underlined 'Wor'
12 'compositionupdate' Underlined 'Wor' ''
13 'input' 'deleteCompositionText' ''
14 'beforeinput' 'insertFromComposition' '' 'Work'
15 'input' 'insertFromComposition' 'Work'
16 'compositionend' 'Work' 'Work'
@chong-z
Copy link
Contributor Author

chong-z commented Sep 27, 2016

RESOLUTION: Yes we can do the issues 33/34 proposal for cancelable at each end of IME.
https://www.w3.org/2016/09/22-webapps-minutes.html#resolution02

@johanneswilm
Copy link
Contributor

@choniong I tried to add a non-normative note in humanly readable language about how the beforeinput events should relate to IME composition [1]. This is meant largely for JavaScript developers who try to built their apps on top of the spec. Some terms still need to be linked to the p[roper definitions, but let me know if this is more or less correct.

[1] https://w3c.github.io/input-events/#h-note4

@chong-z
Copy link
Contributor Author

chong-z commented Sep 28, 2016

Thanks for the explainer, that's really helpful!

Just a small concern about

4.. ...or a part of it does while another part of the composition string continues to be composed in the IME...

This feels confusing to me, as there is actually no way for IME to remove/commit part of the composition.
What Japanese IME doing is just commit the original composition and start a new one (although visually looks weird, it actually makes sense from JS's side), and JS shouldn't need to be aware of it as long as they have the implementation for normal IME.

If we really want to mention the Japanese IME case, would it be better to put it into a separate note, and have the current note only for atomic actions?

@johanneswilm
Copy link
Contributor

@choniong I see. yes that can be confusing. I agree that it would be better to have two different notes. But also I think I misunderstood part of this for these Japanese IMEs:

So in the case of this Japanese IME, when this happens, how many beforeinput events do we get whent his happens? Do we get

  1. deleteCompositionText - removes the entire text.
  2. inputFromComposition - inserts the entire text.
  3. deleteByComposition - removes the part of the text that is to be put into the next composition
  4. insertCompositionText - inserts the part of the text that is to be edited in the IME

?

If this is the case, I think this can be problematic if the JS handles step 2 and inserts the string in a way the browser doesn't expect, because that will mean it won't know what it can delete in step 3. Of course, the JS could simply handle both 2 and 3, but the JS doesn't know which part of the string will be moved back into the DOM.

That's why I thought that it would only move that part into the DOM in step 2 that is finished and can be committed.

Is there any way we can expose which part of the string is done and can be committed permanently in step 2?

@chong-z
Copy link
Contributor Author

chong-z commented Sep 28, 2016

So during a single keydown/up, there will be 1 compositionend and 1 new compositionstart.

  1. deleteCompositionText - One, for the first composition
  2. insertFromComposition - One, for the first composition
  3. deleteByComposition - None
  4. insertCompositionText - One, for the second composition

Here is the expected event order for pressing 'a' when the marked text is 'あああああああああああああ' (x13), the result is 2 committed 'あ' and 12 marked ''.

Event InputType/data DOM Notes
keydown 'a' 'あああああああああああああ' x13 This key will cause 'partial commit'
beforeinput 'deleteCompositionText' 'あああああああああああああ' About to commit composition 1, clearing marked text
compositionupdate '' 'あああああああああああああ'
input 'deleteCompositionText' ''
-Important- beforeinput 'insertFromComposition', 'ああ' '' 'partial commit' first 2 characters, no 'compositionupdate'
input 'insertFromComposition' 'ああ'
compositionend 'ああ' 'ああ' 'Composition 1, commit 2 characters'
--Just-- --A-- --hr-- ----
compositionstart '' 'ああ' 'Start composition 2'
beforeinput 'insertCompositionText', 'ああああああああああああ' 'ああ' Re-insert leftover text
compositionupdate 'ああああああああああああ' 'ああ'
input 'insertCompositionText' 'ああ_ああああああああああああ_' x14 The rear 12 characters are composition
keyup 'a' 'ああ_ああああああああああああ_' x14 All of this happens in one keydown/up

@johanneswilm
Copy link
Contributor

@choniong Ah sorry. Then I had understood you correctly the first time and it's all not a problem. Then it's all a matter of turning 1 note into 2 notes. I will make sure to do that. I have tried to avoid such words as "commit" or say they will become part of the "permanent DOM" as that may invoke wrong connotations. But now I am thinking maybe we need to use the term "commit" and then add a description for it.

The point of this exercise is of course to make IME handling so obvious and easy to the JS readers of the spec that they all will do it. For that purpose: could I include your above example as well?

@chong-z
Copy link
Contributor Author

chong-z commented Sep 28, 2016

Yep sure.

Just want to conclude, my point is: JS doesn't need to have any knowledge about Japanese IME, all they need to do is to handle those 4 inputTypes, and there is no special cases.

@johanneswilm
Copy link
Contributor

Just want to conclude, my point is: JS doesn't need to have any knowledge about Japanese IME, all they need to do is to handle those 4 inputTypes, and there is no special cases.

Yes, I get it now. The thing I wasn't sure about was whether it would reinsert the entire composition string and then delete just the part that needs to go into the next composition. That's what I feared after I read

What Japanese IME doing is just commit the original composition and start a new one

But with your example you made it clear that only the part that is final goes into the first 'insertFromComposition'. That way it's all quite easy for JS. In many cases, JS developers only need to listen to 'deleteByComposition' and 'insertFromComposition', handling those themselves and recording them in their undo stack.

@johanneswilm
Copy link
Contributor

Reopened because the event order should probably be added as normative text to a spec. For example tge new event order spec, @garykac?

And the example could be added to the input events spec.

@johanneswilm
Copy link
Contributor

@choniong What do you think should happen in these cases:

  1. JS doesn't preventDefault beforeinput - 'deleteByComposition' but does make changes to the DOM that possibly interrupt the range which covers the initial composition string?
  2. JS handles beforeinput - 'deleteByComposition' and does some changes to the selection and the DOM which mean that the selection is not collapsed or there is no selection at the end of the JS function that handles the event?
  3. Same as 2 but for beforeinput - 'insertFromComposition' in the case where only part of the string is submitted.

All these are situations which JS may mess up and I think it's totally fine to say that JS is responsible for leaving a collapsed selection for or else the site simple doesn't work as expected. But I'm a bit afraid that if for example Chrome and Firefox coincidentally end up implementing this in a way where removing the selection in the code that handles 'deleteByComposition' leads to the IME process being cancelled, JS developers will discover that and start depending on this behavior.

@chong-z
Copy link
Contributor Author

chong-z commented Oct 18, 2016

That's some difficult situations, and I'm afraid it will cause more potential issues by splitting the first text replacing into 'deleteByComposition' and 'insertCompositionText'.

Do you think it would be better to make 'deleteByComposition' as a flag and only do real mutation in the next event? e.g.

  • 'deleteByComposition' doesn't have default action by it self
    • If cancelled, next 'insertCompositionText' will collapse selection forward and insert marked text.
    • If not cancelled, next 'insertCompositionText' will replace selection, similar to current IME behavior.

@johanneswilm
Copy link
Contributor

Do you think it would be better to make 'deleteByComposition' as a flag and only do real mutation in the next event?

I would prefer to keep things as they are now if at all possible, as it's quite easy to understand. JS editor developer should be perfectly capable of ensuring that they leave a collapsed selection and to remove all that is is covered by the target ranges. But we should still spec what happens in those situations. One simple solution would be to say that the composition is canceled automatically if there is no collapsed selection at the end of those JS functions. But that would break a bit with the principle that canceling beforeinput events only prevents DOM changes.

Or are there other reasons why you would prefer to unite the two dom changes? I don't think it would be a huge issue either, as JS should still be able to do everything in more or less the same way. The only "odd" thing would be that the second beforeinput event will be called before any of the DOM has changed, right?

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

No branches or pull requests

2 participants