-
Notifications
You must be signed in to change notification settings - Fork 19
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
Provide a per App event catcher #383
Comments
This sounds like a good idea. I would like to suggest a simplification for the API: use Take a look at the following code sample. Notice params could be "x, y", "direction", etc. but the sample below would do fine for a generic event handler. What do you think? Shoes.app do
event do |evt, params|
case evt
when :click
para "click: x #{params.first}, y #{params.last}"
when :wheel
para "wheel: direction #{params}"
else
para "evt #{evt}, params #{params.inspect}"
end
end
end Shoes.app do
event do |evt, direction|
if evt.eql? :wheel
para "wheel is rolling #{direction}"
end
end
end |
One thing I think would be interesting is to control the events of one window (shoes app) from another app - not just interception but feeding events to it . It needs more thought and some extra events like app_loaded and app_closed |
The way things work in Shoes is that a block should be enough. Shoes.app can be assigned to a variable and its members are readily available, which would include event. So it would be possible to write something like... Shoes.app do
w = Shoes.app {}
w.event do |evt, p|
# second window event handler here
end
end By the way, I am not against using a proc but hoping to keep the API clean and simple. Shoes API always favoured block instead of proc. Though perhaps you have sample case that would make sense to use a proc instead.
Sounds interesting. Would you care to elaborate on your thoughts?
|
GUI testing was my thought. Collect the events generated by a human - serialize, Play back as often as you like. |
This would be an amazing feature. It would certainly elevate Shoes. I'm liking it. A lot. :))) |
The event method is more Shoes like. There are issues. In the user supplied event handler above - we need to provide a way to tell shoes to distribute the event to the shoes_canvas methods . An example would help. shoes_app_click(shoes_app *app, ...) would build an 'event' (class or hash) and look in app->event_handler for something to call - the optional handler and pass the arg(s) for the event. That handler has to return a value to the handler invocation from back to shoes_app_click() - pass it on to shoes_ canvas_clicked or don't pass it and then shoe_app_click() has to return SHOES_OK Shoes.app do
def handler(time, evt, hash)
if evt == :click
# save click to to yaml
return true # pass event to shoes
end
return false # DISABLE all other events
end
para "Collector";
button "start" do
# open yaml file
@watch = window do
para "you are being spied upon"
edit_line "text entry"
end
@watch.events = handler
end
button "stop" do
# close yaml file
@watch.events = NIL
end |
This approach could even allow multiple event handlers if need to. By the way, it doesn't need to be plural because an event handler only handles one event at a time. I would also suggest to use Shoes approach to remove an event handler using Also, just to make things clear (as in writing specs), Shoes should assume any handler let events be passed to other handlers by default ( |
The other side is replay or sending events to an app. We'll need a new method in app.c, call it 'play_event' for now. Shoes.app do
para "tester"
button "Chose App & yaml" do
# open yaml
# load script in new @window
end
button "start"
yaml.each_line do |args|
# build event
@window.play_event event
sleep 0.1
end
end
end This is not well thought out - just illustration. |
What do you think about It is noteworthy that |
Exactly! A gui builder might want several handlers it can switch to depending on the state of the built app.
Examples drag in assumptions :-) There is no chain of handlers - there is the default and the one set in app->event_handler. It has to deal with all event types. Writing handlers would be a very difficult task but this proposal is just a cut out in the event flow for clever people to exploit. Even describing it is a problem. |
I am getting cold feet with the proc approach after looking at your work in progress. Shoes should really focus on blocks. The good news is that a proc can in fact be passed as block. Here is a sample to illustrate how one would do. So we don't need to sacrifice the simplicity of Shoes for flexibility. We get both by using a block approach. Shoes.app do
def event(&block)
yield if block_given?
end
event do
para "block\n"
end
p = proc { para "proc\n" }
event &p
end |
Yeah, there is a lot going on and proc may not be correct - 'event do ..end` is essentially a canvas method - I want one app.event method (could be a block, perhaps) - one issue with blocks is returning true so the event cutout will call the canvas functions normally. Probably not a big issue but I'm not writing event handlers in ruby so what do I know? The big issue with a block is all the C app event functions need to know that there is a cutout to call (or not). I need to find the internal C for obj.respond_to? I haven't found it yet. One way forward would be to have a app.event_handler = t/f (that solves the tricky remove issues so that's good). So Shoes.app do
@w = window "controlled" do
end
@w.event do |time, event, args_hash|
# return true for shoes to process this event normally
# return false if we don't want normal
end
para "Controller"
button "start" do @w.event_handler = true end
button "stop" do @w.event_handler = false end
end I'm OK with that two step process. I kind of like it. I 'm warming up, a lot to having an Event class object being passed to the block/proc @w.event do |evt|
if evt.type == :keypress
evt.status = false # ignore keyevent
if evt,type == :click
evt.args["top"] = 200; evt.args["left"]= 300;
evt.status = true
end
end That solves the return value issue - when the Event obj is created in C app__code the status field will be set true (pass it on to canvas methods) and the event handler can change change the event. - a lot. In the C code for app_shoes_clicked after calling the handler with the event it can use the status and contents of the Event object sent/returns instead of the args on the initial call. That would allow uber-control of the event system. |
blocks can have a return value but it won't use return keyword. It's no different from, say, array methods with blocks (collect, select, etc). Shoes.app do
def event(&block)
retval = yield if block_given?
para "retval: #{retval}\n"
end
event do
true
end
event do
false
end
end
This is way too C-ish here. Perhaps we should consider supporting Now just remember that it should be modelled based on other Shoes events (click, release, keydown, etc) where they don't really get to be turned on/off. And it seems that My experience with Shoes events so far is that I always handled the logic by myself. If something had to be momentarily unavailable, I had instance variables to that effect, e.g. colours sample and curve control point sample. So we should keep things in the spirit of Shoes. Or get new shiny feature for all the events. :)
It would be a good idea but we need to make sure it makes sense. Right now everything is growing in complexity very fast. It's easy to get over engineered. There is actually no return value issue (see above). The However, having an Event class would make it easy to implement things like the hash |
It's not a canvas thing with multiple user handlers for each and every event type and for each slot - that would be over engineering and a lot of code and name pollution . It's an app thing! app.events = t/f works for me because the handler has to deal with all events so plural is appropriate.
Perhaps a simple request is not so simple? I'm writing the code and I don't want to write any more than I have to considering only one to two people will ever use this feature. But when I do, it ought to be useful for things you and I haven't thought enough about.
It's only an pseudo code example - I happen to like |time, event, x, y {event specific stuff in hash} but I haven't begun to see whats common and whats to be in the hash. |
We both agree it's on app level. Let's say we would deal with Shoes.app do
event do
if @status
# perform event selection and action
end
end
button "start" do @status = true end
button "stop" do @status = false end
start { @status = true }
end
We both have been around for a long time. Plural form is a rare thing in programming and applies to things like arrays, databases, etc. Events tend to be pooled and sent one at a time. For example, you have GetMessage, TranslateMessage and DispatchMessage in a Windows application. The messages can be just about anything. Still no plural.
It ought to be useful. That's why we have a conversation about it!
There is no compelling reason for an Event class at this time. It creates an additional unnecessary layer of complexity for very little gain. The mandatory hash in the block might come as a problem because it is not a necessity for most use cases. In fact, we may just have an array instead and do just fine, e.g. |time, evt, x, y| and subsequently do [time, evt, x, y].to_yaml to save for playback. |
Normally you are the voice of reason when it comes to Shoes legacy. It kinda feels the other way around right now. The truth is that a simple event handler would do. It matches Shoes philosophy and how things work in Shoes and I can see all my event dreams be feasible with it. Why do you want to make it so complicated? |
Here is something that would embody all the features you want but without the complexity. Perhaps you would better understand what I am talking about if you see the code. Shoes.app do
event do |e, t, args|
case e
when :click
x, y = args
para "click at #{t} with coordinates #{x}@#{y}\n"
when :wheel
para "wheel direction #{args}\n"
app.event.next # prevent slot wheel events and move to next event
when :motion
left, top = args
para "motion at #{left}@#{top}\n"
else
# saving for playback
[e, t, args].to_yaml
# ...
end if @status
end
button "start" do @status = true end
button "stop" do @status = false end
button "playback" do
# load some yaml file
yaml.each_line do |e, t, args|
# time can be passed to replicate the execution timeline
# ...or use a number such as 0.1 for equivalent to sleep 0.1
app.event.playback e, t, args
end
end
button "clear" do
handler = proc { |e, t, args|
para "proc: #{e}, #{t}, #{args.inspect}\n"
app.event.next
}
event &handler
end
start { @status = true }
end |
code seems legit I like it. Simple and useful. |
A small revision for the suggested API. You could name
|
Wow. Lots to ideas to process - I should just wait another day and maybe the code will appear! Shoes.app do
event do |e, t, args|
case e
when :click
x, y = args
para "click at #{t} with coordinates #{x}@#{y}\n"
....
end if @status
end
button "start" do @status = true end
button "stop" do @status = false end the C code in app.c has no way to know that you have chosen @status as your variable. It has no way to know that you've have an event block in your code. Look at app.c - its not big or tricky - the event handlers are all together. It't can assume you have because that would be all existing scripts - it has to be told you have one. I don't want to write an Events class unless I have to but there maybe some compelling reasons like getters and setters for all the different evt types - for example keypress doesn't have x, and y.. if event args was an array like some examples above then you have know (document) what the array contents are for each eventype.
would only work if app.events was an obj that respnded to 'playback_events' so app.events can't be an array. It could be object of class EventManager (or some other name) but would require you write Yes, it just grew again and it's not shoes like. |
Are we grumpy again? Please, bear with me. I'm doing my best so we have a good understanding of the problem and its solutions.
The block is called from C but its execution is done by the Ruby interpreter. Basically, we don't need to worry about it. This technique has proven to be working over and over. This is what I used in colour and curve control point samples, Numinoes and several other Shoes programs. It's a not an issue. It's already working. To make sure you totally understand: C side only needs to prepare what it will send to the block (evt, time, parameters) and then call the Ruby function to execute the block. Ruby execute the block and has access to all relevant global/class/instance variables.
Actually, we can subclass Array but it's not a necessity. I am suggesting a coherent API here but we can work out some details. EventManager would do. The user doesn't have direct access to EventManager (similar to Canvas). Shoes will internally instantiate it into How does it sound? |
Lots of good ideas but C Ruby api and the existing code base (fricking macros will be the death of us all) will determine what is possible. It's always confusing at the C level with the junction/mixin of app vs canvas .
If only that were true. - event is not like click or keypress or a widget. Your canvas |
We both brought significative improvements to Shoes internals but there are still legacy code haunting us. We might open an issue related to this. Macroland would need improvements and simplification, or a whole new approach. For example, macros for native widgets are using C convention but we would be able to completely move shoes/types/* to Ruby if Ruby convention would used instead. Shoes widgets are written in C but they are in fact Ruby calls. We are shortchanging ourselves here.
I am not sure where the hurdles are. My latest suggestions are based on already existing things in Shoes. Some of the proof-of-concepts I wrote before included extending DSL with blocks. It should be noted it's not necessary to mixin when it comes to By the way, I thought about how to return an array and we should just settle for |
The results speak for themselves, very good indeed. Thanks for sharing your progress. Quick trick question: What happens with events when something is on top of something else? Shoes.app(width: 200, height: 200) do
@img1 = image "#{DIR}/static/shoes-icon-walkabout.png", left: 0, top: 0, width: 200, height: 200
@img2 = image "#{DIR}/static/shoes-icon-blue.png", left: 50, top: 50, width: 100, height: 100
end |
Why not write a a few lines of Shoes to find out? Shoes.app(width: 200, height: 200) do
@img1 = image "#{DIR}/static/shoes-icon-walkabout.png", left: 0, top: 0, width: 200, height: 200
@img2 = image "#{DIR}/static/shoes-icon-blue.png", left: 50, top: 50, width: 100, height: 100
event do |evt|
$stderr.puts "event called: #{evt.type} at #{evt.x},#{evt.y} mods: #{evt.modifiers}"
if evt.object
# Note: for Textblocks the evt.obj is the String of the text block
$stderr.puts " for widget: #{evt.object.class} width #{evt.width} height #{evt.height}"
end
evt.accept = true
end
end I notice a problem, perhaps, depending how how you think it should work. Speaking of which, maybe some one could try using this "feature" with better use cases. |
* not working yet since there can be slot handlers and they need to be sent to non-native widget (svg,plot,image...) Much like click does * Just checking in the code.
* can be sent to the event block (if there is one) * clean up the C code a bit.
Preliminary - docs for events |
* capture/replay in simple/samples/chipmunk.rb * work in progress
* save more info in capture * need app.replay_event() assumes we have shoes_native_move_cursor
* replay wait times are wrong but, It Works!
* better replay timing * a button press is now :btn_activate for event handler/blocks instead of :click. Updated Test/events/* to accomodate - it still calls the button proc/block if there is one. * won't move the cursor on replay - really hard/impossible? to do and violates many GUI principles - only one mouse pointer/cursor
* replay effort will stall soon. It may not be possible.
Warning - I'm going to merge the event branch to master branch very soon. It is not feature complete but it does answer #287 and you get jky modifiers for click,wheel,motion (another issue to close). The event handling stuff only happens if the user asks for it - harmless to existing scripts. Without an active I need this developer to help, it can lay dormant in master. I also need to update my box to something new (Mint 18.1 Cinnamon is my choice) and it's very likely my health status and attention will wane soon. |
Great! What do you mean by "health status and attention" ? Mint does not support Shoes so you are stepping down from shoes? or just taking a break to check the new features ? Please do not leave @ccoupe :)! |
Mint is a derivative of Ubuntu. Shoes will run. Health means I need to get a biopsy and none of the outcomes it will show will be pleasant. That's all I know, but having been down the cancer path before I know what happens to 'attention' Leaving is not up to me in this case. |
I am so sorry to hear that. I am a coeliac but I found it out not before I almost died 2-3 years ago. If I can give any advice at all, it is the fish and buckwheat diet combined with mangnesium oil, it fixed my broken stomach and intestines almost completely since then. @ccoupe I wish you all good! We will be sticking around! |
@ccoupe let's hope that you get good news on those biopsies. You are a generous and very capable man. I would be sad to see you go. We also accomplished so much together despite the frictions between us. Don't you worry about Shoes right now. You need to take good care of yourself first. |
If you have ever been in the meat grinder of the health care system you'll remember that you had plenty of free time waiting for test results, scheduling delays for doctors and treatments and so on. Since I can't know the the future, I'm just going to continue on with Shoes maintenance because it pleases me to find and fix bugs and do the right thing for the code we have.
Heart felt discussions about the Shoes 'vision' and future. |
Welcome to Canada. :)
May you be able to continue to enjoy it for many years to come. Hopefully you will continue to grant me the privilege to be your wing man.
It sure is. Eventually everything will fall in place as it usually does. |
Closing issue. If the 3.3.5 event mechanism doesn't work in the real world (for an actual app) then create new issues. |
In issue #287 and others, @backorder asks for additional control of events delivered to a window (a shoes app) to write a Shoes GUI builder in Shoes. Instead of overriding canvas.click we could create an app.event = {proc} and modify all the shoes_app_event in app.c to to call it, if specified.
It would return true if its handled the event and false if it wants normal Shoes event handling. Or vice versa? Each of the shoes_app_ could build a hash {event: :click, x: y: key: } and its up to the that overseer method to do the right thing with the hash.
The text was updated successfully, but these errors were encountered: