[TW5] Request: Refresh Delay in edit-text widget

241 views
Skip to first unread message

Evan Balster

unread,
Sep 22, 2015, 12:16:51 PM9/22/15
to TiddlyWiki
There's a lovely TiddlyWiki feature called refresh delay which makes preview mode usable by inducing a brief lag between the latest keypress and the refreshing of the wiki.  (If we transclude the content of a draft tiddler, it can be seen that this affects the wiki globally.)

I'm currently working on a wiki with a fairly complex dataflow (it's a modular automated character sheet for role-playing games) and I notice that my <$edit-text> widgets always trigger a refresh with every keypress.  The same is true of the TW5 search bar, and as a result it's possible for each of these to become very unresponsive when typing if there's a lot of information to process.

Is there any way refresh delay could be implemented as a setting of the edit-text widget?

Tobias Beer

unread,
Sep 22, 2015, 1:14:14 PM9/22/15
to TiddlyWiki
Hi Evan,
 
Is there any way refresh delay could be implemented as a setting of the edit-text widget?

For now there's only this...

#1494 refresh delay for text input fields
https://github.com/Jermolene/TiddlyWiki5/issues/1494

Best wishes,

— tb 

Evan Balster

unread,
Sep 22, 2015, 3:09:31 PM9/22/15
to TiddlyWiki
Hmm...  Reading through the old topic.

So the source of objection to this feature is that the data might be erased if the containing widget tree is refreshed within that small interval of time?  I would consider that acceptable...

However, based on a quick look at the code, I can speculate about two ways of implementing this change that do not involve storing editor state in the widget tree:
  1. Have a field other than "draft.of" which marks a tiddler as a "draft" for the purposes of refresh time.
  2. Modify the "change" event listener receive an optional parameter for the timeout associated with any given change, allowing any source of changes to define its own timeout value.
