Skip to content

7GUIs Temperature Converter#

Example's page

This page describes an example listed in 7guis category.

Fahrenheit to Celsius and vice versa.

Challenges: bidirectional data flow, user-provided text input.

The task is to build a frame containing two textfields TC and TF representing the temperature in Celsius and Fahrenheit, respectively. Initially, both TC and TF are empty. When the user enters a numerical value into TC the corresponding value in TF is automatically updated and vice versa. When the user enters a non-numerical string into TC the value in TF is not updated and vice versa. The formula for converting a temperature C in Celsius into a temperature F in Fahrenheit is C = (F - 32) * (5/9) and the dual direction is F = C * (9/5) + 32.

Temperature Converter increases the complexity of Counter by having bidirectional data flow between the Celsius and Fahrenheit inputs and the need to check the user input for validity. A good solution will make the bidirectional dependency very clear with minimal boilerplate code.

/*
 * AUI Framework - Declarative UI toolkit for modern C++20
 * Copyright (C) 2020-2025 Alex2772 and Contributors
 *
 * SPDX-License-Identifier: MPL-2.0
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

#include <AUI/Platform/Entry.h>
#include <AUI/Platform/AWindow.h>
#include <AUI/Util/UIBuildingHelpers.h>
#include <AUI/View/AButton.h>
#include "AUI/View/ATextField.h"
#include "AUI/View/ANumberPicker.h"

using namespace declarative;

auto myPicker() {
    return _new<ANumberPicker>() AUI_LET {
        it->setMin(-999);
        it->setMax(999);
    };
}

class TemperatureConverterWindow : public AWindow {
public:
    TemperatureConverterWindow() : AWindow("AUI - 7GUIs - TempConv", 300_dp, 50_dp) {
        setContents(Centered {
          Horizontal {
            myPicker() AUI_LET {
                biConnect(it->value(), mCelsius);
                it->focus();
            },
            Label { "°C" },
            Label { "=" } AUI_WITH_STYLE { Margin { {}, 16_dp } },
            myPicker() AUI_LET { biConnect(it->value(), mFahrenheit); },
            Label { "°F" },
          },
        });

        connect(mFahrenheit.changed, [&] { mCelsius = (*mFahrenheit - 32.f) * (5.f / 9.f); });
        connect(mCelsius.changed, [&] { mFahrenheit = *mCelsius * (9.f / 5.f) + 32.f; });
    }

private:
    AProperty<int> mCelsius, mFahrenheit;
};

AUI_ENTRY {
    _new<TemperatureConverterWindow>()->show();
    return 0;
}

Comparison to Jetpack Compose#

Here's implementation of the same app with Jetpack Compose:

package ...

import ...

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        enableEdgeToEdge()
        setContent {
            CounterTheme {
                Counter()
            }
        }
    }
}

@Composable
fun TemperatureBiConverter() {
    var celsiusInputData by remember { mutableStateOf("0.0") }
    var fahrenheitInputData by remember { mutableStateOf("32.0") }
    Row(
        modifier = Modifier
            .fillMaxSize()
            .padding(horizontal = 8.dp),
        verticalAlignment = Alignment.CenterVertically,
        horizontalArrangement = Arrangement.SpaceBetween
    ) {
        OutlinedTextField(
            modifier = Modifier.weight(1f),
            value = celsiusInputData,
            onValueChange = { input ->
                celsiusInputData = input
                input.toFloatOrNull()?.also {
                    fahrenheitInputData = (it * (9f / 5f) + 32f).toString()
                }
            },
            label = { Text("Celsius") }
        )
        Text(
            text = "=", fontSize = 18.sp,
            modifier = Modifier.padding(horizontal = 8.dp)
        )
        OutlinedTextField(
            modifier = Modifier.weight(1f),
            value = fahrenheitInputData,
            onValueChange = { input ->
                fahrenheitInputData = input
                input.toFloatOrNull()
                    ?.also {
                        celsiusInputData = ((it - 32) * (5f / 9f)).toString()
                    }
            },
            label = { Text("Fahrenheit") }
        )
    }
}

Source Code#

Repository

CMakeLists.txt#

aui_executable(aui.example.temperature_converter)
aui_link(aui.example.temperature_converter PRIVATE aui::views)

src/main.cpp#

#include <AUI/Platform/Entry.h>
#include <AUI/Platform/AWindow.h>
#include <AUI/Util/UIBuildingHelpers.h>
#include <AUI/View/AButton.h>
#include "AUI/View/ATextField.h"
#include "AUI/View/ANumberPicker.h"

using namespace declarative;

auto myPicker() {
    return _new<ANumberPicker>() AUI_LET {
        it->setMin(-999);
        it->setMax(999);
    };
}

class TemperatureConverterWindow : public AWindow {
public:
    TemperatureConverterWindow() : AWindow("AUI - 7GUIs - TempConv", 300_dp, 50_dp) {
        setContents(Centered {
          Horizontal {
            myPicker() AUI_LET {
                biConnect(it->value(), mCelsius);
                it->focus();
            },
            Label { "°C" },
            Label { "=" } AUI_WITH_STYLE { Margin { {}, 16_dp } },
            myPicker() AUI_LET { biConnect(it->value(), mFahrenheit); },
            Label { "°F" },
          },
        });

        connect(mFahrenheit.changed, [&] { mCelsius = (*mFahrenheit - 32.f) * (5.f / 9.f); });
        connect(mCelsius.changed, [&] { mFahrenheit = *mCelsius * (9.f / 5.f) + 32.f; });
    }

private:
    AProperty<int> mCelsius, mFahrenheit;
};

AUI_ENTRY {
    _new<TemperatureConverterWindow>()->show();
    return 0;
}