Discussion:
keypress events interfering with editor update
(too old to reply)
Dave Doty
2017-10-18 22:34:34 UTC
Permalink
I am using this Ace Editor adapted to
Elm: https://github.com/DenisKolodin/elm-ace

However, my problem seems not specific to that editor, but applies to any
program that wants to support an embedded editor whose contents can be
updated by the program, and also that can respond to global keypress events.

The editor text can be updated either by

1) the user typing, or
2) the program updating the text (e.g., pressing a "load default text"
button)

The user can also press some keyboard shortcuts (',' and '.') to do other
actions. These are handled by my update function:

update msg model =
...
KeyMsg keyCode ->
if keyCode == Char.toCode ',' then
update Backward model
else if keyCode == Char.toCode '.' then
update Forward model
else
( model, Cmd.none )

This is called whenever the user presses any key because of this
subscription that uses the elm-lang/keyboard library:

subscriptions model =
Keyboard.presses KeyMsg

The problem is that all of the keypresses made when editing cause the
update function to be called, not just the two that I care about. Most of
them are not ',' or '.' so the model is returned unchanged in the else
clause above.

But this causes the following problem: in my view function, the editor
updates its contents to be whatever the model says the text should be (for
instance, the user may press a button that loads some default text):

view model =
...
Ace.toHtml
[ Ace.value model.text -- updates editor to display model.
text
, Ace.onSourceChange NewText -- updates model.text to be latest
editor contents
]
[]

The purpose of onSourceChange above is to trigger another call to the update
function, which allows me to update my model text with the new editor
contents. In other words, the two lines above are the bi-directional data
binding between model.text and the editor's text contents. The second one
implies a call to the update function:

update msg model =
...
NewText newText ->
( {model | text = newText}, Cmd.none )

The problem is that the Elm runtime is ordering events so that the text in
the editor never gets updated when the user tries to type new text.

Here is the order of events:

1. User types to edit text in the editor.
2. Because of the subscription I registered for global keystroke events,
Elm runtime calls update with the message KeyMsg indicating which key
was pressed.
3. Model is updated (in the KeyMsg case of my update function) to the
"new" model, but most of the time this is identical to the old model since
only ',' and '.' actually return a changed model. Importantly, this is
the *old* text, not containing the new character that the user typed.
4. The view function is called, and the Ace.toHtml updates the text
contents of the editor to be what the model.text field is... which erases
in the editor the new character that the user just tried to enter, since
model.text still has the old, pre-keystroke text.
5. The update function is called with a NewText message, but it's too
late. By now the editor has had its new text erased and replaced with the
old text. Ideally, this event would have happened in between steps 1 and 2,
but steps 2,3,4 seem to happen before this does.

I'm not sure how to get around this.

*Idea 1:* figure out a way to prevent the update function from being called
at all when a keystroke other than ',' or '.' occurs. This is not ideal
because then they can't type ',' or '.' in the editor, but it would be a
workaround because I could perhaps assign Ctrl+... keys or something.

*Idea 2:* Re-order the updates so that, when the user enters new text in
the editor, it is guaranteed that update is called with the NewText message
before it is called with the KeyMsg message. I don't know of any way to do
this.

*Idea 3:* Re-design how I'm passing messages through the system so that, no
matter the order in which messages are delivered, if the user tries to
enter new text, eventually that new text makes its way into the model.

*Idea 4:* don't use the Keyboard library to respond to global keypress
events, and find something that is more specific to what I want (to give
the user a couple of keyboard shortcuts, without calling the update
function every time any key is pressed). Suggestions for such a library?
--
You received this message because you are subscribed to the Google Groups "Elm Discuss" group.
To unsubscribe from this group and stop receiving emails from it, send an email to elm-discuss+***@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
Simon
2017-10-19 18:31:24 UTC
Permalink
I think you need to add a variable to your model that records whether the
editor is being used and then in yout subscription you have

