Widgets init

NotPike 2022-01-21 00:09:12 -05:00
parent bca83a8f29
commit a0186dddf3

554
Widgets.md Normal file

@ -0,0 +1,554 @@
## About
Widgets are the elements that compose the UI of your custom app. Widgets are defined inside [`firmware\common\ui_widget.hpp`](https://github.com/eried/portapack-mayhem/blob/next/firmware/common/ui_widget.hpp) and widget functions can be found inside [`firmware\common\ui_widget.cpp`](https://github.com/eried/portapack-mayhem/blob/next/firmware/common/ui_widget.cpp). In order to be able to use them, you must `#include "ui_widget.hpp"` into your app .hpp file.
There are different type of widget. here you will find a list of available widget, with their respecting constructor. For all the methods available, you should go and see the [ui_widget.hpp](https://github.com/eried/portapack-mayhem/blob/next/firmware/common/ui_widget.hpp) file.
## Attach a Generic Widget to Your Application
In order to display a widget into your app, you can either use the function `add_child()` or `add_children()`.
Both those functions shall be called within the code of your `NewAppGameView(NavigationView &nav){}` constructor. The difference between the two function is simple: the first one allows you to add a single widget, while the second one allows you to add an undefined number of widgets.
Widgets must be passed as pointers to the functions. A correct way of calling the two functions would then be:
```
add_child(&my_widget);
```
or
```
add_children({
&widget_1,
&widget_2
});
```
## Available widgets
There are several different widgets, and more might be added, so you should always go and check whether new widgets have been added or not. Here you will find a list of most basic widgets.
#### Text
The text widgets add a simple text area to your app. Here you can find it's declaration and prototype:
```
Text my_text_widget{
Rect parent_rect,
std::string text
};
```
To be noted that `Rect parent_rect` has it's own definition inside another file, but let's say that you would like to add a text widget with the text "Hello World", positioned at the top left corner (spaced 10 from both top margin and left margin), with width 100 and height 24, you cold do it in this way:
```
Text hello_world_text_widget(
{10, 10, 100, 24}, // Coordinates are: int:x (px), int:y (px), int:width (px), int:height (px)
"Hello world!"
);
```
#### Button
Buttons allows you to do something when you press them. Here you can find it's declaration and prototype:
```
Button my_button_widget{
Rect parent_rect,
std::string text
};
```
Be aware that every time you create a button, you then have to implement this method: `my_button_widget.on_select = [&nav](Button &){}`. You can leave it empty (even though it should not, as here you define what action the button should perform), but it must be present in your code.
For example, let's say you want a button called `my_button`, with the same dimensions as the previous widget. You will then do:
```
Button my_button(
{10, 10, 100, 24}, // Coordinates are: int:x (px), int:y (px), int:width (px), int:height (px)
"my_button_text"
);
```
#### Labels
Labels are a text element that can be used to describe other widgets. Here you can find it's declaration and prototype:
```
Labels my_label_widget{
std::initializer_list<Label> labels
};
```
For example, let's say you want a label called `my_label`. Because the constructor is looking for list you'll need to add a set of brackets `{}` around each label. You will need to add this to `apps/ui_newapp.hpp`:
```
Labels my_label{
{{10, 10}, // Coordinates are: int:x (px), int:y (px)
"my_label_text:", // Label text
Color::light_grey()}, // Label color
};
```
**Note:** Colors are defined in [`firmware/common/ui.hpp`](https://github.com/eried/portapack-mayhem/blob/next/firmware/common/ui.hpp).
In `apps/ui_newapp.cpp` you'll need to add the `my_label` pointer to add_child() or add_children():
```
NewAppView::NewAppView(NavigationView &nav) {
// Widget pointers
add_children({
&my_label,
});
}
```
#### LiveDateTime
LiveDateTime gives you the dynamic date and time. Here you can find it's declaration and prototype:
```
LiveDateTime my_liveDateTime_widget{
Rect parent_rect
};
```
For example, let's say you want a label called `my_liveDateTime`. You will need to add this to `apps/ui_newapp.hpp`:
```
LiveDateTime my_liveDateTime {
{ 2, 10, 19*8, 16 }, // Coordinates are: int:x (px), int:y (px), int:width (px), int:height (px)
};
```
In `apps/ui_newapp.cpp` you'll need to add the `my_liveDateTime` pointer to add_child() or add_children():
```
NewAppView::NewAppView(NavigationView &nav) {
// Widget pointers
add_children({
&my_liveDateTime,
});
}
```
If you want to enable seconds you'll need use the `set_seconds_enabled(bool new_value)` function:
```
my_liveDateTime.set_seconds_enabled(true);
```
#### BigFrequency
BigFrequency is used for displaying a radio frequency. Here you can find it's declaration and prototype:
```
Labels my_bigFrequency_widget{
Rect parent_rect,
rf::Frequency frequency
};
```
For example, let's say you want a label called `my_bigFrequency`. You will need to add this to `apps/ui_newapp.hpp`:
```
Labels my_bigFrequency(
{10, 10, 28*8, 52}, // Coordinates are: int:x (px), int:y (px), int:width (px), int:height (px)
0 // Beginning frequency in hz
);
```
In `apps/ui_newapp.cpp` you'll need to add the `my_bigFrequency` pointer to add_child() or add_children():
```
NewAppView::NewAppView(NavigationView &nav) {
// Widget pointers
add_children({
&my_bigFrequency,
});
}
```
To set a frequency you can use the function `set(const rf::Frequency frequency)`:
```
my_bigFrequency.set(433000000); // 433MHz
```
#### ProgressBar
Progress bars are a visual representation of progress that let us know how far a long a task is. Here you can find it's declaration and prototype:
```
Labels my_progressBar_widget{
Rect parent_rect
};
```
For example, let's say you want a label called `my_progressBar`. You will need to add this to `apps/ui_newapp.hpp`:
```
ProgressBar my_progressBar {
{ 2, 10, 208, 16 }, // Coordinates are: int:x (px), int:y (px), int:width (px), int:height (px)
};
```
In `apps/ui_newapp.cpp` you'll need to add the `my_progressBar` pointer to add_child() or add_children():
```
NewAppView::NewAppView(NavigationView &nav) {
// Widget pointers
add_children({
&my_progressBar,
});
}
```
To set the maximum value for the progress bar use the `set_max(const uint32_t max)` function:
```
my_progressBar.set_max(10); // 10 is 100%
```
To change the value of progress for the progress bar use the `set_value(const uint32_t value)` function:
```
my_progressBar.set_value(5); // 50% Complete
```
#### Console
Console can be used as large text field where you can output mutable lines of text. Here you can find it's declaration and prototype:
```
Console my_console_widget{
Rect parent_rect
};
```
For example, let's say you want a label called `my_console`. You will need to add this to `apps/ui_newapp.hpp`:
```
Console my_console {
{ 2*8, 10, 208, 200 }, // Coordinates are: int:x (px), int:y (px), int:width (px), int:height (px)
};
```
In `apps/ui_newapp.cpp` you'll need to add the `my_console` pointer to add_child() or add_children():
```
NewAppView::NewAppView(NavigationView &nav) {
// Widget pointers
add_children({
&my_console,
});
}
```
To write to 'my_console' you'll need to use the `write(std::string message)` function:
```
my_console.write("Hello World");
```
For automatic new line, you can also use the `writeln(std::string message)` function if you don't want to add a `\n' at the end of every string:
```
my_console.writeln("Hello World but on a new line!");
```
To enable scrolling you can use the `enable_scrolling(bool enable)` function::
```
my_console.enable_scrolling(true);
```
#### Checkbox
Checkboxs are a boolean (True/False) widget that allows you chose between one of two options . Here you can find it's declaration and prototype:
```
Checkbox my_checkbox_widget{
Point parent_pos,
size_t length,
std::string text,
bool small
};
```
For example, let's say you want a checkbox called `my_checkbox`. You will need to add this to `apps/ui_newapp.hpp`:
```
Checkbox my_checkbox(
{10, 20}, // Coordinates are: int:x (px), int:y (px)
4, // Length
"my_checkbox_text", // Title
false // Checkbox Size: true == small(16X16px), false == regular(24X24px)
);
```
In `apps/ui_newapp.cpp` you'll need to add the `my_checkbox` pointer to add_child() or add_children():
```
NewAppView::NewAppView(NavigationView &nav) {
// Widget pointers
add_children({
&my_checkbox,
});
}
```
Functions within `apps/ui_newapp.cpp` are able to lookup the value of `my_checkbox` with Checkbox's `value()` function:
```
if(my_checkbox.value()) {
do_a(); // If checkbox is selected (green check mark)
} else {
do_b(); // If checkbox is NOT selected (red X)
}
```
You can also set the value for `my_checkbox` with the `set_value(const bool value)` function:
```
my_checkbox.set_value(true); // Checkbox selected (green check mark)
my_checkbox.set_value(false); // Checkbox deselected (red X)
```
If you want your checkbox to automatically perform an action when toggled you can add this [Lambda](https://www.geeksforgeeks.org/lambda-expression-in-c/) to `apps/ui_newapp.cpp`:
```
NewAppView::NewAppView(NavigationView &nav) {
// Add widget pointers
add_children({
&my_checkbox,
});
// When checkbox is toggled do...
my_checkbox.on_select = [this](Checkbox&, bool v) {
if(v){
do_a(); // If checkbox is selected (green check mark)
} else {
do_b(); // If checkbox is NOT selected (red X)
}
};
}
```
#### Image
Images can be displayed within your app. Here you can find it's declaration and prototype:
```
Image my_Image_widget{
Rect parent_rect,
Bitmap* bitmap,
Color foreground,
Color background
};
```
Images need to be a Bitmap object before they can be displayed. Bellow is an example of the code needed to create a Bitmap from [`firmware/application/bitmap.hpp`](https://github.com/eried/portapack-mayhem/blob/next/firmware/application/bitmap.hpp).
```
static constexpr uint8_t bitmap_stripes_data[] = {
0xFF, 0x03, 0xC0,
0xFF, 0x01, 0xE0,
0xFF, 0x00, 0xF0,
0x7F, 0x00, 0xF8,
0x3F, 0x00, 0xFC,
0x1F, 0x00, 0xFE,
0x0F, 0x00, 0xFF,
0x07, 0x80, 0xFF,
};
static constexpr Bitmap bitmap_stripes {
{ 24, 8 }, bitmap_stripes_data
};
```
With the Bitmap object created we can now define the image `my_image`. You will need to add this to `apps/ui_newapp.hpp`:
```
Image my_image(
{10, 10, 24, 8}, // Coordinates are: int:x (px), int:y (px), int:width (px), int:height (px)
&Bitmap, // Pointer to your bitmap
Color::white(), // Color Foreground
Color::black() // Color Background
);
```
**Note:** Colors are defined in [`firmware/common/ui.hpp`](https://github.com/eried/portapack-mayhem/blob/next/firmware/common/ui.hpp)
In `apps/ui_newapp.cpp` you'll need to add the `my_image` pointer to add_child() or add_children():
```
NewAppView::NewAppView(NavigationView &nav) {
// Widget pointers
add_children({
&my_image,
});
}
```
#### OptionsField
OptionsField is a widget that allows you to create a field, in which you can change its value with the wheel on your portapack. Here you can find it's declaration and prototype:
```
OptionsField my_OptionsField_widget{
Point parent_pos,
int length,
options_t options
};
```
parent_pos is an array of two integer which tells where the top left corner of the widget should be positioned. The length is an integer which tells how many options you have into your options parameter. The options_t field is an array of options in which your portapack can choose to display.
For example, let's say you want an optionsFild called `my_optionsField`, with 3 options, positioned at 10 from top and 10 from left:
```
OptionsField my_optionsField{
{10,10}, // Coordinates are: int:x (px), int:y (px)
7, // Char length for option title
{
{"option1",0},
{"option2",1}, // Options {"KEY", int VALUE}
{"option3",2}
}
};
```
Note that the number following the "option_x" string value, should be the value that you could retrieve from the optionField with the function `my_optionsField.selected_index_value();`
#### NumberField
NumberField is similar to the OptionsField widget except that it only deals with numbers. You can change its value with the wheel on your portapack. Here you can find it's declaration and prototype:
```
NumberField my_NumberField_widget{
Point parent_pos,
int length,
range_t range,
int32_t step,
char fill_char,
bool can_loop
};
```
For example, let's say you want a NumberField called `my_numberField`. You will need to add this to `apps/ui_newapp.hpp`:
```
// Example 3 digit number starting at "000", ends at "255"
NumberField my_numberField(
{10, 10}, // Coordinates are: int:x (px), int:y (px)
3, // Length
{0, 255}, // MIN -> MAX Range
1, // Step
'0', // Fill Char
false // Can Loop
);
```
In `apps/ui_newapp.cpp` you'll need to add the `my_numberField` pointer to add_child() or add_children():
```
NewAppView::NewAppView(NavigationView &nav) {
// Widget pointers
add_children({
&my_numberField,
});
}
```
Functions within `apps/ui_newapp.cpp` are able to lookup the value of `my_numberField` with NumberField's `value()` function:
```
int number = my_numberField.value();
```
You can also set the value for `my_numberField` with the `set_value(int32_t new_value, bool trigger_change)` function:
```
my_numberField.set_value(123);
```
If you want your NumberField to change a value (int number for example) you'll need to add this [Lambda](https://www.geeksforgeeks.org/lambda-expression-in-c/) to `apps/ui_newapp.cpp`:
```
NewAppView::NewAppView(NavigationView &nav) {
// Add widget pointers
add_children({
&my_numberField,
});
// When NumberField is changed
my_numberField.on_change = [this](int32_t v) {
number = v;
};
}
```
#### Waveform
Waveforms are used to display a sign wave from a signal source. **Note:** The X axes represents time while the Y axes represents amplitude. Here you can find it's declaration and prototype:
```
Labels my_waveform_widget{
Rect parent_rect,
int16_t * data,
uint32_t length,
int32_t offset,
bool digital,
Color color
};
```
For example, let's say you want a Waveform called `my_waveform`. You will need to add this to `apps/ui_newapp.hpp`:
```
Waveform my_waveform(
{0, 5*16, 240, 64}, // Coordinates are: int:x (px), int:y (px), int:width (px), int:height (px)
waveform_buffer, // RX data
128, // Length, how many elements in the waveform_buffer array
0, // Offset
false, // Digital
Color::white() // Sine wave color
);
```
**Note:** Colors are defined in [`firmware/common/ui.hpp`](https://github.com/eried/portapack-mayhem/blob/next/firmware/common/ui.hpp).
The data being displayed by `my_waveform` needs to be a `int16_t` array. You can declare this variable in `apps/ui_newapp.hpp`:
```
class NewAppView : public View
{
public:
...
private:
int16_t waveform_buffer[128]; // Data for Waveform
...
};
```
In `apps/ui_newapp.cpp` you'll need to add the `my_waveform` pointer to add_child() or add_children():
```
NewAppView::NewAppView(NavigationView &nav) {
// Widget pointers
add_children({
&my_waveform,
});
}
```
If your input data has a variable length you can use the `set_length(const uint32_t new_length)` function to update the Waveform:
```
my_waveform.set_length(9001) // THAT'S OVER 18KB!!
```
#### VuMeter
VuMeters are used to visually represent the sound intensity of an audio source. Here you can find it's declaration and prototype:
```
Labels my_vuMeter_widget{
Rect parent_rect,
uint32_t LEDs,
bool show_max
};
```
For example, let's say you want a VuMeter called `my_vuMeter`. You will need to add this to `apps/ui_newapp.hpp`:
```
VuMeter my_vuMeter(
{ 0*8, 1*8, 2*8, 33*8}, // Coordinates are: int:x (px), int:y (px), int:width (px), int:height (px)
12, // LEDs
true // Show max
);
```
In `apps/ui_newapp.cpp` you'll need to add the `my_vuMeter` pointer to add_child() or add_children():
```
NewAppView::NewAppView(NavigationView &nav) {
// Widget pointers
add_children({
&my_vuMeter,
});
}
```
To set the value for `my_vuMeter` use the `set_value(const uint32_t new_value)` function:
```
my_vuMeter.set_value(123); // Max is 255
```