Defining a signal handler in a "base" component is pretty nifty when that functionality is going to be frequently used by many derived components.
However, in QML installing a new handler in a derived component does not replace the original handler, it merely stacks on top of it. As handlers are not really unique per signal, they are merely connections, and you can have multiple connections per signal.
One solution is to simply not provide a default handler in the base component, but then you have to copy and paste the handler each and every time a component is used. So is there a better way?
Given that when overriding functions in QML, the base implementation is no more available such that there must be a distinct name for each implementation.
First define a naming scheme for slot handler functions, let's say onSomethingHappened executes handleOnSomethingHappened. And
ComponentA
's implementation is handleOnSomethingHappened_ComponentA. InhandleOnSomethingHappened
there is superHandleOnSomethingHappened, which executes the base class' implementation.With these naming conventions we can achieve a setup with a bunch of nice properties:
In the first example we have 3 Buttons that handle clicks, 1. using the default implementation, 2. using a custom implementation and 3. using a custom implementation plus the base implementation (at any point):
This can be done by defining
BaseButton
like thisThe base implementation is available in the function
superHandleOnClicked
.Slots with arguments
When arguments are used, nothing changes:
with BaseMouseArea defined as
Multiple inheritance
Now we have
instance
is aPointerMouseArea
is aBaseMouseArea
, with instance being defined aswhich requires the following definition of
PointerMouseArea
:What you see in PointerMouseArea is
super*
methods to it's concrete implementationThe foll sample project is available here: https://github.com/webmaster128/derived-qml-slots
As peppe mentioned, one solution would be to instead of installing the handler directly, just make the handler a call to a function that can be overriden. However, function overriding itself is a mixed bag, when there is intent to reuse base implementations in the derived components, not necessarily in the order handlers stack with component inheritance.
I actually came up with a flexible, albeit a little clunky solution. It is to manually disconnect the previous installed handler and manually connect a new one. This has two implications:
The handlers cannot be anonymous expressions, they have to implemented as functions so they can be referenced for disconnect.
The handlers cannot be bound using the declarative syntax (
onSignal: handler()
), as this doesn't connect to the handler function, but to an anonymous expression which invokes the handler function. So you can't disconnect.So it looks something like this:
The basic pattern is disconnect the previous base handler in every derived component which overrides it. This way you get to access the base handlers from the derived components if there is a need to do that, if instead there is only one overridden handler function, the base implementations will not be accessible from the derived classes due to how overriding is implemented in QML (there will be two identically named functions as members of the object, but both of them will refer to the derived component override).
It would be niceand useful if QML provided a pretty way to make a "unique" binding - something that purges all previous connections before making the new one. Then all that workaround code would not be needed.