Page 1 of 1

Rerouting Events to other Gadgets ?

Posted: Mon Sep 10, 2012 7:45 pm
by grabiller
Hi,

Is it possible to reroute an Event from one Gadget to another one ?

For instance let says I have a CanvasGadget and a StringGadget and I want to resend the '#PB_EventType_LeftButtonDown' recieved by the CanvasGadget to the StringGadget when I recieve it.

Is it possible directly in PureBasic ?

On Windows I workaround the problem by subclassing the control, but on linux, I'm not sure what to do.

Any idea ?

Cheers,
Guy.

Re: Rerouting Events to other Gadgets ?

Posted: Mon Sep 10, 2012 8:07 pm
by grabiller
Never mind,

Got the solution:

PB_Gadget_SendGadgetCommand.l(Window.l, EventType.l)

(Internal PB command imported through Import "")

Cheers,
Guy.

Re: Rerouting Events to other Gadgets ?

Posted: Mon Sep 10, 2012 8:20 pm
by grabiller
Hmm, in fact, it's for Windows only, it does not work for Linux..

So my question is still valid.

If someone has an idea ?

Cheers,
Guy.

Re: Rerouting Events to other Gadgets ?

Posted: Mon Sep 10, 2012 9:14 pm
by idle
There are numerous ways to do it but the easiest is to add a signal callback to a gadget
on 32 bit linux you can use

g_signal_connect(gadgetID,Signal$,*function,*userdata)

for 64 bit you need to import it

ImportC "-gtk"
gsignal_connect(instance,signal.p-ascii,*fn,*vdata,destroy=0,flags=0) As "g_signal_connect_data"
EndImport

for details of the signals see http://developer.gnome.org/gtk/2.24/GtkWidget.html

Here's an example of a workaround fix for the calender gadget so it behaves like it does on windows

Code: Select all

ImportC  "-gtk"
  gsignal_connect(instance,signal.p-ascii,*fn,*vdata,destroy=0,flags=0) As "g_signal_connect_data"
  g_object_set_property(*widget,prop.p-ascii,*val.GValue); ;PB import not defined right for unicode  
  g_date_new()   ;pb import not defined right 
  g_date_valid(*date.GDate) ;pb import not defined right
EndImport 

Macro G_TYPE_MAKE_FUNDAMENTAL(x)
  ((x) << 2)
EndMacro
#G_TYPE_BOOLEAN = G_TYPE_MAKE_FUNDAMENTAL(5)

Prototype ProtoDateCB(date.s) 
Global iCurrentDate.s,idateinvalid.i,ikey