#1 could be implemented quickly by changing this line of the render code to something like:

                        if(!tiddler || !tiddler.hasField("draft.of") || !tiddler.hasField("refresh.slow")) {

#2 is a system-wide change and I really don't know enough about TiddlyWiki to speculate what it would take.  I'll await Jeremy's input on this point.

Tobias Beer

unread,
Sep 22, 2015, 4:33:59 PM9/22/15
to tiddl...@googlegroups.com
Hi Evan,

Tbh, I never quite grasped the problem of the delay with respect to the refresh cycle,
as you can possibly infer from that issue's discussion thread.

If a widget is destroyed before it can submit its state, then it's gone.
If it is refreshed before it can submit its state,
the editing state should be gone or
the widget should figure that it it is unrelateable via some hash.
If the data it is editing is changed in the store
before it commits the next keypress, so be it.

When the widget finally gets to commit the updated data,
that's what it will do, insofar as it is still able to do so.

Best wishes,

— tb

PMario

unread,
Sep 23, 2015, 4:52:52 AM9/23/15
to TiddlyWiki


On Tuesday, September 22, 2015 at 9:09:31 PM UTC+2, Evan Balster wrote:
However, based on a quick look at the code, I can speculate about two ways of implementing this change that do not involve storing editor state in the widget tree:
  1. Have a field other than "draft.of" which marks a tiddler as a "draft" for the purposes of refresh time.
  2. Modify the "change" event listener receive an optional parameter for the timeout associated with any given change, allowing any source of changes to define its own timeout value.
#1 could be implemented quickly by changing this line of the render code to something like:

                        if(!tiddler || !tiddler.hasField("draft.of") || !tiddler.hasField("refresh.slow")) {


What if you just add a field "draft.of" to your temporary input tiddler. There would be no need to change the core and it's easy for you to add the field to your code.

-m

Jeremy Ruston

unread,
Sep 23, 2015, 1:23:56 PM9/23/15
to TiddlyWiki
Hi Evan

> So the source of objection to this feature is that the data might be erased if the containing widget tree is refreshed within that small interval of time?  I would consider that acceptable...

I don't think random, hard to find data loss is at all acceptable. The basic design of TiddlyWiki has to be robust, I think.

Anyhow, #1494 rather endlessly repeats the reasons why the originally proposed solution doesn't work within the context of TiddlyWiki's design.

The best solution to the problem is to extend the existing refresh dampening mechanism as you propose. Using a field like "refresh.slow" as you suggest is nice and quick, but may be a bit restrictive. One alternative would be to consult $:/config/refreshdelay/<tiddlertitle>, but of course that would be rather expensive, adding a store lookup for every changed tiddler.

Best wishes

Jeremy.







--
You received this message because you are subscribed to the Google Groups "TiddlyWiki" group.
To unsubscribe from this group and stop receiving emails from it, send an email to tiddlywiki+...@googlegroups.com.
To post to this group, send email to tiddl...@googlegroups.com.
Visit this group at http://groups.google.com/group/tiddlywiki.
To view this discussion on the web visit https://groups.google.com/d/msgid/tiddlywiki/d1e6e6e0-7b4e-4b15-aa3e-8a49afb83a06%40googlegroups.com.

For more options, visit https://groups.google.com/d/optout.



--
Jeremy Ruston
mailto:jeremy...@gmail.com

Evan Balster

unread,
Sep 23, 2015, 2:21:39 PM9/23/15
to TiddlyWiki, jeremy...@gmail.com
Hey, Jeremy --

The other day I picked through the code to ascertain what would be necessary to implement a refresh delay setting in $edit-text and friends.

It looks like a time value would need to percolate through a few abstractions:
  • State Modifying Widgets
    • Add a new "delay" property, and possibly means to fall back to default values
    • Store this property in the AddTiddler hashmap
  • AddTiddler microkernel function
    • Extract the delay property from the hashmap argument
    • Pass the property to enqueueTiddlerEvent
  • enqueueTiddlerEvent wiki.js function
    • Accept the delay property as a third argument
    • Add this property to the data for the change event
  • - "change" event handler
    • Extract the delay property from the event argument and schedule delay
One nice thing about this approach is that it would no longer be necessary to check for draft tiddlers in the change event handler -- instead, the editor widgets in the EditTemplate would simply assign a delay value.  Probably the most obtrusive element of this solution is that the parameter of the change event would need to be redefined as a hashmap...  It looks as if there is a lot of code that handles that event, and I don't know enough about TiddlyWiki to identify other possible datapaths for the delay variable.

If multiple change events are aggregated, it would be sensible to use the lowest delay value associated with any of them for scheduling purposes.

...Anyway, at this point I'm feeling as if I could try my hand at this modification -- but I might end up out of my depth as I'm not a JavaScript expert.  Jeremy, let me know if this scheme sounds remotely sensible.

Tobias Beer

unread,
Sep 23, 2015, 3:15:10 PM9/23/15
to tiddl...@googlegroups.com, jeremy...@gmail.com
Hi Evan, Jeremy,

Can't a focused editor, one that is actively used and wanting to delay commits, put a write-lock on the field being edited?
That way it's pretty clear that when some other place tries to apply changes, that it can't ...and react accordingly.

Best wishes,

— tb 

Jeremy Ruston

unread,
Sep 23, 2015, 5:01:46 PM9/23/15
to Evan Balster, TiddlyWiki
Hi Evan

On Wed, Sep 23, 2015 at 8:21 PM, Evan Balster <balste...@gmail.com> wrote:
The other day I picked through the code to ascertain what would be necessary to implement a refresh delay setting in $edit-text and friends.

Great stuff, there's a lot to navigate there. 
  • State Modifying Widgets
    • Add a new "delay" property, and possibly means to fall back to default values
    • Store this property in the AddTiddler hashmap
  • AddTiddler microkernel function
    • Extract the delay property from the hashmap argument
    • Pass the property to enqueueTiddlerEvent
  • enqueueTiddlerEvent wiki.js function
    • Accept the delay property as a third argument
What would it do if the same tiddler were written twice with different delays specified?
    • Add this property to the data for the change event
  • - "change" event handler
    • Extract the delay property from the event argument and schedule delay
I think this proposal essentially duplicates an existing mechanism, but is not as well encapsulated. Here you have logic in the render.js change handler, and in the wiki store. The apparent benefit is that the caller gets to control the delay, but at a great cost: the change event handler must keep track of a whole bunch of delays, rather than just one. The caller doesn't in fact have control over the delay because of competition with other code writing to the same tiddler (including the core).

It feels much neater to make the delay be a concern of the main render module. The need for these delays is a user interface concern, and shouldn't be a part of the store.

(Just to be clear that none of this negates the need to extend the existing mechanism so that we can defer refresh for tiddlers other than the usual draft tiddlers).
 
One nice thing about this approach is that it would no longer be necessary to check for draft tiddlers in the change event handler -- instead, the editor widgets in the EditTemplate would simply assign a delay value.

Surely lots of different delay values?
 
Probably the most obtrusive element of this solution is that the parameter of the change event would need to be redefined as a hashmap...  It looks as if there is a lot of code that handles that event, and I don't know enough about TiddlyWiki to identify other possible datapaths for the delay variable.

Since the end of the beta the interfaces within TiddlyWiki have been constrained to backwards compatibility. It's actually not that much of a constraint, one can usually fix things by adding rather than changing or taking away.
 
If multiple change events are aggregated, it would be sensible to use the lowest delay value associated with any of them for scheduling purposes.

This again means that the caller has even less control than indicated by the interface. There are two places where the specified delays are aggregated.
 

...Anyway, at this point I'm feeling as if I could try my hand at this modification -- but I might end up out of my depth as I'm not a JavaScript expert.  Jeremy, let me know if this scheme sounds remotely sensible.

I welcome contributions but would urge you to consider having a go at something less speculative and therefore controversial. I think there are a lot of popular issues on GitHub that are amenable to a fix by anyone with the time, and of course I'll always be happy to advise when I can.

Best wishes

Jeremy.

Evan Balster

unread,
Sep 23, 2015, 7:38:50 PM9/23/15
to TiddlyWiki, balste...@gmail.com, jeremy...@gmail.com
Haha, well, my motivations are selfish -- I'm working on an interactive application which is having responsiveness issues due to the current updating behavior.


My proposal relies on a few assumptions, which may be faulty:
  1. The user's direct interaction initiates all changes to the state of the tiddler store -- or at least the vast majority of changes.
  2. The user's interaction with editor widgets can change the state of at most one tiddler at a time.  (I understand this does not apply to triggering widgets!)
You make a strong case that editor widgets should not have state independent of the store, to avoid conflicts in or loss of data.  This makes sense to me.  I also agree that delay is a feature of the interface / UX design, and it is my view that the degree of delay should be a function of the means by which the information is changed, rather than what information is changed -- hence the proposal that it be a feature of the instigating widgets.

The question then becomes how the editor widgets can communicate to the renderer that the changes they have instigated should be delayed.  There needs to be a means of associating the changes (which the renderer receives as event notifications from the store) with the specific source and delay value.  If assumption 2 is correct, only a single delay value need be set per change.


What would it do if the same tiddler were written twice with different delays specified?

I don't have a strong understanding of the situations where this might occur, other than a keypress followed by another keypress (which should reset the delay).

Jeremy Ruston

unread,
Sep 24, 2015, 4:06:31 AM9/24/15
to Evan Balster, TiddlyWiki
Hi Evan
  1. The user's direct interaction initiates all changes to the state of the tiddler store -- or at least the vast majority of changes.
  2. The user's interaction with editor widgets can change the state of at most one tiddler at a time.  (I understand this does not apply to triggering widgets!)
That's not quite right. Firstly, some user actions might trigger an asynchronous change to the store - for example, triggering the loading of a lazily loaded tiddler. Second, changes made by other users in multi-user scenarios are planned to automatically ripple to other connected browsers. Third, imagine a clock plugin that just sets $:/currentTime every 1s to the current time. That would make it easy to display an automatically updating clock or timer in any required format, and would lead to asynchronous changes to the tiddler store.

You make a strong case that editor widgets should not have state independent of the store, to avoid conflicts in or loss of data.  This makes sense to me.  I also agree that delay is a feature of the interface / UX design, and it is my view that the degree of delay should be a function of the means by which the information is changed, rather than what information is changed -- hence the proposal that it be a feature of the instigating widgets.

I understand the motivation, but as I mentioned above I think it's hard for such a system to offer any solid guarantees as to how the specified delay will be treated.
 
The question then becomes how the editor widgets can communicate to the renderer that the changes they have instigated should be delayed.  There needs to be a means of associating the changes (which the renderer receives as event notifications from the store) with the specific source and delay value.  If assumption 2 is correct, only a single delay value need be set per change.

The problem here is how one coalesces adjacent updates. But I maintain that the fundamental mistake to couple the operation of the tiddler store to the user interface concerns of how we handle refreshing. Remember that the refresh cycle is not intrinsic to the tiddler store; it's built on top of the mechanisms that it provides.

Best wishes

Jeremy.

Danielo Rodríguez

unread,
Sep 24, 2015, 6:29:18 AM9/24/15
to TiddlyWiki
I can see people focusing on delaying the write to the tiddler store where the actual problem, in my opinion, is the refresh of the widget tree.
From my point of view, for consistency we should keep the changes to the tiddler store immediate, and focus in adding a delay to the refresh mechanism. 

Regards

Tobias Beer

unread,
Sep 24, 2015, 6:54:39 AM9/24/15
to tiddl...@googlegroups.com
Hi Danielo,
 
I can see people focusing on delaying the write to the tiddler store where the actual problem, in my opinion, is the refresh of the widget tree.
From my point of view, for consistency we should keep the changes to the tiddler store immediate, and focus in adding a delay to the refresh mechanism.

Seeing as how the computation of any filtered search results are only brought about through the refresh mechanism, if I understand correctly, then this sounds ok to me. Same for a live-preview. The user would be typing and commiting to the tiddler store instantaneously, but any preview or updates elsewhere would only refresh and propagate once the user has stopped typing for a given time.

Here's how I would interpret possible global refresh-delay settings...

refresh-delay: 300ms
  • after an update, do not refresh for 300ms
  • after those 300ms only refresh so long as the triggering widget did not fire another refresh event
refresh-delay-max: 2s
  • if the user has managed to accumulate delays for 2s, do a refresh anyway
Best wishes,

— tb

Jeremy Ruston

unread,
Sep 24, 2015, 8:08:24 AM9/24/15
to TiddlyWiki
Hi Tobias

Here's how I would interpret possible refresh-delay settings...


delay: 300ms
  • after an update, do not refresh for 300ms
  • after those 300ms only refresh so long as the triggering widget did not fire another refresh event
max-delay: 2s

  • if the user has managed to accumulate delays for 2s, do a refresh anyway

I'd urge you to examine the implementation of the existing refresh dampening mechanism:


If I've not been clear enough, I am against introducing a new mechanism to achieve the same thing as an existing mechanism. The only choice is to adapt the existing mechanism. I don't see why that is a problem; it works well, and avoids the problems I've pointed out above. The only thing we need to improve is the way that dampened tiddlers are configured.

Best wishes

Jeremy.



 
Best wishes,

— tb

--
You received this message because you are subscribed to the Google Groups "TiddlyWiki" group.
To unsubscribe from this group and stop receiving emails from it, send an email to tiddlywiki+...@googlegroups.com.
To post to this group, send email to tiddl...@googlegroups.com.
Visit this group at http://groups.google.com/group/tiddlywiki.

For more options, visit https://groups.google.com/d/optout.

Tobias Beer

unread,
Sep 24, 2015, 8:37:31 AM9/24/15
to tiddl...@googlegroups.com, jeremy...@gmail.com
Hi Jeremy,
 
I'd urge you to examine the implementation of the existing refresh dampening mechanism:

Mhhh, now I feel mildly stupid. o.O
Half or more of what I keep rambling on about is already there.
Will re-read the issue thread on github, again.
Not sure, how I missed that.
Sorry for the latency. 

So, what could be done is extending these bits so as to:
  1. also delay refreshing for (certain, perhaps state / temp) tiddlers instead of just drafts, e.g. those storing a search-terms
    • those tiddlers could perhaps simply have a tag $:/tags/DelayRefresh or some field tw-refresh-delay:2
  2. implement a max-refresh-delay
    • seems rather straight forward, using two timers eventually canceling each other out
    • the max-timer not being cleared, though
Do you see problems with 1. or 2.?

Best wishes,

— tb

Jeremy Ruston

unread,
Sep 25, 2015, 11:21:46 AM9/25/15
to Tobias Beer, TiddlyWiki
Hi Tobias

So, what could be done is extending these bits so as to:
  1. also delay refreshing for (certain, perhaps state / temp) tiddlers instead of just drafts, e.g. those storing a search-terms
    • those tiddlers could perhaps simply have a tag $:/tags/DelayRefresh or some field tw-refresh-delay:2

Yes, I think that agrees with the proposal we've started with - see my comments above "refresh.slow".
 
  1. implement a max-refresh-delay
    • seems rather straight forward, using two timers eventually canceling each other out
    • the max-timer not being cleared, though

There's no maximum delay at the moment with the draft handling. If the user keeps typing, the refresh is deferred until there's a pause of 400ms, or a non-draft tiddler is modified. I think the only situation where a maximum delay would be needed would be if the user were typing for a sustained period. I think it's reasonable that screen updates are suspended until the typing ends.
 
Best wishes

Jeremy.


Do you see problems with 1. or 2.?

Best wishes,

— tb

Tobias Beer

unread,
Oct 6, 2015, 11:28:25 AM10/6/15
to TiddlyWiki, beert...@gmail.com, jeremy...@gmail.com
@Jeremy,

Now, I'm a little confused, again. Sorry.

With this...


I'd urge you to examine the implementation of the existing refresh dampening mechanism:
https://github.com/Jermolene/TiddlyWiki5/blob/master/core/modules/startup/render.js

...how come this delay works well with the preview in edit mode
but not when I edit a field of the current tiddler in view-mode?

<$edit-text field="foo"/>

Best wishes,

— tb 

Jeremy Ruston

unread,
Oct 6, 2015, 11:39:29 AM10/6/15
to Tobias Beer, TiddlyWiki
Hi Tobias

...how come this delay works well with the preview in edit mode
but not when I edit a field of the current tiddler in view-mode?

Because the mechanism works on draft tiddlers, and there’s no draft tiddler involved when you use the edit-text widget directly.

Best wishes

Jeremy.

Tobias Beer

unread,
Oct 6, 2015, 11:46:54 AM10/6/15
to tiddl...@googlegroups.com, beert...@gmail.com
Hi Jeremy,

Because the mechanism works on draft tiddlers, and there’s no draft tiddler involved when you use the edit-text widget directly.

So, could we perhaps introduce a single state tiddler to...
  • store the text  of any edit-text widget instance when given focus
  • store any modifications during editing
  • commit the text back to the tiddler (field) after the edit delay passed?
Best wishes,

— tb

Jeremy Ruston

unread,
Oct 6, 2015, 11:49:58 AM10/6/15
to tiddl...@googlegroups.com, beert...@gmail.com
Hi Tobias

So, could we perhaps introduce a single state tiddler to...
  • store the information of any edit-text widget instance given focus
  • store the text being edited in it
  • only commit the text back to the tiddler (field) after the edit delay passed?
You’re circling around the same point that we generally end up at!

The problem with the approach you’ve outlined is that while an edit is deferred the only copy of the edited text is that within the DOM node. But any parent widget of that DOM node is free to destroy the widget tree containing it, and reconstruct it. That loses the deferred state.

We’ve discussed this before: it’s why the existing mechanism defers the refresh cycle, and doesn’t try to defer updating the value of the tiddler.

Best wishes

Jeremy.



Best wishes,

— tb

--
You received this message because you are subscribed to the Google Groups "TiddlyWiki" group.
To unsubscribe from this group and stop receiving emails from it, send an email to tiddlywiki+...@googlegroups.com.
To post to this group, send email to tiddl...@googlegroups.com.
Visit this group at http://groups.google.com/group/tiddlywiki.

Tobias Beer

unread,
Oct 6, 2015, 3:49:57 PM10/6/15
to tiddl...@googlegroups.com, beert...@gmail.com
Hi Jeremy,
 
We’ve discussed this before: it’s why the existing mechanism defers the refresh cycle, and doesn’t try to defer updating the value of the tiddler.

If we managed to dynamically rebind the input to a state tiddler during editing — rather than the tiddler which we look at — then no refresh of the tiddler we look at should take place (while typing). But yes, the moment after the delay when we refresh the current tiddler we still have the problem of re-focusing the widget that got destroyed at the position we were when editing. However (!), the state tiddler that we use to store the edited text could (perhaps) via some qualify identifier be used to store a reference to the editing widget and thus any edit-text widget could check upon its creation if it is that widget and steal the focus back. If you don't want to compute that value with every edit-text widget created, then we could perhaps cache it somehow so long as no further edits are made.

Best wishes,

— tb

Evan Balster

unread,
Oct 6, 2015, 4:39:43 PM10/6/15
to tiddlywiki, beert...@gmail.com
I'll get around to finishing the message I drafted two weeks ago here...

I see a lot of sense in Jeremy's point about always editing the Tiddler store directly, and I won't contest that.  My main objection to the tag / field approach to refresh delay is that the delay isn't a descriptor of the data being changed.  To attach that data to a persistent tiddler would mean associating ephemeral data with long-term information.  This is particularly unintuitive if it must be applied by hand.

To review, the basic UX problem is that it feels very bad when >250ms of processing delay the response to each keystroke in an edit-box.  The necessary solution involves a query within the rendering mechanism that determines a quantity of delay to be associated with a given change.  Ideally this solution should be as similar to the existing one as possible.

Within the architectural constraints discussed so far, we could:
  1. Add associative state to data which should be delayed, such as a tag, or slow.refresh field (where the latter could specify a millisecond value).  Editor widgets would apply these tags or fields in addition to normal changes.
  2. Have a global state tiddler, like "$:/Delay", governing the render mechanism, which describes which tiddlers should be refresh-delayed at any given moment.  Editor widgets would modify this state tiddler if delay is specified.
  3. Use some datapath not involving the tiddler store to communicate a list of delayed changes between the editor widgets and the rendering mechanism.
I would tend to prefer (2) because it's simple, avoids the associativity issue and also seems like it would be easier to "clean up" when the refresh goes through.  In most cases there would only be one delayed item (because only one editor widget is typically be used at a time) so it doesn't seem like the performance hit from parsing the refresh state would be too bad either.

...But of course this is all relatively uninformed opinion.  Interested in your thoughts, Jeremy.

Regards,
Evan

On Tue, Oct 6, 2015 at 2:49 PM, Tobias Beer <beert...@gmail.com> wrote:
Hi Jeremy,
 
We’ve discussed this before: it’s why the existing mechanism defers the refresh cycle, and doesn’t try to defer updating the value of the tiddler.

If we managed to dynamically rebind the input to a state tiddler during editing — rather than the tiddler which we look at — then no refresh of the tiddler we look at should take place (while typing). But yes, the moment after the delay when we refresh the current tiddler we still have the problem of re-focusing the widget that got destroyed at the position we were when editing. However (!), the state tiddler that we use to store the edited text could (perhaps) via some qualify identifier be used to store a reference to the editing widget and thus a widget could check upon creation if it is that widget and steal the focus back.

Best wishes,

— tb

--
You received this message because you are subscribed to a topic in the Google Groups "TiddlyWiki" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/tiddlywiki/hlIvXE6jRys/unsubscribe.
To unsubscribe from this group and all its topics, send an email to tiddlywiki+...@googlegroups.com.

To post to this group, send email to tiddl...@googlegroups.com.
Visit this group at http://groups.google.com/group/tiddlywiki.
Reply all
Reply to author
Forward
0 new messages