Prevent “inherited” signal handlers from executing

前端 未结 2 1845
孤独总比滥情好
孤独总比滥情好 2021-01-14 05:35

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

2条回答
  •  無奈伤痛
    2021-01-14 06:07

    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. In handleOnSomethingHappened there is superHandleOnSomethingHappened, which executes the base class' implementation.

    With these naming conventions we can achieve a setup with a bunch of nice properties:

    1. Multiple inheritance possible
    2. base class' implementation can optionally be called at any specific point
    3. A class must only know its direct base class
    4. Caller code is nicer than implementation
    5. It is complex but not complicated (and Complex is better than complicated.)
    6. I guess it can be code-generated easily

    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):

    BaseButton {
        text: "Button 1"
    }
    
    BaseButton {
        text: "Button 2"
        handleOnClicked: function() {
            console.log("Custom click handler")
        }
    }
    
    BaseButton {
        text: "Button 3"
        handleOnClicked: function() {
            console.log("Custom click handler")
            superHandleOnClicked()
        }
    }
    

    This can be done by defining BaseButton like this

    Button {
        property var handleOnClicked: superHandleOnClicked
    
        // "super" from the instance's perspective. Use this in implementations of handleOnDoubleClicked
        property var superHandleOnClicked: handleOnClicked_BaseButton
    
        function handleOnClicked_BaseButton() {
            console.log("BaseButton clicked.")
        }
    
        onClicked: handleOnClicked()
    }
    

    The base implementation is available in the function superHandleOnClicked.

    Slots with arguments

    When arguments are used, nothing changes:

    Rectangle {
        width: 100
        height: 40
        color: "green"
        BaseMouseArea {
        }
    }
    
    Rectangle {
        width: 100
        height: 40
        color: "green"
        BaseMouseArea {
            handleOnDoubleClicked: function(mouse) {
                console.log("Custom click handler", mouse.x, mouse.y)
            }
        }
    }
    
    Rectangle {
        width: 100
        height: 40
        color: "green"
        BaseMouseArea {
            handleOnDoubleClicked: function(mouse) {
                console.log("Custom click handler", mouse.x, mouse.y)
                superHandleOnDoubleClicked(mouse)
            }
        }
    }
    

    with BaseMouseArea defined as

    MouseArea {
        anchors.fill: parent
    
        property var handleOnDoubleClicked: superHandleOnDoubleClicked
    
        // "super" from the instance's perspective. Use this in implementations of handleOnDoubleClicked
        property var superHandleOnDoubleClicked: handleOnDoubleClicked_BaseMouseArea
    
        function handleOnDoubleClicked_BaseMouseArea(mouse) {
            console.log("BaseMouseArea clicked", mouse.x, mouse.y, ".")
        }
    
        onDoubleClicked: handleOnDoubleClicked(mouse)
    }
    

    Multiple inheritance

    Now we have instance is a PointerMouseArea is a BaseMouseArea, with instance being defined as

    Rectangle {
        width: 100
        height: 40
        color: "blue"
        PointerMouseArea {
        }
    }
    
    Rectangle {
        width: 100
        height: 40
        color: "blue"
        PointerMouseArea {
            handleOnDoubleClicked: function(mouse) {
                console.log("Don't tell father and grandfather", mouse.x, mouse.y)
            }
        }
    }
    
    Rectangle {
        width: 100
        height: 40
        color: "blue"
        PointerMouseArea {
            handleOnDoubleClicked: function(mouse) {
                console.log("Tell father and grandfather", mouse.x, mouse.y)
                superHandleOnDoubleClicked(mouse)
            }
        }
    }
    

    which requires the following definition of PointerMouseArea:

    BaseMouseArea {
        cursorShape: Qt.PointingHandCursor
    
        superHandleOnDoubleClicked: handleOnDoubleClicked_PointerMouseArea
    
        function handleOnDoubleClicked_PointerMouseArea(mouse, superImplementation) {
            console.log("Pointed at something") // I just add my comment and then call super
            handleOnDoubleClicked_BaseMouseArea(mouse)
        }
    }
    

    What you see in PointerMouseArea is

    1. It adds some properties over BaseMouseArea (cursor shape)
    2. It overrides the super* methods to it's concrete implementation
    3. It implements its concrete implementation and calls the parents implementation

    The foll sample project is available here: https://github.com/webmaster128/derived-qml-slots

提交回复
热议问题