myKbdSubs model =
if model.editorBeingUsed then
Sub.none
else <whatever you already have>
Post by Dave Doty
https://github.com/DenisKolodin/elm-ace
However, my problem seems not specific to that editor, but applies to any
program that wants to support an embedded editor whose contents can be
updated by the program, and also that can respond to global keypress events.
The editor text can be updated either by
1) the user typing, or
2) the program updating the text (e.g., pressing a "load default text"
button)
The user can also press some keyboard shortcuts (',' and '.') to do other
update msg model =
...
KeyMsg keyCode ->
if keyCode == Char.toCode ',' then
update Backward model
else if keyCode == Char.toCode '.' then
update Forward model
else
( model, Cmd.none )
This is called whenever the user presses any key because of this
subscriptions model =
Keyboard.presses KeyMsg
The problem is that all of the keypresses made when editing cause the
update function to be called, not just the two that I care about. Most of
them are not ',' or '.' so the model is returned unchanged in the else
clause above.
But this causes the following problem: in my view function, the editor
updates its contents to be whatever the model says the text should be (for
view model =
...
Ace.toHtml
[ Ace.value model.text -- updates editor to display model.
text
, Ace.onSourceChange NewText -- updates model.text to be latest
editor contents
]
[]
The purpose of onSourceChange above is to trigger another call to the
update function, which allows me to update my model text with the new
editor contents. In other words, the two lines above are the bi-directional
data binding between model.text and the editor's text contents. The
update msg model =
...
NewText newText ->
( {model | text = newText}, Cmd.none )
The problem is that the Elm runtime is ordering events so that the text in
the editor never gets updated when the user tries to type new text.
1. User types to edit text in the editor.
2. Because of the subscription I registered for global keystroke
events, Elm runtime calls update with the message KeyMsg indicating
which key was pressed.
3. Model is updated (in the KeyMsg case of my update function) to the
"new" model, but most of the time this is identical to the old model since
only ',' and '.' actually return a changed model. Importantly, this is
the *old* text, not containing the new character that the user typed.
4. The view function is called, and the Ace.toHtml updates the text
contents of the editor to be what the model.text field is... which erases
in the editor the new character that the user just tried to enter, since
model.text still has the old, pre-keystroke text.
5. The update function is called with a NewText message, but it's too
late. By now the editor has had its new text erased and replaced with the
old text. Ideally, this event would have happened in between steps 1 and 2,
but steps 2,3,4 seem to happen before this does.
I'm not sure how to get around this.
*Idea 1:* figure out a way to prevent the update function from being
called at all when a keystroke other than ',' or '.' occurs. This is not
ideal because then they can't type ',' or '.' in the editor, but it would
be a workaround because I could perhaps assign Ctrl+... keys or something.
*Idea 2:* Re-order the updates so that, when the user enters new text in
the editor, it is guaranteed that update is called with the NewText message
before it is called with the KeyMsg message. I don't know of any way to do
this.
*Idea 3:* Re-design how I'm passing messages through the system so that,
no matter the order in which messages are delivered, if the user tries to
enter new text, eventually that new text makes its way into the model.
*Idea 4:* don't use the Keyboard library to respond to global keypress
events, and find something that is more specific to what I want (to give
the user a couple of keyboard shortcuts, without calling the update
function every time any key is pressed). Suggestions for such a library?
​
--
You received this message because you are subscribed to the Google Groups "Elm Discuss" group.
To unsubscribe from this group and stop receiving emails from it, send an email to elm-discuss+***@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
Dave Doty
2017-10-19 23:29:05 UTC
Permalink
Thank you! That was the key idea: not generating the message at all in some
circumstances, by doing a check in the subscription.
Post by Simon
I think you need to add a variable to your model that records whether the
editor is being used and then in yout subscription you have
myKbdSubs model =
if model.editorBeingUsed then
Sub.none
else <whatever you already have>
Post by Dave Doty
https://github.com/DenisKolodin/elm-ace
However, my problem seems not specific to that editor, but applies to any
program that wants to support an embedded editor whose contents can be
updated by the program, and also that can respond to global keypress events.
The editor text can be updated either by
1) the user typing, or
2) the program updating the text (e.g., pressing a "load default text"
button)
The user can also press some keyboard shortcuts (',' and '.') to do
update msg model =
...
KeyMsg keyCode ->
if keyCode == Char.toCode ',' then
update Backward model
else if keyCode == Char.toCode '.' then
update Forward model
else
( model, Cmd.none )
This is called whenever the user presses any key because of this
subscriptions model =
Keyboard.presses KeyMsg
The problem is that all of the keypresses made when editing cause the
update function to be called, not just the two that I care about. Most of
them are not ',' or '.' so the model is returned unchanged in the else
clause above.
But this causes the following problem: in my view function, the editor
updates its contents to be whatever the model says the text should be (for
view model =
...
Ace.toHtml
[ Ace.value model.text -- updates editor to display model.
text
, Ace.onSourceChange NewText -- updates model.text to be latest
editor contents
]
[]
The purpose of onSourceChange above is to trigger another call to the
update function, which allows me to update my model text with the new
editor contents. In other words, the two lines above are the bi-directional
data binding between model.text and the editor's text contents. The
update msg model =
...
NewText newText ->
( {model | text = newText}, Cmd.none )
The problem is that the Elm runtime is ordering events so that the text
in the editor never gets updated when the user tries to type new text.
1. User types to edit text in the editor.
2. Because of the subscription I registered for global keystroke
events, Elm runtime calls update with the message KeyMsg indicating
which key was pressed.
3. Model is updated (in the KeyMsg case of my update function) to the
"new" model, but most of the time this is identical to the old model since
only ',' and '.' actually return a changed model. Importantly, this
is the *old* text, not containing the new character that the user
typed.
4. The view function is called, and the Ace.toHtml updates the text
contents of the editor to be what the model.text field is... which erases
in the editor the new character that the user just tried to enter, since
model.text still has the old, pre-keystroke text.
5. The update function is called with a NewText message, but it's too
late. By now the editor has had its new text erased and replaced with the
old text. Ideally, this event would have happened in between steps 1 and 2,
but steps 2,3,4 seem to happen before this does.
I'm not sure how to get around this.
*Idea 1:* figure out a way to prevent the update function from being
called at all when a keystroke other than ',' or '.' occurs. This is not
ideal because then they can't type ',' or '.' in the editor, but it would
be a workaround because I could perhaps assign Ctrl+... keys or something.
*Idea 2:* Re-order the updates so that, when the user enters new text in
the editor, it is guaranteed that update is called with the NewText message
before it is called with the KeyMsg message. I don't know of any way to do
this.
*Idea 3:* Re-design how I'm passing messages through the system so that,
no matter the order in which messages are delivered, if the user tries to
enter new text, eventually that new text makes its way into the model.
*Idea 4:* don't use the Keyboard library to respond to global keypress
events, and find something that is more specific to what I want (to give
the user a couple of keyboard shortcuts, without calling the update
function every time any key is pressed). Suggestions for such a library?
​
--
You received this message because you are subscribed to the Google Groups "Elm Discuss" group.
To unsubscribe from this group and stop receiving emails from it, send an email to elm-discuss+***@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
Loading...