AUI Framework  develop
Cross-platform base for C++ UI apps
Loading...
Searching...
No Matches
7GUIs Temperature Converter

Note
This page describes an example listed in 7GUIs.
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-2024 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>() 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() let {
                biConnect(it->value(), mCelsius);
                it->focus();
            },
            Label { "°C" },
            Label { "=" } with_style { Margin { {}, 16_dp } },
            myPicker() 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;
};
    _new<TemperatureConverterWindow>()->show();
    return 0;
}
void setContents(const _< AViewContainer > &container)
Moves (like via std::move) all children and layout of the specified container to this container.
Represents a window in the underlying windowing system.
Definition AWindow.h:45
static void biConnect(PropertySource &&propertySource, PropertyDestination &&propertyDestination)
Connects source property to the destination property and opposite (bidirectionally).
Definition AObject.h:156
static decltype(auto) connect(const Signal &signal, Object *object, Function &&function)
Connects signal to the slot of the specified object.
Definition AObject.h:86
#define let
Performs multiple operations on a single object without repeating its name (in place) This function c...
Definition kAUI.h:262
#define with_style
Allows to define a style to the view right in place.
Definition kAUI.h:287
#define AUI_ENTRY
Application entry point.
Definition Entry.h:90

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 Files#