[Solved] QML, how to make the contents of one menu dynamically depend on the contents of another



  • I am trying to understand how best to use QML by implementing my own Android action bar.
    The action bar has the traditional app icon and name, then some action buttons representing common actions, then an 'action overflow' button for further actions. When the action bar is narrow, there is less room for the action buttons and I want them to overflow to the secondary menu under the action overflow button.

    I have an action bar with 4 action buttons that can resize, so buttons that overlap with the app name are not showing, and I have a secondary menu that shows zero or all of the actions depending on an int property. My problem is how do I set that int dynamically depending on which action buttons are showing.

    If I set the visible property of the action buttons (so as to use visibleChildren.length of the action bar) then the QML seems to loop. If I don't use the visible property but instead set the icon source to an empty string, then how can I dynamically determine how many action buttons have an empty string?

    I would appreciate suggestions on what might be the recommended way to do this in QML.

    Thank you.

    main.qml:

    @import QtQuick 2.1
    import QtQuick.Controls 1.0
    import QtGraphicalEffects 1.0
    import QtQuick.Window 2.1

    ApplicationWindow {
    property real scaleFactor: Screen.pixelDensity / 5.0
    property int intScaleFactor: Math.max(1, scaleFactor)

    width: 640; height: 480
    
    property list<Action> actionsList: [
        Action { text: "Sightings"; iconSource: "qrc:/sightings"; onTriggered: image1.visible = !image1.visible },
        Action { text: "Alerts";    iconSource: "qrc:/alerts";    onTriggered: image1.visible = !image1.visible },
        Action { text: "Search";    iconSource: "qrc:/search";    onTriggered: image1.visible = !image1.visible }
    ]
    property list<Action> moreActionsList: [
        Action { text: "About"; onTriggered: image1.visible = !image1.visible }
    ]
    
    ActionBar {
        id: actionBar
        barHeight: slider.value
        actions: actionsList
        moreActions: moreActionsList
    }
    
    Image {
        id: image1 x: 270 y: 257
        width: 100 height: 100 source: "qrc:/drs"
    }
    Slider {
        id: slider
        anchors { topMargin: 150; top: parent.top; }
        minimumValue: 30 maximumValue : 150 value: 80
    }
    

    }
    @

    ActionBar.qml

    @import QtQuick 2.1
    import QtQuick.Controls 1.0
    import QtGraphicalEffects 1.0

    Rectangle {
    // Android-style Action bar
    property alias barHeight: actionBar.height
    property list<Action> actions
    property list<Action> moreActions

    id: actionBar
    height: 80
    anchors { right: parent.right; left: parent.left; top: parent.top; }
    Image {
        id: image2
        height: barHeight * 0.9; width: height
        anchors { leftMargin: 15; left: parent.left; verticalCenter: parent.verticalCenter; }
        source: "qrc:/drs"
    }
    Text {
        id: titletext
        text: "ApplicationName" color: "white" font.bold: true
        font.pixelSize: barHeight * 0.33
        anchors { leftMargin: barHeight / 5; left: image2.right; verticalCenter: parent.verticalCenter; }
    }
    property int actCount: 0
    Row {
        id: actionsRow
        anchors { right: parent.right; verticalCenter: parent.verticalCenter; }
        Repeater {
            id: repeat
            model: actions
            ActionBarActionDelegate {
                // Does action icon overlap with the title text
                property bool isNoOverlap: ((titletext.x + titletext.width) < (actionsRow.x + actionsRow.children[index].x))
                iconHeight: barHeight * 0.9
                action: Action {
                    // Have an enpty icon if it would overlap the title text
                    iconSource: isNoOverlap ? modelData.iconSource : ""
                    text: modelData.text
                    onTriggered: { modelData.triggered(); }
                }
                // Disable actions that would overlap the title text
                isEnabled: isNoOverlap
            }
        }
        // Now the optional More options icon
        ActionBarActionDelegate {
            iconHeight: barHeight * 0.9
            action: Action { iconSource: "qrc:/more"; onTriggered: { console.log(actCount);
                    overflowMenu.popup() }
            }
        }
    }
    
    Rectangle {
        // Action bar shadow
        id: actionBarShadow
        height: barHeight > 60 ? 5 : 2
        width: parent.width color: "darkgray" anchors { top: parent.bottom; }
    }
    property int overflowCount: 1 //actionsRow.visibleChildren.length
    
    Menu {
        id: overflowMenu
        title: "Edit"
    
        Instantiator {
            id: inst
            model: actionsList
            MenuItem {
                text: modelData.text
                visible: (index < overflowCount)
                onTriggered: modelData.triggered()
            }
            onObjectAdded: overflowMenu.insertItem(index, object)
            onObjectRemoved: overflowMenu.removeItem(object)
        }
    
        MenuSeparator { visible: (overflowCount > 0) }
    
        MenuItem { text: "About"
            onTriggered: console.log("overflowCount =", overflowCount); }
    }
    

    }
    @

    ActionBarActionDelegate.qml

    @import QtQuick 2.0
    import QtQuick.Controls 1.1

    Item {
    property Action action
    property alias iconHeight: actionButton.height
    property alias isEnabled: actionButton.enabled

    id: actionButton height: 70 width: height
    
    Image {
        id: iconImage
        anchors.fill: parent
        source: action ? action.iconSource : ""
    }
    Rectangle {
        // Hint to show when icon is clicked
        id: iconClickedArea
        anchors.fill: parent
        color: "darkgray" opacity: 0
    }
    MouseArea {
        anchors.fill: parent
        onClicked: action.triggered()
        onEntered: iconClickedArea.opacity = action ? 0.5 : 0
        onExited:  iconClickedArea.opacity = 0
    }
    

    }
    @



  • I think I may have finally worked out a way to do this. In the ActionBarActionDelegate I need to latch onto the enabled property and then effectively export this by emitting a signal for the ActionBar to handle. It can handle it by setting the int property to the appropriate action button count.

    in ActionBarActionDelegate:
    @Item {
    property int actionIndex: 0 // Index of the action in the action bar list (if set)
    property Action action
    property alias iconHeight: actionButton.height
    signal activationChanged(int actionIndex, bool active)

    id: actionButton
    height: 70
    width: height
    
    onEnabledChanged: activationChanged(actionIndex, enabled)
    

    ...@

    and in ActionBar
    @ ActionBarActionDelegate {
    iconHeight: barHeight * 0.9
    actionIndex: index
    action: Action {
    iconSource: modelData.iconSource
    text: modelData.text
    onTriggered: { modelData.triggered(); }
    }
    // Does action icon overlap with the title text?
    // Disable (and implicitly hide) actions that would overlap the title text
    enabled: ((titletext.x + titletext.width) < (actionsRow.x + actionsRow.children[index].x))
    onActivationChanged: {
    if (active && actionIndex < overflowCount)
    overflowCount = actionIndex
    else if (!active && actionIndex >= overflowCount)
    overflowCount = actionIndex + 1
    console.log("Activated on index:", actionIndex, "as", active, "overflowCount:", overflowCount)
    }
    }
    @


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.