Closed ccoupe closed 6 years ago
This sounds like a good idea. I would like to suggest a simplification for the API: use event
! It would integrate better in Shoes DSL and still make it distinguishable from other slot events.
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.
not just interception but feeding events to it
Sounds interesting. Would you care to elaborate on your thoughts?
It needs more thought and some extra events like app_loaded and app_closed
Isn't it like start
and finish
? aka #25 and #26
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_appclick() - 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 @watch.event.remove
such as any other Shoes element would behave.
Also, just to make things clear (as in writing specs), Shoes should assume any handler let events be passed to other handlers by default (return true
even when not explicitly written).
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 playback
instead of play_event
?
It is noteworthy that sleep
had some troubles on Windows in the past and may still be in need of fixing.
This approach could even allow multiple event handlers if need.
Exactly! A gui builder might want several handlers it can switch to depending on the state of the built app.
Also, just to make things clear (as in writing specs), Shoes should assume any handler let events be passed to other handlers by default (return true even when not explicitly written).
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_
RE: return value
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
RE: app.event_handler = t/f
This is way too C-ish here. Perhaps we should consider supporting event.enable
and event.disable
(or start/stop in a timer/animate parlance).
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 remove
doesn't exist either. Not sure why I thought there was but we cannot remove those events (we may override them though).
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. :)
I 'm warming up, a lot to having an Event class object being passed to the block/proc
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 evt.args
would be a pain to program with compared to block parameters.
However, having an Event class would make it easy to implement things like the hash {event: :click, x: y: key: }
you mentioned earlier because we could have an instance of Event do evt.to_yaml
.
consider supporting event.enable and event.disable
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.
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).
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.
The evt.args would be a pain to program with compared to block parameters.
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. case type
is not a big improvement over writing case evt.type
- remember no body is going to use it - it's so deep in the wood we probably don't need to document it.
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.
We both agree it's on app level. Let's say we would deal with event
as any other Shoes events. Most apps that will use a global event handler will use it all the time but let's say you absolutely need a way to turn on and off events. It would look like this:
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
RE: events vs event
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.
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 ought to be useful. That's why we have a conversation about it!
RE: Event class
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 app.events
with an s if it is generally considered and behaving like a pool of events, only and only one instance per window and is preferably an internal class (cannot be instantiated). event block
should however remain without an s. Making it a class attached to app (or window or so) will allow us to prevent polluting app.*
namespace and make it easy to grow the event API as we go.
app.events
app.events.to_yaml
app.events.record [filename]
or app.events.recording [filename]
app.events.playback event, time, parameters
app.events.next
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.
app.events.playback event
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
app.event = EventManager.new()
and assign your block to some method of that object which is less shoes like than what I have know. However, it has some benefits - an initializer so It can inform shoea_app_clicked ... that there code to call into and since its an object of Shoes we can depend on the method name and data defined in there. Also a place to keep the list of 'events'
Yes, it just grew again and it's not shoes like.
Wow. Lots to ideas to process - I should just wait another day and maybe the code will appear!
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.
RE: @status
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.
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
app.event = EventManager.new()
and assign your block to some method of that object which is less shoes like than what I have know.
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 app.events
when a new Shoes.app/Window/Dialog is created.
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 .
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.
If only that were true. - event is not like click or keypress or a widget. Your canvas event {|args| block
} preference is really annoying at the C level. Not sure why but it is. Deep in the woods. I'll figure it out.
RE: Macroland and mixins
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.
If only that were true. - event is not like click or keypress or a widget. Your canvas event {|args| block} preference is really annoying at the C level. Not sure why but it is. Deep in the woods. I'll figure it out.
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 app.events
. Only instantiating EventManager in Shoes.app and write an app.events
method that always return the said EventManager instance. That should do the trick.
By the way, I thought about how to return an array and we should just settle for app.events.to_a
as it is the convention in Ruby.
[Updated] Finally, some success!
Shoes.app do
event do |evt, args|
$stderr.puts "event handler called #{evt} #{args}"
true
end
app.events = true
end
produces event handler called click [1, 306, 301]
on the terminal. and it goes on to call the real click code in C. More good news - we don't need the app.events = true
. Bad news is that you can't put an event block in window 2 from window 1 which reduces the utility. It may be possible to do that with the older app.event_handler = proc {}
This is very good news!
More good news - we don't need the
app.events = true
.
Tell me something I don't know! haha
Bad news is that you can't put an event block in window 2 from window 1 which reduces the utility. It may be possible to do that with the older app.event_handler = proc {}
It's not really a new problem. The window is only available at runtime and thus you cannot attach a new event until start {}
begins. app.event_handler
wouldn't work either (sic!).
Shoes.app do
@w = window {}
start do
@w.click do
@w.para "click"
end
end
end
And if you want to statically define it...
Shoes.app do
window do
click do
para "click"
end
end
end
if utility includes gui testing or event recording of unmodified shoes scripts then we need the feature. There is still much to do it's not trapping the click on a button for example - not calling shoes_button_send_click so that some unused code in the refactor.
We had heated conversations about how difficult it would be to ensure Shoes elements are readily available. I think it's possible without that much work and you think otherwise - our usual dynamics. :)
So it won't be strange for Shoes users to use it. They should be declaring the event within the Window declaration in a typical scenario. Sometimes it may be necessary to declaring it at runtime and thus one would use the start
block as it is customary.
Don't get me wrong. I would love to have it exactly as you suggest. Just saying here that the usage is acceptable because it's always been that way.
Typical scenario:
Shoes.app do
window do
event do |e, t, params|
para "#{e}, #{t}, #{params}"
end
end
end
Adding event at runtime:
Shoes.app do
@w = window {}
start do
@w.event do |e, t, params|
@w.para "#{e}, #{t}, #{params}"
end
end
end
our usual dynamics. :)
This works too - tested. We can have both. we do have both.
Shoes.app do
stack do
flow do
button "click here" do
$stderr.puts "button clicked"
end
end
end
app.event = proc do |evt, args|
$stderr.puts "event handler2 called #{evt} #{args}"
true
end
end
When I click on the middle of the screen and then on the button we get this on the terminal
set app event handler
have event_handler, invoking...
event handler2 called click [1, 215, 194]
button click seeks permission
button: don't have event - but should
button clicked
Sadly, button's presses don't go the shoes_app_click -> shoes_canvas_send_click path and as example shows we can't (yet) find the 'event' block on the first canvas from nested slots. We just need to climb up via parent until we find it in shoes_control_send() . It's also clear that many widgets chose to report via shoes_control_send().
It also brings the question - for a captured button press what are the args to the event handler? #384 related. :click, [self_of_control] ? x,y don't exist at this level.
This works too - tested. We can have both. we do have both.
Sorry? The code you have shown is for the current app rather than another window. Did you test for another window? That was your initial concern.
It also brings the question - for a captured button press what are the args to the event handler?
You mean the global event handler? The point of the global event handler is to be a meta event handler and not a replacement for existing Shoes handlers. It shouldn't care about the button at all because the button has its own handler.
Glad to see progress. It's exciting!
If it works in one app it will work on another app.(window). If you want to use this feature to intercept clicks (for example) anywhere then we need a way to know if the click is actually a button press -- two different things.
You may consider that a bug in Shoes but it's a deep and everywhere bug and appears to be toolkit or maintainer based do 'what works'. From this perspective its a bug/misfeature.
If it works in one app it will work on another app.(window).
I guess I will see later on.
If you want to use this feature to intercept clicks (for example) anywhere then we need a way to know if the click is actually a button press -- two different things.
Why? The click event doesn't know about it. The button block handles the button press. So why the meta click has to know whether a button is pressed or not?
We could perhaps need to know if there is a widget where the mouse is clicked. Typical GUI programming normally requires one to search through a window controls list with given information (such as position) and then will return a reference to the control.
You may consider that a bug in Shoes but it's a deep and everywhere bug and appears to be toolkit or maintainer based do 'what works'. From this perspective its a bug/misfeature.
We got a lot of those in Shoes.
Typical GUI programming normally requires one to search through a window controls list with given information (such as position) and then will return a reference to the control.
we are not typical
we are not typical
Not an excuse to bastardize Shoes.
We will be stuck to deal with a useless value in the parameters, that might often be nil or something, if you force this into the global click event because most case scenarios will not need to know this information. Global event handler does not and should not replace local events. How would the code look like anyway? Is there any way to be optional?
You might have the GUI builder in mind but it would be the GUI builder responsibility to find out which widget is clicked on. I would rather we improve traversing features: contents
, parent
, perhaps adding some find
element, etc. Then a GUI builder would rely on traversing features.
Slightly better event1.rb
Shoes.app do
event do |evt,args|
case evt
when :click
$stderr.puts "event handler called: #{evt} #{args}"
return false
else
return true
end
end
stack do
flow do
button "click here" do
$stderr.puts "button clicked"
end
click do
puts "flow click"
end
end
end
click do
puts "app click"
end
end
line 6, return false
turns of all click handler and the button press, as currently written. return true allows all of them to be processes it that's where the mouse is. The button click reports as event handler called: click [(Shoes::Types::Button)]
- not particularly useful.
Global event handler does not and should not replace local events. How would the code look like anyway? Is there any way to be optional?
You seem to have slightly different definition of 'global event handler' I suspect you just want to collect the lexical event code in one block so it easier to write? You can do that as proposed - return true and everything gets it's events. Or you could deny keyboard events but keep the clicks flowing. Depends on what you want to do.
I do want to allow grabbing events, modifying events and creating events and that's a lot more work because it's a confusing mess in there.
I think that I don't fully understand what you are doing and why. The relationship with the new event system, slot events and widget events are somehow becoming unclear now.
My objections are mostly focused on something that will fit in Shoes ecosystem. Sometimes it seems to me that you don't use Shoes that much because the new APIs are always terrible. Though the C code you produce is always good.
We don't always see eye-to-eye but we do find compromises on most things. API changes is however something you never agree to. So we must make sure that we introduce a very good API.
RE: returning true/false
This is obviously work in progress. While it was my initial suggestion, I'd like to outline that some methods may return true/false and cause mayhem when a return is not explicitly specified. Thus I corrected it with the subsequent suggestion of app.events.next
. This method would, say, allow to change flags in the EventManager for which Shoes will take the appropriate action.
The button click reports as
event handler called: click [(Shoes::Types::Button)]
- not particularly useful.
What is reported when clicking anywhere where there is no widgets?
I do want to allow grabbing events, modifying events and creating events and that's a lot more work because it's a confusing mess in there.
This is a very powerful idea. I'm agreeing with you but just don't like your approach. We need to make sure to keep things simple, coherent and consistent.
What is reported when clicking anywhere where there is no widgets?
event handler called: click [1, 253, 241]
This is a very powerful idea. I'm agreeing with you but just don't like your approach. We need to make sure to keep things simple, coherent and consistent.
You know, you could answer some of your questions by compiling Shoes and running the test scripts faster than asking me on github? Perhaps you could write the Ruby level specs for EventManager and Event classes and such, maybe the code too and I'll move on to other branches and bugs.
event handler called: click [(Shoes::Types::Button)] event handler called: click [1, 253, 241]
Are we basically loosing button id and coordinates when one click on a widget?
Perhaps you could write the Ruby level specs for EventManager and Event classes and such, maybe the code too and I'll move on to other branches and bugs.
A little difficult to do out of the blue because it has to be tied up with Shoes internal event mechanism. It's also likely to be written on the C side of Shoes. It would be relatively simple (see below an outline).
class EventManager
attr_accessor :filename
attr_accessor :logging
def initialize
@events = []
@logging = true
@filename = "shoes-event-#{Time.now.strftime("%Y%m%d-%H%M%S")}.log"
end
def to_a
@events
end
def to_yaml
@events.to_yaml
end
# Shoes event loop log event to EventManager
def record(event, time, parameters)
@events << [event, time, parameters]
open(@filename, "a") { |f| f.puts(@events.last.to_yaml) } if @logging
end
def playback(event, time, parameters)
shoes_send_event(event, time, parameters) ### C call
end
# Tell Shoes to stop dispatching this event
# and wait for next event
def next
shoes_cancel_dispatch_event() ### C call
end
end
Are we basically loosing button id and coordinates when one click on a widget?
Correct - it's that gray hole in the 'click' space when using shoes_control_send() to report widget activity. Osx uses it a lot for (s_change) often that gtk and thats a little worrisome. At that level, widgets don't report their outer most window co-ords and I don't know if there is a way to get that easily from the tool kits. Clicks on images, svg and some other visual things go through a much different path where shoes uses the outermost x,y and finds the proper 'widget' by crawling down the slots/canvas tree.
# Tell Shoes to stop dispatching this event
# and wait for next event
def next
shoes_cancel_dispatch_event() ### C call
end
Sadly, that belongs in the Events class - the arg to event do |eventobj| method - the simple event_name, other_args []
I have now is not ruby or shoes like. Needs a class.
I also don't want to pollute the Shoes/Ruby name space with the Event Class or EventManager Class names. Those name could easily clash with other gems or something the user would like to use. consider this:
Assume that we can create meaningful Shoes events from gtk/cocoa. Assume
Shoes_EventManager obviously manages objects of Shoes_Events class. There is only one one Shoes_EventManager obj for all windows/app so the triggering 'app' is a member var in the Shoes_Event object. It's started at shoes startup - normal coders don't know it's there. New GUI events are appended to the events queue (an []?) in Shoes_EventManager , The users 'event do' method will get the first event, given a change to say yay/nay and /or modify the event. If they don't have an event do
set (99% of user scripts won't) it's just push/pop and pass it on to the existing shoes widgets, pseudo widgets and any of click/keypress methods - just like normal nobody needs to know anything about Shoes_EventManager or Shoes_Event
Shoes_Events objects are the harder thing to define and code. We know from the description above that an object of Shoes_Events needs a member for the event type (click, keypress...) and it needs a field/member/var for the 'app' that created it and it needs a var for 'what do do with it` - pass it on so shoes can use it or subsume it, do thing more with it. Call the field/var 'use_event' or 'status' or whatever you want. it's a boolean. True means Shoes should do the regular thing (or you've modified the Shoes_Event and want to use the modification.
What else is in a Shoe_Event object - click needs outer most x,y,and button (and shift_key and meta-press if we can get them, keypress need the keycode (and the meta keys flags) What does a button click report? A combo_box selection? I don't know!
About those Assumptions While I expect I can get the top_level x,y for a widget "action" it's not a given for all widgets and all toolkits (gtk/cocoa) and all for all events . It could be a large amount of C/Obj-C work and all for naught if it can't do what the user want. I'm well aware that some folks want to know what is available before they decide what's important. Considering the amount of C/Obj->C work to answer that , I'm going to need a spec target(s)
It would be worth learning what what java/jruby reports in the svt::event class. http://www.informit.com/articles/article.aspx?p=354574&seqNum=3
Correct - it's that gray hole in the 'click' space when using shoes_control_send() to report widget activity.
This is something I am concerned about. I like the feature but not with this approach. One of my complains about slot event click is that we don't get the proper coordinates when clicking over a widget! Your current approach also creates an inconsistency where you may be returned coordinates or a Shoes widget and thus leaving additional burden to the Shoes users.
Osx uses it a lot for (s_change) often that gtk and thats a little worrisome.
There is no doubt there are some parts in Shoes that are rotting and downright abusive that went unchecked for years.
Sadly, that belongs in the Events class - the arg to event do |eventobj| method - the simple event_name, other_args [] I have now is not ruby or shoes like. Needs a class.
I also don't want to pollute the Shoes/Ruby name space with the Event Class or EventManager Class names. Those name could easily clash with other gems or something the user would like to use. consider this:
We have more flexibility on this considering this is an internal class. Even an anonymous class could do. The pseudo Ruby class is just for your consideration, an inspiration. So what you deem appropriate should be alright as long as the Shoes API is acceptable.
RE: NSEvent and GTK Event
Excellent resources. We should however first focus on the core event system we want to put in place and make sure we can extend it in a coherent and consistent manner. Then we can grow the API as time goes. Still, it won't hurt to get inspired by both event structures.
This is something I am concerned about. I like the feature but not with this approach. One of my complains about slot event click is that we don't get the proper coordinates when clicking over a widget! Your current approach also creates an inconsistency where you may be returned coordinates or a Shoes widget and thus leaving additional burden to the Shoes users.
This one of the critical assumptions that I'm working on. Shoes knows where the widget is (ie shoes_place) so we don't have to work with gtk/cocoa, hopefully. In this approach you'd get the start x,y darkspace (widget) but in top level co-ords. The 'puts' in the example may be confusing re: widgets - that value is the button object (self) so it can respond to many methods. Is that good enough?
I'm going to write the Shoes_Event. It's still exploratory so don't expect perfection. We need several getters and one setter so it can't be hidden/anonymous.
Shoes.app do
@b1 = button "one"
@b2 = button "two"
event do |evt|
if evt.type == :click && evt.object == @b2
evt.accept = false # no clicks for you, b2
else
evt.accept = true
end
end
end
The evt
as presented in the code snippet is a huge departure of everything else in Shoes. A considerable complexity not seen in a typical Shoes application. It may be the easiest approach to implement but it's changing the paradigm of Shoes. I'm not keen on it and continue to think we can use the current DSL to reach our objectives.
Exploring is fine. We may learn what we need to learn to find a viable solution.
Design your dsl for events, my friend. There is nothing Shoes-like or trivial about poking your head into an event stream and picking winners and losers. Experts only territory. If you can make it simple, please do so.
Design your dsl for events, my friend. There is nothing Shoes-like or trivial about poking your head into an event stream and picking winners and losers. Experts only territory. If you can make it simple, please do so.
Well I did submit to you some ideas and code samples. This is work in progress on both sides. However, I totally agree with you that there is nothing easy about this project. It does feel easier to use a data structure approach but this ain't no Pascal programming club here!
Let's keep talking and going back and forth. It might help when we have something substantial to play with and get a feel of it. Eventually one of us or both of us will figure out. We made a lot of progress already.
Pascal? - those good old days on the Apple II! If you think about it , Shoes/Ruby blocks modify objects in or out their block scope all the time. Would naming event do..end
to event! do ...end
appease you? It's more pure to Ruby.
This works
Shoes.app do
event do |evt|
case evt.type
when :click
$stderr.puts "event handler called: #{evt.type} #{evt.button}, #{evt.x} #{evt.y}"
evt.accept = @ck1.checked?
if evt.object == @btn
$stderr.puts "have widget #{evt.object}"
end
else
evt.accept = true
end
end
stack do
para "Click test 1"
flow do
@ck1 = check checked: true; para "pass clicks to Shoes"
end
flow do
@btn = button "click here" do
$stderr.puts "button clicked"
end
click do
puts "flow click"
end
end
end
click do
puts "app click"
end
end
Is an if - else chain with mentally conflicting states and names confusing? Yes it is. I said it before, It's not something I would want to code. It's not easy. Maybe you could use a state machine gem with it's own DSL. Won't be shoes-like but this feature never was.
Pascal? - those good old days on the Apple II!
I also have fond memories of Pascal programming. Been using Turbo Pascal for years.
RE: DSL
All the new major features (SVG, Plot, Event) suffer from being poorly designed API. You are not entirely to blame because Shoes comes with a shallow DSL and the features work with multiple calls where most things in Shoes don't (para, button, etc). These features are great and hopefully you don't take often to my comment. The problem really is about how to use them.
I had previously experimented in implementing a better DSL for Shoes and did some test on GtkNotebook. Many of the things I come up with don't make it up to here, for various reasons, as I mentioned before. Notebook might not make it here today but it seems appropriate to talk about my DSL experiments.
The basic idea is simply to provide methods within a block in the DSL.
Notebook example
notebook do
page do
...
end
page do
...
end
end
Here is a proof of concept for Event. We could could have Shoes.app event return an instance of Event or something, so there won't be a need for e.event do ... end
but simply event do ... end
instead.
class Event
def event(&block)
@self_before_instance_eval = eval("self", block.binding)
instance_eval(&block)
end
def method_missing(method, *args, &block)
@self_before_instance_eval.send(method, *args, &block)
end
def accept(yn)
puts yn
end
def object
"button"
end
end
e = Event.new
e.event do
accept true
puts object
end
We can probably come up with a module DSL to use into anything we need a DSL with inner method support.
This approach for DSL will greatly improve Shoes because it gives us more flexibility in complex cases such as SVG, Plot, Event, Notebook, etc. It is also well within Shoes way of doing things. One could only suspect this approach was not well known in early days of Shoes.
A clarification: I still suggest we keep block paramaters in event do |evt, ...| ... end
in addition to the DSL extention.
How much of that has to be done in the Ruby-C api? How much is Shoes? How much is Ruby? We are currently limited to using the Shoes DSL (and those FUNC_M... macros) to define anything in C to Shoes. That's a huge constraint in flexibility along with the problem that I'm just keeping shoes 3 alive - just one C coder and there is some serious code slinging ahead for the Ruby 2.4 work and the gtk3 issues. Building a new Shoes from this code base is fun to think about but it's unlikely.
The more important questions are What do you need to write your GUI builder?" What was the stumbling block? Is the proposed solution good enough?
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 shoesapp 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.