[Gambas-user] A Gambas odissey

Benoit Minisini gambas at ...1...
Wed Aug 13 12:36:18 CEST 2008


On lundi 11 août 2008, Doriano Blengino wrote:
> Hello all...
>
> I am writing a file manager, hybrid between FileRunner and a more
> modern one.
>
> I started with a DirView, and soon discovered that it doesn't raise the
> KeyPress event (but the IDE makes you think that). There were other
> things missing in DirView, so I wrote my MyDirView the way I wanted. It
> was easy at first, but then I discovered that it is difficult, or at
> least not clear, how to move the "Current" cursor. 

The Current property is not writable at the moment in TreeView, ListView and 
ColumnView. I don't remember if there is a specific reason for that. If not, 
I will make it writable in Gambas 3.

> I am not sure why,  
> but it does work by setting the Selected property of an item. I failed,
> instead, in removing all the children of an item, one by one (there is a
> clear() method, but I need to delete them one by one). The "for each ...
> in item.children" does not work; it is not the first time I see similar
> anomalies, especially while stepping the source.

Items are not enumerable, because they are not true objects.

Instead, you must use the Move* methods that move an internal cursor. Use the 
TreeView.Item property to get the item pointed by that internal cursor.

>
> Writing MyDirView revealed another problem. If in a custom control I
> receive the KeyPress event, I can not re-raise it (stack overflow). 

If "you" (I think "you" mean "an object A") receives an event from an 
object "B", raising it "again" does not mean anything. Only the object "B" 
can raise the event again.

If you mean that the object A is its own event observer, why would it need to 
send itself an event?

> So I 
> have to raise another event with a different name. This breaks the
> consistency of naming - in a row of four controls, each derived from the
> other, the same event must be called with four different names. I took a
> look at the FileView control to see what has been done in there. It
> receives Click events, and raises Click events. What inside? It does not
> inherit from ListView or similar. Now I understand why it works, but I
> don't like it.

I think you are confusing the sender and the receiver. These Click events are 
not all the same.

>
> Anyway, I went ahead. I put two FileView on the form. They don't raise
> KeyPress event. It's getting tiring. 

FileView and DirView are compound controls, they do not inherit TreeView or 
whatever directly. This is mandatory for the FileView control, as it has 
actually two different views inside: a ListView and an IconView.

You don't see KeyPress event sent by the inside views, for the same reason a 
Container does not see the KeyPress events sent by its children.

I admit this seems a bit stupid from the outside.

> I don't want to rewrite MyFileView 
> just now (I will do, because I need more functionalities). I only need
> to catch Backspace and Enter, so I make a menu voice 

A menu "voice"? What's that?

> which has the 
> Backspace key as its shortcut. Now the backspace works, but... how the
> hell can I tell which control is focused? 

Application.ActiveControl will tell you.

> The only way I found was to 
> catch the GotFocus and LostFocus events, and track the focus by hand. I
> have only three controls, but what about if I had dozens of them?
> Grouping them would group all their events, while I am interested in
> grouping only GotFocus/LostFocus events.
>
> To make it short, I succeded in make it work, and passed to the
> Enter key. The Enter key should invoke the selected file in one of the
> two FileView's. A menu voice has only one shortcut, but the Enter keys
> are two (Gambas calls them Enter and Return). Uhm... no way. Then I try
> to use the Form_Keypress event, but it does not fire - the controls are
> stealing the keys. So, there is no way to do this thing. If a FileView
> does not "activate" a file when the user presses Enter, then you can not:
>
>     1: inherit a new control which does, because you must rename all its
> events
>     2: use a menu voice to do it, because a menu voice has only one
> shortcut; if you want use more keys, no way
>     3: use the form KeyPress, which does not fire
>
> The only way left is to create a new control, and then instantiate a
> FileView inside it. This is not inheritance, because you have to
> replicate all the things you need: events, properties, methods.

KeyPress (and other) events emitted by a control are for its event 
observer(s), not for itself.

