The Code for QShark Attack

                        
                            import QtQuick
                            import QtQuick.Controls
                            import QtQuick.Layouts
                            import QMortgage
                            import Qt.labs.qmlmodels
                            import loanHelperModule
                            import "components"
                            import "layouts"
                            import "tables"
                            import "js/common.js" as Common

                            GridLayout {

                                property int fontSize: 28
                                property string currencySymbol: "$"

                                id: appLayout
                                columns: 2
                                flow: GridLayout.LeftToRight
                                rowSpacing: 16

                                LoanHelper{
                                    id: loanHelper
                                }

                                Inputs {
                                    id: leftBox
                                    Layout.columnSpan: 2
                                    Layout.maximumWidth: 480
                                    Layout.fillHeight: true
                                    Layout.fillWidth: true
                                }

                                Outputs {
                                    id: rightBox
                                    Layout.maximumWidth: appLayout.width
                                    Layout.columnSpan: 2
                                    spacing: 16
                                }

                                CalculateButtonLayout {
                                    id: buttonLayout
                                    Layout.columnSpan: 2
                                    Layout.maximumWidth: 480
                                    Layout.fillHeight: true
                                    Layout.fillWidth: true
                                    visible: true
                                }

                                AmortTable{
                                    id: tableLayout
                                    Layout.columnSpan: 2
                                    Layout.alignment: Qt.AlignHCenter
                                    Layout.fillWidth: true
                                    visible: false
                                    loanHelper: loanHelper
                                }

                                Connections{
                                    target: leftBox
                                    function onCalculateInterest(loanAmount, loanRate, loanTerm){            
                                        loanHelper.calculateInterest();
                                        tableLayout.visible = true
                                    }
                                }

                                Connections{
                                    target: buttonLayout
                                    function onCalculateInterest(){
                                        loanHelper.calculateInterest();
                                        tableLayout.visible = true
                                    }
                                }

                                Connections{
                                    target: appLayout
                                    function onWidthChanged(){
                                        tableLayout.updateLayout(appLayout.width)
                                    }
                                }

                                states: [
                                    State {
                                        when: appLayout.width <= 480
                                        PropertyChanges {
                                            target: leftBox
                                            Layout.columnSpan: 2
                                        }
                                        PropertyChanges {
                                            target: rightBox
                                            Layout.columnSpan: 2
                                        }
                                    },

                                    State {
                                        when: appLayout.width > 480
                                        PropertyChanges {
                                            target: leftBox
                                            Layout.columnSpan: 1
                                        }

                                        PropertyChanges {
                                            target: leftBox.calculateButton
                                            visible: true
                                            Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
                                        }

                                        PropertyChanges {
                                            target: rightBox
                                            Layout.columnSpan: 1
                                        }

                                        PropertyChanges {
                                            target: buttonLayout
                                            Layout.columnSpan: 1
                                            visible: false
                                        }
                                    }
                                ]

                                Binding{loanHelper.amount: leftBox.loanAmount}
                                Binding{loanHelper.rate: leftBox.loanRate}
                                Binding{loanHelper.term: leftBox.loanTerm}
                                Binding{rightBox.paymentAmount: Number(loanHelper.payment).toLocaleCurrencyString(Qt.locale(), currencySymbol)}
                                Binding{rightBox.principal: Number(loanHelper.amount).toLocaleCurrencyString(Qt.locale(), currencySymbol)}
                                Binding{rightBox.cost: Number(loanHelper.totalCost).toLocaleCurrencyString(Qt.locale(), currencySymbol)}
                                Binding{rightBox.interest: Number(loanHelper.totalInterest).toLocaleCurrencyString(Qt.locale(), currencySymbol)}
                            }
                        
                    

Screen01.ui.qml

This is where all the qml components come together to be rendered to the screen. The layout of the application is based on a grid that can resize depending on the width of the screen.

Custom Components

LoanHelper

This is a component created in C++. It is the components handling all the calculations and business logic.

Inputs