ProcedureC iDateGadgetClick(*editable.GtkEditable,*event.GdkEventButton,*usrdata)
  Protected startpos,*entry.GtkEntry,currentdate.s
  ;get the current date abd set the selection
  *entry = *editable 
  iCurrentdate = PeekS(*entry\text,*entry\text_length,#PB_UTF8) 
  startpos = gtk_editable_get_position_(*editable) 
  If (startpos <> 2 And startpos <> 5) 
    gtk_editable_select_region_(*editable,startpos,startpos+1);
  EndIf 
EndProcedure 

ProcedureC iDateGadgetKeyPress(*editable.GtkEditable,*event.GdkEventKey,*usrdata)
   Protected key,startpos,*entry.GtkEntry 
   Protected *date
   ;need to test if the key entry is going to result in a valid date 
   ;so check the current entry text and replace the charicter with the new one 
   key = *event\keyval & $FF  
   If key >= '0' And key <= '9'
     ikey=1
     *date = g_date_new()
     *entry = *editable 
     startpos = gtk_editable_get_position_(*editable) 
     tdate.s = PeekS(*entry\text,*entry\text_length,#PB_UTF8 )
     key = *event\keyval & $FF  
     tdate = Left(tdate,startpos-1) + Chr(key) + Right(tdate,10-startpos)
     g_date_set_parse_(*date,tdate)
     If g_date_valid(*date) 
        iCurrentDate = tdate 
     Else 
       idateinvalid =1
     EndIf  
     g_date_free_(*date) 
   EndIf  
 EndProcedure  
 
 ProcedureC iDateGadgetKeyRel(*editable.GtkEditable,*event.GdkEventKey,*usrdata)
  Protected key,startpos 
 ;handle arrow keys to move selection 
  startpos = gtk_editable_get_position_(*editable) 
  key = *event\keyval & $FF
  If startpos > 0
    If key = 81 
      If (startpos <> 3 And startpos <> 6)   
        startpos -1
      Else 
        startpos-2
      EndIf   
       gtk_editable_select_region_(*editable,startpos,startpos+1);
    ElseIf key = 83 
      If (startpos <> 1 And startpos <> 4)   
        startpos +1
      Else 
        startpos +2
      EndIf   
       gtk_editable_select_region_(*editable,startpos,startpos+1);
    EndIf   
  EndIf 
EndProcedure  

ProcedureC iDateChanged(*entry.GtkEntry,*usrdata)
  Protected sdate.s, cb.ProtoDateCB 
  Protected *date
  Static eat 
  cb = *usrdata
  If Not idateinvalid 
    If Not ikey 
       sdate = Trim(PeekS(*entry\text,*entry\text_length,#PB_UTF8 ))
      cb(sdate)
    Else 
      ikey=0
    EndIf
   Else   
     ;if the date is invalid reset it to the last entry   
     gtk_entry_set_text_(*entry,iCurrentDate)
    idateinvalid = 0
   EndIf   
 EndProcedure 

;user functions

Procedure DateGadgetEx(gadget,x,y,width,height,format.s,*cbDateChanged) 
  Protected *entry,gad
  Protected gval.GValue
  If gadget = #PB_Any 
      gad = DateGadget(gadget, x, y, width, height,format)
  Else 
     DateGadget(gadget, x, y, width, height,format)
     gad = gadget 
  EndIf   
  If *cbDateChanged  
      *entry = GadgetID(gad);gtk_widget_get_ancestor_(GadgetID(gad), gtk_entry_get_type_());
      If *entry
          g_value_init_(@gval,#G_TYPE_BOOLEAN) 
          g_value_set_boolean_(@gval, #True)
          g_object_set_property(*entry,"editable",@gval);
          gtk_entry_set_max_length_(*entry,10)
          gsignal_connect(*entry,"button-release-event",@iDateGadgetClick(),0)
          gsignal_connect(*entry,"key-release-event",@iDateGadgetKeyRel(),0)
          gsignal_connect(*entry,"key-press-event",@iDateGadgetKeyPress(),0)
          gsignal_connect(*entry,"changed",@iDateChanged(),*cbDateChanged)
          ProcedureReturn gad    
      EndIf 
 EndIf 
 ProcedureReturn gad 
 EndProcedure 

Procedure DateChanged(sdate.s)
  Debug sdate
EndProcedure 

If OpenWindow(0, 0, 0, 300, 300, "DateGadgetTest", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
  DateGadgetEx(1, 8, 8, 120, 28, "%dd.%mm.%yyyy",@DateChanged())
          
  Repeat
    Select WaitWindowEvent()
      Case #PB_Event_Gadget
        Debug "In Linux no event at all"
      Case #PB_Event_CloseWindow
        Break
    EndSelect
  ForEver
EndIf

Re: Rerouting Events to other Gadgets ?

Posted: Mon Sep 10, 2012 9:31 pm
by grabiller
That's exactly what I was looking for.

A big thanks to you.

Cheers,
Guy.

Re: Rerouting Events to other Gadgets ?

Posted: Tue Sep 11, 2012 12:37 am
by grabiller
In fact I would need an additional information:

Once I'm in the iDateGadgetClick callback for instance, what should I do to 'pass-through' or 're-send' the same event I just recieve to another Widget ?

Cheers,
Guy.

Re: Rerouting Events to other Gadgets ?

Posted: Tue Sep 11, 2012 2:55 am
by idle
you can use

Code: Select all

gtk_signal_emit_by_name_(GadgetID,"signal",0);
and catch the event in the main loop

Re: Rerouting Events to other Gadgets ?

Posted: Tue Sep 11, 2012 4:48 am
by grabiller
idle wrote:you can use

Code: Select all

gtk_signal_emit_by_name_(GadgetID,"signal",0);
and catch the event in the main loop
Ah, thanks a lot, it seems to be the solution yes, but strangely when I use it to send to the "other" GadgetID I get an "Invalid Memory Access" (yet the pointer is not null) while when I send it to itself it works ( I get multiple events ).

Yet the "other" GadgetID is used with other gtk api calls without triggering the error. It works.

I'm puzzled.. I'll investigate further..


Cheers,
Guy.

Re: Rerouting Events to other Gadgets ?

Posted: Tue Sep 11, 2012 12:18 pm
by grabiller
I've found the solution, I have to make room for the returned values:

gtk_signal_emit_by_name( GadgetID, "button-press-event", @event, @retval )

It does not crash anymore.

However the signal is not resent in the PB event main loop. Are you sure it should appear in the event main loop ?

Cheers,
Guy.

Re: Rerouting Events to other Gadgets ?

Posted: Tue Sep 11, 2012 8:33 pm
by idle
some but not all events will be caught in the main loop.
but I suppose you could connect a callback to handle the emitted signal.

Re: Rerouting Events to other Gadgets ?

Posted: Tue Sep 11, 2012 9:58 pm
by grabiller
That's what I'm doing but then I need to forward the signal to another widget wich does not recieve the user inputs.

Connecting callbacks to this other widget would be useless as it wont recieve events.

Anyway, I'm still investigating but I'm also reconsidering my linux approach.

Until now I was trying to 'port' my windows code wich works perfectly well (very fast, very easy to forward messages, to alter widgets programmaticaly and have immediate update of the layout before it gets painted, wich does not work on linux afaik).

But perhaps I should considere a different way more suitable to linux/gtk. The challenge beeing to achieve the exact same look and behavior of the widgets accross plateforms (and I did not touched Mac OS X yet.. ). This is the foundation of a heavy project and before I go any further I need to do this part absolutely right.

Someone on IRC suggested me to use only GTK on all tree platforms but I'm not convainced. From my experiments, native win32 UI is much faster (but perhaps someone will prove me wrong) and UI performance is also a big concern to me. Cocoa should be faster than gtk too on Mac ( but I did not use it yet so I can't really be sure). Plus, that would defeat the purpose of taking advantage of PureBasic and it's cross-plateform loop/events system (even though there is a need to use natives api calls here and there but this is far different from integrating a all new toolkit on all tree plateforms into PB).

This is not an easy problem, but I want to find the best approach. Perhaps I should forget all about native api calls and only use custom widgets entierly done in PB with the CanvasGadget. That's what I'll experiment next, I'm not sure about the performances of this approach yet but perhaps this is the solution.

Cheers,
Guy.

Re: Rerouting Events to other Gadgets ?

Posted: Wed Sep 12, 2012 3:54 pm
by BorisTheOld
To avoid the complication of generating events, why not try doing a simple procedure call. We've been using this technique for many years with Visual Basic, Power Basic, and now PB.

If our program needs to create a non-standard or user event in order to process some gadget code, we just call the appropriate procedure and don't bother creating an actual event. This technique allows pseudo-events to be directed at any gadget code we choose. And if timing is an issue then we schedule the call after a known real event, or use a timer set to a short delay.

Another technique is to use standard events in a non-standard way. For example, set up an off-screen gadget that can be used to trap got-focus events, then code its event routine to perform the various functions you need. These events can be initiated from within code by setting focus to the off-screen gadget. You can use this gadget's data word to hold a user message number.

These techniques have the advantage of being cross-platform, because they don't require API code.