[Gambas-user] How can I instantiate a virtual class?
Benoît Minisini
g4mba5 at gmail.com
Thu Sep 13 22:43:54 CEST 2018
Le 13/09/2018 à 19:48, Tobias Boege a écrit :
> On Thu, 13 Sep 2018, Me wrote:
>> Dumb question, I know but...
>>
>> Specifically I want to get a copy of MyMenu.Children into an array of Menu[] so I can re-order it. However,
>> Menu.Children is virtual (.Menu.Children).
>> (I have a request to make the order of (a particular menu) user configureable.)
>> I figured that if I can get a copy of the array, I could re-order it, clear the actual menu and re-add the menus in
>> the required order.
>>
>> tia
>> b
>> p.s. I just noticed that Menu.Children is a collection not an array, but that doen't change my problem.
>>
>
> TL;DR: You can't do that *in-place* with a virtual object. You have to
> make a completely new menu with the desired order and "plug that in",
> though I'm not sure how to do that, see last paragraph. (Alternatively:
> bug Benoit to implement Move* methods on .Menu.Children like we have
> in TreeView, a Sort event like GridView or at least a Reparent method.)
>
> There are two types of virtual classes: native and Gambas virtual classes.
> You can distinguish them roughly by the component they are coming from,
> or by the first character in their name:
>
> * a leading dot means native, e.g. .Menu.Children from the native gb.qt4
> component or .Watch.Events in gb.inotify, vs.
> * a leading underscore which means it's written in Gambas, e.g. _MapTile
> in gb.map.
>
> This naming is not a requirement but a convention. [*]
>
> A virtual class is meant to provide an alternate interface to some part of
> an object. In native components, a virtual property is oftentimes implemented
> by returning the exact same object but telling the interpreter to "cast" it
> to some other class, so that you have a more specialised set of methods
> available. The property accessor usually sets some sort of flag or records
> some access parameter like the settings slot in _Settings_Keys._get() so
> that the more specialised methods know where you wanted them to operate. [#]
> You can see that with the way native virtual classes are named: .Menu.Children
> is a virtual class for managing the .Children property of a Menu object and
> .Watch.Events does the same for the .Events of a Watch. [~]
>
> You may see the problem now: virtual objects don't actually exist. They are
> just a volatile view on a specific part of an object. So you can't put them
> in an array. As soon as the line where you accessed MyMenu.Children finished
> executing, the virtual object is destroyed; the hidden state used by the
> Menu object to tell that you are accessing .Children now might have been
> overwritten already. Virtual classes can be declared in native components
> but they aren't creatable and always have size zero <-- also answering your
> Subject line.
>
> You can of course re-use the menu items that the virtual Menu.Children shows
> you, like so:
>
> Dim hMenu As Menu, aSubMenus As New Menu[]
>
> For Each hMenu In MyMenu.Children
> aSubMenus.Push(hMenu)
> Next
>
> But even then, I'm stuck. You can reorder the submenus, put them into a new
> parent menu but how do you get the new parent menu into your existing menu
> hierarchy? I'm missing a Menu.Reparent() method (why does Menu not inherit
> Control?) to plug just the changed menu into what you already have.
> The only way I see right now is to rebuild your entire menu -- even the
> remote parts that didn't change -- every time you have to reorder some
> submenu. I've never been a menu user in Gambas. Maybe others found a recipe?
>
> Regards,
> Tobi
>
> PS: I moved into a new apartment and was absent from the internet for two
> weeks. In my case, it went quite a way to show how much of my social life
> happens over the cable. Anyway, this was a nice question to find on my
> first day back. I may have written too much again, but that's because
> I missed it :-)
>
> [*] An outlier is _GridView_Cell in gb.qt4 which seems like it's both or
> neither. The truth is that by now GridView is implemented in Gambas, in a
> hidden gb.gui.base component and only *documented* in gb.qt4. So the leading
> underscore tells the truth and the docs, as usual, lie.
>
> [#] BTW, this is one of the obstacles of making Gambas thread-safe.
> Lots of (all?) components rely on the assumption that they can just
> put some data into a field in the object's underlying data structure
> and return themselves to implement a virtual property and that the
> data won't be touched until the virtual object is done. This is a
> very cheap way of decluttering the interface of a class but also
> very wrong in a multi-threaded environment.
>
> [~] Actually, Gambas virtual classes (the ones which are non-native) are
> just normal classes. The "casting part of an object to a different class"
> is a strictly native feature. They probably shouldn't even be called
> "virtual" but they are often used for the same purpose. The leading
> underscore is generally used to make classes hidden, e.g. to the IDE
> autocompletion popup. You could theoretically store these objects into
> an array (and I remember doing that once) but that isn't safe either,
> depending on what assumptions the class makes on its lifetime.
>
The problem is that Menus are not implemented in the underlying toolkits
as a tree of objects that I can reorder easily and freely.
So I had to make a minimal interface that allows to add menus only on
the end of a hierarchy, delete menus, and browse them. Hence the lack of
power of the Menus virtual class.
As for GridView reimplemented in Gambas, I had to mimic the old API.
And yes, it's not multithreading at all, but the toolkit GUI are usually
single-thread (the other threads can't do GUI stuff), except maybe the
new multithreaded CSS-based layout engines of Mozilla.
And I agree that "virtual" was badly chosen. It's right in its primary
sense, but it has too many associated meaning in computing language that
does not relate to what it means in Gambas.
Regards,
--
Benoît Minisini
More information about the User
mailing list