This is a custom qml component that is in charge of taking input from users. It renders a few textFields that check for valid input. If the user tries to enter something other than numbers, the input fields will not accept the text.

Outputs

This component is made out of labels that will display the results of the calculation after pressing the calculate button.

CalculateButtonLayout

This is a layout that contains the calculate button. The reason I put a single button inside a layout was for easier control of its positioning in the parent grid.

AmortTable

This component is the TableView that displays the amortization information and how long will it take to pay the debt.

Connections Section

There are 3 Connections elements that target leftBox, buttonLayout and appLayout respectively.
leftBox contains an input button that emits a calculateInterest signal when pressed. This signal will in turn call on to the loanHelper's C++ calculateInterest function.

buttonLayout works in a similar fashion to leftBox signals. The reason to have it defined in two places is that depending on the size of the screen the button will become visible or invisible.

The appLayout connection is trigerred when the screen is being resized. If the screen becomes small, then the app will be rendered in a single column, but after there is some more room, then two columns will be used to render the UI.

States Section

The states allow to control how the UI will be drawn to the screen. If the screen width is less than 480 pixels, then the UI will be in rendered as a single column.
This effect is achieved by forcing the layout to take 2 columns of space instead of one. In short, it creates the illusion of it being a single column.
When the screen is bigger than 480 pixels, then layout elements will each use a single column, changing the positioningof the UI elements.

Bindings Section

The bindings I am using allow me to assign values to variables in a simple fashion. One advantage of using Bindings is that I don't have to manually update elements upon change. For example, when entering one value into one of the text fieds, the binding will update automatically.

                        
                            InputsForm {
                                signal calculateInterest()
                                calculateButton.onClicked: {
                                    if(!loanAmount || !loanRate || !loanTerm){
                                        return
                                    }
                            
                                    calculateInterest();
                                }
                            }
                        
                    

Inputs.qml

This file is the implementation file of the InputsForm. I use implementation files as a way to try to UI and logic separated. As I am instantiating an InputsForm component, I still get access to the UI elements, but I don't need to modify the InputsForm directly.

calculateButton.onClicked

The calculateButton is a button that has been defined in InputForms.ui.qml, here we are setting a clicked event to call on a custom qml signal calculateInterest(); When this signal is send, there will be a listener in the main screen waiting for the event, so that the calculations can be performed.

                        
                            import QtQuick
                            import QtQuick.Controls
                            import QtQuick.Layouts
                            import "../components"

                            ColumnLayout {
                                property alias loanAmount: loanAmountField.amountTextField
                                property alias loanRate: rateAmountField.amountTextField
                                property alias loanTerm: termAmountField.amountTextField
                                property alias calculateButton: calculateButton

                                id: root
                                spacing: 16
                                Label {
                                    id: appTitleLabel
                                    text: qsTr("Loan Calculator")
                                    horizontalAlignment: Text.AlignHCenter
                                    verticalAlignment: Text.AlignVCenter
                                    Layout.fillWidth: true
                                    font {
                                        pixelSize: 32
                                        family: "Roboto"
                                    }
                                }

                                NumberInput {
                                    id: loanAmountField
                                    amountLabelText: qsTr("Loan Amount")
                                }

                                NumberInput {
                                    id: termAmountField
                                    amountLabelText: qsTr("Loan Term")
                                    placeholderText: qsTr("Months")
                                }

                                NumberInput {
                                    id: rateAmountField
                                    amountLabelText: qsTr("Loan Rate")
                                }

                                Button {
                                    id: calculateButton
                                    Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter
                                    text: qsTr("Calculate")
                                    Layout.fillWidth: true
                                    Layout.maximumWidth: root.width / 3
                                    visible: false
                                    font {
                                        pixelSize: fontSize
                                        family: "Roboto"
                                    }
                                }
                            }
                        
                    

InputsForm.ui.qml

This file is the implementation file of the InputsForm. I use implementation files as a way to try to UI and logic separated. As I am instantiating an InputsForm component, I still get access to the UI elements, but I don't need to modify the InputsForm directly.

property alias loanAmount