[Gambas-user] How can I instantiate a virtual class?

Me adamnt42 at gmail.com
Fri Sep 14 04:16:25 CEST 2018


On Thu, 13 Sep 2018 22:43:54 +0200
Benoît Minisini <g4mba5 at gmail.com> wrote:

> 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
> 
> ----[ Gambas mailing-list is hosted by https://www.hostsharing.net ]----

Well, I have gotten soooooo close, but. :-(
With everyones' input (and a lot of QT doc searching), here's what I have found, noting 
that the final step fails so the whole thing is moot really.

1. I can't re-order an existing menu but it is possible to insert a new menu at a 
specific position by copying all the subsequent menu items from the insertion point
in the parent menu out to an array, creating the target menu item in the parent and then
re-adding the copied out items. This got around the virtual class problem with .Menu.Children
as I can use Me.Children[index].

2. I can't just "copy" those subsequent menu items, I have to unmarshall all their properties
into a Variant[] and then create new menu items in the parent. This has the drawback that
the copied items cannot have children, but that is outside the scope of my immediate goal.
The reason they have to be unmarshalled is that when I delete the original menu items it renders
any "copy" of the menu invalid.

3. To do the above I created a MenuManager class that knows about the current form's 
menus and can manipulate them. It also knows about a "Position" property for the specific 
menus I am looking at.

4. The whole idea falls over because the menu manager does its stuff when the menu is created.
Unfortunately, this seems to be too early in the form loading/startup when the .form file has not been
read. I have had that problem before with some custom controls and solved that by deferring the 
mechanism until the control_Activate event fired, Menus don't have such an event, nor any other 
suitably delayed event I could leverage.

Such is life. Anyway I have used up the client's budget for this so I'll be puttting it away for now.

One thing I have noticed though. The QT4/5 QWidget classes both have an 
"insertAction(QAction * before, QAction * action)" function. My shallow reading of the docs leads me 
to ask the question, "If the CMenu.cpp class used that instead 
of addAction() in Menu_new, with an optional Position parameter could that get rid of the need to 
rebuild the whole menu?" (or does &^*%E$$ gtk preclude that as an alternative?). Note the only thing 
I can do with C++ is spell it.

regards
b
-- 
Me <adamnt42 at gmail.com>


More information about the User mailing list