To catch them, you need to create an Observer object on the control. You can 
catch the event before it is raised, or just after. If you catch the event 
before it is raised (it is the default), then you can stop it if you want.

For the FileView control, it is more complex as you have to observe the view, 
which is the child control of FileView. And that view can change.

>
> Coming from Delphi, I must say the following:
>
> Delphi forms have a KeyPreview property. If you set it, then *all* the
> keypresses are first passed to the form. The form then decides wether to
> ignore the key, or make additional things, to let the key pass, or to
> stop it.

I think this is possible to implement.

>
> Delphi containers have an ActiveControl property which tells which
> control has the focus. 

See below.

> Moreover, every control has a Focused property 
> which tells if a specific control has the focus or not. 

I can implement that too. Maybe I will call it "HasFocus" instead.

> The list of 
> things Delphi has and Gambas doesn't is long, but I don't want to
> criticize. 

You are welcome, as I don't know Delphi at all.

> I am really missing keyboard and focus control. It seems that 
> VisualBasic programmers use the mouse a lot, and neglect keyboards, and
> so do many users, so this could be the reason why Gambas seems to do the
> same.

I admit I personnaly neglicted the keyboard when implementing the DirView and 
FileView controls, but I don't think so in Gambas itself.

>
> I don't know Gambas very well, and I could have missed a lot of things.
> But it is easy to miss things - in the documentation a "See also"
> section in every topic could really help.
>
> Finally, I must say that I think, and *repeat*, I think, the event
> management is wrong or, at least, it is not as flexible as it could be.
> Once a control raises event, you can not make it stop. If you group
> controls, you group all their events. Once you assign a handler to an
> event, you can no more change it. 

Mmm. Not really. Maybe you should first understand that it works differently 
in Gambas than in Delphi.

I suggest you read the three following documentation pages:

http://gambasdoc.org/help/doc/object-model
http://gambasdoc.org/help/cat/event
http://gambasdoc.org/help/comp/gb/observer

> Probably Gambas is too developed to 
> change things at this point but, for who cares or is courious, I will
> explain how events work in Delphi. An event in delphi has an associated
> property which contains the address of a routine to launch. For example,
> if a control has a Click event, then it has a OnClick property. That
> property contains the address of the routine to invoke when the control
> wants to "raise" the Click event. The OnClick property is a procedural
> type, a thing that does not exist in Gambas. But being a property, the
> main program can disable that event by setting that property to NIL
> (NULL), or change the handler by setting the property to another value.
> It is possible to associate many controls to the same handler, but only
> the events we want, not *all* the events of *all* the selected controls.
> In fact, events do not exist in delphi, they are simply procedure calls.
> Instead of writing:
>
>     raise KeyPress   ' Gambas code
>
> which, I suppose, unleashes a textual search for a public subroutine
> named in some correct way, in Delphi one would write:
>
>     if OnKeypress<>NIL then OnKeypress();   // procedural call
>
>
> This paradigm is faster, more sure, more flexible, more easy to
> understand. When deriving controls, the derived one can override the
> methods of the ancestor, and still call the inherited one. 

Gambas internally almost does the same thing, except that all methods must be 
members of a same object, the event observer.

You are mixing again the object that raises the event with the object that 
receives it. Apparently, this concept does not exist in Delphi, or it is not 
clearly visible. Not really object-oriented... :-)

Note that you can make an event observer whose class inherits another one and 
that reimplements the event handler, and that calls the event handler of its 
inherited class with the SUPER keyword.

And note that the textual search for public event handlers is done when the 
object is attached only.

> Gambas has an 
> interesting feature which does the opposite: instead of calling the
> ancestor method on demand, it can stop it on demand. It is quite the
> same, I think.

The event propagation has nothing to do with inheritance tree. It is just sent 
to the "Before" observer objects, the event observer, and then the "After" 
observer objects. The STOP EVENT instruction immediately stops this 
algorithm.

Regards,

-- 
Benoit Minisini




More information about the User mailing list