Sticky Button Down State
[1/11] from: ishtahara:yaho:o at: 30-Jun-2003 9:19
Thanks to Anton and Carl for helpful button solutions. One more question, if I may. Given: sites: [ http://www.rebol.com http://www.rebol.org http://www.reboltech.com http://www.rebolforces.com ] view layout [ button "Start Scan" [ foreach site sites [print read site] ] ] The button state remains as pressed until the foreach operation is complete (may be long operations). The sticky button is not very disirable to see, but even less, if user clicks button during wait, the events held in queue and then processed. Returning button immediately to "up" state is desireable but invites user clicks. Any list advice for this situations? Progress is one way, but I maybe dont want to attach progress to all such buttons. I plan to release some reblets with SDK, and extra thorogh style will reduce users support questions. Thanks again! ~D
[2/11] from: SunandaDH::aol::com at: 30-Jun-2003 14:43
> The sticky button is not very disirable to see, but > even less, if user clicks button during wait, the > events held in queue and then processed. Returning > button immediately to "up" state is desireable but > invites user clicks.
You have several "real world" issues here and, as usual, they complicate code that looks very simple until you start adding the stuff to handle them. As I see it, you have four main issues: 1. How to stop a block of code being executed a number of times concurrently if someone repeatedly presses the button. This is important as REBOL can crash if someone tries this; 2. How to allow the user to cancel a long-running operation; 3. How to change a button state to show "in-progess" or some such; 4. How to show a progress bar. The last two are relatively trivial, so I'll leave them up to you. The first two need some extra code. In the rewrite of your code below, I've added: -- two extra "state variables" so we can track if a process is underway, and we can request a termination; -- A cancel button so the user can request the termination; -- wait 0 in the action loop -- important as otherwise the cancel action will be deferred until the main process is complete -- not what you want. ===== Cancel-requested: false in-process: false sites: [ http://www.rebol.com http://www.rebol.org http://www.reboltech.com http://www.rebolforces.com ] unview/all view layout [ button "Start Scan" [ if in-process [return true] in-process: true cancel-requested: false foreach site sites [ wait 0 if cancel-requested [in-process: false return true] print read site ] ;; for in-process: false ] ;; action button "Cancel" red [ cancel-requested: true ] ;; action ] ;; layout ===== [In the real, real world, you'd want to surround some of this with a 'try block so that the extra variables get set correctly in the event of an error -- otherwise, higher level recovery and restart code may leave the variables incorrectly set....If in-process is accidentally left true for any reason, the button becomes effectively disabled. Error-recover code can seem endless :-) ] Sunanda
[3/11] from: maximo:meteorstudios at: 30-Jun-2003 16:42
Hi, here is my unsticky solution... with this example everything stays active... the last suggestion, if run, still jams the ui when I tested it. The code below is simplified, but the wait loop is the real issue. it tests to see if we are currently scanning and if the user presses start again, it opens a warning box... if you want to do it for multiple files just iterate on the do-start and add an argument to it for the file to browse. ========== rebol  gui: layout [ button "start" [do-start] button "stop" [do-stop] ] user-cancel: false started: false do-start: does [ either not started [ started: true data: open/lines http://www.rebol.com user-cancel: false until [ print first data wait 0 if user-cancel [break] data: next data tail? data ] if tail? data [print "==== END OF FILE ===="] close data started: false ][ request/ok "==== ALREADY PRINTING ====" ] ] do-stop: does [ user-cancel: true started: false ] view gui ========== HTH -max
[4/11] from: ishtahara:ya:hoo at: 30-Jun-2003 15:47
Thanks to Maxim and Sunanda-- Your examples are very helpful. I can see the necessary extra work and code when programming for the real world. Sadly much simplicity and elegance are lost, especially after adding all the try/attempt code. Thank you very much for this insight, which would have been much longer arrived without your help. ~D
[5/11] from: antonr:iinet:au at: 1-Jul-2003 11:51
Gabriele Santilli first showed me this technique of covering the button with a blurred face: view layout [ b: button "go" [cover/offset: face/offset show cover] c: button "cancel" [cover/offset: negate cover/size show cover] at (negate b/size) cover: box b/size effect [merge emboss] ] (You can try the other effects than emboss: luma -20, blur etc.) Anton.
[6/11] from: greggirwin:mindspring at: 30-Jun-2003 22:28
Hi Daril, DN> I can see the necessary extra work and code when programming for the DN> real world. Sadly much simplicity and elegance are DN> lost, especially after adding all the try/attempt code. I don't have time to respond in proper detail, but don't forget that this is true for any event driven system, not just REBOL (I'm guessing you know that, so I say it for the benefit of lurkers who may not). I remember reviewing VB apps suffering from cascading events and unhandled multiply-triggered events (multivents? :). So, what to do? In small apps, you can treat things specifically with only minor pain and suffering but, as you point out, a loss of elegance and simplicity. In more complex situations another solution may be called for. I haven't done it, but you could probably write styles that handle multivents at least better than the raw styles do now. My main tool against this kind of problem is a state machine. Events are placed into a queue and dispatched from there. It means thinking about things in a different way, and isn't always a good fit but it's served me well in both VB and REBOL. -- Gregg
[7/11] from: ishtahara:yah:oo at: 1-Jul-2003 5:06
--- Gregg Irwin wrote:
> I don't have time to respond in proper detail, but > don't forget that this is true for any event driven > system, not just REBOL
Very true. Program hardening is not avoided in any development.
> In more complex situations another solution may be > called for. I haven't done it, but you could > probably write styles that handle multivents at > least better than the raw styles do now.
I don't do much View programming now, but when I do it would be great to see examples or a How-to. ^_^
> My main tool against this kind of problem is a state > machine. Events are placed into a queue and > dispatched from there.
Sounds complex... Thinking further about Anton's (Gabriele's) trick: Maybe the following could work: - Set button text to gray - Set feel to none at beginning of action block - Process actions - Reset feel to accept button clicks - Reset button text to original color Maybe this would not stop events from building in queue though. ~D
[8/11] from: antonr:iinet:au at: 1-Jul-2003 23:48
Yes it does stop events. If a face has no feel to process events, then it is ignored by view's event system. view layout [ b: button "go" [ face/font/color: gray face/state: no show face face/feel: none ] do [my-feel: b/feel] button "cancel" [ b/font/color: white b/feel: my-feel show b ] ] Anton.
[9/11] from: greggirwin:mindspring at: 1-Jul-2003 9:47
Hi Daril, DN> I don't do much View programming now, but when I do it DN> would be great to see examples or a How-to. ^_^ Agreed. I think we all want that.
>> My main tool against this kind of problem is a state >> machine. Events are placed into a queue and dispatched from there.
DN> Sounds complex... The complexity is there already. State machines help to *manage* it by making things explicit. Most programs contain an informal state machine of some kind. As soon as you need to disallow events, or handle them differently based on what the app is doing, you've got a state machine. The only difference in what I'm talking about is that you design them explicitly rather than setting flags here and there, disabling event triggers, and using nested SWITCH statements. Designing the state machine for your program forces you to identify all the possible states it could be in, what events can occur, and what you do for each event in each state. All that information is going to be expressed implicitly in your program anyway. What makes the "normal" way seem simpler is that we tend to ignore a lot of the things that can actually happen, so we're handling only a portion of all the possible cases. This is a great way to keep complexity at bay, but not a great way to build mission critical software. It is always best to express things as simply and directly as possible. Sometimes the only way to find the best solution is to try all the ways you can think of and compare them. :) -- Gregg
[10/11] from: ishtahara:y:ahoo at: 1-Jul-2003 10:11
Very nice, both you and Gregg. Thank you. In a handful of messages over a few days we have learned: - how to change the font of button text - how to remove highlight of button text - how to interupt processing with a Cancel button - techniques for addressing "button down" issues This is very useful information, is everyone taking notes? ^_^ The people on this list are great! Thanks to all who helped. I think your responses help many more than you are aware. Happy REBOLing ~D --- Anton wrote:
[11/11] from: rotenca:telvia:it at: 1-Jul-2003 19:33
> > Maybe the following could work: > > - Set button text to gray
<<quoted lines omitted: 5>>> process events, then it is ignored by view's > event system.
It only works if you return to the event handling system. To kill events you can use one of functions in eat.r: http://www.reboltech.com/library/scripts/eat.r see it in http://www.reboltech.com/library/html/eat.html --- Ciao Romano
- Quoted lines have been omitted from some messages.
View the message alone to see the lines that have been omitted