This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
About Widgets
Widgets are elements to compose the UI of your custom app. Widgets are defined in firmware\common\ui_widget.hpp
and widget functions can be found in firmware\common\ui_widget.cpp
. To use them, you must add the line #include "ui_widget.hpp"
into your app .hpp file.
There are different types of widgets. Here you will find a list of available widgets, with their constructor. For all methods available, you should check the ui_widget.hpp file.
Attach a generic Widget to your Application
To display a widget in your app, you can use the functions add_child()
for a single widget or add_children()
to add an undefined number of widgets.
Both functions shall be called within the code of the NewAppGameView(NavigationView &nav){}
constructor.
Widgets must be passed as pointers to the functions.
Declaration and prototype
add_child(&my_widget);
or
add_children({
&widget_1,
&widget_2
});
Available widgets
There are several widgets available and more might be added. You should always check ui_widget.hpp if new widgets have been added.
In this document you will find a list of most basic widgets.
Text
The text widget add a simple text area widget in the app.
Declaration and prototype
Text my_text_widget{
Rect parent_rect,
std::string text
};
Note: Rect parent_rect
has it's own definition inside another file.
Example
Add a text widget with the content "Hello World" positioned at 10x10 px from the top left corner with width 100 and height 24.
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 to do something when you press it.
Declaration and prototype
Button my_button_widget{
Rect parent_rect,
std::string text
};
Note: Every time you create a button, you have to implement the method my_button_widget.on_select = [&nav](Button &){}
. It could be empty (even though it shouldn't, as here you define the action for a button), but it must be present in the code.
Example
A button called my_button
, with the same dimensions as the previous widget.
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.
Declaration and prototype
Labels my_label_widget{
std::initializer_list<Label> labels
};
Example
A label called my_label
. Because the constructor is looking for a list you'll need to add brackets {}
around each label.
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
.
In apps/ui_newapp.cpp
add the my_label
pointer to add_child()
or add_children()
:
NewAppView::NewAppView(NavigationView &nav) {
// Widget pointers
add_children({
&my_label,
});
}
LiveDateTime
LiveDateTime gives the dynamic date and time.
Declaration and prototype
LiveDateTime my_liveDateTime_widget{
Rect parent_rect
};
Example
For a label called my_liveDateTime
add the folowing code 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
add the my_liveDateTime
pointer to add_child()
or add_children()
:
NewAppView::NewAppView(NavigationView &nav) {
// Widget pointers
add_children({
&my_liveDateTime,
});
}
To enable seconds use the function set_seconds_enabled(bool new_value)
:
my_liveDateTime.set_seconds_enabled(true);
BigFrequency
BigFrequency is used for displaying a radio frequency.
Declaration and prototype
BigFrequency my_bigFrequency_widget{
Rect parent_rect,
rf::Frequency frequency
};
Example
For a big frequency widget called my_bigFrequency
add the following code to apps/ui_newapp.hpp
:
BigFrequency 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
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 use the function set(const rf::Frequency frequency)
:
my_bigFrequency.set(433000000); // 433MHz
ProgressBar
Progress bars are a visual representation of the progress of a task.
Declaration and prototype
Labels my_progressBar_widget{
Rect parent_recthttps://github.com/portapack-mayhem/mayhem-firmware.wiki.git
};
Example
For a label called my_progressBar
add the following code 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
add the my_progressBar
pointer to add_child()
or add_children()
:
NewAppView::NewAppView(NavigationView &nav) {
// Widget pointers
add_children({
&my_progressBar,
});
}
Set the maximum value for the progress bar with the function set_max(const uint32_t max)
:
my_progressBar.set_max(10); // 10 is 100%
Change the value of progress for the progress bar with the function set_value(const uint32_t value)
:
my_progressBar.set_value(5); // 50% Complete
Console
Console can be used as large text field where you can output mutable lines of text.
Declaration and prototype
Console my_console_widget{
Rect parent_rect
};
Example
For a label called my_console
add the following code 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
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' use the function write(std::string message)
:
my_console.write("Hello World");
For automatic new line use the function writeln(std::string message)
. It will add \n
at the end of every string:
my_console.writeln("Hello World but on a new line!");
To enable scrolling use the function enable_scrolling(bool enable)
:
my_console.enable_scrolling(true);
Note: The buffer size is limited to 256 char
Checkbox
Checkbox is a boolean (true/false) widget, that allows you to chose between two options. The Checkbox is displayed as a rectangle with a green check mark (true) or a red cross (false).
Declaration and prototype
Checkbox my_checkbox_widget{
Point parent_pos,
size_t length,
std::string text,
bool small
};
Example
For a checkbox called my_checkbox
add the following code 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
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
can 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)
}
Set the value for my_checkbox
with the function set_value(const bool value)
:
my_checkbox.set_value(true); // Checkbox selected (green check mark)
my_checkbox.set_value(false); // Checkbox deselected (red X)
To automatically perform an action when the checkbox is toggled, add Lambda 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.
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. Below is an example of the code needed to create a Bitmap from 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
};
Example
With the Bitmap object created we can define the image my_image
. Add the following code 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
In apps/ui_newapp.cpp
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 to create a field, in which you can change its value with the wheel on your PortaPack.
Declaration and prototype
OptionsField my_OptionsField_widget{
Point parent_pos,
int length,
options_t options,
bool centered,
};
parent_pos
is an array of two integer, represents 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.
Centered is a bool that if you true it, you can make all the text in it in the center. It’s originally init-ed, so if you don’t define it, it will be default false.
Example
An OptionsField 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: The number following the option_n
string value, should be the value that could be retrieved from the OptionsField 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.
Declaration and prototype
NumberField my_NumberField_widget{
Point parent_pos,
int length,
range_t range,
int32_t step,
char fill_char,
bool can_loop
};
Example
For a NumberField called my_numberField
add the following code 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
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();
Set the value for my_numberField
with the function set_value(int32_t new_value, bool trigger_change)
:
my_numberField.set_value(123);
If you want your NumberField to change a value (int number for example) you'll need to add Lambda 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 axis represents time while the Y axis represents amplitude.
Declaration and prototype
Labels my_waveform_widget{
Rect parent_rect,
int16_t * data,
uint32_t length,
int32_t offset,
bool digital,
Color color
};
Example
For a waveform widget called my_waveform
add the following code 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
.
The data being displayed by my_waveform
needs to be a int16_t
array. 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
add the my_waveform
pointer to add_child()
or add_children()
:
NewAppView::NewAppView(NavigationView &nav) {
// Widget pointers
add_children({
&my_waveform,
});
}
If the input data has a variable length, use the function set_length(const uint32_t new_length)
to update the waveform:
my_waveform.set_length(9001) // THAT'S OVER 18KB!!
VuMeter
VuMeter is used to visually represent the sound intensity of an audio source.
Declaration and prototype
Labels my_vuMeter_widget{
Rect parent_rect,
uint32_t LEDs,
bool show_max
};
Example
For a VuMeter called my_vuMeter
add the following code 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
add the my_vuMeter
pointer to add_child()
or add_children()
:
NewAppView::NewAppView(NavigationView &nav) {
// Widget pointers
add_children({
&my_vuMeter,
});
}
Set the value for my_vuMeter
with the function set_value(const uint32_t new_value)
:
my_vuMeter.set_value(123); // Max is 255
How to collaborate
How to ask questions correctly
User manual
- First steps
- Usage cautions
- Intended use and Legality
- Features
- PortaPack Versions (which one to buy)
- HackRF Versions
- Firmware update procedure
- Description of the hardware
- User interface
- Powering the PortaPack
- Troubleshooting
- Applications
Developer Manual
- Compilation of the firmware
- Compile on WSL with ninja
- How to compile on Windows faster with WSL 2
- Using Docker and Kitematic
- Docker command-line reference
- Using Buddyworks and other CI platforms
- Notes for Buddy.Works (and other CI platforms)
- Using ARM on Debian host
- All in one script for ARM on Debian host
- Compile on Arch based distro (exclude Asahi)
- Dev build versions
- Notes About ccache
- Create a custom map
- Code formatting
- PR process
- Description of the Structure
- Software Dev Guides
- Tools
- Research
- UI Screenshots
- Maintaining
- Creating a prod/stable release (Maintainers only)
- Maintaining rules
- Development States Notes
Note
The wiki is incomplete. Please add content and collaborate.
Important
- This is a public wiki. Everything is visible to everyone. Don't use it for personal notes.
- Avoid linking to external tutorials/articles; they may become outdated or contain false information.