Building complex systems is never simple, but there are a few things you can do to make your life a bit easier. One of the hardest parts in designing these things is to make them testable, so that work tomorrow doesn’t break work today. I’ll go over a few concepts to make life easier when doing this.

Composition - UI

Composition isn’t just a tool to use when writing code and classes. You can build UI widgets via composition as well.

Let’s look at an example from a project I am building. QML is a markup language for expressing graphical views in the Qt engine (C++).

In this project, I need a way to capture time logs on a day-by-day basis. The business specifics are unimportant, but I for sure need to express an individual day’s information.

So let’s make a DailyLogRow view and it’s associated controller.

import QtQuick 2.4
import QtQuick.Controls 2.2
import QtQuick.Layouts 1.2

Item {

    property date day
    property alias mouseArea: mouseArea
    property alias directLabel: directLabel
    property alias indirectLabel: indirectLabel
    property alias superLabel: superLabel
    property alias dayLabel: dayLabel

    signal dayClicked(date date)

    MouseArea {
        id: mouseArea
        anchors.fill: parent

        Row {
            id: rowLayout
            width: parent.width
            height: parent.height
            spacing: 5

            Text {
                id: dayLabel
                width: parent.width / 3
                text: "day"
            }

            Text {
                id: directLabel
                horizontalAlignment: Text.AlignRight
                width: parent.width / 6
                text: "d"
            }

            Text {
                id: indirectLabel
                horizontalAlignment: Text.AlignRight
                width: parent.width / 6
                text: "i"
            }

            Text {
                id: superLabel
                horizontalAlignment: Text.AlignRight
                width: parent.width / 6
                text: "s"
            }
        }
    }
}
import QtQuick 2.4
import us.parich.DailyLogApp 1.0

DayLogRowForm {

    DailyLogSerivce {
        id: svc
    }

    mouseArea.onClicked: {
        dayClicked(day)
    }

    function setDate(newDay) {
        day = newDay
        var model = svc.getLogByDate(day)

        dayLabel.text = day.toLocaleString(Qt.locale("en_US"), "d") + " - " + day.toLocaleString(Qt.locale("en_US"), "dddd")
        directLabel.text = model.direct + "d"
        indirectLabel.text = model.indirect+"i"
        superLabel.text = model.super+"s"

        if(model.direct > 0 || model.indirect > 0 || model.super > 0) {
            visible = true
            height = 30
        }
    }

    Component.onCompleted: {
    }
}

In this example, the DailyLogService via the svc property is a backend data contract that we can request day-indexed information from. We also announce to any consumer that the row has been clicked on via the dayClicked signal.

Let’s make a week of it!

Item {

    ColumnLayout {

        RowLayout {
            id: labelRow

            Text {
                id: weekLabel
                text: qsTr("This Week")
                font.pointSize: 18
            }

            Item {
                // spacer
                Layout.fillWidth: true
            }

            Button {
                id: weekShowHide
            }
        }

        Column {
            id: column

            DayLogRow {
                id: satLogRow
            }

            DayLogRow {
                id: friLogRow
            }

            DayLogRow {
                id: thuLogRow
            }

            DayLogRow {
                id: wedLogRow
            }

            DayLogRow {
                id: tueLogRow
            }

            DayLogRow {
                id: monLogRow
            }

            DayLogRow {
                id: sunLogRow
            }
        }
    }
}

Now we have 7 self-contained daily log rows in each week element. In this way, a UI element is built out of the behaviors it should contain, instead of conditional rendering of each specific user interaction.

An additional benefit of this is we can make a simple container page that embeds only 1 of an element and test it rigorously.

Item {

	property alias test: test

	DailyLogRow {
		id: test
	}

}

I’ll leave this as an excercise to the reader on applying this principle to other UI systems (tcl, html, xaml, swing).