mirror of
https://github.com/portapack-mayhem/mayhem-firmware.git
synced 2025-08-15 03:47:42 +00:00
Formatted code (#1007)
* Updated style * Updated files * fixed new line * Updated spacing * File fix WIP * Updated to clang 13 * updated comment style * Removed old comment code
This commit is contained in:
@@ -31,94 +31,87 @@
|
||||
namespace ui {
|
||||
|
||||
AlphanumView::AlphanumView(
|
||||
NavigationView& nav,
|
||||
std::string& str,
|
||||
size_t max_length
|
||||
) : TextEntryView(nav, str, max_length)
|
||||
{
|
||||
size_t n;
|
||||
NavigationView& nav,
|
||||
std::string& str,
|
||||
size_t max_length)
|
||||
: TextEntryView(nav, str, max_length) {
|
||||
size_t n;
|
||||
|
||||
add_children({
|
||||
&button_mode,
|
||||
&text_raw,
|
||||
&field_raw
|
||||
});
|
||||
add_children({&button_mode,
|
||||
&text_raw,
|
||||
&field_raw});
|
||||
|
||||
const auto button_fn = [this](Button& button) {
|
||||
this->on_button(button);
|
||||
};
|
||||
const auto button_fn = [this](Button& button) {
|
||||
this->on_button(button);
|
||||
};
|
||||
|
||||
n = 0;
|
||||
for (auto& button : buttons) {
|
||||
button.id = n;
|
||||
button.on_highlight = [this](Button& button) {
|
||||
focused_button = button.id;
|
||||
};
|
||||
button.on_select = button_fn;
|
||||
button.set_parent_rect({
|
||||
static_cast<Coord>((n % 5) * (240 / 5)),
|
||||
static_cast<Coord>((n / 5) * 38 + 24),
|
||||
240 / 5, 38
|
||||
});
|
||||
add_child(&button);
|
||||
n++;
|
||||
}
|
||||
|
||||
set_mode(mode);
|
||||
|
||||
button_mode.on_select = [this](Button&) {
|
||||
set_mode(mode + 1);
|
||||
};
|
||||
|
||||
field_raw.set_value('0');
|
||||
field_raw.on_select = [this](NumberField&) {
|
||||
char_add(field_raw.value());
|
||||
};
|
||||
n = 0;
|
||||
for (auto& button : buttons) {
|
||||
button.id = n;
|
||||
button.on_highlight = [this](Button& button) {
|
||||
focused_button = button.id;
|
||||
};
|
||||
button.on_select = button_fn;
|
||||
button.set_parent_rect({static_cast<Coord>((n % 5) * (240 / 5)),
|
||||
static_cast<Coord>((n / 5) * 38 + 24),
|
||||
240 / 5, 38});
|
||||
add_child(&button);
|
||||
n++;
|
||||
}
|
||||
|
||||
set_mode(mode);
|
||||
|
||||
button_mode.on_select = [this](Button&) {
|
||||
set_mode(mode + 1);
|
||||
};
|
||||
|
||||
field_raw.set_value('0');
|
||||
field_raw.on_select = [this](NumberField&) {
|
||||
char_add(field_raw.value());
|
||||
};
|
||||
}
|
||||
|
||||
void AlphanumView::set_mode(const uint32_t new_mode) {
|
||||
size_t n = 0;
|
||||
|
||||
if (new_mode < 3)
|
||||
mode = new_mode;
|
||||
else
|
||||
mode = 0;
|
||||
|
||||
const char * key_list = key_sets[mode].second;
|
||||
|
||||
for (auto& button : buttons) {
|
||||
const std::string label {
|
||||
key_list[n]
|
||||
};
|
||||
button.set_text(label);
|
||||
n++;
|
||||
}
|
||||
|
||||
if (mode < 2)
|
||||
button_mode.set_text(key_sets[mode + 1].first);
|
||||
else
|
||||
button_mode.set_text(key_sets[0].first);
|
||||
size_t n = 0;
|
||||
|
||||
if (new_mode < 3)
|
||||
mode = new_mode;
|
||||
else
|
||||
mode = 0;
|
||||
|
||||
const char* key_list = key_sets[mode].second;
|
||||
|
||||
for (auto& button : buttons) {
|
||||
const std::string label{
|
||||
key_list[n]};
|
||||
button.set_text(label);
|
||||
n++;
|
||||
}
|
||||
|
||||
if (mode < 2)
|
||||
button_mode.set_text(key_sets[mode + 1].first);
|
||||
else
|
||||
button_mode.set_text(key_sets[0].first);
|
||||
}
|
||||
|
||||
void AlphanumView::on_button(Button& button) {
|
||||
const auto c = button.text()[0];
|
||||
|
||||
if (c == '<')
|
||||
char_delete();
|
||||
else
|
||||
char_add(c);
|
||||
const auto c = button.text()[0];
|
||||
|
||||
if (c == '<')
|
||||
char_delete();
|
||||
else
|
||||
char_add(c);
|
||||
}
|
||||
|
||||
bool AlphanumView::on_encoder(const EncoderEvent delta) {
|
||||
focused_button += delta;
|
||||
if (focused_button < 0) {
|
||||
focused_button = buttons.size() - 1;
|
||||
}
|
||||
else if (focused_button >= (int16_t)buttons.size()) {
|
||||
focused_button = 0;
|
||||
}
|
||||
buttons[focused_button].focus();
|
||||
return true;
|
||||
focused_button += delta;
|
||||
if (focused_button < 0) {
|
||||
focused_button = buttons.size() - 1;
|
||||
} else if (focused_button >= (int16_t)buttons.size()) {
|
||||
focused_button = 0;
|
||||
}
|
||||
buttons[focused_button].focus();
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace ui
|
||||
|
@@ -32,58 +32,53 @@
|
||||
namespace ui {
|
||||
|
||||
class AlphanumView : public TextEntryView {
|
||||
public:
|
||||
AlphanumView(NavigationView& nav, std::string& str, size_t max_length);
|
||||
|
||||
AlphanumView(const AlphanumView&) = delete;
|
||||
AlphanumView(AlphanumView&&) = delete;
|
||||
AlphanumView& operator=(const AlphanumView&) = delete;
|
||||
AlphanumView& operator=(AlphanumView&&) = delete;
|
||||
public:
|
||||
AlphanumView(NavigationView& nav, std::string& str, size_t max_length);
|
||||
|
||||
bool on_encoder(const EncoderEvent delta) override;
|
||||
AlphanumView(const AlphanumView&) = delete;
|
||||
AlphanumView(AlphanumView&&) = delete;
|
||||
AlphanumView& operator=(const AlphanumView&) = delete;
|
||||
AlphanumView& operator=(AlphanumView&&) = delete;
|
||||
|
||||
private:
|
||||
const char * const keys_upper = "ABCDEFGHIJKLMNOPQRSTUVWXYZ' .<";
|
||||
const char * const keys_lower = "abcdefghijklmnopqrstuvwxyz' .<";
|
||||
const char * const keys_digit = "0123456789!\"#'()*+-/:;=>?@[\\]<";
|
||||
|
||||
const std::pair<std::string, const char *> key_sets[3] = {
|
||||
{ "Upper", keys_upper },
|
||||
{ "Lower", keys_lower },
|
||||
{ "Digit", keys_digit }
|
||||
};
|
||||
|
||||
int16_t focused_button = 0;
|
||||
uint32_t mode = 0; // Uppercase
|
||||
|
||||
void set_mode(const uint32_t new_mode);
|
||||
void on_button(Button& button);
|
||||
bool on_encoder(const EncoderEvent delta) override;
|
||||
|
||||
std::array<Button, 30> buttons { };
|
||||
private:
|
||||
const char* const keys_upper = "ABCDEFGHIJKLMNOPQRSTUVWXYZ' .<";
|
||||
const char* const keys_lower = "abcdefghijklmnopqrstuvwxyz' .<";
|
||||
const char* const keys_digit = "0123456789!\"#'()*+-/:;=>?@[\\]<";
|
||||
|
||||
Button button_mode {
|
||||
{ 21 * 8, 33 * 8, 8 * 8, 32 },
|
||||
""
|
||||
};
|
||||
|
||||
Text text_raw {
|
||||
{ 1 * 8, 33 * 8, 4 * 8, 16 },
|
||||
"Raw:"
|
||||
};
|
||||
NumberField field_raw {
|
||||
{ 5 * 8, 33 * 8 },
|
||||
3,
|
||||
{ 1, 255 },
|
||||
1,
|
||||
'0'
|
||||
};
|
||||
const std::pair<std::string, const char*> key_sets[3] = {
|
||||
{"Upper", keys_upper},
|
||||
{"Lower", keys_lower},
|
||||
{"Digit", keys_digit}};
|
||||
|
||||
Button button_ok {
|
||||
{ 10 * 8, 33 * 8, 9 * 8, 32 },
|
||||
"OK"
|
||||
};
|
||||
int16_t focused_button = 0;
|
||||
uint32_t mode = 0; // Uppercase
|
||||
|
||||
void set_mode(const uint32_t new_mode);
|
||||
void on_button(Button& button);
|
||||
|
||||
std::array<Button, 30> buttons{};
|
||||
|
||||
Button button_mode{
|
||||
{21 * 8, 33 * 8, 8 * 8, 32},
|
||||
""};
|
||||
|
||||
Text text_raw{
|
||||
{1 * 8, 33 * 8, 4 * 8, 16},
|
||||
"Raw:"};
|
||||
NumberField field_raw{
|
||||
{5 * 8, 33 * 8},
|
||||
3,
|
||||
{1, 255},
|
||||
1,
|
||||
'0'};
|
||||
|
||||
Button button_ok{
|
||||
{10 * 8, 33 * 8, 9 * 8, 32},
|
||||
"OK"};
|
||||
};
|
||||
|
||||
} /* namespace ui */
|
||||
|
||||
#endif/*__ALPHANUM_H__*/
|
||||
#endif /*__ALPHANUM_H__*/
|
||||
|
@@ -28,45 +28,41 @@
|
||||
namespace ui {
|
||||
|
||||
void Audio::paint(Painter& painter) {
|
||||
const auto r = screen_rect();
|
||||
const auto r = screen_rect();
|
||||
|
||||
constexpr int db_min = -96;
|
||||
constexpr int db_max = 0;
|
||||
constexpr int db_delta = db_max - db_min;
|
||||
const range_t<int> x_rms_range { 0, r.width() - 1 };
|
||||
const auto x_rms = x_rms_range.clip((rms_db_ - db_min) * r.width() / db_delta);
|
||||
const range_t<int> x_max_range { x_rms + 1, r.width() };
|
||||
const auto x_max = x_max_range.clip((max_db_ - db_min) * r.width() / db_delta);
|
||||
constexpr int db_min = -96;
|
||||
constexpr int db_max = 0;
|
||||
constexpr int db_delta = db_max - db_min;
|
||||
const range_t<int> x_rms_range{0, r.width() - 1};
|
||||
const auto x_rms = x_rms_range.clip((rms_db_ - db_min) * r.width() / db_delta);
|
||||
const range_t<int> x_max_range{x_rms + 1, r.width()};
|
||||
const auto x_max = x_max_range.clip((max_db_ - db_min) * r.width() / db_delta);
|
||||
|
||||
const Rect r0 { r.left(), r.top(), x_rms, r.height() };
|
||||
painter.fill_rectangle(
|
||||
r0,
|
||||
Color::green()
|
||||
);
|
||||
const Rect r0{r.left(), r.top(), x_rms, r.height()};
|
||||
painter.fill_rectangle(
|
||||
r0,
|
||||
Color::green());
|
||||
|
||||
const Rect r1 { r.left() + x_rms, r.top(), 1, r.height() };
|
||||
painter.fill_rectangle(
|
||||
r1,
|
||||
Color::black()
|
||||
);
|
||||
const Rect r1{r.left() + x_rms, r.top(), 1, r.height()};
|
||||
painter.fill_rectangle(
|
||||
r1,
|
||||
Color::black());
|
||||
|
||||
const Rect r2 { r.left() + x_rms + 1, r.top(), x_max - (x_rms + 1), r.height() };
|
||||
painter.fill_rectangle(
|
||||
r2,
|
||||
Color::red()
|
||||
);
|
||||
const Rect r2{r.left() + x_rms + 1, r.top(), x_max - (x_rms + 1), r.height()};
|
||||
painter.fill_rectangle(
|
||||
r2,
|
||||
Color::red());
|
||||
|
||||
const Rect r3 { r.left() + x_max, r.top(), r.width() - x_max, r.height() };
|
||||
painter.fill_rectangle(
|
||||
r3,
|
||||
Color::black()
|
||||
);
|
||||
const Rect r3{r.left() + x_max, r.top(), r.width() - x_max, r.height()};
|
||||
painter.fill_rectangle(
|
||||
r3,
|
||||
Color::black());
|
||||
}
|
||||
|
||||
void Audio::on_statistics_update(const AudioStatistics& statistics) {
|
||||
rms_db_ = statistics.rms_db;
|
||||
max_db_ = statistics.max_db;
|
||||
set_dirty();
|
||||
rms_db_ = statistics.rms_db;
|
||||
max_db_ = statistics.max_db;
|
||||
set_dirty();
|
||||
}
|
||||
|
||||
} /* namespace ui */
|
||||
|
@@ -33,31 +33,29 @@
|
||||
namespace ui {
|
||||
|
||||
class Audio : public Widget {
|
||||
public:
|
||||
Audio(
|
||||
const Rect parent_rect
|
||||
) : Widget { parent_rect },
|
||||
rms_db_ { -120 },
|
||||
max_db_ { -120 }
|
||||
{
|
||||
}
|
||||
public:
|
||||
Audio(
|
||||
const Rect parent_rect)
|
||||
: Widget{parent_rect},
|
||||
rms_db_{-120},
|
||||
max_db_{-120} {
|
||||
}
|
||||
|
||||
void paint(Painter& painter) override;
|
||||
void paint(Painter& painter) override;
|
||||
|
||||
private:
|
||||
int32_t rms_db_;
|
||||
int32_t max_db_;
|
||||
private:
|
||||
int32_t rms_db_;
|
||||
int32_t max_db_;
|
||||
|
||||
MessageHandlerRegistration message_handler_statistics {
|
||||
Message::ID::AudioStatistics,
|
||||
[this](const Message* const p) {
|
||||
this->on_statistics_update(static_cast<const AudioStatisticsMessage*>(p)->statistics);
|
||||
}
|
||||
};
|
||||
|
||||
void on_statistics_update(const AudioStatistics& statistics);
|
||||
MessageHandlerRegistration message_handler_statistics{
|
||||
Message::ID::AudioStatistics,
|
||||
[this](const Message* const p) {
|
||||
this->on_statistics_update(static_cast<const AudioStatisticsMessage*>(p)->statistics);
|
||||
}};
|
||||
|
||||
void on_statistics_update(const AudioStatistics& statistics);
|
||||
};
|
||||
|
||||
}
|
||||
} // namespace ui
|
||||
|
||||
#endif/*__UI_AUDIO_H__*/
|
||||
#endif /*__UI_AUDIO_H__*/
|
||||
|
@@ -29,176 +29,170 @@ namespace ui {
|
||||
/* BtnGridView **************************************************************/
|
||||
|
||||
BtnGridView::BtnGridView(
|
||||
Rect new_parent_rect,
|
||||
bool keep_highlight
|
||||
) : keep_highlight { keep_highlight }
|
||||
{
|
||||
set_parent_rect(new_parent_rect);
|
||||
Rect new_parent_rect,
|
||||
bool keep_highlight)
|
||||
: keep_highlight{keep_highlight} {
|
||||
set_parent_rect(new_parent_rect);
|
||||
|
||||
set_focusable(true);
|
||||
set_focusable(true);
|
||||
|
||||
signal_token_tick_second = rtc_time::signal_tick_second += [this]() {
|
||||
this->on_tick_second();
|
||||
};
|
||||
signal_token_tick_second = rtc_time::signal_tick_second += [this]() {
|
||||
this->on_tick_second();
|
||||
};
|
||||
|
||||
add_child(&arrow_more);
|
||||
arrow_more.set_focusable(false);
|
||||
arrow_more.set_foreground(Color::black());
|
||||
add_child(&arrow_more);
|
||||
arrow_more.set_focusable(false);
|
||||
arrow_more.set_foreground(Color::black());
|
||||
}
|
||||
|
||||
BtnGridView::~BtnGridView() {
|
||||
rtc_time::signal_tick_second -= signal_token_tick_second;
|
||||
rtc_time::signal_tick_second -= signal_token_tick_second;
|
||||
|
||||
for (auto item : menu_item_views) {
|
||||
delete item;
|
||||
}
|
||||
for (auto item : menu_item_views) {
|
||||
delete item;
|
||||
}
|
||||
}
|
||||
|
||||
void BtnGridView::set_max_rows(int rows) {
|
||||
rows_ = rows;
|
||||
rows_ = rows;
|
||||
}
|
||||
|
||||
int BtnGridView::rows() {
|
||||
return rows_;
|
||||
return rows_;
|
||||
}
|
||||
|
||||
void BtnGridView::set_parent_rect(const Rect new_parent_rect) {
|
||||
View::set_parent_rect(new_parent_rect);
|
||||
View::set_parent_rect(new_parent_rect);
|
||||
|
||||
displayed_max = (parent_rect().size().height() / button_h);
|
||||
arrow_more.set_parent_rect( { 228, (Coord)(displayed_max * button_h), 8, 8 } );
|
||||
displayed_max *= rows_;
|
||||
displayed_max = (parent_rect().size().height() / button_h);
|
||||
arrow_more.set_parent_rect({228, (Coord)(displayed_max * button_h), 8, 8});
|
||||
displayed_max *= rows_;
|
||||
|
||||
// TODO: Clean this up :(
|
||||
if (menu_item_views.size()) {
|
||||
// TODO: Clean this up :(
|
||||
if (menu_item_views.size()) {
|
||||
for (auto item : menu_item_views) {
|
||||
remove_child(item);
|
||||
delete item;
|
||||
}
|
||||
menu_item_views.clear();
|
||||
}
|
||||
|
||||
for (auto item : menu_item_views) {
|
||||
remove_child(item);
|
||||
delete item;
|
||||
}
|
||||
menu_item_views.clear();
|
||||
}
|
||||
button_w = 240 / rows_;
|
||||
for (size_t c = 0; c < displayed_max; c++) {
|
||||
auto item = new NewButton{};
|
||||
menu_item_views.push_back(item);
|
||||
add_child(item);
|
||||
|
||||
button_w = 240 / rows_;
|
||||
for (size_t c = 0; c < displayed_max; c++) {
|
||||
auto item = new NewButton { };
|
||||
menu_item_views.push_back(item);
|
||||
add_child(item);
|
||||
item->set_parent_rect({(int)(c % rows_) * button_w,
|
||||
(int)(c / rows_) * button_h,
|
||||
button_w, button_h});
|
||||
}
|
||||
|
||||
item->set_parent_rect({
|
||||
(int)(c % rows_) * button_w,
|
||||
(int)(c / rows_) * button_h,
|
||||
button_w, button_h
|
||||
});
|
||||
}
|
||||
|
||||
update_items();
|
||||
update_items();
|
||||
}
|
||||
|
||||
void BtnGridView::set_arrow_enabled(bool new_value) {
|
||||
if(new_value){
|
||||
add_child(&arrow_more);
|
||||
}
|
||||
else{
|
||||
remove_child(&arrow_more);
|
||||
}
|
||||
if (new_value) {
|
||||
add_child(&arrow_more);
|
||||
} else {
|
||||
remove_child(&arrow_more);
|
||||
}
|
||||
};
|
||||
|
||||
void BtnGridView::on_tick_second() {
|
||||
if (more && blink)
|
||||
arrow_more.set_foreground(Color::white());
|
||||
else
|
||||
arrow_more.set_foreground(Color::black());
|
||||
if (more && blink)
|
||||
arrow_more.set_foreground(Color::white());
|
||||
else
|
||||
arrow_more.set_foreground(Color::black());
|
||||
|
||||
blink = !blink;
|
||||
blink = !blink;
|
||||
|
||||
arrow_more.set_dirty();
|
||||
arrow_more.set_dirty();
|
||||
}
|
||||
|
||||
void BtnGridView::clear() {
|
||||
menu_items.clear();
|
||||
menu_items.clear();
|
||||
}
|
||||
|
||||
void BtnGridView::add_items(std::initializer_list<GridItem> new_items) {
|
||||
for (auto item : new_items) {
|
||||
menu_items.push_back(item);
|
||||
}
|
||||
update_items();
|
||||
for (auto item : new_items) {
|
||||
menu_items.push_back(item);
|
||||
}
|
||||
update_items();
|
||||
}
|
||||
|
||||
void BtnGridView::update_items() {
|
||||
size_t i = 0;
|
||||
size_t i = 0;
|
||||
|
||||
if ((menu_items.size()) > (displayed_max + offset)) {
|
||||
more = true;
|
||||
blink = true;
|
||||
} else
|
||||
more = false;
|
||||
if ((menu_items.size()) > (displayed_max + offset)) {
|
||||
more = true;
|
||||
blink = true;
|
||||
} else
|
||||
more = false;
|
||||
|
||||
for (NewButton* item : menu_item_views) {
|
||||
if ((i + offset) >= menu_items.size()) {
|
||||
item->hidden(true);
|
||||
item->set_text(" ");
|
||||
item->set_bitmap(nullptr);
|
||||
item->on_select = [](){};
|
||||
item->set_dirty();
|
||||
}
|
||||
else {
|
||||
// Assign item data to NewButtons according to offset
|
||||
item->hidden(false);
|
||||
item->set_text(menu_items[i + offset].text);
|
||||
item->set_bitmap(menu_items[i + offset].bitmap);
|
||||
item->set_color(menu_items[i + offset].color);
|
||||
item->on_select = menu_items[i + offset].on_select;
|
||||
item->set_dirty();
|
||||
}
|
||||
for (NewButton* item : menu_item_views) {
|
||||
if ((i + offset) >= menu_items.size()) {
|
||||
item->hidden(true);
|
||||
item->set_text(" ");
|
||||
item->set_bitmap(nullptr);
|
||||
item->on_select = []() {};
|
||||
item->set_dirty();
|
||||
} else {
|
||||
// Assign item data to NewButtons according to offset
|
||||
item->hidden(false);
|
||||
item->set_text(menu_items[i + offset].text);
|
||||
item->set_bitmap(menu_items[i + offset].bitmap);
|
||||
item->set_color(menu_items[i + offset].color);
|
||||
item->on_select = menu_items[i + offset].on_select;
|
||||
item->set_dirty();
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
NewButton* BtnGridView::item_view(size_t index) const {
|
||||
return menu_item_views[index];
|
||||
return menu_item_views[index];
|
||||
}
|
||||
|
||||
bool BtnGridView::set_highlighted(int32_t new_value) {
|
||||
int32_t item_count = (int32_t)menu_items.size();
|
||||
int32_t item_count = (int32_t)menu_items.size();
|
||||
|
||||
if (new_value < 0)
|
||||
return false;
|
||||
if (new_value < 0)
|
||||
return false;
|
||||
|
||||
if (new_value >= item_count)
|
||||
new_value = item_count - 1;
|
||||
if (new_value >= item_count)
|
||||
new_value = item_count - 1;
|
||||
|
||||
if (((uint32_t)new_value > offset) && ((new_value - offset) >= displayed_max)) {
|
||||
// Shift BtnGridView up
|
||||
highlighted_item = new_value;
|
||||
offset = new_value - displayed_max + rows_;
|
||||
update_items();
|
||||
set_dirty();
|
||||
} else if ((uint32_t)new_value < offset) {
|
||||
// Shift BtnGridView down
|
||||
highlighted_item = new_value;
|
||||
offset = (new_value / rows_) * rows_;
|
||||
update_items();
|
||||
set_dirty();
|
||||
} else {
|
||||
// Just update highlight
|
||||
highlighted_item = new_value;
|
||||
}
|
||||
if (((uint32_t)new_value > offset) && ((new_value - offset) >= displayed_max)) {
|
||||
// Shift BtnGridView up
|
||||
highlighted_item = new_value;
|
||||
offset = new_value - displayed_max + rows_;
|
||||
update_items();
|
||||
set_dirty();
|
||||
} else if ((uint32_t)new_value < offset) {
|
||||
// Shift BtnGridView down
|
||||
highlighted_item = new_value;
|
||||
offset = (new_value / rows_) * rows_;
|
||||
update_items();
|
||||
set_dirty();
|
||||
} else {
|
||||
// Just update highlight
|
||||
highlighted_item = new_value;
|
||||
}
|
||||
|
||||
if (visible())
|
||||
item_view(highlighted_item - offset)->focus();
|
||||
if (visible())
|
||||
item_view(highlighted_item - offset)->focus();
|
||||
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
uint32_t BtnGridView::highlighted_index() {
|
||||
return highlighted_item;
|
||||
return highlighted_item;
|
||||
}
|
||||
|
||||
void BtnGridView::on_focus() {
|
||||
item_view(highlighted_item - offset)->focus();
|
||||
item_view(highlighted_item - offset)->focus();
|
||||
}
|
||||
|
||||
void BtnGridView::on_blur() {
|
||||
@@ -209,32 +203,32 @@ void BtnGridView::on_blur() {
|
||||
}
|
||||
|
||||
bool BtnGridView::on_key(const KeyEvent key) {
|
||||
switch(key) {
|
||||
case KeyEvent::Up:
|
||||
return set_highlighted(highlighted_item - rows_);
|
||||
switch (key) {
|
||||
case KeyEvent::Up:
|
||||
return set_highlighted(highlighted_item - rows_);
|
||||
|
||||
case KeyEvent::Down:
|
||||
return set_highlighted(highlighted_item + rows_);
|
||||
case KeyEvent::Down:
|
||||
return set_highlighted(highlighted_item + rows_);
|
||||
|
||||
case KeyEvent::Right:
|
||||
return set_highlighted(highlighted_item + 1);
|
||||
case KeyEvent::Right:
|
||||
return set_highlighted(highlighted_item + 1);
|
||||
|
||||
case KeyEvent::Left:
|
||||
return set_highlighted(highlighted_item - 1);
|
||||
case KeyEvent::Left:
|
||||
return set_highlighted(highlighted_item - 1);
|
||||
|
||||
case KeyEvent::Select:
|
||||
if( menu_items[highlighted_item].on_select ) {
|
||||
menu_items[highlighted_item].on_select();
|
||||
}
|
||||
return true;
|
||||
case KeyEvent::Select:
|
||||
if (menu_items[highlighted_item].on_select) {
|
||||
menu_items[highlighted_item].on_select();
|
||||
}
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool BtnGridView::on_encoder(const EncoderEvent event) {
|
||||
return set_highlighted(highlighted_item + event);
|
||||
return set_highlighted(highlighted_item + event);
|
||||
}
|
||||
|
||||
} /* namespace ui */
|
||||
|
@@ -37,64 +37,63 @@
|
||||
namespace ui {
|
||||
|
||||
struct GridItem {
|
||||
std::string text;
|
||||
ui::Color color;
|
||||
const Bitmap* bitmap;
|
||||
std::function<void(void)> on_select;
|
||||
std::string text;
|
||||
ui::Color color;
|
||||
const Bitmap* bitmap;
|
||||
std::function<void(void)> on_select;
|
||||
|
||||
// TODO: Prevent default-constructed GridItems.
|
||||
// TODO: Prevent default-constructed GridItems.
|
||||
};
|
||||
|
||||
class BtnGridView : public View {
|
||||
public:
|
||||
BtnGridView(Rect new_parent_rect = { 0, 0, 240, 304 }, bool keep_highlight = false);
|
||||
public:
|
||||
BtnGridView(Rect new_parent_rect = {0, 0, 240, 304}, bool keep_highlight = false);
|
||||
|
||||
~BtnGridView();
|
||||
~BtnGridView();
|
||||
|
||||
void add_items(std::initializer_list<GridItem> new_items);
|
||||
void set_max_rows(int rows);
|
||||
int rows();
|
||||
void clear();
|
||||
void add_items(std::initializer_list<GridItem> new_items);
|
||||
void set_max_rows(int rows);
|
||||
int rows();
|
||||
void clear();
|
||||
|
||||
NewButton* item_view(size_t index) const;
|
||||
NewButton* item_view(size_t index) const;
|
||||
|
||||
bool set_highlighted(int32_t new_value);
|
||||
uint32_t highlighted_index();
|
||||
bool set_highlighted(int32_t new_value);
|
||||
uint32_t highlighted_index();
|
||||
|
||||
void set_parent_rect(const Rect new_parent_rect) override;
|
||||
void set_arrow_enabled(bool new_value);
|
||||
void on_focus() override;
|
||||
void on_blur() override;
|
||||
bool on_key(const KeyEvent event) override;
|
||||
bool on_encoder(const EncoderEvent event) override;
|
||||
void set_parent_rect(const Rect new_parent_rect) override;
|
||||
void set_arrow_enabled(bool new_value);
|
||||
void on_focus() override;
|
||||
void on_blur() override;
|
||||
bool on_key(const KeyEvent event) override;
|
||||
bool on_encoder(const EncoderEvent event) override;
|
||||
|
||||
private:
|
||||
int rows_ { 3 };
|
||||
void update_items();
|
||||
void on_tick_second();
|
||||
private:
|
||||
int rows_{3};
|
||||
void update_items();
|
||||
void on_tick_second();
|
||||
|
||||
bool keep_highlight { false };
|
||||
bool keep_highlight{false};
|
||||
|
||||
SignalToken signal_token_tick_second { };
|
||||
std::vector<GridItem> menu_items { };
|
||||
std::vector<NewButton*> menu_item_views { };
|
||||
SignalToken signal_token_tick_second{};
|
||||
std::vector<GridItem> menu_items{};
|
||||
std::vector<NewButton*> menu_item_views{};
|
||||
|
||||
Image arrow_more {
|
||||
{ 228, 320 - 8, 8, 8 },
|
||||
&bitmap_more,
|
||||
Color::white(),
|
||||
Color::black()
|
||||
};
|
||||
Image arrow_more{
|
||||
{228, 320 - 8, 8, 8},
|
||||
&bitmap_more,
|
||||
Color::white(),
|
||||
Color::black()};
|
||||
|
||||
int button_w = 240 / rows_;
|
||||
static constexpr int button_h = 48;
|
||||
bool blink = false;
|
||||
bool more = false;
|
||||
size_t displayed_max { 0 };
|
||||
size_t highlighted_item { 0 };
|
||||
size_t offset { 0 };
|
||||
int button_w = 240 / rows_;
|
||||
static constexpr int button_h = 48;
|
||||
bool blink = false;
|
||||
bool more = false;
|
||||
size_t displayed_max{0};
|
||||
size_t highlighted_item{0};
|
||||
size_t offset{0};
|
||||
};
|
||||
|
||||
} /* namespace ui */
|
||||
|
||||
#endif/*__UI_BTNGRID_H__*/
|
||||
#endif /*__UI_BTNGRID_H__*/
|
||||
|
@@ -28,36 +28,33 @@
|
||||
namespace ui {
|
||||
|
||||
void Channel::paint(Painter& painter) {
|
||||
const auto r = screen_rect();
|
||||
const auto r = screen_rect();
|
||||
|
||||
constexpr int db_min = -96;
|
||||
constexpr int db_max = 0;
|
||||
constexpr int db_delta = db_max - db_min;
|
||||
const range_t<int> x_max_range { 0, r.width() - 1 };
|
||||
const auto x_max = x_max_range.clip((max_db_ - db_min) * r.width() / db_delta);
|
||||
constexpr int db_min = -96;
|
||||
constexpr int db_max = 0;
|
||||
constexpr int db_delta = db_max - db_min;
|
||||
const range_t<int> x_max_range{0, r.width() - 1};
|
||||
const auto x_max = x_max_range.clip((max_db_ - db_min) * r.width() / db_delta);
|
||||
|
||||
const Rect r0 { r.left(), r.top(), x_max, r.height() };
|
||||
painter.fill_rectangle(
|
||||
r0,
|
||||
Color::blue()
|
||||
);
|
||||
const Rect r0{r.left(), r.top(), x_max, r.height()};
|
||||
painter.fill_rectangle(
|
||||
r0,
|
||||
Color::blue());
|
||||
|
||||
const Rect r1 { r.left() + x_max, r.top(), 1, r.height() };
|
||||
painter.fill_rectangle(
|
||||
r1,
|
||||
Color::white()
|
||||
);
|
||||
const Rect r1{r.left() + x_max, r.top(), 1, r.height()};
|
||||
painter.fill_rectangle(
|
||||
r1,
|
||||
Color::white());
|
||||
|
||||
const Rect r2 { r.left() + x_max + 1, r.top(), r.width() - (x_max + 1), r.height() };
|
||||
painter.fill_rectangle(
|
||||
r2,
|
||||
Color::black()
|
||||
);
|
||||
const Rect r2{r.left() + x_max + 1, r.top(), r.width() - (x_max + 1), r.height()};
|
||||
painter.fill_rectangle(
|
||||
r2,
|
||||
Color::black());
|
||||
}
|
||||
|
||||
void Channel::on_statistics_update(const ChannelStatistics& statistics) {
|
||||
max_db_ = statistics.max_db;
|
||||
set_dirty();
|
||||
max_db_ = statistics.max_db;
|
||||
set_dirty();
|
||||
}
|
||||
|
||||
} /* namespace ui */
|
||||
|
@@ -35,29 +35,27 @@
|
||||
namespace ui {
|
||||
|
||||
class Channel : public Widget {
|
||||
public:
|
||||
Channel(
|
||||
const Rect parent_rect
|
||||
) : Widget { parent_rect },
|
||||
max_db_ { -120 }
|
||||
{
|
||||
}
|
||||
public:
|
||||
Channel(
|
||||
const Rect parent_rect)
|
||||
: Widget{parent_rect},
|
||||
max_db_{-120} {
|
||||
}
|
||||
|
||||
void paint(Painter& painter) override;
|
||||
void paint(Painter& painter) override;
|
||||
|
||||
private:
|
||||
int32_t max_db_;
|
||||
private:
|
||||
int32_t max_db_;
|
||||
|
||||
MessageHandlerRegistration message_handler_stats {
|
||||
Message::ID::ChannelStatistics,
|
||||
[this](const Message* const p) {
|
||||
this->on_statistics_update(static_cast<const ChannelStatisticsMessage*>(p)->statistics);
|
||||
}
|
||||
};
|
||||
MessageHandlerRegistration message_handler_stats{
|
||||
Message::ID::ChannelStatistics,
|
||||
[this](const Message* const p) {
|
||||
this->on_statistics_update(static_cast<const ChannelStatisticsMessage*>(p)->statistics);
|
||||
}};
|
||||
|
||||
void on_statistics_update(const ChannelStatistics& statistics);
|
||||
void on_statistics_update(const ChannelStatistics& statistics);
|
||||
};
|
||||
|
||||
}
|
||||
} // namespace ui
|
||||
|
||||
#endif/*__UI_CHANNEL_H__*/
|
||||
#endif /*__UI_CHANNEL_H__*/
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -30,6 +30,6 @@ namespace font {
|
||||
extern const ui::Font fixed_8x16;
|
||||
|
||||
} /* namespace font */
|
||||
} /* namspace ui */
|
||||
} // namespace ui
|
||||
|
||||
#endif/*__UI_FONT_FIXED_8X16_H__*/
|
||||
#endif /*__UI_FONT_FIXED_8X16_H__*/
|
||||
|
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
|
||||
* Copyright (C) 2017 Furrtek
|
||||
*
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -35,428 +35,410 @@ using namespace portapack;
|
||||
namespace ui {
|
||||
|
||||
GeoPos::GeoPos(
|
||||
const Point pos,
|
||||
const alt_unit altitude_unit
|
||||
) : altitude_unit_(altitude_unit) {
|
||||
|
||||
set_parent_rect({pos, { 30 * 8, 3 * 16 }});
|
||||
|
||||
add_children({
|
||||
&labels_position,
|
||||
&field_altitude,
|
||||
&text_alt_unit,
|
||||
&field_lat_degrees,
|
||||
&field_lat_minutes,
|
||||
&field_lat_seconds,
|
||||
&text_lat_decimal,
|
||||
&field_lon_degrees,
|
||||
&field_lon_minutes,
|
||||
&field_lon_seconds,
|
||||
&text_lon_decimal
|
||||
});
|
||||
|
||||
// Defaults
|
||||
set_altitude(0);
|
||||
set_lat(0);
|
||||
set_lon(0);
|
||||
const Point pos,
|
||||
const alt_unit altitude_unit)
|
||||
: altitude_unit_(altitude_unit) {
|
||||
set_parent_rect({pos, {30 * 8, 3 * 16}});
|
||||
|
||||
const auto changed_fn = [this](int32_t) {
|
||||
float lat_value = lat();
|
||||
float lon_value = lon();
|
||||
add_children({&labels_position,
|
||||
&field_altitude,
|
||||
&text_alt_unit,
|
||||
&field_lat_degrees,
|
||||
&field_lat_minutes,
|
||||
&field_lat_seconds,
|
||||
&text_lat_decimal,
|
||||
&field_lon_degrees,
|
||||
&field_lon_minutes,
|
||||
&field_lon_seconds,
|
||||
&text_lon_decimal});
|
||||
|
||||
text_lat_decimal.set(to_string_decimal(lat_value, 5));
|
||||
text_lon_decimal.set(to_string_decimal(lon_value, 5));
|
||||
|
||||
if (on_change && report_change)
|
||||
on_change(altitude(), lat_value, lon_value);
|
||||
};
|
||||
|
||||
field_altitude.on_change = changed_fn;
|
||||
field_lat_degrees.on_change = changed_fn;
|
||||
field_lat_minutes.on_change = changed_fn;
|
||||
field_lat_seconds.on_change = changed_fn;
|
||||
field_lon_degrees.on_change = changed_fn;
|
||||
field_lon_minutes.on_change = changed_fn;
|
||||
field_lon_seconds.on_change = changed_fn;
|
||||
|
||||
text_alt_unit.set(altitude_unit_ ? "m" : "ft");
|
||||
// Defaults
|
||||
set_altitude(0);
|
||||
set_lat(0);
|
||||
set_lon(0);
|
||||
|
||||
const auto changed_fn = [this](int32_t) {
|
||||
float lat_value = lat();
|
||||
float lon_value = lon();
|
||||
|
||||
text_lat_decimal.set(to_string_decimal(lat_value, 5));
|
||||
text_lon_decimal.set(to_string_decimal(lon_value, 5));
|
||||
|
||||
if (on_change && report_change)
|
||||
on_change(altitude(), lat_value, lon_value);
|
||||
};
|
||||
|
||||
field_altitude.on_change = changed_fn;
|
||||
field_lat_degrees.on_change = changed_fn;
|
||||
field_lat_minutes.on_change = changed_fn;
|
||||
field_lat_seconds.on_change = changed_fn;
|
||||
field_lon_degrees.on_change = changed_fn;
|
||||
field_lon_minutes.on_change = changed_fn;
|
||||
field_lon_seconds.on_change = changed_fn;
|
||||
|
||||
text_alt_unit.set(altitude_unit_ ? "m" : "ft");
|
||||
}
|
||||
|
||||
void GeoPos::set_read_only(bool v) {
|
||||
for(auto child : children_)
|
||||
child->set_focusable(!v);
|
||||
for (auto child : children_)
|
||||
child->set_focusable(!v);
|
||||
}
|
||||
|
||||
// Stupid hack to avoid an event loop
|
||||
void GeoPos::set_report_change(bool v) {
|
||||
report_change = v;
|
||||
report_change = v;
|
||||
}
|
||||
|
||||
void GeoPos::focus() {
|
||||
field_altitude.focus();
|
||||
field_altitude.focus();
|
||||
}
|
||||
|
||||
void GeoPos::set_altitude(int32_t altitude) {
|
||||
field_altitude.set_value(altitude);
|
||||
field_altitude.set_value(altitude);
|
||||
}
|
||||
|
||||
void GeoPos::set_lat(float lat) {
|
||||
field_lat_degrees.set_value(lat);
|
||||
field_lat_minutes.set_value((uint32_t)abs(lat / (1.0 / 60)) % 60);
|
||||
field_lat_seconds.set_value((uint32_t)abs(lat / (1.0 / 3600)) % 60);
|
||||
field_lat_degrees.set_value(lat);
|
||||
field_lat_minutes.set_value((uint32_t)abs(lat / (1.0 / 60)) % 60);
|
||||
field_lat_seconds.set_value((uint32_t)abs(lat / (1.0 / 3600)) % 60);
|
||||
}
|
||||
|
||||
void GeoPos::set_lon(float lon) {
|
||||
field_lon_degrees.set_value(lon);
|
||||
field_lon_minutes.set_value((uint32_t)abs(lon / (1.0 / 60)) % 60);
|
||||
field_lon_seconds.set_value((uint32_t)abs(lon / (1.0 / 3600)) % 60);
|
||||
field_lon_degrees.set_value(lon);
|
||||
field_lon_minutes.set_value((uint32_t)abs(lon / (1.0 / 60)) % 60);
|
||||
field_lon_seconds.set_value((uint32_t)abs(lon / (1.0 / 3600)) % 60);
|
||||
}
|
||||
|
||||
float GeoPos::lat() {
|
||||
if (field_lat_degrees.value() < 0) {
|
||||
return -1 * ( -1 * field_lat_degrees.value() + (field_lat_minutes.value() / 60.0) + (field_lat_seconds.value() / 3600.0));
|
||||
} else {
|
||||
return field_lat_degrees.value() + (field_lat_minutes.value() / 60.0) + (field_lat_seconds.value() / 3600.0);
|
||||
}
|
||||
if (field_lat_degrees.value() < 0) {
|
||||
return -1 * (-1 * field_lat_degrees.value() + (field_lat_minutes.value() / 60.0) + (field_lat_seconds.value() / 3600.0));
|
||||
} else {
|
||||
return field_lat_degrees.value() + (field_lat_minutes.value() / 60.0) + (field_lat_seconds.value() / 3600.0);
|
||||
}
|
||||
};
|
||||
|
||||
float GeoPos::lon() {
|
||||
if (field_lon_degrees.value() < 0) {
|
||||
return -1 * (-1 * field_lon_degrees.value() + (field_lon_minutes.value() / 60.0) + (field_lon_seconds.value() / 3600.0));
|
||||
} else {
|
||||
return field_lon_degrees.value() + (field_lon_minutes.value() / 60.0) + (field_lon_seconds.value() / 3600.0);
|
||||
}
|
||||
if (field_lon_degrees.value() < 0) {
|
||||
return -1 * (-1 * field_lon_degrees.value() + (field_lon_minutes.value() / 60.0) + (field_lon_seconds.value() / 3600.0));
|
||||
} else {
|
||||
return field_lon_degrees.value() + (field_lon_minutes.value() / 60.0) + (field_lon_seconds.value() / 3600.0);
|
||||
}
|
||||
};
|
||||
|
||||
int32_t GeoPos::altitude() {
|
||||
return field_altitude.value();
|
||||
return field_altitude.value();
|
||||
};
|
||||
|
||||
GeoMap::GeoMap(
|
||||
Rect parent_rect
|
||||
) : Widget { parent_rect }, markerListLen(0)
|
||||
{
|
||||
//set_focusable(true);
|
||||
Rect parent_rect)
|
||||
: Widget{parent_rect}, markerListLen(0) {
|
||||
// set_focusable(true);
|
||||
}
|
||||
|
||||
void GeoMap::paint(Painter& painter) {
|
||||
u_int16_t line;
|
||||
std::array<ui::Color, 240> map_line_buffer;
|
||||
const auto r = screen_rect();
|
||||
|
||||
// Ony redraw map if it moved by at least 1 pixel
|
||||
// or the markers list was updated
|
||||
int x_diff = abs(x_pos-prev_x_pos);
|
||||
int y_diff = abs(y_pos-prev_y_pos);
|
||||
if (markerListUpdated || (x_diff>=3) || (y_diff>=3)) {
|
||||
for (line = 0; line < r.height(); line++) {
|
||||
map_file.seek(4 + ((x_pos + (map_width * (y_pos + line))) << 1));
|
||||
map_file.read(map_line_buffer.data(), r.width() << 1);
|
||||
display.draw_pixels({ 0, r.top() + line, r.width(), 1 }, map_line_buffer);
|
||||
}
|
||||
prev_x_pos = x_pos;
|
||||
prev_y_pos = y_pos;
|
||||
u_int16_t line;
|
||||
std::array<ui::Color, 240> map_line_buffer;
|
||||
const auto r = screen_rect();
|
||||
|
||||
// Draw the other markers
|
||||
for ( int i=0; i<markerListLen; ++i )
|
||||
{
|
||||
GeoMarker & item = markerList[i];
|
||||
double lat_rad = sin(item.lat * pi / 180);
|
||||
int x = (map_width * (item.lon+180)/360) - x_pos;
|
||||
int y = (map_height - ((map_world_lon / 2 * log((1 + lat_rad) / (1 - lat_rad))) - map_offset)) - y_pos; // Offset added for the GUI
|
||||
if ((x>=0) && (x<r.width()) &&
|
||||
(y>10) && (y<r.height()) ) // Dont draw within symbol size of top
|
||||
{
|
||||
ui::Point itemPoint(x,y+r.top());
|
||||
if(y>=32) { // Dont draw text if it would overlap top
|
||||
// Text and symbol
|
||||
draw_marker(painter, itemPoint, item.angle, item.tag, Color::blue(), Color::blue(), Color::magenta() );
|
||||
} else {
|
||||
// Only symbol
|
||||
draw_bearing( itemPoint, item.angle, 10, Color::blue());
|
||||
}
|
||||
}
|
||||
markerListUpdated = false;
|
||||
} // Draw the other markers
|
||||
}
|
||||
|
||||
//Draw the marker in the center
|
||||
draw_marker(painter, r.center(), angle_, tag_, Color::red(), Color::white(), Color::black() );
|
||||
// Ony redraw map if it moved by at least 1 pixel
|
||||
// or the markers list was updated
|
||||
int x_diff = abs(x_pos - prev_x_pos);
|
||||
int y_diff = abs(y_pos - prev_y_pos);
|
||||
if (markerListUpdated || (x_diff >= 3) || (y_diff >= 3)) {
|
||||
for (line = 0; line < r.height(); line++) {
|
||||
map_file.seek(4 + ((x_pos + (map_width * (y_pos + line))) << 1));
|
||||
map_file.read(map_line_buffer.data(), r.width() << 1);
|
||||
display.draw_pixels({0, r.top() + line, r.width(), 1}, map_line_buffer);
|
||||
}
|
||||
prev_x_pos = x_pos;
|
||||
prev_y_pos = y_pos;
|
||||
|
||||
// Draw the other markers
|
||||
for (int i = 0; i < markerListLen; ++i) {
|
||||
GeoMarker& item = markerList[i];
|
||||
double lat_rad = sin(item.lat * pi / 180);
|
||||
int x = (map_width * (item.lon + 180) / 360) - x_pos;
|
||||
int y = (map_height - ((map_world_lon / 2 * log((1 + lat_rad) / (1 - lat_rad))) - map_offset)) - y_pos; // Offset added for the GUI
|
||||
if ((x >= 0) && (x < r.width()) &&
|
||||
(y > 10) && (y < r.height())) // Dont draw within symbol size of top
|
||||
{
|
||||
ui::Point itemPoint(x, y + r.top());
|
||||
if (y >= 32) { // Dont draw text if it would overlap top
|
||||
// Text and symbol
|
||||
draw_marker(painter, itemPoint, item.angle, item.tag, Color::blue(), Color::blue(), Color::magenta());
|
||||
} else {
|
||||
// Only symbol
|
||||
draw_bearing(itemPoint, item.angle, 10, Color::blue());
|
||||
}
|
||||
}
|
||||
markerListUpdated = false;
|
||||
} // Draw the other markers
|
||||
}
|
||||
|
||||
// Draw the marker in the center
|
||||
draw_marker(painter, r.center(), angle_, tag_, Color::red(), Color::white(), Color::black());
|
||||
}
|
||||
|
||||
bool GeoMap::on_touch(const TouchEvent event) {
|
||||
if ((event.type == TouchEvent::Type::Start) && (mode_ == PROMPT)) {
|
||||
set_highlighted(true);
|
||||
if (on_move) {
|
||||
Point p = event.point - screen_rect().center();
|
||||
on_move(p.x() / 2.0 * lon_ratio, p.y() / 2.0 * lat_ratio);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
if ((event.type == TouchEvent::Type::Start) && (mode_ == PROMPT)) {
|
||||
set_highlighted(true);
|
||||
if (on_move) {
|
||||
Point p = event.point - screen_rect().center();
|
||||
on_move(p.x() / 2.0 * lon_ratio, p.y() / 2.0 * lat_ratio);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void GeoMap::move(const float lon, const float lat) {
|
||||
lon_ = lon;
|
||||
lat_ = lat;
|
||||
|
||||
Rect map_rect = screen_rect();
|
||||
|
||||
// Using WGS 84/Pseudo-Mercator projection
|
||||
x_pos = map_width * (lon_+180)/360 - (map_rect.width() / 2);
|
||||
lon_ = lon;
|
||||
lat_ = lat;
|
||||
|
||||
// Latitude calculation based on https://stackoverflow.com/a/10401734/2278659
|
||||
double lat_rad = sin(lat * pi / 180);
|
||||
y_pos = map_height - ((map_world_lon / 2 * log((1 + lat_rad) / (1 - lat_rad))) - map_offset) - 128; // Offset added for the GUI
|
||||
Rect map_rect = screen_rect();
|
||||
|
||||
// Cap position
|
||||
if (x_pos > (map_width - map_rect.width()))
|
||||
x_pos = map_width - map_rect.width();
|
||||
if (y_pos > (map_height + map_rect.height()))
|
||||
y_pos = map_height - map_rect.height();
|
||||
// Using WGS 84/Pseudo-Mercator projection
|
||||
x_pos = map_width * (lon_ + 180) / 360 - (map_rect.width() / 2);
|
||||
|
||||
// Latitude calculation based on https://stackoverflow.com/a/10401734/2278659
|
||||
double lat_rad = sin(lat * pi / 180);
|
||||
y_pos = map_height - ((map_world_lon / 2 * log((1 + lat_rad) / (1 - lat_rad))) - map_offset) - 128; // Offset added for the GUI
|
||||
|
||||
// Cap position
|
||||
if (x_pos > (map_width - map_rect.width()))
|
||||
x_pos = map_width - map_rect.width();
|
||||
if (y_pos > (map_height + map_rect.height()))
|
||||
y_pos = map_height - map_rect.height();
|
||||
}
|
||||
|
||||
bool GeoMap::init() {
|
||||
auto result = map_file.open("ADSB/world_map.bin");
|
||||
if (result.is_valid())
|
||||
return false;
|
||||
|
||||
map_file.read(&map_width, 2);
|
||||
map_file.read(&map_height, 2);
|
||||
|
||||
map_center_x = map_width >> 1;
|
||||
map_center_y = map_height >> 1;
|
||||
|
||||
lon_ratio = 180.0 / map_center_x;
|
||||
lat_ratio = -90.0 / map_center_y;
|
||||
auto result = map_file.open("ADSB/world_map.bin");
|
||||
if (result.is_valid())
|
||||
return false;
|
||||
|
||||
map_bottom = sin(-85.05 * pi / 180); // Map bitmap only goes from about -85 to 85 lat
|
||||
map_world_lon = map_width / (2 * pi);
|
||||
map_file.read(&map_width, 2);
|
||||
map_file.read(&map_height, 2);
|
||||
|
||||
map_center_x = map_width >> 1;
|
||||
map_center_y = map_height >> 1;
|
||||
|
||||
lon_ratio = 180.0 / map_center_x;
|
||||
lat_ratio = -90.0 / map_center_y;
|
||||
|
||||
map_bottom = sin(-85.05 * pi / 180); // Map bitmap only goes from about -85 to 85 lat
|
||||
map_world_lon = map_width / (2 * pi);
|
||||
map_offset = (map_world_lon / 2 * log((1 + map_bottom) / (1 - map_bottom)));
|
||||
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void GeoMap::set_mode(GeoMapMode mode) {
|
||||
mode_ = mode;
|
||||
mode_ = mode;
|
||||
}
|
||||
|
||||
void GeoMap::draw_bearing(const Point origin, const uint16_t angle, uint32_t size, const Color color) {
|
||||
Point arrow_a, arrow_b, arrow_c;
|
||||
|
||||
for (size_t thickness = 0; thickness < 3; thickness++) {
|
||||
arrow_a = fast_polar_to_point((int)angle, size) + origin;
|
||||
arrow_b = fast_polar_to_point((int)(angle + 180 - 35), size) + origin;
|
||||
arrow_c = fast_polar_to_point((int)(angle + 180 + 35), size) + origin;
|
||||
|
||||
display.draw_line(arrow_a, arrow_b, color);
|
||||
display.draw_line(arrow_b, arrow_c, color);
|
||||
display.draw_line(arrow_c, arrow_a, color);
|
||||
|
||||
size--;
|
||||
}
|
||||
Point arrow_a, arrow_b, arrow_c;
|
||||
|
||||
for (size_t thickness = 0; thickness < 3; thickness++) {
|
||||
arrow_a = fast_polar_to_point((int)angle, size) + origin;
|
||||
arrow_b = fast_polar_to_point((int)(angle + 180 - 35), size) + origin;
|
||||
arrow_c = fast_polar_to_point((int)(angle + 180 + 35), size) + origin;
|
||||
|
||||
display.draw_line(arrow_a, arrow_b, color);
|
||||
display.draw_line(arrow_b, arrow_c, color);
|
||||
display.draw_line(arrow_c, arrow_a, color);
|
||||
|
||||
size--;
|
||||
}
|
||||
}
|
||||
|
||||
void GeoMap::draw_marker(Painter& painter, const ui::Point itemPoint, const uint16_t itemAngle, const std::string itemTag,
|
||||
const Color color, const Color fontColor, const Color backColor )
|
||||
{
|
||||
int tagOffset = 10;
|
||||
if (mode_ == PROMPT) {
|
||||
// Cross
|
||||
display.fill_rectangle({ itemPoint - Point(16, 1), { 32, 2 } }, color);
|
||||
display.fill_rectangle({ itemPoint - Point(1, 16), { 2, 32 } }, color);
|
||||
tagOffset = 16;
|
||||
} else if (angle_ < 360){
|
||||
//if we have a valid angle draw bearing
|
||||
draw_bearing( itemPoint, itemAngle, 10, color);
|
||||
tagOffset = 10;
|
||||
}
|
||||
else {
|
||||
//draw a small cross
|
||||
display.fill_rectangle({ itemPoint - Point(8, 1), { 16, 2 } }, color);
|
||||
display.fill_rectangle({ itemPoint - Point(1, 8), { 2, 16 } }, color);
|
||||
tagOffset = 8;
|
||||
}
|
||||
//center tag above point
|
||||
if(itemTag.find_first_not_of(' ') != itemTag.npos){ //only draw tag if we have something other than spaces
|
||||
painter.draw_string( itemPoint - Point(((int)itemTag.length() * 8 / 2), 14 + tagOffset ),
|
||||
style().font, fontColor, backColor, itemTag);
|
||||
}
|
||||
void GeoMap::draw_marker(Painter& painter, const ui::Point itemPoint, const uint16_t itemAngle, const std::string itemTag, const Color color, const Color fontColor, const Color backColor) {
|
||||
int tagOffset = 10;
|
||||
if (mode_ == PROMPT) {
|
||||
// Cross
|
||||
display.fill_rectangle({itemPoint - Point(16, 1), {32, 2}}, color);
|
||||
display.fill_rectangle({itemPoint - Point(1, 16), {2, 32}}, color);
|
||||
tagOffset = 16;
|
||||
} else if (angle_ < 360) {
|
||||
// if we have a valid angle draw bearing
|
||||
draw_bearing(itemPoint, itemAngle, 10, color);
|
||||
tagOffset = 10;
|
||||
} else {
|
||||
// draw a small cross
|
||||
display.fill_rectangle({itemPoint - Point(8, 1), {16, 2}}, color);
|
||||
display.fill_rectangle({itemPoint - Point(1, 8), {2, 16}}, color);
|
||||
tagOffset = 8;
|
||||
}
|
||||
// center tag above point
|
||||
if (itemTag.find_first_not_of(' ') != itemTag.npos) { // only draw tag if we have something other than spaces
|
||||
painter.draw_string(itemPoint - Point(((int)itemTag.length() * 8 / 2), 14 + tagOffset),
|
||||
style().font, fontColor, backColor, itemTag);
|
||||
}
|
||||
}
|
||||
|
||||
void GeoMap::clear_markers()
|
||||
{
|
||||
markerListLen = 0;
|
||||
void GeoMap::clear_markers() {
|
||||
markerListLen = 0;
|
||||
}
|
||||
|
||||
MapMarkerStored GeoMap::store_marker(GeoMarker & marker)
|
||||
{
|
||||
MapMarkerStored ret;
|
||||
MapMarkerStored GeoMap::store_marker(GeoMarker& marker) {
|
||||
MapMarkerStored ret;
|
||||
|
||||
// Check if it could be on screen
|
||||
// Only checking one direction to reduce CPU
|
||||
const auto r = screen_rect();
|
||||
double lat_rad = sin(marker.lat * pi / 180);
|
||||
int x = (map_width * (marker.lon+180)/360) - x_pos;
|
||||
int y = (map_height - ((map_world_lon / 2 * log((1 + lat_rad) / (1 - lat_rad))) - map_offset)) - y_pos; // Offset added for the GUI
|
||||
if (false==((x>=0) && (x<r.width()) && (y>10) && (y<r.height()))) // Dont draw within symbol size of top
|
||||
{
|
||||
ret = MARKER_NOT_STORED;
|
||||
}
|
||||
else if (markerListLen<NumMarkerListElements)
|
||||
{
|
||||
markerList[markerListLen] = marker;
|
||||
markerListLen++;
|
||||
markerListUpdated = true;
|
||||
ret = MARKER_STORED;
|
||||
} else {
|
||||
ret = MARKER_LIST_FULL;
|
||||
}
|
||||
return ret;
|
||||
// Check if it could be on screen
|
||||
// Only checking one direction to reduce CPU
|
||||
const auto r = screen_rect();
|
||||
double lat_rad = sin(marker.lat * pi / 180);
|
||||
int x = (map_width * (marker.lon + 180) / 360) - x_pos;
|
||||
int y = (map_height - ((map_world_lon / 2 * log((1 + lat_rad) / (1 - lat_rad))) - map_offset)) - y_pos; // Offset added for the GUI
|
||||
if (false == ((x >= 0) && (x < r.width()) && (y > 10) && (y < r.height()))) // Dont draw within symbol size of top
|
||||
{
|
||||
ret = MARKER_NOT_STORED;
|
||||
} else if (markerListLen < NumMarkerListElements) {
|
||||
markerList[markerListLen] = marker;
|
||||
markerListLen++;
|
||||
markerListUpdated = true;
|
||||
ret = MARKER_STORED;
|
||||
} else {
|
||||
ret = MARKER_LIST_FULL;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
void GeoMapView::focus() {
|
||||
geopos.focus();
|
||||
|
||||
if (!map_opened)
|
||||
nav_.display_modal("No map", "No world_map.bin file in\n/ADSB/ directory", ABORT, nullptr);
|
||||
geopos.focus();
|
||||
|
||||
if (!map_opened)
|
||||
nav_.display_modal("No map", "No world_map.bin file in\n/ADSB/ directory", ABORT, nullptr);
|
||||
}
|
||||
|
||||
void GeoMapView::update_position(float lat, float lon, uint16_t angle, int32_t altitude) {
|
||||
lat_ = lat;
|
||||
lon_ = lon;
|
||||
altitude_ = altitude;
|
||||
|
||||
// Stupid hack to avoid an event loop
|
||||
geopos.set_report_change(false);
|
||||
geopos.set_lat(lat_);
|
||||
geopos.set_lon(lon_);
|
||||
geopos.set_altitude(altitude_);
|
||||
geopos.set_report_change(true);
|
||||
lat_ = lat;
|
||||
lon_ = lon;
|
||||
altitude_ = altitude;
|
||||
|
||||
geomap.set_angle(angle);
|
||||
geomap.move(lon_, lat_);
|
||||
geomap.set_dirty();
|
||||
// Stupid hack to avoid an event loop
|
||||
geopos.set_report_change(false);
|
||||
geopos.set_lat(lat_);
|
||||
geopos.set_lon(lon_);
|
||||
geopos.set_altitude(altitude_);
|
||||
geopos.set_report_change(true);
|
||||
|
||||
geomap.set_angle(angle);
|
||||
geomap.move(lon_, lat_);
|
||||
geomap.set_dirty();
|
||||
}
|
||||
|
||||
void GeoMapView::update_tag(const std::string tag) {
|
||||
geomap.set_tag(tag);
|
||||
geomap.set_tag(tag);
|
||||
}
|
||||
|
||||
|
||||
void GeoMapView::setup() {
|
||||
add_child(&geomap);
|
||||
|
||||
geopos.set_altitude(altitude_);
|
||||
geopos.set_lat(lat_);
|
||||
geopos.set_lon(lon_);
|
||||
add_child(&geomap);
|
||||
|
||||
geopos.on_change = [this](int32_t altitude, float lat, float lon) {
|
||||
altitude_ = altitude;
|
||||
lat_ = lat;
|
||||
lon_ = lon;
|
||||
geomap.move(lon_, lat_);
|
||||
geomap.set_dirty();
|
||||
};
|
||||
|
||||
geomap.on_move = [this](float move_x, float move_y) {
|
||||
lon_ += move_x;
|
||||
lat_ += move_y;
|
||||
|
||||
// Stupid hack to avoid an event loop
|
||||
geopos.set_report_change(false);
|
||||
geopos.set_lon(lon_);
|
||||
geopos.set_lat(lat_);
|
||||
geopos.set_report_change(true);
|
||||
|
||||
geomap.move(lon_, lat_);
|
||||
geomap.set_dirty();
|
||||
};
|
||||
geopos.set_altitude(altitude_);
|
||||
geopos.set_lat(lat_);
|
||||
geopos.set_lon(lon_);
|
||||
|
||||
geopos.on_change = [this](int32_t altitude, float lat, float lon) {
|
||||
altitude_ = altitude;
|
||||
lat_ = lat;
|
||||
lon_ = lon;
|
||||
geomap.move(lon_, lat_);
|
||||
geomap.set_dirty();
|
||||
};
|
||||
|
||||
geomap.on_move = [this](float move_x, float move_y) {
|
||||
lon_ += move_x;
|
||||
lat_ += move_y;
|
||||
|
||||
// Stupid hack to avoid an event loop
|
||||
geopos.set_report_change(false);
|
||||
geopos.set_lon(lon_);
|
||||
geopos.set_lat(lat_);
|
||||
geopos.set_report_change(true);
|
||||
|
||||
geomap.move(lon_, lat_);
|
||||
geomap.set_dirty();
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
GeoMapView::~GeoMapView() {
|
||||
if (on_close_)
|
||||
on_close_();
|
||||
if (on_close_)
|
||||
on_close_();
|
||||
}
|
||||
|
||||
// Display mode
|
||||
GeoMapView::GeoMapView(
|
||||
NavigationView& nav,
|
||||
const std::string& tag,
|
||||
int32_t altitude,
|
||||
GeoPos::alt_unit altitude_unit,
|
||||
float lat,
|
||||
float lon,
|
||||
uint16_t angle,
|
||||
const std::function<void(void)> on_close
|
||||
) : nav_ (nav),
|
||||
altitude_ (altitude),
|
||||
altitude_unit_ (altitude_unit),
|
||||
lat_ (lat),
|
||||
lon_ (lon),
|
||||
angle_ (angle),
|
||||
on_close_(on_close)
|
||||
{
|
||||
mode_ = DISPLAY;
|
||||
|
||||
add_child(&geopos);
|
||||
|
||||
map_opened = geomap.init();
|
||||
if (!map_opened) return;
|
||||
|
||||
setup();
|
||||
|
||||
geomap.set_mode(mode_);
|
||||
geomap.set_tag(tag);
|
||||
geomap.set_angle(angle);
|
||||
geomap.move(lon_, lat_);
|
||||
|
||||
geopos.set_read_only(true);
|
||||
NavigationView& nav,
|
||||
const std::string& tag,
|
||||
int32_t altitude,
|
||||
GeoPos::alt_unit altitude_unit,
|
||||
float lat,
|
||||
float lon,
|
||||
uint16_t angle,
|
||||
const std::function<void(void)> on_close)
|
||||
: nav_(nav),
|
||||
altitude_(altitude),
|
||||
altitude_unit_(altitude_unit),
|
||||
lat_(lat),
|
||||
lon_(lon),
|
||||
angle_(angle),
|
||||
on_close_(on_close) {
|
||||
mode_ = DISPLAY;
|
||||
|
||||
add_child(&geopos);
|
||||
|
||||
map_opened = geomap.init();
|
||||
if (!map_opened) return;
|
||||
|
||||
setup();
|
||||
|
||||
geomap.set_mode(mode_);
|
||||
geomap.set_tag(tag);
|
||||
geomap.set_angle(angle);
|
||||
geomap.move(lon_, lat_);
|
||||
|
||||
geopos.set_read_only(true);
|
||||
}
|
||||
|
||||
// Prompt mode
|
||||
GeoMapView::GeoMapView(
|
||||
NavigationView& nav,
|
||||
int32_t altitude,
|
||||
GeoPos::alt_unit altitude_unit,
|
||||
float lat,
|
||||
float lon,
|
||||
const std::function<void(int32_t, float, float)> on_done
|
||||
) : nav_ (nav),
|
||||
altitude_ (altitude),
|
||||
altitude_unit_ (altitude_unit),
|
||||
lat_ (lat),
|
||||
lon_ (lon)
|
||||
{
|
||||
mode_ = PROMPT;
|
||||
|
||||
add_child(&geopos);
|
||||
|
||||
map_opened = geomap.init();
|
||||
if (!map_opened) return;
|
||||
|
||||
setup();
|
||||
add_child(&button_ok);
|
||||
|
||||
geomap.set_mode(mode_);
|
||||
geomap.move(lon_, lat_);
|
||||
|
||||
button_ok.on_select = [this, on_done, &nav](Button&) {
|
||||
if (on_done)
|
||||
on_done(altitude_, lat_, lon_);
|
||||
nav.pop();
|
||||
};
|
||||
NavigationView& nav,
|
||||
int32_t altitude,
|
||||
GeoPos::alt_unit altitude_unit,
|
||||
float lat,
|
||||
float lon,
|
||||
const std::function<void(int32_t, float, float)> on_done)
|
||||
: nav_(nav),
|
||||
altitude_(altitude),
|
||||
altitude_unit_(altitude_unit),
|
||||
lat_(lat),
|
||||
lon_(lon) {
|
||||
mode_ = PROMPT;
|
||||
|
||||
add_child(&geopos);
|
||||
|
||||
map_opened = geomap.init();
|
||||
if (!map_opened) return;
|
||||
|
||||
setup();
|
||||
add_child(&button_ok);
|
||||
|
||||
geomap.set_mode(mode_);
|
||||
geomap.move(lon_, lat_);
|
||||
|
||||
button_ok.on_select = [this, on_done, &nav](Button&) {
|
||||
if (on_done)
|
||||
on_done(altitude_, lat_, lon_);
|
||||
nav.pop();
|
||||
};
|
||||
}
|
||||
|
||||
void GeoMapView::clear_markers()
|
||||
{
|
||||
geomap.clear_markers();
|
||||
void GeoMapView::clear_markers() {
|
||||
geomap.clear_markers();
|
||||
}
|
||||
|
||||
MapMarkerStored GeoMapView::store_marker(GeoMarker & marker)
|
||||
{
|
||||
return geomap.store_marker(marker);
|
||||
MapMarkerStored GeoMapView::store_marker(GeoMarker& marker) {
|
||||
return geomap.store_marker(marker);
|
||||
}
|
||||
|
||||
} /* namespace ui */
|
||||
|
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
|
||||
* Copyright (C) 2017 Furrtek
|
||||
*
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -33,230 +33,236 @@
|
||||
namespace ui {
|
||||
|
||||
enum GeoMapMode {
|
||||
DISPLAY,
|
||||
PROMPT
|
||||
DISPLAY,
|
||||
PROMPT
|
||||
};
|
||||
|
||||
struct GeoMarker {
|
||||
public:
|
||||
float lat {0};
|
||||
float lon {0};
|
||||
uint16_t angle {0};
|
||||
std::string tag {""};
|
||||
public:
|
||||
float lat{0};
|
||||
float lon{0};
|
||||
uint16_t angle{0};
|
||||
std::string tag{""};
|
||||
|
||||
GeoMarker & operator=(GeoMarker & rhs){
|
||||
lat = rhs.lat;
|
||||
lon = rhs.lon;
|
||||
angle = rhs.angle;
|
||||
tag = rhs.tag;
|
||||
GeoMarker& operator=(GeoMarker& rhs) {
|
||||
lat = rhs.lat;
|
||||
lon = rhs.lon;
|
||||
angle = rhs.angle;
|
||||
tag = rhs.tag;
|
||||
|
||||
return *this;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class GeoPos : public View {
|
||||
public:
|
||||
enum alt_unit {
|
||||
FEET = 0,
|
||||
METERS
|
||||
};
|
||||
|
||||
std::function<void(int32_t, float, float)> on_change { };
|
||||
|
||||
GeoPos(const Point pos, const alt_unit altitude_unit);
|
||||
|
||||
void focus() override;
|
||||
|
||||
void set_read_only(bool v);
|
||||
void set_altitude(int32_t altitude);
|
||||
void set_lat(float lat);
|
||||
void set_lon(float lon);
|
||||
int32_t altitude();
|
||||
float lat();
|
||||
float lon();
|
||||
|
||||
void set_report_change(bool v);
|
||||
public:
|
||||
enum alt_unit {
|
||||
FEET = 0,
|
||||
METERS
|
||||
};
|
||||
|
||||
private:
|
||||
bool read_only { false };
|
||||
bool report_change { true };
|
||||
alt_unit altitude_unit_ { };
|
||||
std::function<void(int32_t, float, float)> on_change{};
|
||||
|
||||
Labels labels_position {
|
||||
{ { 1 * 8, 0 * 16 }, "Alt:", Color::light_grey() },
|
||||
{ { 1 * 8, 1 * 16 }, "Lat: * ' \"", Color::light_grey() }, // No ° symbol in 8x16 font
|
||||
{ { 1 * 8, 2 * 16 }, "Lon: * ' \"", Color::light_grey() },
|
||||
};
|
||||
|
||||
NumberField field_altitude {
|
||||
{ 6 * 8, 0 * 16 },
|
||||
5,
|
||||
{ -1000, 50000 },
|
||||
250,
|
||||
' '
|
||||
};
|
||||
Text text_alt_unit {
|
||||
{ 12 * 8, 0 * 16, 2 * 8, 16 },
|
||||
""
|
||||
};
|
||||
|
||||
NumberField field_lat_degrees {
|
||||
{ 5 * 8, 1 * 16 }, 4, { -90, 90 }, 1, ' '
|
||||
};
|
||||
NumberField field_lat_minutes {
|
||||
{ 10 * 8, 1 * 16 }, 2, { 0, 59 }, 1, ' '
|
||||
};
|
||||
NumberField field_lat_seconds {
|
||||
{ 13 * 8, 1 * 16 }, 2, { 0, 59 }, 1, ' '
|
||||
};
|
||||
Text text_lat_decimal {
|
||||
{ 17 * 8, 1 * 16, 13 * 8, 1 * 16 },
|
||||
""
|
||||
};
|
||||
|
||||
NumberField field_lon_degrees {
|
||||
{ 5 * 8, 2 * 16 }, 4, { -180, 180 }, 1, ' '
|
||||
};
|
||||
NumberField field_lon_minutes {
|
||||
{ 10 * 8, 2 * 16 }, 2, { 0, 59 }, 1, ' '
|
||||
};
|
||||
NumberField field_lon_seconds {
|
||||
{ 13 * 8, 2 * 16 }, 2, { 0, 59 }, 1, ' '
|
||||
};
|
||||
Text text_lon_decimal {
|
||||
{ 17 * 8, 2 * 16, 13 * 8, 1 * 16 },
|
||||
""
|
||||
};
|
||||
GeoPos(const Point pos, const alt_unit altitude_unit);
|
||||
|
||||
void focus() override;
|
||||
|
||||
void set_read_only(bool v);
|
||||
void set_altitude(int32_t altitude);
|
||||
void set_lat(float lat);
|
||||
void set_lon(float lon);
|
||||
int32_t altitude();
|
||||
float lat();
|
||||
float lon();
|
||||
|
||||
void set_report_change(bool v);
|
||||
|
||||
private:
|
||||
bool read_only{false};
|
||||
bool report_change{true};
|
||||
alt_unit altitude_unit_{};
|
||||
|
||||
Labels labels_position{
|
||||
{{1 * 8, 0 * 16}, "Alt:", Color::light_grey()},
|
||||
{{1 * 8, 1 * 16}, "Lat: * ' \"", Color::light_grey()}, // No ° symbol in 8x16 font
|
||||
{{1 * 8, 2 * 16}, "Lon: * ' \"", Color::light_grey()},
|
||||
};
|
||||
|
||||
NumberField field_altitude{
|
||||
{6 * 8, 0 * 16},
|
||||
5,
|
||||
{-1000, 50000},
|
||||
250,
|
||||
' '};
|
||||
Text text_alt_unit{
|
||||
{12 * 8, 0 * 16, 2 * 8, 16},
|
||||
""};
|
||||
|
||||
NumberField field_lat_degrees{
|
||||
{5 * 8, 1 * 16},
|
||||
4,
|
||||
{-90, 90},
|
||||
1,
|
||||
' '};
|
||||
NumberField field_lat_minutes{
|
||||
{10 * 8, 1 * 16},
|
||||
2,
|
||||
{0, 59},
|
||||
1,
|
||||
' '};
|
||||
NumberField field_lat_seconds{
|
||||
{13 * 8, 1 * 16},
|
||||
2,
|
||||
{0, 59},
|
||||
1,
|
||||
' '};
|
||||
Text text_lat_decimal{
|
||||
{17 * 8, 1 * 16, 13 * 8, 1 * 16},
|
||||
""};
|
||||
|
||||
NumberField field_lon_degrees{
|
||||
{5 * 8, 2 * 16},
|
||||
4,
|
||||
{-180, 180},
|
||||
1,
|
||||
' '};
|
||||
NumberField field_lon_minutes{
|
||||
{10 * 8, 2 * 16},
|
||||
2,
|
||||
{0, 59},
|
||||
1,
|
||||
' '};
|
||||
NumberField field_lon_seconds{
|
||||
{13 * 8, 2 * 16},
|
||||
2,
|
||||
{0, 59},
|
||||
1,
|
||||
' '};
|
||||
Text text_lon_decimal{
|
||||
{17 * 8, 2 * 16, 13 * 8, 1 * 16},
|
||||
""};
|
||||
};
|
||||
|
||||
enum MapMarkerStored {
|
||||
MARKER_NOT_STORED,
|
||||
MARKER_STORED,
|
||||
MARKER_LIST_FULL
|
||||
MARKER_NOT_STORED,
|
||||
MARKER_STORED,
|
||||
MARKER_LIST_FULL
|
||||
};
|
||||
|
||||
class GeoMap : public Widget {
|
||||
public:
|
||||
std::function<void(float, float)> on_move { };
|
||||
public:
|
||||
std::function<void(float, float)> on_move{};
|
||||
|
||||
GeoMap(Rect parent_rect);
|
||||
GeoMap(Rect parent_rect);
|
||||
|
||||
void paint(Painter& painter) override;
|
||||
void paint(Painter& painter) override;
|
||||
|
||||
bool on_touch(const TouchEvent event) override;
|
||||
|
||||
bool init();
|
||||
void set_mode(GeoMapMode mode);
|
||||
void move(const float lon, const float lat);
|
||||
void set_tag(std::string new_tag) {
|
||||
tag_ = new_tag;
|
||||
}
|
||||
bool on_touch(const TouchEvent event) override;
|
||||
|
||||
void set_angle(uint16_t new_angle){
|
||||
angle_ = new_angle;
|
||||
}
|
||||
bool init();
|
||||
void set_mode(GeoMapMode mode);
|
||||
void move(const float lon, const float lat);
|
||||
void set_tag(std::string new_tag) {
|
||||
tag_ = new_tag;
|
||||
}
|
||||
|
||||
static const int NumMarkerListElements = 30;
|
||||
void set_angle(uint16_t new_angle) {
|
||||
angle_ = new_angle;
|
||||
}
|
||||
|
||||
void clear_markers();
|
||||
MapMarkerStored store_marker(GeoMarker & marker);
|
||||
static const int NumMarkerListElements = 30;
|
||||
|
||||
private:
|
||||
void draw_bearing(const Point origin, const uint16_t angle, uint32_t size, const Color color);
|
||||
void draw_marker(Painter& painter, const ui::Point itemPoint, const uint16_t itemAngle, const std::string itemTag,
|
||||
const Color color = Color::red(), const Color fontColor = Color::white(), const Color backColor = Color::black() );
|
||||
void clear_markers();
|
||||
MapMarkerStored store_marker(GeoMarker& marker);
|
||||
|
||||
GeoMapMode mode_ { };
|
||||
File map_file { };
|
||||
uint16_t map_width { }, map_height { };
|
||||
int32_t map_center_x { }, map_center_y { };
|
||||
float lon_ratio { }, lat_ratio { };
|
||||
double map_bottom { };
|
||||
double map_world_lon { };
|
||||
double map_offset { };
|
||||
private:
|
||||
void draw_bearing(const Point origin, const uint16_t angle, uint32_t size, const Color color);
|
||||
void draw_marker(Painter& painter, const ui::Point itemPoint, const uint16_t itemAngle, const std::string itemTag, const Color color = Color::red(), const Color fontColor = Color::white(), const Color backColor = Color::black());
|
||||
|
||||
int32_t x_pos { }, y_pos { };
|
||||
int32_t prev_x_pos { 0xFFFF }, prev_y_pos { 0xFFFF };
|
||||
float lat_ { };
|
||||
float lon_ { };
|
||||
uint16_t angle_ { };
|
||||
std::string tag_ { };
|
||||
GeoMapMode mode_{};
|
||||
File map_file{};
|
||||
uint16_t map_width{}, map_height{};
|
||||
int32_t map_center_x{}, map_center_y{};
|
||||
float lon_ratio{}, lat_ratio{};
|
||||
double map_bottom{};
|
||||
double map_world_lon{};
|
||||
double map_offset{};
|
||||
|
||||
int markerListLen {0};
|
||||
GeoMarker markerList[NumMarkerListElements];
|
||||
bool markerListUpdated {false};
|
||||
int32_t x_pos{}, y_pos{};
|
||||
int32_t prev_x_pos{0xFFFF}, prev_y_pos{0xFFFF};
|
||||
float lat_{};
|
||||
float lon_{};
|
||||
uint16_t angle_{};
|
||||
std::string tag_{};
|
||||
|
||||
int markerListLen{0};
|
||||
GeoMarker markerList[NumMarkerListElements];
|
||||
bool markerListUpdated{false};
|
||||
};
|
||||
|
||||
class GeoMapView : public View {
|
||||
public:
|
||||
GeoMapView(
|
||||
NavigationView& nav,
|
||||
const std::string& tag,
|
||||
int32_t altitude,
|
||||
GeoPos::alt_unit altitude_unit,
|
||||
float lat,
|
||||
float lon,
|
||||
uint16_t angle,
|
||||
const std::function<void(void)> on_close = nullptr
|
||||
);
|
||||
GeoMapView(NavigationView& nav,
|
||||
int32_t altitude,
|
||||
GeoPos::alt_unit altitude_unit,
|
||||
float lat,
|
||||
float lon,
|
||||
const std::function<void(int32_t, float, float)> on_done
|
||||
);
|
||||
~GeoMapView();
|
||||
|
||||
GeoMapView(const GeoMapView&) = delete;
|
||||
GeoMapView(GeoMapView&&) = delete;
|
||||
GeoMapView& operator=(const GeoMapView&) = delete;
|
||||
GeoMapView& operator=(GeoMapView&&) = delete;
|
||||
|
||||
void focus() override;
|
||||
|
||||
void update_position(float lat, float lon, uint16_t angle, int32_t altitude);
|
||||
|
||||
std::string title() const override { return "Map view"; };
|
||||
public:
|
||||
GeoMapView(
|
||||
NavigationView& nav,
|
||||
const std::string& tag,
|
||||
int32_t altitude,
|
||||
GeoPos::alt_unit altitude_unit,
|
||||
float lat,
|
||||
float lon,
|
||||
uint16_t angle,
|
||||
const std::function<void(void)> on_close = nullptr);
|
||||
GeoMapView(NavigationView& nav,
|
||||
int32_t altitude,
|
||||
GeoPos::alt_unit altitude_unit,
|
||||
float lat,
|
||||
float lon,
|
||||
const std::function<void(int32_t, float, float)> on_done);
|
||||
~GeoMapView();
|
||||
|
||||
void clear_markers();
|
||||
MapMarkerStored store_marker(GeoMarker & marker);
|
||||
GeoMapView(const GeoMapView&) = delete;
|
||||
GeoMapView(GeoMapView&&) = delete;
|
||||
GeoMapView& operator=(const GeoMapView&) = delete;
|
||||
GeoMapView& operator=(GeoMapView&&) = delete;
|
||||
|
||||
void update_tag(const std::string tag);
|
||||
void focus() override;
|
||||
|
||||
void update_position(float lat, float lon, uint16_t angle, int32_t altitude);
|
||||
|
||||
private:
|
||||
NavigationView& nav_;
|
||||
|
||||
void setup();
|
||||
|
||||
const std::function<void(int32_t, float, float)> on_done { };
|
||||
|
||||
const Dim banner_height = 3 * 16;
|
||||
GeoMapMode mode_ { };
|
||||
int32_t altitude_ { };
|
||||
GeoPos::alt_unit altitude_unit_ { };
|
||||
float lat_ { };
|
||||
float lon_ { };
|
||||
uint16_t angle_ { };
|
||||
std::function<void(void)> on_close_ { nullptr };
|
||||
|
||||
bool map_opened { };
|
||||
|
||||
GeoPos geopos {
|
||||
{ 0, 0 },
|
||||
altitude_unit_
|
||||
};
|
||||
|
||||
GeoMap geomap {
|
||||
{ 0, banner_height, 240, 320 - 16 - banner_height }
|
||||
};
|
||||
|
||||
Button button_ok {
|
||||
{ 20 * 8, 8, 8 * 8, 2 * 16 },
|
||||
"OK"
|
||||
};
|
||||
std::string title() const override { return "Map view"; };
|
||||
|
||||
void clear_markers();
|
||||
MapMarkerStored store_marker(GeoMarker& marker);
|
||||
|
||||
void update_tag(const std::string tag);
|
||||
|
||||
private:
|
||||
NavigationView& nav_;
|
||||
|
||||
void setup();
|
||||
|
||||
const std::function<void(int32_t, float, float)> on_done{};
|
||||
|
||||
const Dim banner_height = 3 * 16;
|
||||
GeoMapMode mode_{};
|
||||
int32_t altitude_{};
|
||||
GeoPos::alt_unit altitude_unit_{};
|
||||
float lat_{};
|
||||
float lon_{};
|
||||
uint16_t angle_{};
|
||||
std::function<void(void)> on_close_{nullptr};
|
||||
|
||||
bool map_opened{};
|
||||
|
||||
GeoPos geopos{
|
||||
{0, 0},
|
||||
altitude_unit_};
|
||||
|
||||
GeoMap geomap{
|
||||
{0, banner_height, 240, 320 - 16 - banner_height}};
|
||||
|
||||
Button button_ok{
|
||||
{20 * 8, 8, 8 * 8, 2 * 16},
|
||||
"OK"};
|
||||
};
|
||||
|
||||
} /* namespace ui */
|
||||
|
@@ -28,264 +28,256 @@ namespace ui {
|
||||
/* MenuItemView **********************************************************/
|
||||
|
||||
void MenuItemView::set_item(MenuItem* item_) {
|
||||
item = item_;
|
||||
item = item_;
|
||||
}
|
||||
|
||||
void MenuItemView::highlight() {
|
||||
set_highlighted(true);
|
||||
set_dirty();
|
||||
set_highlighted(true);
|
||||
set_dirty();
|
||||
}
|
||||
|
||||
void MenuItemView::unhighlight() {
|
||||
set_highlighted(false);
|
||||
set_dirty();
|
||||
set_highlighted(false);
|
||||
set_dirty();
|
||||
}
|
||||
|
||||
void MenuItemView::paint(Painter& painter) {
|
||||
Coord offset_x { };
|
||||
|
||||
if (!item) return;
|
||||
|
||||
const auto r = screen_rect();
|
||||
Coord offset_x{};
|
||||
|
||||
const auto paint_style = (highlighted() && (parent()->has_focus() || keep_highlight)) ? style().invert() : style();
|
||||
if (!item) return;
|
||||
|
||||
const auto font_height = paint_style.font.line_height();
|
||||
|
||||
ui::Color final_item_color = (highlighted() && (parent()->has_focus() || keep_highlight)) ? paint_style.foreground : item->color;
|
||||
ui::Color final_bg_color = (highlighted() && (parent()->has_focus() || keep_highlight)) ? item->color : paint_style.background;
|
||||
const auto r = screen_rect();
|
||||
|
||||
if (final_item_color.v == final_bg_color.v) final_item_color = paint_style.foreground;
|
||||
const auto paint_style = (highlighted() && (parent()->has_focus() || keep_highlight)) ? style().invert() : style();
|
||||
|
||||
painter.fill_rectangle(
|
||||
r,
|
||||
final_bg_color
|
||||
);
|
||||
|
||||
if (item->bitmap) {
|
||||
painter.draw_bitmap(
|
||||
{ r.location().x() + 4, r.location().y() + 4 },
|
||||
*item->bitmap,
|
||||
final_item_color,
|
||||
final_bg_color
|
||||
);
|
||||
offset_x = 26;
|
||||
} else
|
||||
offset_x = 0;
|
||||
const auto font_height = paint_style.font.line_height();
|
||||
|
||||
Style text_style {
|
||||
.font = paint_style.font,
|
||||
.background = final_bg_color,
|
||||
.foreground = final_item_color
|
||||
};
|
||||
ui::Color final_item_color = (highlighted() && (parent()->has_focus() || keep_highlight)) ? paint_style.foreground : item->color;
|
||||
ui::Color final_bg_color = (highlighted() && (parent()->has_focus() || keep_highlight)) ? item->color : paint_style.background;
|
||||
|
||||
painter.draw_string(
|
||||
{ r.location().x() + offset_x, r.location().y() + (r.size().height() - font_height) / 2 },
|
||||
text_style,
|
||||
item->text
|
||||
);
|
||||
if (final_item_color.v == final_bg_color.v) final_item_color = paint_style.foreground;
|
||||
|
||||
painter.fill_rectangle(
|
||||
r,
|
||||
final_bg_color);
|
||||
|
||||
if (item->bitmap) {
|
||||
painter.draw_bitmap(
|
||||
{r.location().x() + 4, r.location().y() + 4},
|
||||
*item->bitmap,
|
||||
final_item_color,
|
||||
final_bg_color);
|
||||
offset_x = 26;
|
||||
} else
|
||||
offset_x = 0;
|
||||
|
||||
Style text_style{
|
||||
.font = paint_style.font,
|
||||
.background = final_bg_color,
|
||||
.foreground = final_item_color};
|
||||
|
||||
painter.draw_string(
|
||||
{r.location().x() + offset_x, r.location().y() + (r.size().height() - font_height) / 2},
|
||||
text_style,
|
||||
item->text);
|
||||
}
|
||||
|
||||
/* MenuView **************************************************************/
|
||||
|
||||
MenuView::MenuView(
|
||||
Rect new_parent_rect,
|
||||
bool keep_highlight
|
||||
) : keep_highlight { keep_highlight }
|
||||
{
|
||||
set_parent_rect(new_parent_rect);
|
||||
|
||||
set_focusable(true);
|
||||
|
||||
signal_token_tick_second = rtc_time::signal_tick_second += [this]() {
|
||||
this->on_tick_second();
|
||||
};
|
||||
|
||||
add_child(&arrow_more);
|
||||
arrow_more.set_focusable(false);
|
||||
arrow_more.set_foreground(Color::black());
|
||||
Rect new_parent_rect,
|
||||
bool keep_highlight)
|
||||
: keep_highlight{keep_highlight} {
|
||||
set_parent_rect(new_parent_rect);
|
||||
|
||||
set_focusable(true);
|
||||
|
||||
signal_token_tick_second = rtc_time::signal_tick_second += [this]() {
|
||||
this->on_tick_second();
|
||||
};
|
||||
|
||||
add_child(&arrow_more);
|
||||
arrow_more.set_focusable(false);
|
||||
arrow_more.set_foreground(Color::black());
|
||||
}
|
||||
|
||||
MenuView::~MenuView() {
|
||||
rtc_time::signal_tick_second -= signal_token_tick_second;
|
||||
|
||||
for (auto item : menu_item_views) {
|
||||
delete item;
|
||||
}
|
||||
rtc_time::signal_tick_second -= signal_token_tick_second;
|
||||
|
||||
for (auto item : menu_item_views) {
|
||||
delete item;
|
||||
}
|
||||
}
|
||||
|
||||
void MenuView::set_parent_rect(const Rect new_parent_rect) {
|
||||
View::set_parent_rect(new_parent_rect);
|
||||
|
||||
displayed_max = (parent_rect().size().height() / item_height);
|
||||
arrow_more.set_parent_rect( { 228, (Coord)(displayed_max * item_height), 8, 8 } );
|
||||
|
||||
// TODO: Clean this up :(
|
||||
if (menu_item_views.size()) {
|
||||
|
||||
for (auto item : menu_item_views) {
|
||||
remove_child(item);
|
||||
delete item;
|
||||
}
|
||||
|
||||
menu_item_views.clear();
|
||||
}
|
||||
|
||||
for (size_t c = 0; c < displayed_max; c++) {
|
||||
auto item = new MenuItemView { keep_highlight };
|
||||
menu_item_views.push_back(item);
|
||||
add_child(item);
|
||||
|
||||
Coord y_pos = c * item_height;
|
||||
item->set_parent_rect({
|
||||
{ 0, y_pos },
|
||||
{ size().width(), (Coord)item_height }
|
||||
});
|
||||
}
|
||||
|
||||
update_items();
|
||||
View::set_parent_rect(new_parent_rect);
|
||||
|
||||
displayed_max = (parent_rect().size().height() / item_height);
|
||||
arrow_more.set_parent_rect({228, (Coord)(displayed_max * item_height), 8, 8});
|
||||
|
||||
// TODO: Clean this up :(
|
||||
if (menu_item_views.size()) {
|
||||
for (auto item : menu_item_views) {
|
||||
remove_child(item);
|
||||
delete item;
|
||||
}
|
||||
|
||||
menu_item_views.clear();
|
||||
}
|
||||
|
||||
for (size_t c = 0; c < displayed_max; c++) {
|
||||
auto item = new MenuItemView{keep_highlight};
|
||||
menu_item_views.push_back(item);
|
||||
add_child(item);
|
||||
|
||||
Coord y_pos = c * item_height;
|
||||
item->set_parent_rect({{0, y_pos},
|
||||
{size().width(), (Coord)item_height}});
|
||||
}
|
||||
|
||||
update_items();
|
||||
}
|
||||
|
||||
void MenuView::on_tick_second() {
|
||||
if (more && blink)
|
||||
arrow_more.set_foreground(Color::white());
|
||||
else
|
||||
arrow_more.set_foreground(Color::black());
|
||||
|
||||
blink = !blink;
|
||||
|
||||
arrow_more.set_dirty();
|
||||
if (more && blink)
|
||||
arrow_more.set_foreground(Color::white());
|
||||
else
|
||||
arrow_more.set_foreground(Color::black());
|
||||
|
||||
blink = !blink;
|
||||
|
||||
arrow_more.set_dirty();
|
||||
}
|
||||
|
||||
void MenuView::clear() {
|
||||
for (auto item : menu_item_views) {
|
||||
item->set_item(nullptr);
|
||||
}
|
||||
|
||||
menu_items.clear();
|
||||
highlighted_item = 0;
|
||||
offset = 0;
|
||||
for (auto item : menu_item_views) {
|
||||
item->set_item(nullptr);
|
||||
}
|
||||
|
||||
menu_items.clear();
|
||||
highlighted_item = 0;
|
||||
offset = 0;
|
||||
}
|
||||
|
||||
size_t MenuView::item_count() const {
|
||||
return menu_items.size();
|
||||
return menu_items.size();
|
||||
}
|
||||
|
||||
void MenuView::add_item(MenuItem new_item) {
|
||||
menu_items.push_back(new_item);
|
||||
|
||||
update_items();
|
||||
menu_items.push_back(new_item);
|
||||
|
||||
update_items();
|
||||
}
|
||||
|
||||
void MenuView::add_items(std::initializer_list<MenuItem> new_items) {
|
||||
for (auto item : new_items) {
|
||||
add_item(item);
|
||||
}
|
||||
for (auto item : new_items) {
|
||||
add_item(item);
|
||||
}
|
||||
}
|
||||
|
||||
void MenuView::update_items() {
|
||||
size_t i = 0;
|
||||
|
||||
if (menu_items.size() > displayed_max + offset) {
|
||||
more = true;
|
||||
blink = true;
|
||||
} else
|
||||
more = false;
|
||||
|
||||
for (auto item : menu_item_views) {
|
||||
if (i >= menu_items.size()) break;
|
||||
|
||||
// Assign item data to MenuItemViews according to offset
|
||||
item->set_item(&menu_items[i + offset]);
|
||||
item->set_dirty();
|
||||
|
||||
if (highlighted_item == (i + offset)) {
|
||||
item->highlight();
|
||||
} else
|
||||
item->unhighlight();
|
||||
|
||||
i++;
|
||||
}
|
||||
size_t i = 0;
|
||||
|
||||
if (menu_items.size() > displayed_max + offset) {
|
||||
more = true;
|
||||
blink = true;
|
||||
} else
|
||||
more = false;
|
||||
|
||||
for (auto item : menu_item_views) {
|
||||
if (i >= menu_items.size()) break;
|
||||
|
||||
// Assign item data to MenuItemViews according to offset
|
||||
item->set_item(&menu_items[i + offset]);
|
||||
item->set_dirty();
|
||||
|
||||
if (highlighted_item == (i + offset)) {
|
||||
item->highlight();
|
||||
} else
|
||||
item->unhighlight();
|
||||
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
MenuItemView* MenuView::item_view(size_t index) const {
|
||||
return menu_item_views[index];
|
||||
return menu_item_views[index];
|
||||
}
|
||||
|
||||
bool MenuView::set_highlighted(int32_t new_value) {
|
||||
int32_t item_count = (int32_t)menu_items.size();
|
||||
|
||||
if (new_value < 0 || item_count == 0)
|
||||
return false;
|
||||
|
||||
if (new_value >= item_count)
|
||||
new_value = item_count - 1;
|
||||
|
||||
if (((uint32_t)new_value > offset) && ((new_value - offset) >= displayed_max)) {
|
||||
// Shift MenuView up
|
||||
highlighted_item = new_value;
|
||||
offset = new_value - displayed_max + 1;
|
||||
update_items();
|
||||
} else if ((uint32_t)new_value < offset) {
|
||||
// Shift MenuView down
|
||||
highlighted_item = new_value;
|
||||
offset = new_value;
|
||||
update_items();
|
||||
} else {
|
||||
// Just update highlight
|
||||
item_view(highlighted_item - offset)->unhighlight();
|
||||
highlighted_item = new_value;
|
||||
item_view(highlighted_item - offset)->highlight();
|
||||
}
|
||||
|
||||
if (on_highlight)
|
||||
on_highlight();
|
||||
|
||||
return true;
|
||||
int32_t item_count = (int32_t)menu_items.size();
|
||||
|
||||
if (new_value < 0 || item_count == 0)
|
||||
return false;
|
||||
|
||||
if (new_value >= item_count)
|
||||
new_value = item_count - 1;
|
||||
|
||||
if (((uint32_t)new_value > offset) && ((new_value - offset) >= displayed_max)) {
|
||||
// Shift MenuView up
|
||||
highlighted_item = new_value;
|
||||
offset = new_value - displayed_max + 1;
|
||||
update_items();
|
||||
} else if ((uint32_t)new_value < offset) {
|
||||
// Shift MenuView down
|
||||
highlighted_item = new_value;
|
||||
offset = new_value;
|
||||
update_items();
|
||||
} else {
|
||||
// Just update highlight
|
||||
item_view(highlighted_item - offset)->unhighlight();
|
||||
highlighted_item = new_value;
|
||||
item_view(highlighted_item - offset)->highlight();
|
||||
}
|
||||
|
||||
if (on_highlight)
|
||||
on_highlight();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
uint32_t MenuView::highlighted_index() const {
|
||||
return highlighted_item;
|
||||
return highlighted_item;
|
||||
}
|
||||
|
||||
void MenuView::on_focus() {
|
||||
item_view(highlighted_item - offset)->highlight();
|
||||
item_view(highlighted_item - offset)->highlight();
|
||||
}
|
||||
|
||||
void MenuView::on_blur() {
|
||||
if (!keep_highlight)
|
||||
item_view(highlighted_item - offset)->unhighlight();
|
||||
if (!keep_highlight)
|
||||
item_view(highlighted_item - offset)->unhighlight();
|
||||
}
|
||||
|
||||
bool MenuView::on_key(const KeyEvent key) {
|
||||
switch(key) {
|
||||
case KeyEvent::Up:
|
||||
return set_highlighted(highlighted_item - 1);
|
||||
switch (key) {
|
||||
case KeyEvent::Up:
|
||||
return set_highlighted(highlighted_item - 1);
|
||||
|
||||
case KeyEvent::Down:
|
||||
return set_highlighted(highlighted_item + 1);
|
||||
case KeyEvent::Down:
|
||||
return set_highlighted(highlighted_item + 1);
|
||||
|
||||
case KeyEvent::Select:
|
||||
case KeyEvent::Right:
|
||||
if( menu_items[highlighted_item].on_select ) {
|
||||
menu_items[highlighted_item].on_select(key);
|
||||
}
|
||||
return true;
|
||||
case KeyEvent::Select:
|
||||
case KeyEvent::Right:
|
||||
if (menu_items[highlighted_item].on_select) {
|
||||
menu_items[highlighted_item].on_select(key);
|
||||
}
|
||||
return true;
|
||||
|
||||
case KeyEvent::Left:
|
||||
if( on_left ) {
|
||||
on_left();
|
||||
}
|
||||
return true;
|
||||
case KeyEvent::Left:
|
||||
if (on_left) {
|
||||
on_left();
|
||||
}
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool MenuView::on_encoder(const EncoderEvent event) {
|
||||
set_highlighted(highlighted_item + event);
|
||||
return true;
|
||||
set_highlighted(highlighted_item + event);
|
||||
return true;
|
||||
}
|
||||
|
||||
/* TODO: This could be handled by default behavior, if the UI stack were to
|
||||
@@ -294,15 +286,15 @@ bool MenuView::on_encoder(const EncoderEvent event) {
|
||||
*/
|
||||
/*
|
||||
bool MenuView::on_touch(const TouchEvent event) {
|
||||
size_t i = 0;
|
||||
for(const auto child : children_) {
|
||||
if( child->screen_rect().contains(event.point) ) {
|
||||
return set_highlighted(i);
|
||||
}
|
||||
i++;
|
||||
}
|
||||
size_t i = 0;
|
||||
for(const auto child : children_) {
|
||||
if( child->screen_rect().contains(event.point) ) {
|
||||
return set_highlighted(i);
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
return false;
|
||||
return false;
|
||||
}
|
||||
*/
|
||||
} /* namespace ui */
|
||||
|
@@ -36,91 +36,89 @@
|
||||
namespace ui {
|
||||
|
||||
struct MenuItem {
|
||||
std::string text;
|
||||
ui::Color color;
|
||||
const Bitmap* bitmap;
|
||||
std::function<void(KeyEvent)> on_select;
|
||||
std::string text;
|
||||
ui::Color color;
|
||||
const Bitmap* bitmap;
|
||||
std::function<void(KeyEvent)> on_select;
|
||||
|
||||
// TODO: Prevent default-constructed MenuItems.
|
||||
// I managed to construct a menu with three extra, unspecified menu items
|
||||
// in the array that were default constructed...
|
||||
// TODO: Prevent default-constructed MenuItems.
|
||||
// I managed to construct a menu with three extra, unspecified menu items
|
||||
// in the array that were default constructed...
|
||||
};
|
||||
|
||||
class MenuItemView : public Widget {
|
||||
public:
|
||||
MenuItemView(
|
||||
bool keep_highlight
|
||||
) : keep_highlight { keep_highlight }
|
||||
{
|
||||
}
|
||||
|
||||
MenuItemView(const MenuItemView&) = delete;
|
||||
MenuItemView(MenuItemView&&) = delete;
|
||||
MenuItemView& operator=(const MenuItemView&) = delete;
|
||||
MenuItemView& operator=(MenuItemView&&) = delete;
|
||||
public:
|
||||
MenuItemView(
|
||||
bool keep_highlight)
|
||||
: keep_highlight{keep_highlight} {
|
||||
}
|
||||
|
||||
void paint(Painter& painter) override;
|
||||
|
||||
void set_item(MenuItem* item_);
|
||||
MenuItemView(const MenuItemView&) = delete;
|
||||
MenuItemView(MenuItemView&&) = delete;
|
||||
MenuItemView& operator=(const MenuItemView&) = delete;
|
||||
MenuItemView& operator=(MenuItemView&&) = delete;
|
||||
|
||||
void highlight();
|
||||
void unhighlight();
|
||||
void paint(Painter& painter) override;
|
||||
|
||||
private:
|
||||
MenuItem* item { nullptr };
|
||||
bool keep_highlight = false;
|
||||
void set_item(MenuItem* item_);
|
||||
|
||||
void highlight();
|
||||
void unhighlight();
|
||||
|
||||
private:
|
||||
MenuItem* item{nullptr};
|
||||
bool keep_highlight = false;
|
||||
};
|
||||
|
||||
class MenuView : public View {
|
||||
public:
|
||||
std::function<void(void)> on_left { };
|
||||
std::function<void(void)> on_highlight { nullptr };
|
||||
public:
|
||||
std::function<void(void)> on_left{};
|
||||
std::function<void(void)> on_highlight{nullptr};
|
||||
|
||||
MenuView(Rect new_parent_rect = { 0, 0, 240, 304 }, bool keep_highlight = false);
|
||||
|
||||
~MenuView();
|
||||
MenuView(Rect new_parent_rect = {0, 0, 240, 304}, bool keep_highlight = false);
|
||||
|
||||
void add_item(MenuItem new_item);
|
||||
void add_items(std::initializer_list<MenuItem> new_items);
|
||||
void clear();
|
||||
size_t item_count() const;
|
||||
|
||||
MenuItemView* item_view(size_t index) const;
|
||||
~MenuView();
|
||||
|
||||
bool set_highlighted(int32_t new_value);
|
||||
uint32_t highlighted_index() const;
|
||||
void add_item(MenuItem new_item);
|
||||
void add_items(std::initializer_list<MenuItem> new_items);
|
||||
void clear();
|
||||
size_t item_count() const;
|
||||
|
||||
void set_parent_rect(const Rect new_parent_rect) override;
|
||||
void on_focus() override;
|
||||
void on_blur() override;
|
||||
bool on_key(const KeyEvent event) override;
|
||||
bool on_encoder(const EncoderEvent event) override;
|
||||
|
||||
private:
|
||||
void update_items();
|
||||
void on_tick_second();
|
||||
|
||||
bool keep_highlight { false };
|
||||
|
||||
SignalToken signal_token_tick_second { };
|
||||
std::vector<MenuItem> menu_items { };
|
||||
std::vector<MenuItemView*> menu_item_views { };
|
||||
|
||||
Image arrow_more {
|
||||
{ 228, 320 - 8, 8, 8 },
|
||||
&bitmap_more,
|
||||
Color::white(),
|
||||
Color::black()
|
||||
};
|
||||
MenuItemView* item_view(size_t index) const;
|
||||
|
||||
const size_t item_height = 24;
|
||||
bool blink = false;
|
||||
bool more = false;
|
||||
size_t displayed_max { 0 };
|
||||
size_t highlighted_item { 0 };
|
||||
size_t offset { 0 };
|
||||
bool set_highlighted(int32_t new_value);
|
||||
uint32_t highlighted_index() const;
|
||||
|
||||
void set_parent_rect(const Rect new_parent_rect) override;
|
||||
void on_focus() override;
|
||||
void on_blur() override;
|
||||
bool on_key(const KeyEvent event) override;
|
||||
bool on_encoder(const EncoderEvent event) override;
|
||||
|
||||
private:
|
||||
void update_items();
|
||||
void on_tick_second();
|
||||
|
||||
bool keep_highlight{false};
|
||||
|
||||
SignalToken signal_token_tick_second{};
|
||||
std::vector<MenuItem> menu_items{};
|
||||
std::vector<MenuItemView*> menu_item_views{};
|
||||
|
||||
Image arrow_more{
|
||||
{228, 320 - 8, 8, 8},
|
||||
&bitmap_more,
|
||||
Color::white(),
|
||||
Color::black()};
|
||||
|
||||
const size_t item_height = 24;
|
||||
bool blink = false;
|
||||
bool more = false;
|
||||
size_t displayed_max{0};
|
||||
size_t highlighted_item{0};
|
||||
size_t offset{0};
|
||||
};
|
||||
|
||||
} /* namespace ui */
|
||||
|
||||
#endif/*__UI_MENU_H__*/
|
||||
#endif /*__UI_MENU_H__*/
|
||||
|
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
|
||||
* Copyright (C) 2017 Furrtek
|
||||
*
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -33,116 +33,97 @@ using namespace portapack;
|
||||
#include "string_format.hpp"
|
||||
#include "complex.hpp"
|
||||
|
||||
|
||||
namespace ui {
|
||||
|
||||
QRCodeImage::QRCodeImage(
|
||||
Rect parent_rect
|
||||
) : Widget { parent_rect }
|
||||
{
|
||||
|
||||
Rect parent_rect)
|
||||
: Widget{parent_rect} {
|
||||
}
|
||||
|
||||
QRCodeImage::~QRCodeImage( )
|
||||
{
|
||||
|
||||
QRCodeImage::~QRCodeImage() {
|
||||
}
|
||||
|
||||
QRCodeImage::QRCodeImage(const QRCodeImage&Image) : Widget { }
|
||||
{
|
||||
QRCodeImage::QRCodeImage(const QRCodeImage& Image)
|
||||
: Widget{} {
|
||||
(void)Image;
|
||||
}
|
||||
|
||||
QRCodeImage & QRCodeImage::operator=(const QRCodeImage&Image)
|
||||
{
|
||||
QRCodeImage& QRCodeImage::operator=(const QRCodeImage& Image) {
|
||||
(void)Image;
|
||||
return *this;
|
||||
}
|
||||
|
||||
void QRCodeImage::paint(Painter& painter) {
|
||||
(void)painter;
|
||||
|
||||
(void)painter ;
|
||||
// The structure to manage the QR code
|
||||
QRCode qrcode;
|
||||
|
||||
// The structure to manage the QR code
|
||||
QRCode qrcode;
|
||||
// Either small or large QR code can be shown..
|
||||
|
||||
if (portapack::persistent_memory::show_bigger_qr_code()) { // show large QR code
|
||||
int qr_version = 2;
|
||||
|
||||
//Either small or large QR code can be shown..
|
||||
// Allocate a chunk of memory to store the QR code
|
||||
uint8_t qrcodeBytes[qrcode_getBufferSize(qr_version)];
|
||||
|
||||
if(portapack::persistent_memory::show_bigger_qr_code()) { // show large QR code
|
||||
int qr_version = 2;
|
||||
qrcode_initText(&qrcode, qrcodeBytes, qr_version, ECC_HIGH, qr_text_);
|
||||
|
||||
// Allocate a chunk of memory to store the QR code
|
||||
uint8_t qrcodeBytes[qrcode_getBufferSize(qr_version)];
|
||||
display.fill_rectangle(Rect(10, 30, 220, 220), Color::white());
|
||||
|
||||
qrcode_initText(&qrcode, qrcodeBytes, qr_version, ECC_HIGH, qr_text_);
|
||||
for (uint8_t y = 0; y < qrcode.size; y++) {
|
||||
for (uint8_t x = 0; x < qrcode.size; x++) {
|
||||
if (qrcode_getModule(&qrcode, x, y)) {
|
||||
display.fill_rectangle(Rect(20 + (x * 8), 40 + (y * 8), 8, 8), Color::black());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
display.fill_rectangle(Rect(10, 30, 220, 220), Color::white());
|
||||
else { // show small QR code
|
||||
int qr_version = 10;
|
||||
|
||||
for (uint8_t y = 0; y < qrcode.size; y++) {
|
||||
for (uint8_t x = 0; x < qrcode.size; x++) {
|
||||
if (qrcode_getModule(&qrcode, x, y)) {
|
||||
display.fill_rectangle(Rect(20+(x*8), 40+(y*8), 8, 8), Color::black());
|
||||
// Allocate a chunk of memory to store the QR code
|
||||
uint8_t qrcodeBytes[qrcode_getBufferSize(qr_version)];
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
qrcode_initText(&qrcode, qrcodeBytes, qr_version, ECC_HIGH, qr_text_);
|
||||
|
||||
}
|
||||
display.fill_rectangle(Rect(92, 97, 63, 63), Color::white());
|
||||
|
||||
else { // show small QR code
|
||||
int qr_version = 10;
|
||||
|
||||
// Allocate a chunk of memory to store the QR code
|
||||
uint8_t qrcodeBytes[qrcode_getBufferSize(qr_version)];
|
||||
|
||||
qrcode_initText(&qrcode, qrcodeBytes, qr_version, ECC_HIGH, qr_text_);
|
||||
|
||||
|
||||
display.fill_rectangle(Rect(92, 97, 63, 63), Color::white());
|
||||
|
||||
for (uint8_t y = 0; y < qrcode.size; y++) {
|
||||
for (uint8_t x = 0; x < qrcode.size; x++) {
|
||||
if (qrcode_getModule(&qrcode, x, y)) {
|
||||
display.draw_pixel(Point(95+x,100+y), Color::black());
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
for (uint8_t y = 0; y < qrcode.size; y++) {
|
||||
for (uint8_t x = 0; x < qrcode.size; x++) {
|
||||
if (qrcode_getModule(&qrcode, x, y)) {
|
||||
display.draw_pixel(Point(95 + x, 100 + y), Color::black());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void QRCodeView::focus() {
|
||||
button_close.focus();
|
||||
button_close.focus();
|
||||
}
|
||||
|
||||
QRCodeView::~QRCodeView() {
|
||||
if (on_close_)
|
||||
on_close_();
|
||||
if (on_close_)
|
||||
on_close_();
|
||||
}
|
||||
|
||||
QRCodeView::QRCodeView(
|
||||
NavigationView& nav,
|
||||
const char * qr_text,
|
||||
const std::function<void(void)> on_close
|
||||
) : nav_ (nav),
|
||||
on_close_(on_close)
|
||||
{
|
||||
NavigationView& nav,
|
||||
const char* qr_text,
|
||||
const std::function<void(void)> on_close)
|
||||
: nav_(nav),
|
||||
on_close_(on_close) {
|
||||
add_children({&qr_code,
|
||||
&button_close});
|
||||
// text_qr.set(qr_text);
|
||||
qr_code.set_text(qr_text);
|
||||
|
||||
|
||||
add_children({
|
||||
&qr_code,
|
||||
&button_close});
|
||||
//text_qr.set(qr_text);
|
||||
qr_code.set_text(qr_text);
|
||||
|
||||
button_close.on_select = [&nav](Button&){
|
||||
nav.pop();
|
||||
};
|
||||
button_close.on_select = [&nav](Button&) {
|
||||
nav.pop();
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
||||
} /* namespace ui */
|
||||
|
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
|
||||
* Copyright (C) 2017 Furrtek
|
||||
*
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -33,59 +33,53 @@
|
||||
namespace ui {
|
||||
|
||||
class QRCodeImage : public Widget {
|
||||
public:
|
||||
public:
|
||||
QRCodeImage(Rect parent_rect);
|
||||
void set_text(const char* qr_text) {
|
||||
qr_text_ = qr_text;
|
||||
}
|
||||
void paint(Painter& painter) override;
|
||||
// for -weffc++ to be killed
|
||||
~QRCodeImage(); // destructor
|
||||
QRCodeImage(const QRCodeImage& Image);
|
||||
QRCodeImage& operator=(const QRCodeImage& Image); // assignment
|
||||
|
||||
QRCodeImage(Rect parent_rect);
|
||||
void set_text(const char * qr_text) {
|
||||
qr_text_ = qr_text;
|
||||
}
|
||||
void paint(Painter& painter) override;
|
||||
// for -weffc++ to be killed
|
||||
~QRCodeImage(); // destructor
|
||||
QRCodeImage(const QRCodeImage&Image);
|
||||
QRCodeImage & operator=(const QRCodeImage &Image); // assignment
|
||||
|
||||
private:
|
||||
const char * qr_text_ = NULL ;
|
||||
private:
|
||||
const char* qr_text_ = NULL;
|
||||
};
|
||||
|
||||
class QRCodeView : public View {
|
||||
public:
|
||||
QRCodeView(
|
||||
NavigationView& nav,
|
||||
const char * qr_text,
|
||||
const std::function<void(void)> on_close = nullptr
|
||||
);
|
||||
~QRCodeView();
|
||||
|
||||
QRCodeView(const QRCodeView&) = delete;
|
||||
QRCodeView(QRCodeView&&) = delete;
|
||||
QRCodeView& operator=(const QRCodeView&) = delete;
|
||||
QRCodeView& operator=(QRCodeView&&) = delete;
|
||||
|
||||
std::string title() const override { return "QR code"; };
|
||||
void focus() override;
|
||||
public:
|
||||
QRCodeView(
|
||||
NavigationView& nav,
|
||||
const char* qr_text,
|
||||
const std::function<void(void)> on_close = nullptr);
|
||||
~QRCodeView();
|
||||
|
||||
private:
|
||||
NavigationView& nav_;
|
||||
QRCodeView(const QRCodeView&) = delete;
|
||||
QRCodeView(QRCodeView&&) = delete;
|
||||
QRCodeView& operator=(const QRCodeView&) = delete;
|
||||
QRCodeView& operator=(QRCodeView&&) = delete;
|
||||
|
||||
std::string title() const override { return "QR code"; };
|
||||
void focus() override;
|
||||
|
||||
std::function<void(void)> on_close_ { nullptr };
|
||||
private:
|
||||
NavigationView& nav_;
|
||||
|
||||
std::function<void(void)> on_close_{nullptr};
|
||||
|
||||
QRCodeImage qr_code {
|
||||
{ 50, 100, 100, 100 }
|
||||
};
|
||||
QRCodeImage qr_code{
|
||||
{50, 100, 100, 100}};
|
||||
|
||||
//Text text_qr {
|
||||
// { 0 * 8, 10 * 16, 32 * 8, 1 * 8 },
|
||||
// "-"
|
||||
//};
|
||||
// Text text_qr {
|
||||
// { 0 * 8, 10 * 16, 32 * 8, 1 * 8 },
|
||||
// "-"
|
||||
// };
|
||||
|
||||
Button button_close {
|
||||
{ 9 * 8, 31 * 8, 12 * 8, 3 * 16 },
|
||||
"Back"
|
||||
};
|
||||
Button button_close{
|
||||
{9 * 8, 31 * 8, 12 * 8, 3 * 16},
|
||||
"Back"};
|
||||
};
|
||||
|
||||
} /* namespace ui */
|
||||
|
@@ -35,362 +35,350 @@ namespace ui {
|
||||
/* FrequencyField ********************************************************/
|
||||
|
||||
FrequencyField::FrequencyField(
|
||||
const Point parent_pos
|
||||
) : Widget { { parent_pos, { 8 * 10, 16 } } },
|
||||
length_ { 11 },
|
||||
range(rf::tuning_range)
|
||||
{
|
||||
set_focusable(true);
|
||||
const Point parent_pos)
|
||||
: Widget{{parent_pos, {8 * 10, 16}}},
|
||||
length_{11},
|
||||
range(rf::tuning_range) {
|
||||
set_focusable(true);
|
||||
}
|
||||
|
||||
rf::Frequency FrequencyField::value() const {
|
||||
return value_;
|
||||
return value_;
|
||||
}
|
||||
|
||||
void FrequencyField::set_value(rf::Frequency new_value) {
|
||||
new_value = clamp_value(new_value);
|
||||
new_value = clamp_value(new_value);
|
||||
|
||||
if( new_value != value_ ) {
|
||||
value_ = new_value;
|
||||
if( on_change ) {
|
||||
on_change(value_);
|
||||
}
|
||||
set_dirty();
|
||||
}
|
||||
if (new_value != value_) {
|
||||
value_ = new_value;
|
||||
if (on_change) {
|
||||
on_change(value_);
|
||||
}
|
||||
set_dirty();
|
||||
}
|
||||
}
|
||||
|
||||
void FrequencyField::set_step(rf::Frequency new_value) {
|
||||
step = new_value;
|
||||
// TODO: Quantize current frequency to a step of the new size?
|
||||
step = new_value;
|
||||
// TODO: Quantize current frequency to a step of the new size?
|
||||
}
|
||||
|
||||
void FrequencyField::paint(Painter& painter) {
|
||||
const std::string str_value = to_string_short_freq(value_);
|
||||
const std::string str_value = to_string_short_freq(value_);
|
||||
|
||||
const auto paint_style = has_focus() ? style().invert() : style();
|
||||
const auto paint_style = has_focus() ? style().invert() : style();
|
||||
|
||||
painter.draw_string(
|
||||
screen_pos(),
|
||||
paint_style,
|
||||
str_value
|
||||
);
|
||||
painter.draw_string(
|
||||
screen_pos(),
|
||||
paint_style,
|
||||
str_value);
|
||||
}
|
||||
|
||||
bool FrequencyField::on_key(const ui::KeyEvent event) {
|
||||
if( event == ui::KeyEvent::Select ) {
|
||||
if( on_edit ) {
|
||||
on_edit();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
if (event == ui::KeyEvent::Select) {
|
||||
if (on_edit) {
|
||||
on_edit();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool FrequencyField::on_encoder(const EncoderEvent delta) {
|
||||
if (step == 0) { // 'Auto' mode.'
|
||||
auto ms = RTT2MS(halGetCounterValue());
|
||||
auto delta_ms = last_ms_ <= ms ? ms - last_ms_ : ms;
|
||||
last_ms_ = ms;
|
||||
if (step == 0) { // 'Auto' mode.'
|
||||
auto ms = RTT2MS(halGetCounterValue());
|
||||
auto delta_ms = last_ms_ <= ms ? ms - last_ms_ : ms;
|
||||
last_ms_ = ms;
|
||||
|
||||
// The goal is to map 'scale' to a range of about 10 to 10M.
|
||||
// The faster the encoder is rotated, the larger the step.
|
||||
// Linear doesn't feel right. Hyperbolic felt better.
|
||||
// To get these magic numbers, I graphed the function until the
|
||||
// curve shape seemed about right then tested on device.
|
||||
delta_ms = std::min(145ull, delta_ms) + 5; // Prevent DIV/0
|
||||
int64_t scale = 200'000'000 / (0.001'55 * pow(delta_ms, 5.45)) + 8;
|
||||
set_value(value() + (delta * scale));
|
||||
} else {
|
||||
set_value(value() + (delta * step));
|
||||
}
|
||||
return true;
|
||||
// The goal is to map 'scale' to a range of about 10 to 10M.
|
||||
// The faster the encoder is rotated, the larger the step.
|
||||
// Linear doesn't feel right. Hyperbolic felt better.
|
||||
// To get these magic numbers, I graphed the function until the
|
||||
// curve shape seemed about right then tested on device.
|
||||
delta_ms = std::min(145ull, delta_ms) + 5; // Prevent DIV/0
|
||||
int64_t scale = 200'000'000 / (0.001'55 * pow(delta_ms, 5.45)) + 8;
|
||||
set_value(value() + (delta * scale));
|
||||
} else {
|
||||
set_value(value() + (delta * step));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FrequencyField::on_touch(const TouchEvent event) {
|
||||
if( event.type == TouchEvent::Type::Start ) {
|
||||
focus();
|
||||
}
|
||||
return true;
|
||||
if (event.type == TouchEvent::Type::Start) {
|
||||
focus();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void FrequencyField::on_focus() {
|
||||
if( on_show_options ) {
|
||||
on_show_options();
|
||||
}
|
||||
if (on_show_options) {
|
||||
on_show_options();
|
||||
}
|
||||
}
|
||||
|
||||
rf::Frequency FrequencyField::clamp_value(rf::Frequency value) {
|
||||
return range.clip(value);
|
||||
return range.clip(value);
|
||||
}
|
||||
|
||||
/* FrequencyKeypadView ***************************************************/
|
||||
|
||||
bool FrequencyKeypadView::on_encoder(const EncoderEvent delta) {
|
||||
focused_button += delta;
|
||||
if (focused_button < 0) {
|
||||
focused_button = buttons.size() - 1;
|
||||
}
|
||||
else if (focused_button >= (int16_t)buttons.size()) {
|
||||
focused_button = 0;
|
||||
}
|
||||
buttons[focused_button].focus();
|
||||
return true;
|
||||
focused_button += delta;
|
||||
if (focused_button < 0) {
|
||||
focused_button = buttons.size() - 1;
|
||||
} else if (focused_button >= (int16_t)buttons.size()) {
|
||||
focused_button = 0;
|
||||
}
|
||||
buttons[focused_button].focus();
|
||||
return true;
|
||||
}
|
||||
|
||||
FrequencyKeypadView::FrequencyKeypadView(
|
||||
NavigationView& nav,
|
||||
const rf::Frequency value
|
||||
) {
|
||||
add_child(&text_value);
|
||||
NavigationView& nav,
|
||||
const rf::Frequency value) {
|
||||
add_child(&text_value);
|
||||
|
||||
const auto button_fn = [this](Button& button) {
|
||||
this->on_button(button);
|
||||
};
|
||||
const auto button_fn = [this](Button& button) {
|
||||
this->on_button(button);
|
||||
};
|
||||
|
||||
const char* const key_caps = "123456789<0.";
|
||||
const char* const key_caps = "123456789<0.";
|
||||
|
||||
int n = 0;
|
||||
for(auto& button : buttons) {
|
||||
add_child(&button);
|
||||
const std::string label {
|
||||
key_caps[n]
|
||||
};
|
||||
button.id = n;
|
||||
button.on_highlight = [this](Button& button) {
|
||||
focused_button = button.id;
|
||||
};
|
||||
button.on_select = button_fn;
|
||||
button.set_parent_rect({
|
||||
(n % 3) * button_w,
|
||||
(n / 3) * button_h + 24,
|
||||
button_w, button_h
|
||||
});
|
||||
button.set_text(label);
|
||||
n++;
|
||||
}
|
||||
|
||||
add_children({
|
||||
&button_save,
|
||||
&button_load,
|
||||
&button_close
|
||||
});
|
||||
|
||||
button_save.on_select = [this, &nav](Button&) {
|
||||
nav.push<FrequencySaveView>(this->value());
|
||||
};
|
||||
button_load.on_select = [this, &nav](Button&) {
|
||||
auto load_view = nav.push<FrequencyLoadView>();
|
||||
load_view->on_frequency_loaded = [this](rf::Frequency value) {
|
||||
set_value(value);
|
||||
};
|
||||
};
|
||||
int n = 0;
|
||||
for (auto& button : buttons) {
|
||||
add_child(&button);
|
||||
const std::string label{
|
||||
key_caps[n]};
|
||||
button.id = n;
|
||||
button.on_highlight = [this](Button& button) {
|
||||
focused_button = button.id;
|
||||
};
|
||||
button.on_select = button_fn;
|
||||
button.set_parent_rect({(n % 3) * button_w,
|
||||
(n / 3) * button_h + 24,
|
||||
button_w, button_h});
|
||||
button.set_text(label);
|
||||
n++;
|
||||
}
|
||||
|
||||
button_close.on_select = [this, &nav](Button&) {
|
||||
if( on_changed ) {
|
||||
on_changed(this->value());
|
||||
}
|
||||
nav.pop();
|
||||
};
|
||||
add_children({&button_save,
|
||||
&button_load,
|
||||
&button_close});
|
||||
|
||||
set_value(value);
|
||||
button_save.on_select = [this, &nav](Button&) {
|
||||
nav.push<FrequencySaveView>(this->value());
|
||||
};
|
||||
button_load.on_select = [this, &nav](Button&) {
|
||||
auto load_view = nav.push<FrequencyLoadView>();
|
||||
load_view->on_frequency_loaded = [this](rf::Frequency value) {
|
||||
set_value(value);
|
||||
};
|
||||
};
|
||||
|
||||
button_close.on_select = [this, &nav](Button&) {
|
||||
if (on_changed) {
|
||||
on_changed(this->value());
|
||||
}
|
||||
nav.pop();
|
||||
};
|
||||
|
||||
set_value(value);
|
||||
}
|
||||
|
||||
void FrequencyKeypadView::focus() {
|
||||
button_close.focus();
|
||||
button_close.focus();
|
||||
}
|
||||
|
||||
rf::Frequency FrequencyKeypadView::value() const {
|
||||
return mhz.as_int() * 1000000ULL + submhz.as_int() * submhz_base;
|
||||
return mhz.as_int() * 1000000ULL + submhz.as_int() * submhz_base;
|
||||
}
|
||||
|
||||
void FrequencyKeypadView::set_value(const rf::Frequency new_value) {
|
||||
mhz.set(new_value / 1000000);
|
||||
mhz.remove_leading_zeros();
|
||||
mhz.set(new_value / 1000000);
|
||||
mhz.remove_leading_zeros();
|
||||
|
||||
submhz.set((new_value % 1000000) / submhz_base);
|
||||
submhz.remove_trailing_zeros();
|
||||
submhz.set((new_value % 1000000) / submhz_base);
|
||||
submhz.remove_trailing_zeros();
|
||||
|
||||
update_text();
|
||||
update_text();
|
||||
}
|
||||
|
||||
void FrequencyKeypadView::on_button(Button& button) {
|
||||
const auto s = button.text();
|
||||
if( s == "." ) {
|
||||
field_toggle();
|
||||
} else if( s == "<" ) {
|
||||
digit_delete();
|
||||
} else {
|
||||
digit_add(s[0]);
|
||||
}
|
||||
update_text();
|
||||
const auto s = button.text();
|
||||
if (s == ".") {
|
||||
field_toggle();
|
||||
} else if (s == "<") {
|
||||
digit_delete();
|
||||
} else {
|
||||
digit_add(s[0]);
|
||||
}
|
||||
update_text();
|
||||
}
|
||||
|
||||
void FrequencyKeypadView::digit_add(const char c) {
|
||||
if( state == State::DigitMHz ) {
|
||||
if( clear_field_if_digits_entered ) {
|
||||
mhz.clear();
|
||||
}
|
||||
mhz.add_digit(c);
|
||||
} else {
|
||||
submhz.add_digit(c);
|
||||
}
|
||||
clear_field_if_digits_entered = false;
|
||||
if (state == State::DigitMHz) {
|
||||
if (clear_field_if_digits_entered) {
|
||||
mhz.clear();
|
||||
}
|
||||
mhz.add_digit(c);
|
||||
} else {
|
||||
submhz.add_digit(c);
|
||||
}
|
||||
clear_field_if_digits_entered = false;
|
||||
}
|
||||
|
||||
void FrequencyKeypadView::digit_delete() {
|
||||
if( state == State::DigitMHz ) {
|
||||
mhz.delete_digit();
|
||||
} else {
|
||||
submhz.delete_digit();
|
||||
}
|
||||
if (state == State::DigitMHz) {
|
||||
mhz.delete_digit();
|
||||
} else {
|
||||
submhz.delete_digit();
|
||||
}
|
||||
}
|
||||
|
||||
void FrequencyKeypadView::field_toggle() {
|
||||
if( state == State::DigitMHz ) {
|
||||
state = State::DigitSubMHz;
|
||||
submhz.clear();
|
||||
} else {
|
||||
state = State::DigitMHz;
|
||||
clear_field_if_digits_entered = true;
|
||||
}
|
||||
if (state == State::DigitMHz) {
|
||||
state = State::DigitSubMHz;
|
||||
submhz.clear();
|
||||
} else {
|
||||
state = State::DigitMHz;
|
||||
clear_field_if_digits_entered = true;
|
||||
}
|
||||
}
|
||||
|
||||
void FrequencyKeypadView::update_text() {
|
||||
const auto s = mhz.as_string() + "." + submhz.as_string();
|
||||
text_value.set(s);
|
||||
const auto s = mhz.as_string() + "." + submhz.as_string();
|
||||
text_value.set(s);
|
||||
}
|
||||
|
||||
/* FrequencyOptionsView **************************************************/
|
||||
|
||||
FrequencyOptionsView::FrequencyOptionsView(
|
||||
const Rect parent_rect,
|
||||
const Style* const style
|
||||
) : View { parent_rect }
|
||||
{
|
||||
set_style(style);
|
||||
const Rect parent_rect,
|
||||
const Style* const style)
|
||||
: View{parent_rect} {
|
||||
set_style(style);
|
||||
|
||||
field_step.on_change = [this](size_t n, OptionsField::value_t v) {
|
||||
(void)n;
|
||||
this->on_step_changed(v);
|
||||
};
|
||||
field_step.on_change = [this](size_t n, OptionsField::value_t v) {
|
||||
(void)n;
|
||||
this->on_step_changed(v);
|
||||
};
|
||||
|
||||
field_ppm.on_change = [this](int32_t v) {
|
||||
this->on_reference_ppm_correction_changed(v);
|
||||
};
|
||||
field_ppm.on_change = [this](int32_t v) {
|
||||
this->on_reference_ppm_correction_changed(v);
|
||||
};
|
||||
|
||||
add_children({
|
||||
&text_step,
|
||||
&field_step,
|
||||
});
|
||||
add_children({
|
||||
&text_step,
|
||||
&field_step,
|
||||
});
|
||||
|
||||
if( portapack::clock_manager.get_reference().source == ClockManager::ReferenceSource::Xtal ) {
|
||||
add_child(&field_ppm);
|
||||
add_child(&text_ppm);
|
||||
}
|
||||
if (portapack::clock_manager.get_reference().source == ClockManager::ReferenceSource::Xtal) {
|
||||
add_child(&field_ppm);
|
||||
add_child(&text_ppm);
|
||||
}
|
||||
}
|
||||
|
||||
void FrequencyOptionsView::set_step(rf::Frequency f) {
|
||||
field_step.set_by_value(f);
|
||||
field_step.set_by_value(f);
|
||||
}
|
||||
|
||||
void FrequencyOptionsView::set_reference_ppm_correction(int32_t v) {
|
||||
field_ppm.set_value(v);
|
||||
field_ppm.set_value(v);
|
||||
}
|
||||
|
||||
void FrequencyOptionsView::on_step_changed(rf::Frequency v) {
|
||||
if( on_change_step ) {
|
||||
on_change_step(v);
|
||||
}
|
||||
if (on_change_step) {
|
||||
on_change_step(v);
|
||||
}
|
||||
}
|
||||
|
||||
void FrequencyOptionsView::on_reference_ppm_correction_changed(int32_t v) {
|
||||
if( on_change_reference_ppm_correction ) {
|
||||
on_change_reference_ppm_correction(v);
|
||||
}
|
||||
if (on_change_reference_ppm_correction) {
|
||||
on_change_reference_ppm_correction(v);
|
||||
}
|
||||
}
|
||||
|
||||
/* RFAmpField ************************************************************/
|
||||
|
||||
RFAmpField::RFAmpField(
|
||||
Point parent_pos
|
||||
) : NumberField {
|
||||
parent_pos,
|
||||
1,
|
||||
{ 0, 1 },
|
||||
1,
|
||||
' ',
|
||||
}
|
||||
{
|
||||
set_value(receiver_model.rf_amp());
|
||||
Point parent_pos)
|
||||
: NumberField{
|
||||
parent_pos,
|
||||
1,
|
||||
{0, 1},
|
||||
1,
|
||||
' ',
|
||||
} {
|
||||
set_value(receiver_model.rf_amp());
|
||||
|
||||
on_change = [](int32_t v) {
|
||||
receiver_model.set_rf_amp(v);
|
||||
};
|
||||
on_change = [](int32_t v) {
|
||||
receiver_model.set_rf_amp(v);
|
||||
};
|
||||
}
|
||||
|
||||
/* RadioGainOptionsView **************************************************/
|
||||
|
||||
RadioGainOptionsView::RadioGainOptionsView(
|
||||
const Rect parent_rect,
|
||||
const Style* const style
|
||||
) : View { parent_rect }
|
||||
{
|
||||
set_style(style);
|
||||
const Rect parent_rect,
|
||||
const Style* const style)
|
||||
: View{parent_rect} {
|
||||
set_style(style);
|
||||
|
||||
add_children({
|
||||
&label_rf_amp,
|
||||
&field_rf_amp,
|
||||
});
|
||||
add_children({
|
||||
&label_rf_amp,
|
||||
&field_rf_amp,
|
||||
});
|
||||
}
|
||||
|
||||
/* LNAGainField **********************************************************/
|
||||
|
||||
LNAGainField::LNAGainField(
|
||||
Point parent_pos
|
||||
) : NumberField {
|
||||
parent_pos, 2,
|
||||
{ max283x::lna::gain_db_range.minimum, max283x::lna::gain_db_range.maximum },
|
||||
max283x::lna::gain_db_step,
|
||||
' ',
|
||||
}
|
||||
{
|
||||
set_value(receiver_model.lna());
|
||||
Point parent_pos)
|
||||
: NumberField{
|
||||
parent_pos,
|
||||
2,
|
||||
{max283x::lna::gain_db_range.minimum, max283x::lna::gain_db_range.maximum},
|
||||
max283x::lna::gain_db_step,
|
||||
' ',
|
||||
} {
|
||||
set_value(receiver_model.lna());
|
||||
|
||||
on_change = [](int32_t v) {
|
||||
receiver_model.set_lna(v);
|
||||
};
|
||||
on_change = [](int32_t v) {
|
||||
receiver_model.set_lna(v);
|
||||
};
|
||||
}
|
||||
|
||||
void LNAGainField::on_focus() {
|
||||
//Widget::on_focus();
|
||||
if( on_show_options ) {
|
||||
on_show_options();
|
||||
}
|
||||
// Widget::on_focus();
|
||||
if (on_show_options) {
|
||||
on_show_options();
|
||||
}
|
||||
}
|
||||
|
||||
/* VGAGainField **********************************************************/
|
||||
|
||||
VGAGainField::VGAGainField(
|
||||
Point parent_pos
|
||||
) : NumberField {
|
||||
parent_pos, 2,
|
||||
{ max283x::vga::gain_db_range.minimum, max283x::vga::gain_db_range.maximum },
|
||||
max283x::vga::gain_db_step,
|
||||
' ',
|
||||
}
|
||||
{
|
||||
set_value(receiver_model.vga());
|
||||
Point parent_pos)
|
||||
: NumberField{
|
||||
parent_pos,
|
||||
2,
|
||||
{max283x::vga::gain_db_range.minimum, max283x::vga::gain_db_range.maximum},
|
||||
max283x::vga::gain_db_step,
|
||||
' ',
|
||||
} {
|
||||
set_value(receiver_model.vga());
|
||||
|
||||
on_change = [](int32_t v) {
|
||||
receiver_model.set_vga(v);
|
||||
};
|
||||
on_change = [](int32_t v) {
|
||||
receiver_model.set_vga(v);
|
||||
};
|
||||
}
|
||||
|
||||
void VGAGainField::on_focus() {
|
||||
//Widget::on_focus();
|
||||
if( on_show_options ) {
|
||||
on_show_options();
|
||||
}
|
||||
// Widget::on_focus();
|
||||
if (on_show_options) {
|
||||
on_show_options();
|
||||
}
|
||||
}
|
||||
|
||||
} /* namespace ui */
|
||||
|
@@ -37,316 +37,306 @@
|
||||
namespace ui {
|
||||
|
||||
class FrequencyField : public Widget {
|
||||
public:
|
||||
std::function<void(rf::Frequency)> on_change { };
|
||||
std::function<void(void)> on_edit { };
|
||||
std::function<void(void)> on_show_options { };
|
||||
public:
|
||||
std::function<void(rf::Frequency)> on_change{};
|
||||
std::function<void(void)> on_edit{};
|
||||
std::function<void(void)> on_show_options{};
|
||||
|
||||
using range_t = rf::FrequencyRange;
|
||||
using range_t = rf::FrequencyRange;
|
||||
|
||||
FrequencyField(const Point parent_pos);
|
||||
FrequencyField(const Point parent_pos);
|
||||
|
||||
rf::Frequency value() const;
|
||||
rf::Frequency value() const;
|
||||
|
||||
void set_value(rf::Frequency new_value);
|
||||
void set_step(rf::Frequency new_value);
|
||||
void set_value(rf::Frequency new_value);
|
||||
void set_step(rf::Frequency new_value);
|
||||
|
||||
void paint(Painter& painter) override;
|
||||
void paint(Painter& painter) override;
|
||||
|
||||
bool on_key(const ui::KeyEvent event) override;
|
||||
bool on_encoder(const EncoderEvent delta) override;
|
||||
bool on_touch(const TouchEvent event) override;
|
||||
void on_focus() override;
|
||||
bool on_key(const ui::KeyEvent event) override;
|
||||
bool on_encoder(const EncoderEvent delta) override;
|
||||
bool on_touch(const TouchEvent event) override;
|
||||
void on_focus() override;
|
||||
|
||||
private:
|
||||
const size_t length_;
|
||||
const range_t range;
|
||||
rf::Frequency value_ { 0 };
|
||||
rf::Frequency step { 25000 };
|
||||
uint64_t last_ms_ { 0 };
|
||||
private:
|
||||
const size_t length_;
|
||||
const range_t range;
|
||||
rf::Frequency value_{0};
|
||||
rf::Frequency step{25000};
|
||||
uint64_t last_ms_{0};
|
||||
|
||||
rf::Frequency clamp_value(rf::Frequency value);
|
||||
rf::Frequency clamp_value(rf::Frequency value);
|
||||
};
|
||||
|
||||
template<size_t N>
|
||||
template <size_t N>
|
||||
class FieldString {
|
||||
public:
|
||||
enum Justify {
|
||||
Right,
|
||||
Left,
|
||||
};
|
||||
public:
|
||||
enum Justify {
|
||||
Right,
|
||||
Left,
|
||||
};
|
||||
|
||||
constexpr FieldString(
|
||||
Justify justify
|
||||
) : justify { justify }
|
||||
{
|
||||
}
|
||||
constexpr FieldString(
|
||||
Justify justify)
|
||||
: justify{justify} {
|
||||
}
|
||||
|
||||
uint32_t as_int() const {
|
||||
uint32_t value = 0;
|
||||
for(const auto c : s) {
|
||||
const uint_fast8_t digit = (c == ' ') ? 0 : c - '0';
|
||||
value = (value * 10) + digit;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
uint32_t as_int() const {
|
||||
uint32_t value = 0;
|
||||
for (const auto c : s) {
|
||||
const uint_fast8_t digit = (c == ' ') ? 0 : c - '0';
|
||||
value = (value * 10) + digit;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
void set(uint32_t value) {
|
||||
std::generate(s.rbegin(), s.rend(), [&value]() {
|
||||
const char digit = (value % 10) + '0';
|
||||
value /= 10;
|
||||
return digit;
|
||||
});
|
||||
}
|
||||
void set(uint32_t value) {
|
||||
std::generate(s.rbegin(), s.rend(), [&value]() {
|
||||
const char digit = (value % 10) + '0';
|
||||
value /= 10;
|
||||
return digit;
|
||||
});
|
||||
}
|
||||
|
||||
void clear() {
|
||||
s.fill(' ');
|
||||
}
|
||||
void clear() {
|
||||
s.fill(' ');
|
||||
}
|
||||
|
||||
void add_digit(const char c) {
|
||||
insert_right(c);
|
||||
}
|
||||
void add_digit(const char c) {
|
||||
insert_right(c);
|
||||
}
|
||||
|
||||
void delete_digit() {
|
||||
if( justify == Justify::Right ) {
|
||||
shift_right();
|
||||
s.front() = ' ';
|
||||
} else {
|
||||
auto first_digit = std::find_if(s.rbegin(), s.rend(), [](const char& a) {
|
||||
return a != ' ';
|
||||
});
|
||||
if( first_digit != s.rend() ) {
|
||||
*first_digit = ' ';
|
||||
}
|
||||
}
|
||||
}
|
||||
void delete_digit() {
|
||||
if (justify == Justify::Right) {
|
||||
shift_right();
|
||||
s.front() = ' ';
|
||||
} else {
|
||||
auto first_digit = std::find_if(s.rbegin(), s.rend(), [](const char& a) {
|
||||
return a != ' ';
|
||||
});
|
||||
if (first_digit != s.rend()) {
|
||||
*first_digit = ' ';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string as_string() const {
|
||||
return { s.data(), s.size() };
|
||||
}
|
||||
std::string as_string() const {
|
||||
return {s.data(), s.size()};
|
||||
}
|
||||
|
||||
void remove_leading_zeros() {
|
||||
remove_zeros(s.begin(), s.end());
|
||||
}
|
||||
void remove_leading_zeros() {
|
||||
remove_zeros(s.begin(), s.end());
|
||||
}
|
||||
|
||||
void remove_trailing_zeros() {
|
||||
remove_zeros(s.rbegin(), s.rend());
|
||||
}
|
||||
void remove_trailing_zeros() {
|
||||
remove_zeros(s.rbegin(), s.rend());
|
||||
}
|
||||
|
||||
private:
|
||||
using array_type = std::array<char, N>;
|
||||
private:
|
||||
using array_type = std::array<char, N>;
|
||||
|
||||
array_type s { };
|
||||
Justify justify { Justify::Left };
|
||||
array_type s{};
|
||||
Justify justify{Justify::Left};
|
||||
|
||||
template<typename Iterator>
|
||||
void remove_zeros(Iterator begin, Iterator end) {
|
||||
const auto first_significant_digit =
|
||||
std::find_if(begin, end, [](const char& a) {
|
||||
return a != '0';
|
||||
});
|
||||
std::fill(begin, first_significant_digit, ' ');
|
||||
}
|
||||
template <typename Iterator>
|
||||
void remove_zeros(Iterator begin, Iterator end) {
|
||||
const auto first_significant_digit =
|
||||
std::find_if(begin, end, [](const char& a) {
|
||||
return a != '0';
|
||||
});
|
||||
std::fill(begin, first_significant_digit, ' ');
|
||||
}
|
||||
|
||||
void insert_right(const char c) {
|
||||
auto insert_point = s.end() - 1;
|
||||
void insert_right(const char c) {
|
||||
auto insert_point = s.end() - 1;
|
||||
|
||||
if( justify == Justify::Left ) {
|
||||
insert_point = std::find_if(s.begin(), s.end(), [](const char& a) {
|
||||
return a == ' ';
|
||||
});
|
||||
}
|
||||
if (justify == Justify::Left) {
|
||||
insert_point = std::find_if(s.begin(), s.end(), [](const char& a) {
|
||||
return a == ' ';
|
||||
});
|
||||
}
|
||||
|
||||
if( *insert_point != ' ' ) {
|
||||
insert_point = shift_left();
|
||||
}
|
||||
if (*insert_point != ' ') {
|
||||
insert_point = shift_left();
|
||||
}
|
||||
|
||||
*insert_point = c;
|
||||
}
|
||||
*insert_point = c;
|
||||
}
|
||||
|
||||
typename array_type::iterator shift_left() {
|
||||
return std::move(s.begin() + 1, s.end(), s.begin());
|
||||
}
|
||||
typename array_type::iterator shift_left() {
|
||||
return std::move(s.begin() + 1, s.end(), s.begin());
|
||||
}
|
||||
|
||||
typename array_type::iterator shift_right() {
|
||||
return std::move_backward(s.begin(), s.end() - 1, s.end());
|
||||
}
|
||||
typename array_type::iterator shift_right() {
|
||||
return std::move_backward(s.begin(), s.end() - 1, s.end());
|
||||
}
|
||||
};
|
||||
|
||||
class FrequencyKeypadView : public View {
|
||||
public:
|
||||
std::function<void(rf::Frequency)> on_changed { };
|
||||
public:
|
||||
std::function<void(rf::Frequency)> on_changed{};
|
||||
|
||||
FrequencyKeypadView(
|
||||
NavigationView& nav,
|
||||
const rf::Frequency value
|
||||
);
|
||||
FrequencyKeypadView(
|
||||
NavigationView& nav,
|
||||
const rf::Frequency value);
|
||||
|
||||
void focus() override;
|
||||
void focus() override;
|
||||
|
||||
rf::Frequency value() const;
|
||||
void set_value(const rf::Frequency new_value);
|
||||
bool on_encoder(const EncoderEvent delta) override;
|
||||
rf::Frequency value() const;
|
||||
void set_value(const rf::Frequency new_value);
|
||||
bool on_encoder(const EncoderEvent delta) override;
|
||||
|
||||
private:
|
||||
int16_t focused_button = 0;
|
||||
static constexpr int button_w = 240 / 3;
|
||||
static constexpr int button_h = 48;
|
||||
private:
|
||||
int16_t focused_button = 0;
|
||||
static constexpr int button_w = 240 / 3;
|
||||
static constexpr int button_h = 48;
|
||||
|
||||
static constexpr int mhz_digits = 4;
|
||||
static constexpr int submhz_digits = 4;
|
||||
static constexpr int mhz_digits = 4;
|
||||
static constexpr int submhz_digits = 4;
|
||||
|
||||
static constexpr int mhz_mod = pow(10, mhz_digits);
|
||||
static constexpr int submhz_base = pow(10, 6 - submhz_digits);
|
||||
static constexpr int text_digits = mhz_digits + 1 + submhz_digits;
|
||||
static constexpr int mhz_mod = pow(10, mhz_digits);
|
||||
static constexpr int submhz_base = pow(10, 6 - submhz_digits);
|
||||
static constexpr int text_digits = mhz_digits + 1 + submhz_digits;
|
||||
|
||||
Text text_value {
|
||||
{ 0, 4, 240, 16 }
|
||||
};
|
||||
Text text_value{
|
||||
{0, 4, 240, 16}};
|
||||
|
||||
std::array<Button, 12> buttons { };
|
||||
std::array<Button, 12> buttons{};
|
||||
|
||||
Button button_save {
|
||||
{ 0, button_h * 5, 60, button_h },
|
||||
"Save"
|
||||
};
|
||||
Button button_load {
|
||||
{ 60, button_h * 5, 60, button_h },
|
||||
"Load"
|
||||
};
|
||||
Button button_close {
|
||||
{ 128, button_h * 5, 112, button_h },
|
||||
"Done"
|
||||
};
|
||||
Button button_save{
|
||||
{0, button_h * 5, 60, button_h},
|
||||
"Save"};
|
||||
Button button_load{
|
||||
{60, button_h * 5, 60, button_h},
|
||||
"Load"};
|
||||
Button button_close{
|
||||
{128, button_h * 5, 112, button_h},
|
||||
"Done"};
|
||||
|
||||
/* TODO: Template arg required in enum?! */
|
||||
FieldString<mhz_digits> mhz { FieldString<4>::Justify::Right };
|
||||
FieldString<submhz_digits> submhz { FieldString<4>::Justify::Left };
|
||||
/* TODO: Template arg required in enum?! */
|
||||
FieldString<mhz_digits> mhz{FieldString<4>::Justify::Right};
|
||||
FieldString<submhz_digits> submhz{FieldString<4>::Justify::Left};
|
||||
|
||||
enum State {
|
||||
DigitMHz,
|
||||
DigitSubMHz
|
||||
};
|
||||
enum State {
|
||||
DigitMHz,
|
||||
DigitSubMHz
|
||||
};
|
||||
|
||||
State state { DigitMHz };
|
||||
bool clear_field_if_digits_entered { true };
|
||||
State state{DigitMHz};
|
||||
bool clear_field_if_digits_entered{true};
|
||||
|
||||
void on_button(Button& button);
|
||||
void on_button(Button& button);
|
||||
|
||||
void digit_add(const char c);
|
||||
void digit_delete();
|
||||
void digit_add(const char c);
|
||||
void digit_delete();
|
||||
|
||||
void field_toggle();
|
||||
void update_text();
|
||||
void field_toggle();
|
||||
void update_text();
|
||||
};
|
||||
|
||||
class FrequencyStepView : public OptionsField {
|
||||
public:
|
||||
FrequencyStepView(
|
||||
Point parent_pos
|
||||
) : OptionsField {
|
||||
parent_pos,
|
||||
5,
|
||||
{
|
||||
{ " Auto", 0 }, /* Faster == larger step. */
|
||||
{ " 10", 10 }, /* Fine tuning SSB voice pitch,in HF and QO-100 sat #669 */
|
||||
{ " 50", 50 }, /* added 50Hz/10Hz,but we do not increase GUI digit decimal */
|
||||
{ " 100", 100 },
|
||||
{ " 1k ", 1000 },
|
||||
{ " 3k ", 3000 }, /* Approximate SSB bandwidth */
|
||||
{ " 5k ", 5000 },
|
||||
{ " 6k3", 6250 },
|
||||
{ " 9k ", 9000 }, /* channel spacing for LF, MF in some regions */
|
||||
{ " 10k ", 10000 },
|
||||
{ " 12k5", 12500 },
|
||||
{ " 25k ", 25000 },
|
||||
{ "100k ", 100000 },
|
||||
{ " 1M ", 1000000 },
|
||||
{ " 10M ", 10000000 },
|
||||
}
|
||||
}
|
||||
{
|
||||
}
|
||||
public:
|
||||
FrequencyStepView(
|
||||
Point parent_pos)
|
||||
: OptionsField{
|
||||
parent_pos,
|
||||
5,
|
||||
{
|
||||
{" Auto", 0}, /* Faster == larger step. */
|
||||
{" 10", 10}, /* Fine tuning SSB voice pitch,in HF and QO-100 sat #669 */
|
||||
{" 50", 50}, /* added 50Hz/10Hz,but we do not increase GUI digit decimal */
|
||||
{" 100", 100},
|
||||
{" 1k ", 1000},
|
||||
{" 3k ", 3000}, /* Approximate SSB bandwidth */
|
||||
{" 5k ", 5000},
|
||||
{" 6k3", 6250},
|
||||
{" 9k ", 9000}, /* channel spacing for LF, MF in some regions */
|
||||
{" 10k ", 10000},
|
||||
{" 12k5", 12500},
|
||||
{" 25k ", 25000},
|
||||
{"100k ", 100000},
|
||||
{" 1M ", 1000000},
|
||||
{" 10M ", 10000000},
|
||||
}} {
|
||||
}
|
||||
};
|
||||
|
||||
class FrequencyOptionsView : public View {
|
||||
public:
|
||||
std::function<void(rf::Frequency)> on_change_step { };
|
||||
std::function<void(int32_t)> on_change_reference_ppm_correction { };
|
||||
public:
|
||||
std::function<void(rf::Frequency)> on_change_step{};
|
||||
std::function<void(int32_t)> on_change_reference_ppm_correction{};
|
||||
|
||||
FrequencyOptionsView(const Rect parent_rect, const Style* const style);
|
||||
FrequencyOptionsView(const Rect parent_rect, const Style* const style);
|
||||
|
||||
void set_step(rf::Frequency f);
|
||||
void set_reference_ppm_correction(int32_t v);
|
||||
void set_step(rf::Frequency f);
|
||||
void set_reference_ppm_correction(int32_t v);
|
||||
|
||||
private:
|
||||
Text text_step {
|
||||
{ 0 * 8, 0 * 16, 4 * 8, 1 * 16 },
|
||||
"Step"
|
||||
};
|
||||
private:
|
||||
Text text_step{
|
||||
{0 * 8, 0 * 16, 4 * 8, 1 * 16},
|
||||
"Step"};
|
||||
|
||||
FrequencyStepView field_step {
|
||||
{ 5 * 8, 0 * 16 },
|
||||
};
|
||||
FrequencyStepView field_step{
|
||||
{5 * 8, 0 * 16},
|
||||
};
|
||||
|
||||
void on_step_changed(rf::Frequency v);
|
||||
void on_reference_ppm_correction_changed(int32_t v);
|
||||
void on_step_changed(rf::Frequency v);
|
||||
void on_reference_ppm_correction_changed(int32_t v);
|
||||
|
||||
NumberField field_ppm {
|
||||
{ 23 * 8, 0 * 16 },
|
||||
3,
|
||||
{ -99, 99 },
|
||||
1,
|
||||
'0',
|
||||
};
|
||||
Text text_ext {
|
||||
{ 23 * 8, 0 * 16, 3 * 8, 1 * 16 },
|
||||
"EXT",
|
||||
};
|
||||
Text text_ppm {
|
||||
{ 27 * 8, 0 * 16, 3 * 8, 16 },
|
||||
"PPM",
|
||||
};
|
||||
NumberField field_ppm{
|
||||
{23 * 8, 0 * 16},
|
||||
3,
|
||||
{-99, 99},
|
||||
1,
|
||||
'0',
|
||||
};
|
||||
Text text_ext{
|
||||
{23 * 8, 0 * 16, 3 * 8, 1 * 16},
|
||||
"EXT",
|
||||
};
|
||||
Text text_ppm{
|
||||
{27 * 8, 0 * 16, 3 * 8, 16},
|
||||
"PPM",
|
||||
};
|
||||
};
|
||||
|
||||
class RFAmpField : public NumberField {
|
||||
public:
|
||||
RFAmpField(Point parent_pos);
|
||||
public:
|
||||
RFAmpField(Point parent_pos);
|
||||
};
|
||||
|
||||
class RadioGainOptionsView : public View {
|
||||
public:
|
||||
RadioGainOptionsView(const Rect parent_rect, const Style* const style);
|
||||
public:
|
||||
RadioGainOptionsView(const Rect parent_rect, const Style* const style);
|
||||
|
||||
private:
|
||||
Text label_rf_amp {
|
||||
{ 0 * 8, 0 * 16, 3 * 8, 1 * 16 },
|
||||
"Amp"
|
||||
};
|
||||
private:
|
||||
Text label_rf_amp{
|
||||
{0 * 8, 0 * 16, 3 * 8, 1 * 16},
|
||||
"Amp"};
|
||||
|
||||
RFAmpField field_rf_amp {
|
||||
{ 4 * 8, 0 * 16},
|
||||
};
|
||||
RFAmpField field_rf_amp{
|
||||
{4 * 8, 0 * 16},
|
||||
};
|
||||
};
|
||||
|
||||
class LNAGainField : public NumberField {
|
||||
public:
|
||||
std::function<void(void)> on_show_options { };
|
||||
public:
|
||||
std::function<void(void)> on_show_options{};
|
||||
|
||||
LNAGainField(Point parent_pos);
|
||||
LNAGainField(Point parent_pos);
|
||||
|
||||
void on_focus() override;
|
||||
void on_focus() override;
|
||||
};
|
||||
|
||||
class VGAGainField : public NumberField {
|
||||
public:
|
||||
std::function<void(void)> on_show_options { };
|
||||
public:
|
||||
std::function<void(void)> on_show_options{};
|
||||
|
||||
VGAGainField(Point parent_pos);
|
||||
VGAGainField(Point parent_pos);
|
||||
|
||||
void on_focus() override;
|
||||
void on_focus() override;
|
||||
};
|
||||
|
||||
} /* namespace ui */
|
||||
|
||||
#endif/*__UI_RECEIVER_H__*/
|
||||
#endif /*__UI_RECEIVER_H__*/
|
||||
|
@@ -26,308 +26,276 @@
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#define min(a,b) ((a)<(b)?(a):(b))
|
||||
#define max(a,b) ((a)>(b)?(a):(b))
|
||||
#define abs(x) ((x)>0?(x):-(x))
|
||||
#define min(a, b) ((a) < (b) ? (a) : (b))
|
||||
#define max(a, b) ((a) > (b) ? (a) : (b))
|
||||
#define abs(x) ((x) > 0 ? (x) : -(x))
|
||||
|
||||
namespace ui {
|
||||
|
||||
RSSI::RSSI(
|
||||
Rect parent_rect,
|
||||
bool instant_exec
|
||||
) : Widget { parent_rect },
|
||||
instant_exec_ { instant_exec }
|
||||
{
|
||||
}
|
||||
RSSI::RSSI(
|
||||
Rect parent_rect,
|
||||
bool instant_exec)
|
||||
: Widget{parent_rect},
|
||||
instant_exec_{instant_exec} {
|
||||
}
|
||||
|
||||
void RSSI::paint(Painter& painter) {
|
||||
const auto r = screen_rect();
|
||||
void RSSI::paint(Painter& painter) {
|
||||
const auto r = screen_rect();
|
||||
|
||||
constexpr int rssi_sample_range = 256;
|
||||
//constexpr float rssi_voltage_min = 0.4;
|
||||
constexpr float rssi_voltage_max = 2.2;
|
||||
constexpr float adc_voltage_max = 3.3;
|
||||
//constexpr int raw_min = rssi_sample_range * rssi_voltage_min / adc_voltage_max;
|
||||
constexpr int raw_min = 0 ;
|
||||
constexpr int raw_max = rssi_sample_range * rssi_voltage_max / adc_voltage_max;
|
||||
constexpr int raw_delta = raw_max - raw_min;
|
||||
constexpr int rssi_sample_range = 256;
|
||||
// constexpr float rssi_voltage_min = 0.4;
|
||||
constexpr float rssi_voltage_max = 2.2;
|
||||
constexpr float adc_voltage_max = 3.3;
|
||||
// constexpr int raw_min = rssi_sample_range * rssi_voltage_min / adc_voltage_max;
|
||||
constexpr int raw_min = 0;
|
||||
constexpr int raw_max = rssi_sample_range * rssi_voltage_max / adc_voltage_max;
|
||||
constexpr int raw_delta = raw_max - raw_min;
|
||||
|
||||
if (!vertical_rssi_enabled) {
|
||||
// horizontal left to right level meters
|
||||
const range_t<int> x_avg_range{0, r.width() - 1};
|
||||
const int16_t x_avg = x_avg_range.clip((avg_ - raw_min) * r.width() / raw_delta);
|
||||
const range_t<int> x_min_range{0, x_avg};
|
||||
const int16_t x_min = x_min_range.clip((min_ - raw_min) * r.width() / raw_delta);
|
||||
const range_t<int> x_max_range{x_avg + 1, r.width() - 1};
|
||||
const int16_t x_max = x_max_range.clip((max_ - raw_min) * r.width() / raw_delta);
|
||||
const int16_t peak = x_max_range.clip((peak_ - raw_min) * r.width() / raw_delta);
|
||||
|
||||
if( !vertical_rssi_enabled )
|
||||
{
|
||||
// horizontal left to right level meters
|
||||
const range_t<int> x_avg_range { 0, r.width() - 1 };
|
||||
const int16_t x_avg = x_avg_range.clip((avg_ - raw_min) * r.width() / raw_delta);
|
||||
const range_t<int> x_min_range { 0, x_avg };
|
||||
const int16_t x_min = x_min_range.clip((min_ - raw_min) * r.width() / raw_delta);
|
||||
const range_t<int> x_max_range { x_avg + 1, r.width() - 1 };
|
||||
const int16_t x_max = x_max_range.clip((max_ - raw_min) * r.width() / raw_delta);
|
||||
const int16_t peak = x_max_range.clip((peak_ - raw_min) * r.width() / raw_delta);
|
||||
// x_min
|
||||
const Rect r0{r.left(), r.top(), x_min, r.height()};
|
||||
painter.fill_rectangle(
|
||||
r0,
|
||||
Color::blue());
|
||||
|
||||
// x_min
|
||||
const Rect r0 { r.left(), r.top(), x_min, r.height() };
|
||||
// x_avg
|
||||
const Rect r1{r.left() + x_min, r.top(), x_avg - x_min, r.height()};
|
||||
painter.fill_rectangle(
|
||||
r1,
|
||||
Color::red());
|
||||
|
||||
// x_avg middle marker
|
||||
const Rect r2{r.left() + x_avg, r.top(), 1, r.height()};
|
||||
painter.fill_rectangle(
|
||||
r2,
|
||||
Color::white());
|
||||
|
||||
// x_max
|
||||
const Rect r3{r.left() + x_avg + 1, r.top(), x_max - (x_avg + 1), r.height()};
|
||||
painter.fill_rectangle(
|
||||
r3,
|
||||
Color::red());
|
||||
|
||||
// filling last part in black
|
||||
const Rect r4{r.left() + x_max, r.top(), r.width() - x_max, r.height()};
|
||||
painter.fill_rectangle(
|
||||
r4,
|
||||
Color::black());
|
||||
|
||||
// show green peak value
|
||||
if (peak_enabled) {
|
||||
const Rect r5{r.left() + peak - 3, r.top(), 3, r.height()};
|
||||
painter.fill_rectangle(
|
||||
r0,
|
||||
Color::blue()
|
||||
);
|
||||
|
||||
// x_avg
|
||||
const Rect r1 { r.left() + x_min, r.top(), x_avg - x_min, r.height() };
|
||||
painter.fill_rectangle(
|
||||
r1,
|
||||
Color::red()
|
||||
);
|
||||
|
||||
// x_avg middle marker
|
||||
const Rect r2 { r.left() + x_avg, r.top(), 1, r.height() };
|
||||
painter.fill_rectangle(
|
||||
r2,
|
||||
Color::white()
|
||||
);
|
||||
|
||||
// x_max
|
||||
const Rect r3 { r.left() + x_avg + 1, r.top(), x_max - (x_avg + 1), r.height() };
|
||||
painter.fill_rectangle(
|
||||
r3,
|
||||
Color::red()
|
||||
);
|
||||
|
||||
// filling last part in black
|
||||
const Rect r4 { r.left() + x_max, r.top(), r.width() - x_max, r.height() };
|
||||
painter.fill_rectangle(
|
||||
r4,
|
||||
Color::black()
|
||||
);
|
||||
|
||||
// show green peak value
|
||||
if( peak_enabled )
|
||||
{
|
||||
const Rect r5 { r.left() + peak - 3 , r.top() , 3 , r.height() };
|
||||
painter.fill_rectangle(
|
||||
r5,
|
||||
Color::green()
|
||||
);
|
||||
}
|
||||
r5,
|
||||
Color::green());
|
||||
}
|
||||
else
|
||||
{
|
||||
// vertical bottom to top level meters
|
||||
const range_t<int> y_avg_range { 0, r.height() - 1 };
|
||||
const int16_t y_avg = y_avg_range.clip((avg_ - raw_min) * r.height() / raw_delta);
|
||||
const range_t<int> y_min_range { 0, y_avg };
|
||||
const int16_t y_min = y_min_range.clip((min_ - raw_min) * r.height() / raw_delta);
|
||||
const range_t<int> y_max_range { y_avg + 1, r.height() - 1 };
|
||||
const int16_t y_max = y_max_range.clip((max_ - raw_min) * r.height() / raw_delta);
|
||||
const range_t<int> peak_range { 0, r.height() - 1 };
|
||||
const int16_t peak = peak_range.clip((peak_ - raw_min) * r.height() / raw_delta);
|
||||
} else {
|
||||
// vertical bottom to top level meters
|
||||
const range_t<int> y_avg_range{0, r.height() - 1};
|
||||
const int16_t y_avg = y_avg_range.clip((avg_ - raw_min) * r.height() / raw_delta);
|
||||
const range_t<int> y_min_range{0, y_avg};
|
||||
const int16_t y_min = y_min_range.clip((min_ - raw_min) * r.height() / raw_delta);
|
||||
const range_t<int> y_max_range{y_avg + 1, r.height() - 1};
|
||||
const int16_t y_max = y_max_range.clip((max_ - raw_min) * r.height() / raw_delta);
|
||||
const range_t<int> peak_range{0, r.height() - 1};
|
||||
const int16_t peak = peak_range.clip((peak_ - raw_min) * r.height() / raw_delta);
|
||||
|
||||
// y_min
|
||||
const Rect r0 { r.left(), r.bottom() - y_min, r.width() , y_min };
|
||||
// y_min
|
||||
const Rect r0{r.left(), r.bottom() - y_min, r.width(), y_min};
|
||||
painter.fill_rectangle(
|
||||
r0,
|
||||
Color::blue());
|
||||
|
||||
// y_avg
|
||||
const Rect r1{r.left(), r.bottom() - y_avg, r.width(), y_avg - y_min};
|
||||
painter.fill_rectangle(
|
||||
r1,
|
||||
Color::red());
|
||||
|
||||
// y_avg middle marker
|
||||
const Rect r2{r.left(), r.bottom() - y_avg, r.width(), 1};
|
||||
painter.fill_rectangle(
|
||||
r2,
|
||||
Color::white());
|
||||
|
||||
// y_max
|
||||
const Rect r3{r.left(), r.bottom() - y_max, r.width(), y_max - y_avg};
|
||||
// const Rect r3 { r.left(), r.bottom() - y_max , r.width() , y_max - y_avg - 1 };
|
||||
painter.fill_rectangle(
|
||||
r3,
|
||||
Color::red());
|
||||
|
||||
// fill last part of level in black
|
||||
const Rect r4{r.left(), r.top(), r.width(), r.height() - y_max};
|
||||
painter.fill_rectangle(
|
||||
r4,
|
||||
Color::black());
|
||||
|
||||
// show green peak value if enabled
|
||||
if (peak_enabled) {
|
||||
const Rect r5{r.left(), r.bottom() - peak - 3, r.width(), 3};
|
||||
painter.fill_rectangle(
|
||||
r0,
|
||||
Color::blue()
|
||||
);
|
||||
|
||||
// y_avg
|
||||
const Rect r1 { r.left(), r.bottom() - y_avg , r.width() , y_avg - y_min };
|
||||
painter.fill_rectangle(
|
||||
r1,
|
||||
Color::red()
|
||||
);
|
||||
|
||||
// y_avg middle marker
|
||||
const Rect r2 { r.left(), r.bottom() - y_avg , r.width() , 1 };
|
||||
painter.fill_rectangle(
|
||||
r2,
|
||||
Color::white()
|
||||
);
|
||||
|
||||
// y_max
|
||||
const Rect r3 { r.left(), r.bottom() - y_max , r.width() , y_max - y_avg };
|
||||
//const Rect r3 { r.left(), r.bottom() - y_max , r.width() , y_max - y_avg - 1 };
|
||||
painter.fill_rectangle(
|
||||
r3,
|
||||
Color::red()
|
||||
);
|
||||
|
||||
// fill last part of level in black
|
||||
const Rect r4 { r.left(), r.top() , r.width() , r.height() - y_max };
|
||||
painter.fill_rectangle(
|
||||
r4,
|
||||
Color::black()
|
||||
);
|
||||
|
||||
// show green peak value if enabled
|
||||
if( peak_enabled )
|
||||
{
|
||||
const Rect r5 { r.left(), r.bottom() - peak - 3 , r.width() , 3 };
|
||||
painter.fill_rectangle(
|
||||
r5,
|
||||
Color::green()
|
||||
);
|
||||
}
|
||||
}
|
||||
if (pitch_rssi_enabled) {
|
||||
baseband::set_pitch_rssi((avg_ - raw_min) * 2000 / raw_delta, true);
|
||||
}
|
||||
if( has_focus() || highlighted() )
|
||||
{
|
||||
const Rect r6 { r.left(), r.top(), r.width(), r.height() };
|
||||
painter.draw_rectangle(
|
||||
r6,
|
||||
Color::white()
|
||||
);
|
||||
r5,
|
||||
Color::green());
|
||||
}
|
||||
}
|
||||
|
||||
int16_t RSSI::get_min()
|
||||
{
|
||||
return min_ ;
|
||||
if (pitch_rssi_enabled) {
|
||||
baseband::set_pitch_rssi((avg_ - raw_min) * 2000 / raw_delta, true);
|
||||
}
|
||||
|
||||
int16_t RSSI::get_avg()
|
||||
{
|
||||
return avg_ ;
|
||||
if (has_focus() || highlighted()) {
|
||||
const Rect r6{r.left(), r.top(), r.width(), r.height()};
|
||||
painter.draw_rectangle(
|
||||
r6,
|
||||
Color::white());
|
||||
}
|
||||
}
|
||||
|
||||
int16_t RSSI::get_max()
|
||||
{
|
||||
return max_ ;
|
||||
}
|
||||
int16_t RSSI::get_min() {
|
||||
return min_;
|
||||
}
|
||||
|
||||
int16_t RSSI::get_delta()
|
||||
{
|
||||
return max_ - min_ ;
|
||||
}
|
||||
int16_t RSSI::get_avg() {
|
||||
return avg_;
|
||||
}
|
||||
|
||||
int16_t RSSI::get_max() {
|
||||
return max_;
|
||||
}
|
||||
|
||||
void RSSI::set_pitch_rssi(bool enabled) {
|
||||
pitch_rssi_enabled = enabled;
|
||||
if (!enabled) baseband::set_pitch_rssi(0, false);
|
||||
}
|
||||
int16_t RSSI::get_delta() {
|
||||
return max_ - min_;
|
||||
}
|
||||
|
||||
void RSSI::set_vertical_rssi(bool enabled) {
|
||||
if( enabled )
|
||||
vertical_rssi_enabled = true ;
|
||||
else
|
||||
vertical_rssi_enabled = false ;
|
||||
}
|
||||
void RSSI::set_pitch_rssi(bool enabled) {
|
||||
pitch_rssi_enabled = enabled;
|
||||
if (!enabled) baseband::set_pitch_rssi(0, false);
|
||||
}
|
||||
|
||||
void RSSI::set_peak(bool enabled, size_t duration) {
|
||||
peak_enabled = enabled ;
|
||||
peak_duration = duration ;
|
||||
}
|
||||
void RSSI::set_vertical_rssi(bool enabled) {
|
||||
if (enabled)
|
||||
vertical_rssi_enabled = true;
|
||||
else
|
||||
vertical_rssi_enabled = false;
|
||||
}
|
||||
|
||||
void RSSI::on_statistics_update(const RSSIStatistics& statistics) {
|
||||
min_ = statistics.min;
|
||||
avg_ = statistics.accumulator / statistics.count;
|
||||
max_ = statistics.max;
|
||||
if( peak_enabled )
|
||||
{
|
||||
peak_duration_ = peak_duration_ + 100 ;
|
||||
if( max_ > peak_ )
|
||||
{
|
||||
peak_ = max_ ;
|
||||
}
|
||||
if( peak_duration_ > peak_duration )
|
||||
{
|
||||
peak_duration_ = 0 ;
|
||||
peak_ = max_ ;
|
||||
}
|
||||
void RSSI::set_peak(bool enabled, size_t duration) {
|
||||
peak_enabled = enabled;
|
||||
peak_duration = duration;
|
||||
}
|
||||
|
||||
void RSSI::on_statistics_update(const RSSIStatistics& statistics) {
|
||||
min_ = statistics.min;
|
||||
avg_ = statistics.accumulator / statistics.count;
|
||||
max_ = statistics.max;
|
||||
if (peak_enabled) {
|
||||
peak_duration_ = peak_duration_ + 100;
|
||||
if (max_ > peak_) {
|
||||
peak_ = max_;
|
||||
}
|
||||
set_dirty();
|
||||
}
|
||||
|
||||
void RSSIGraph::paint(Painter& painter) {
|
||||
const auto r = screen_rect();
|
||||
|
||||
RSSIGraph_entry& prev_entry = graph_list[0];
|
||||
int xpos = 0 , prev_xpos = r.width();
|
||||
|
||||
for( int n = 1 ; (unsigned)n <= graph_list.size() ; n++ )
|
||||
{
|
||||
xpos = ( r.width() * (graph_list.size() - n ) ) / graph_list.size() ;
|
||||
int size = abs( xpos - prev_xpos );
|
||||
RSSIGraph_entry& entry = graph_list[ n - 1 ];
|
||||
|
||||
// black
|
||||
const Rect r0{ r.right() - prev_xpos , r.top() , size , r.height() };
|
||||
painter.fill_rectangle(
|
||||
r0,
|
||||
Color::black());
|
||||
|
||||
// y_max
|
||||
int top_y_val = max( entry.rssi_max , prev_entry.rssi_max );
|
||||
int width_y = abs( entry.rssi_max - prev_entry.rssi_max );
|
||||
if( width_y == 0 )
|
||||
width_y = 1 ;
|
||||
const Point p1v{ r.right() - prev_xpos , r.bottom() - top_y_val };
|
||||
painter.draw_vline(
|
||||
p1v,
|
||||
width_y,
|
||||
Color::red());
|
||||
const Point p1h{ r.right() - prev_xpos , r.bottom() - entry.rssi_max };
|
||||
painter.draw_hline(
|
||||
p1h,
|
||||
size,
|
||||
Color::red());
|
||||
|
||||
// y_avg
|
||||
top_y_val = max( entry.rssi_avg , prev_entry.rssi_avg );
|
||||
width_y = abs( entry.rssi_avg - prev_entry.rssi_avg );
|
||||
if( width_y == 0 )
|
||||
width_y = 1 ;
|
||||
const Point p2v{ r.right() - prev_xpos , r.bottom() - top_y_val };
|
||||
painter.draw_vline(
|
||||
p2v,
|
||||
width_y,
|
||||
Color::white());
|
||||
const Point p2h{ r.right() - prev_xpos , r.bottom() - entry.rssi_avg };
|
||||
painter.draw_hline(
|
||||
p2h,
|
||||
size,
|
||||
Color::white());
|
||||
|
||||
// y_min
|
||||
top_y_val = max( entry.rssi_min , prev_entry.rssi_min );
|
||||
width_y = abs( entry.rssi_min - prev_entry.rssi_min );
|
||||
if( width_y == 0 )
|
||||
width_y = 1 ;
|
||||
const Point p3v{ r.right() - prev_xpos , r.bottom() - top_y_val };
|
||||
painter.draw_vline(
|
||||
p3v,
|
||||
width_y,
|
||||
Color::blue());
|
||||
const Point p3h{ r.right() - prev_xpos , r.bottom() - entry.rssi_min };
|
||||
painter.draw_hline(
|
||||
p3h,
|
||||
size,
|
||||
Color::blue());
|
||||
|
||||
// hack to display db
|
||||
top_y_val = max( entry.db , prev_entry.db );
|
||||
width_y = abs( entry.db - prev_entry.db );
|
||||
if( width_y == 0 )
|
||||
width_y = 1 ;
|
||||
const Point p4v{ r.right() - prev_xpos , r.bottom() - top_y_val };
|
||||
painter.draw_vline(
|
||||
p4v,
|
||||
width_y,
|
||||
Color::green());
|
||||
const Point p4h{ r.right() - prev_xpos , r.bottom() - entry.db };
|
||||
painter.draw_hline(
|
||||
p4h,
|
||||
size,
|
||||
Color::green());
|
||||
|
||||
prev_entry = entry ;
|
||||
prev_xpos = xpos ;
|
||||
if (peak_duration_ > peak_duration) {
|
||||
peak_duration_ = 0;
|
||||
peak_ = max_;
|
||||
}
|
||||
}
|
||||
set_dirty();
|
||||
}
|
||||
|
||||
void RSSIGraph::paint(Painter& painter) {
|
||||
const auto r = screen_rect();
|
||||
|
||||
RSSIGraph_entry& prev_entry = graph_list[0];
|
||||
int xpos = 0, prev_xpos = r.width();
|
||||
|
||||
/*void RSSIGraph::paintOld(Painter& painter) {
|
||||
for (int n = 1; (unsigned)n <= graph_list.size(); n++) {
|
||||
xpos = (r.width() * (graph_list.size() - n)) / graph_list.size();
|
||||
int size = abs(xpos - prev_xpos);
|
||||
RSSIGraph_entry& entry = graph_list[n - 1];
|
||||
|
||||
// black
|
||||
const Rect r0{r.right() - prev_xpos, r.top(), size, r.height()};
|
||||
painter.fill_rectangle(
|
||||
r0,
|
||||
Color::black());
|
||||
|
||||
// y_max
|
||||
int top_y_val = max(entry.rssi_max, prev_entry.rssi_max);
|
||||
int width_y = abs(entry.rssi_max - prev_entry.rssi_max);
|
||||
if (width_y == 0)
|
||||
width_y = 1;
|
||||
const Point p1v{r.right() - prev_xpos, r.bottom() - top_y_val};
|
||||
painter.draw_vline(
|
||||
p1v,
|
||||
width_y,
|
||||
Color::red());
|
||||
const Point p1h{r.right() - prev_xpos, r.bottom() - entry.rssi_max};
|
||||
painter.draw_hline(
|
||||
p1h,
|
||||
size,
|
||||
Color::red());
|
||||
|
||||
// y_avg
|
||||
top_y_val = max(entry.rssi_avg, prev_entry.rssi_avg);
|
||||
width_y = abs(entry.rssi_avg - prev_entry.rssi_avg);
|
||||
if (width_y == 0)
|
||||
width_y = 1;
|
||||
const Point p2v{r.right() - prev_xpos, r.bottom() - top_y_val};
|
||||
painter.draw_vline(
|
||||
p2v,
|
||||
width_y,
|
||||
Color::white());
|
||||
const Point p2h{r.right() - prev_xpos, r.bottom() - entry.rssi_avg};
|
||||
painter.draw_hline(
|
||||
p2h,
|
||||
size,
|
||||
Color::white());
|
||||
|
||||
// y_min
|
||||
top_y_val = max(entry.rssi_min, prev_entry.rssi_min);
|
||||
width_y = abs(entry.rssi_min - prev_entry.rssi_min);
|
||||
if (width_y == 0)
|
||||
width_y = 1;
|
||||
const Point p3v{r.right() - prev_xpos, r.bottom() - top_y_val};
|
||||
painter.draw_vline(
|
||||
p3v,
|
||||
width_y,
|
||||
Color::blue());
|
||||
const Point p3h{r.right() - prev_xpos, r.bottom() - entry.rssi_min};
|
||||
painter.draw_hline(
|
||||
p3h,
|
||||
size,
|
||||
Color::blue());
|
||||
|
||||
// hack to display db
|
||||
top_y_val = max(entry.db, prev_entry.db);
|
||||
width_y = abs(entry.db - prev_entry.db);
|
||||
if (width_y == 0)
|
||||
width_y = 1;
|
||||
const Point p4v{r.right() - prev_xpos, r.bottom() - top_y_val};
|
||||
painter.draw_vline(
|
||||
p4v,
|
||||
width_y,
|
||||
Color::green());
|
||||
const Point p4h{r.right() - prev_xpos, r.bottom() - entry.db};
|
||||
painter.draw_hline(
|
||||
p4h,
|
||||
size,
|
||||
Color::green());
|
||||
|
||||
prev_entry = entry;
|
||||
prev_xpos = xpos;
|
||||
}
|
||||
}
|
||||
|
||||
/*void RSSIGraph::paintOld(Painter& painter) {
|
||||
const auto r = screen_rect();
|
||||
int16_t size = r.width() / nb_columns ;
|
||||
int16_t top_y_val = 0 ;
|
||||
@@ -336,7 +304,7 @@ namespace ui {
|
||||
if( size < 1 )
|
||||
size = 1 ;
|
||||
|
||||
int xpos = r.right() ;
|
||||
int xpos = r.right() ;
|
||||
for ( int n = 2 ; (unsigned)n <= graph_list.size(); n++) {
|
||||
auto& entry = graph_list[graph_list.size()-n];
|
||||
auto& prev_entry = graph_list[graph_list.size()-(n-1)];
|
||||
@@ -357,7 +325,7 @@ namespace ui {
|
||||
r1,
|
||||
Color::red());
|
||||
|
||||
// y_avg
|
||||
// y_avg
|
||||
top_y_val = max( entry.rssi_avg , prev_entry.rssi_avg );
|
||||
width_y = abs( entry.rssi_avg - prev_entry.rssi_avg );
|
||||
if( width_y == 0 )
|
||||
@@ -397,105 +365,100 @@ namespace ui {
|
||||
}
|
||||
}*/
|
||||
|
||||
void RSSIGraph::add_values(int16_t rssi_min, int16_t rssi_avg, int16_t rssi_max, int16_t db )
|
||||
{
|
||||
const auto r = screen_rect();
|
||||
void RSSIGraph::add_values(int16_t rssi_min, int16_t rssi_avg, int16_t rssi_max, int16_t db) {
|
||||
const auto r = screen_rect();
|
||||
|
||||
constexpr int rssi_sample_range = 256;
|
||||
//constexpr float rssi_voltage_min = 0.4;
|
||||
constexpr float rssi_voltage_max = 2.2;
|
||||
constexpr float adc_voltage_max = 3.3;
|
||||
//constexpr int raw_min = rssi_sample_range * rssi_voltage_min / adc_voltage_max;
|
||||
constexpr int raw_min = 0 ;
|
||||
constexpr int raw_max = rssi_sample_range * rssi_voltage_max / adc_voltage_max;
|
||||
constexpr int raw_delta = raw_max - raw_min;
|
||||
constexpr int rssi_sample_range = 256;
|
||||
// constexpr float rssi_voltage_min = 0.4;
|
||||
constexpr float rssi_voltage_max = 2.2;
|
||||
constexpr float adc_voltage_max = 3.3;
|
||||
// constexpr int raw_min = rssi_sample_range * rssi_voltage_min / adc_voltage_max;
|
||||
constexpr int raw_min = 0;
|
||||
constexpr int raw_max = rssi_sample_range * rssi_voltage_max / adc_voltage_max;
|
||||
constexpr int raw_delta = raw_max - raw_min;
|
||||
|
||||
// vertical bottom to top level meters
|
||||
const range_t<int> y_avg_range { 0, r.height() - 1 };
|
||||
const int16_t y_avg = y_avg_range.clip((rssi_avg - raw_min) * r.height() / raw_delta);
|
||||
const range_t<int> y_min_range { 0, y_avg };
|
||||
const int16_t y_min = y_min_range.clip((rssi_min - raw_min) * r.height() / raw_delta);
|
||||
const range_t<int> y_max_range { y_avg + 1, r.height() - 1 };
|
||||
const int16_t y_max = y_max_range.clip((rssi_max - raw_min) * r.height() / raw_delta);
|
||||
const range_t<int> db_range { -80 , 10 };
|
||||
int16_t db_ = db_range.clip( db );
|
||||
db_ = db_ - 10 ;
|
||||
db_ = db_ * r.height() / 90 ;
|
||||
db_ = r.height() + db_ ;
|
||||
// vertical bottom to top level meters
|
||||
const range_t<int> y_avg_range{0, r.height() - 1};
|
||||
const int16_t y_avg = y_avg_range.clip((rssi_avg - raw_min) * r.height() / raw_delta);
|
||||
const range_t<int> y_min_range{0, y_avg};
|
||||
const int16_t y_min = y_min_range.clip((rssi_min - raw_min) * r.height() / raw_delta);
|
||||
const range_t<int> y_max_range{y_avg + 1, r.height() - 1};
|
||||
const int16_t y_max = y_max_range.clip((rssi_max - raw_min) * r.height() / raw_delta);
|
||||
const range_t<int> db_range{-80, 10};
|
||||
int16_t db_ = db_range.clip(db);
|
||||
db_ = db_ - 10;
|
||||
db_ = db_ * r.height() / 90;
|
||||
db_ = r.height() + db_;
|
||||
|
||||
graph_list . push_back( { y_min, y_avg, y_max , db_ } );
|
||||
while( graph_list.size() > nb_columns )
|
||||
{
|
||||
graph_list.erase( graph_list.begin() );
|
||||
}
|
||||
set_dirty();
|
||||
graph_list.push_back({y_min, y_avg, y_max, db_});
|
||||
while (graph_list.size() > nb_columns) {
|
||||
graph_list.erase(graph_list.begin());
|
||||
}
|
||||
set_dirty();
|
||||
}
|
||||
|
||||
void RSSIGraph::set_nb_columns( int16_t nb )
|
||||
{
|
||||
nb_columns = nb ;
|
||||
while( graph_list.size() > nb_columns )
|
||||
{
|
||||
graph_list.erase( graph_list.begin() );
|
||||
void RSSIGraph::set_nb_columns(int16_t nb) {
|
||||
nb_columns = nb;
|
||||
while (graph_list.size() > nb_columns) {
|
||||
graph_list.erase(graph_list.begin());
|
||||
}
|
||||
}
|
||||
|
||||
void RSSIGraph::on_hide() {
|
||||
nb_columns_before_hide = nb_columns;
|
||||
nb_columns = 1;
|
||||
while (graph_list.size() > nb_columns) {
|
||||
graph_list.erase(graph_list.begin());
|
||||
}
|
||||
}
|
||||
|
||||
void RSSIGraph::on_show() {
|
||||
nb_columns = nb_columns_before_hide;
|
||||
}
|
||||
|
||||
void RSSI::on_focus() {
|
||||
if (on_highlight)
|
||||
on_highlight(*this);
|
||||
}
|
||||
|
||||
bool RSSI::on_key(const KeyEvent key) {
|
||||
if (key == KeyEvent::Select) {
|
||||
if (on_select) {
|
||||
on_select(*this);
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
if (on_dir) {
|
||||
return on_dir(*this, key);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void RSSIGraph::on_hide() {
|
||||
nb_columns_before_hide = nb_columns ;
|
||||
nb_columns = 1 ;
|
||||
while( graph_list.size() > nb_columns )
|
||||
{
|
||||
graph_list.erase( graph_list.begin() );
|
||||
}
|
||||
}
|
||||
|
||||
void RSSIGraph::on_show() {
|
||||
nb_columns = nb_columns_before_hide ;
|
||||
}
|
||||
|
||||
void RSSI::on_focus() {
|
||||
if( on_highlight )
|
||||
on_highlight(*this);
|
||||
}
|
||||
|
||||
bool RSSI::on_key(const KeyEvent key) {
|
||||
if( key == KeyEvent::Select ) {
|
||||
if( on_select ) {
|
||||
bool RSSI::on_touch(const TouchEvent event) {
|
||||
switch (event.type) {
|
||||
case TouchEvent::Type::Start:
|
||||
set_highlighted(true);
|
||||
set_dirty();
|
||||
if (on_touch_press) {
|
||||
on_touch_press(*this);
|
||||
}
|
||||
if (on_select && instant_exec_) {
|
||||
on_select(*this);
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
if( on_dir ) {
|
||||
return on_dir(*this, key);
|
||||
return true;
|
||||
case TouchEvent::Type::End:
|
||||
set_highlighted(false);
|
||||
set_dirty();
|
||||
if (on_touch_release) {
|
||||
on_touch_release(*this);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool RSSI::on_touch(const TouchEvent event) {
|
||||
switch(event.type) {
|
||||
case TouchEvent::Type::Start:
|
||||
set_highlighted(true);
|
||||
set_dirty();
|
||||
if( on_touch_press) {
|
||||
on_touch_press(*this);
|
||||
}
|
||||
if( on_select && instant_exec_ ) {
|
||||
on_select(*this);
|
||||
}
|
||||
return true;
|
||||
case TouchEvent::Type::End:
|
||||
set_highlighted(false);
|
||||
set_dirty();
|
||||
if( on_touch_release) {
|
||||
on_touch_release(*this);
|
||||
}
|
||||
if( on_select && !instant_exec_ ) {
|
||||
on_select(*this);
|
||||
}
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
if (on_select && !instant_exec_) {
|
||||
on_select(*this);
|
||||
}
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} /* namespace ui */
|
||||
|
@@ -31,100 +31,95 @@
|
||||
|
||||
namespace ui {
|
||||
|
||||
class RSSI : public Widget {
|
||||
public:
|
||||
std::function<void(RSSI&)> on_select { };
|
||||
std::function<void(RSSI&)> on_touch_release { }; // Executed when releasing touch, after on_select.
|
||||
std::function<void(RSSI&)> on_touch_press { }; // Executed when touching, before on_select.
|
||||
std::function<bool(RSSI&, KeyEvent)> on_dir { };
|
||||
std::function<void(RSSI&)> on_highlight { };
|
||||
class RSSI : public Widget {
|
||||
public:
|
||||
std::function<void(RSSI&)> on_select{};
|
||||
std::function<void(RSSI&)> on_touch_release{}; // Executed when releasing touch, after on_select.
|
||||
std::function<void(RSSI&)> on_touch_press{}; // Executed when touching, before on_select.
|
||||
std::function<bool(RSSI&, KeyEvent)> on_dir{};
|
||||
std::function<void(RSSI&)> on_highlight{};
|
||||
|
||||
RSSI(Rect parent_rect, bool instant_exec); // instant_exec: Execute on_select when you touching instead of releasing
|
||||
RSSI(
|
||||
Rect parent_rect
|
||||
) : RSSI { parent_rect, false }
|
||||
{
|
||||
}
|
||||
RSSI(Rect parent_rect, bool instant_exec); // instant_exec: Execute on_select when you touching instead of releasing
|
||||
RSSI(
|
||||
Rect parent_rect)
|
||||
: RSSI{parent_rect, false} {
|
||||
}
|
||||
|
||||
RSSI(
|
||||
) : RSSI { { }, { } }
|
||||
{
|
||||
}
|
||||
RSSI()
|
||||
: RSSI{{}, {}} {
|
||||
}
|
||||
|
||||
int16_t get_min();
|
||||
int16_t get_avg();
|
||||
int16_t get_max();
|
||||
int16_t get_delta();
|
||||
void set_vertical_rssi(bool enabled);
|
||||
void set_peak(bool enabled, size_t duration);
|
||||
int16_t get_min();
|
||||
int16_t get_avg();
|
||||
int16_t get_max();
|
||||
int16_t get_delta();
|
||||
void set_vertical_rssi(bool enabled);
|
||||
void set_peak(bool enabled, size_t duration);
|
||||
|
||||
void paint(Painter& painter) override;
|
||||
void on_focus() override;
|
||||
bool on_key(const KeyEvent key) override;
|
||||
bool on_touch(const TouchEvent event) override;
|
||||
void paint(Painter& painter) override;
|
||||
void on_focus() override;
|
||||
bool on_key(const KeyEvent key) override;
|
||||
bool on_touch(const TouchEvent event) override;
|
||||
|
||||
private:
|
||||
int16_t min_ = 0;
|
||||
int16_t avg_ = 0;
|
||||
int16_t max_ = 0;
|
||||
int16_t peak_ = 0 ;
|
||||
size_t peak_duration_ = 0 ;
|
||||
bool instant_exec_ { false };
|
||||
private:
|
||||
int16_t min_ = 0;
|
||||
int16_t avg_ = 0;
|
||||
int16_t max_ = 0;
|
||||
int16_t peak_ = 0;
|
||||
size_t peak_duration_ = 0;
|
||||
bool instant_exec_{false};
|
||||
|
||||
bool pitch_rssi_enabled = false;
|
||||
bool vertical_rssi_enabled = false; // scale [vertically/from bottom to top]
|
||||
// instead of [horizontally/from left to right]
|
||||
bool peak_enabled = false;
|
||||
size_t peak_duration = 1000; // peak duration in msec before being reset to actual max_rssi
|
||||
bool pitch_rssi_enabled = false;
|
||||
bool vertical_rssi_enabled = false; // scale [vertically/from bottom to top]
|
||||
// instead of [horizontally/from left to right]
|
||||
bool peak_enabled = false;
|
||||
size_t peak_duration = 1000; // peak duration in msec before being reset to actual max_rssi
|
||||
|
||||
MessageHandlerRegistration message_handler_stats {
|
||||
Message::ID::RSSIStatistics,
|
||||
[this](const Message* const p) {
|
||||
this->on_statistics_update(static_cast<const RSSIStatisticsMessage*>(p)->statistics);
|
||||
}
|
||||
};
|
||||
MessageHandlerRegistration message_handler_stats{
|
||||
Message::ID::RSSIStatistics,
|
||||
[this](const Message* const p) {
|
||||
this->on_statistics_update(static_cast<const RSSIStatisticsMessage*>(p)->statistics);
|
||||
}};
|
||||
|
||||
MessageHandlerRegistration message_handler_pitch_rssi {
|
||||
Message::ID::PitchRSSIConfigure,
|
||||
[this](const Message* const p) {
|
||||
const auto message = *reinterpret_cast<const PitchRSSIConfigureMessage*>(p);
|
||||
this->set_pitch_rssi(message.enabled);
|
||||
}
|
||||
};
|
||||
MessageHandlerRegistration message_handler_pitch_rssi{
|
||||
Message::ID::PitchRSSIConfigure,
|
||||
[this](const Message* const p) {
|
||||
const auto message = *reinterpret_cast<const PitchRSSIConfigureMessage*>(p);
|
||||
this->set_pitch_rssi(message.enabled);
|
||||
}};
|
||||
|
||||
void on_statistics_update(const RSSIStatistics& statistics);
|
||||
void set_pitch_rssi(bool enabled);
|
||||
};
|
||||
void on_statistics_update(const RSSIStatistics& statistics);
|
||||
void set_pitch_rssi(bool enabled);
|
||||
};
|
||||
|
||||
struct RSSIGraph_entry {
|
||||
int16_t rssi_min { 0 };
|
||||
int16_t rssi_avg { 0 };
|
||||
int16_t rssi_max { 0 };
|
||||
int16_t db { 0 };
|
||||
};
|
||||
struct RSSIGraph_entry {
|
||||
int16_t rssi_min{0};
|
||||
int16_t rssi_avg{0};
|
||||
int16_t rssi_max{0};
|
||||
int16_t db{0};
|
||||
};
|
||||
|
||||
using RSSIGraphList = std::vector<RSSIGraph_entry>;
|
||||
using RSSIGraphList = std::vector<RSSIGraph_entry>;
|
||||
|
||||
class RSSIGraph : public Widget {
|
||||
public:
|
||||
RSSIGraph(
|
||||
const Rect parent_rect
|
||||
) : Widget { parent_rect }
|
||||
{
|
||||
}
|
||||
void paint(Painter& painter) override;
|
||||
void add_values(int16_t rssi_min, int16_t rssi_avg, int16_t rssi_max, int16_t db );
|
||||
void set_nb_columns( int16_t nb );
|
||||
|
||||
void on_hide() override ;
|
||||
void on_show() override ;
|
||||
class RSSIGraph : public Widget {
|
||||
public:
|
||||
RSSIGraph(
|
||||
const Rect parent_rect)
|
||||
: Widget{parent_rect} {
|
||||
}
|
||||
void paint(Painter& painter) override;
|
||||
void add_values(int16_t rssi_min, int16_t rssi_avg, int16_t rssi_max, int16_t db);
|
||||
void set_nb_columns(int16_t nb);
|
||||
|
||||
private:
|
||||
uint16_t nb_columns_before_hide = 16 ;
|
||||
uint16_t nb_columns = 16 ;
|
||||
RSSIGraphList graph_list { } ;
|
||||
};
|
||||
void on_hide() override;
|
||||
void on_show() override;
|
||||
|
||||
}
|
||||
private:
|
||||
uint16_t nb_columns_before_hide = 16;
|
||||
uint16_t nb_columns = 16;
|
||||
RSSIGraphList graph_list{};
|
||||
};
|
||||
|
||||
#endif/*__UI_RSSI_H__*/
|
||||
} // namespace ui
|
||||
|
||||
#endif /*__UI_RSSI_H__*/
|
||||
|
@@ -39,355 +39,337 @@ namespace spectrum {
|
||||
/* AudioSpectrumView******************************************************/
|
||||
|
||||
AudioSpectrumView::AudioSpectrumView(
|
||||
const Rect parent_rect
|
||||
) : View { parent_rect }
|
||||
{
|
||||
set_focusable(true);
|
||||
|
||||
add_children({
|
||||
&labels,
|
||||
&field_frequency,
|
||||
&waveform
|
||||
});
|
||||
|
||||
field_frequency.on_change = [this](int32_t) {
|
||||
set_dirty();
|
||||
};
|
||||
field_frequency.set_value(0);
|
||||
const Rect parent_rect)
|
||||
: View{parent_rect} {
|
||||
set_focusable(true);
|
||||
|
||||
add_children({&labels,
|
||||
&field_frequency,
|
||||
&waveform});
|
||||
|
||||
field_frequency.on_change = [this](int32_t) {
|
||||
set_dirty();
|
||||
};
|
||||
field_frequency.set_value(0);
|
||||
}
|
||||
|
||||
void AudioSpectrumView::paint(Painter& painter) {
|
||||
const auto r = screen_rect();
|
||||
const auto r = screen_rect();
|
||||
|
||||
painter.fill_rectangle(r, Color::black());
|
||||
painter.fill_rectangle(r, Color::black());
|
||||
|
||||
//if( !spectrum_sampling_rate ) return;
|
||||
|
||||
// Cursor
|
||||
const Rect r_cursor {
|
||||
field_frequency.value() / (48000 / 240), r.bottom() - 32 - cursor_band_height,
|
||||
1, cursor_band_height
|
||||
};
|
||||
painter.fill_rectangle(
|
||||
r_cursor,
|
||||
Color::red()
|
||||
);
|
||||
// if( !spectrum_sampling_rate ) return;
|
||||
|
||||
// Cursor
|
||||
const Rect r_cursor{
|
||||
field_frequency.value() / (48000 / 240), r.bottom() - 32 - cursor_band_height,
|
||||
1, cursor_band_height};
|
||||
painter.fill_rectangle(
|
||||
r_cursor,
|
||||
Color::red());
|
||||
}
|
||||
|
||||
void AudioSpectrumView::on_audio_spectrum(const AudioSpectrum* spectrum) {
|
||||
for (size_t i = 0; i < spectrum->db.size(); i++)
|
||||
audio_spectrum[i] = ((int16_t)spectrum->db[i] - 127) * 256;
|
||||
waveform.set_dirty();
|
||||
for (size_t i = 0; i < spectrum->db.size(); i++)
|
||||
audio_spectrum[i] = ((int16_t)spectrum->db[i] - 127) * 256;
|
||||
waveform.set_dirty();
|
||||
}
|
||||
|
||||
/* FrequencyScale ********************************************************/
|
||||
|
||||
void FrequencyScale::on_show() {
|
||||
clear();
|
||||
clear();
|
||||
}
|
||||
|
||||
void FrequencyScale::set_spectrum_sampling_rate(const int new_sampling_rate) {
|
||||
if( (spectrum_sampling_rate != new_sampling_rate) ) {
|
||||
spectrum_sampling_rate = new_sampling_rate;
|
||||
set_dirty();
|
||||
}
|
||||
if ((spectrum_sampling_rate != new_sampling_rate)) {
|
||||
spectrum_sampling_rate = new_sampling_rate;
|
||||
set_dirty();
|
||||
}
|
||||
}
|
||||
|
||||
void FrequencyScale::set_channel_filter(
|
||||
const int low_frequency,
|
||||
const int high_frequency,
|
||||
const int transition
|
||||
) {
|
||||
if( (channel_filter_low_frequency != low_frequency) ||
|
||||
(channel_filter_high_frequency != high_frequency) ||
|
||||
(channel_filter_transition != transition) ) {
|
||||
channel_filter_low_frequency = low_frequency;
|
||||
channel_filter_high_frequency = high_frequency;
|
||||
channel_filter_transition = transition;
|
||||
set_dirty();
|
||||
}
|
||||
const int low_frequency,
|
||||
const int high_frequency,
|
||||
const int transition) {
|
||||
if ((channel_filter_low_frequency != low_frequency) ||
|
||||
(channel_filter_high_frequency != high_frequency) ||
|
||||
(channel_filter_transition != transition)) {
|
||||
channel_filter_low_frequency = low_frequency;
|
||||
channel_filter_high_frequency = high_frequency;
|
||||
channel_filter_transition = transition;
|
||||
set_dirty();
|
||||
}
|
||||
}
|
||||
|
||||
void FrequencyScale::paint(Painter& painter) {
|
||||
const auto r = screen_rect();
|
||||
const auto r = screen_rect();
|
||||
|
||||
clear_background(painter, r);
|
||||
clear_background(painter, r);
|
||||
|
||||
if( !spectrum_sampling_rate ) {
|
||||
// Can't draw without non-zero scale.
|
||||
return;
|
||||
}
|
||||
if (!spectrum_sampling_rate) {
|
||||
// Can't draw without non-zero scale.
|
||||
return;
|
||||
}
|
||||
|
||||
draw_filter_ranges(painter, r);
|
||||
draw_frequency_ticks(painter, r);
|
||||
|
||||
if (_blink) {
|
||||
const Rect r_cursor {
|
||||
118 + cursor_position, r.bottom() - filter_band_height,
|
||||
5, filter_band_height
|
||||
};
|
||||
painter.fill_rectangle(
|
||||
r_cursor,
|
||||
Color::red()
|
||||
);
|
||||
}
|
||||
draw_filter_ranges(painter, r);
|
||||
draw_frequency_ticks(painter, r);
|
||||
|
||||
if (_blink) {
|
||||
const Rect r_cursor{
|
||||
118 + cursor_position, r.bottom() - filter_band_height,
|
||||
5, filter_band_height};
|
||||
painter.fill_rectangle(
|
||||
r_cursor,
|
||||
Color::red());
|
||||
}
|
||||
}
|
||||
|
||||
void FrequencyScale::clear() {
|
||||
spectrum_sampling_rate = 0;
|
||||
set_dirty();
|
||||
spectrum_sampling_rate = 0;
|
||||
set_dirty();
|
||||
}
|
||||
|
||||
void FrequencyScale::clear_background(Painter& painter, const Rect r) {
|
||||
painter.fill_rectangle(r, Color::black());
|
||||
painter.fill_rectangle(r, Color::black());
|
||||
}
|
||||
|
||||
void FrequencyScale::draw_frequency_ticks(Painter& painter, const Rect r) {
|
||||
const auto x_center = r.width() / 2;
|
||||
const auto x_center = r.width() / 2;
|
||||
|
||||
const Rect tick { r.left() + x_center, r.top(), 1, r.height() };
|
||||
painter.fill_rectangle(tick, Color::white());
|
||||
const Rect tick{r.left() + x_center, r.top(), 1, r.height()};
|
||||
painter.fill_rectangle(tick, Color::white());
|
||||
|
||||
constexpr int tick_count_max = 4;
|
||||
float rough_tick_interval = float(spectrum_sampling_rate) / tick_count_max;
|
||||
int magnitude = 1;
|
||||
int magnitude_n = 0;
|
||||
while(rough_tick_interval >= 10.0f) {
|
||||
rough_tick_interval /= 10;
|
||||
magnitude *= 10;
|
||||
magnitude_n += 1;
|
||||
}
|
||||
const int tick_interval = std::ceil(rough_tick_interval);
|
||||
constexpr int tick_count_max = 4;
|
||||
float rough_tick_interval = float(spectrum_sampling_rate) / tick_count_max;
|
||||
int magnitude = 1;
|
||||
int magnitude_n = 0;
|
||||
while (rough_tick_interval >= 10.0f) {
|
||||
rough_tick_interval /= 10;
|
||||
magnitude *= 10;
|
||||
magnitude_n += 1;
|
||||
}
|
||||
const int tick_interval = std::ceil(rough_tick_interval);
|
||||
|
||||
auto tick_offset = tick_interval;
|
||||
while((tick_offset * magnitude) < spectrum_sampling_rate / 2) {
|
||||
const Dim pixel_offset = tick_offset * magnitude * spectrum_bins / spectrum_sampling_rate;
|
||||
auto tick_offset = tick_interval;
|
||||
while ((tick_offset * magnitude) < spectrum_sampling_rate / 2) {
|
||||
const Dim pixel_offset = tick_offset * magnitude * spectrum_bins / spectrum_sampling_rate;
|
||||
|
||||
const std::string zero_pad =
|
||||
((magnitude_n % 3) == 0) ? "" :
|
||||
((magnitude_n % 3) == 1) ? "0" : "00";
|
||||
const std::string unit =
|
||||
(magnitude_n >= 6) ? "M" :
|
||||
(magnitude_n >= 3) ? "k" : "";
|
||||
const std::string label = to_string_dec_uint(tick_offset) + zero_pad + unit;
|
||||
const auto label_width = style().font.size_of(label).width();
|
||||
|
||||
const Coord offset_low = r.left() + x_center - pixel_offset;
|
||||
const Rect tick_low { offset_low, r.top(), 1, r.height() };
|
||||
painter.fill_rectangle(tick_low, Color::white());
|
||||
painter.draw_string({ offset_low + 2, r.top() }, style(), label );
|
||||
const std::string zero_pad =
|
||||
((magnitude_n % 3) == 0) ? "" : ((magnitude_n % 3) == 1) ? "0"
|
||||
: "00";
|
||||
const std::string unit =
|
||||
(magnitude_n >= 6) ? "M" : (magnitude_n >= 3) ? "k"
|
||||
: "";
|
||||
const std::string label = to_string_dec_uint(tick_offset) + zero_pad + unit;
|
||||
const auto label_width = style().font.size_of(label).width();
|
||||
|
||||
const Coord offset_high = r.left() + x_center + pixel_offset;
|
||||
const Rect tick_high { offset_high, r.top(), 1, r.height() };
|
||||
painter.fill_rectangle(tick_high, Color::white());
|
||||
painter.draw_string({ offset_high - 2 - label_width, r.top() }, style(), label );
|
||||
const Coord offset_low = r.left() + x_center - pixel_offset;
|
||||
const Rect tick_low{offset_low, r.top(), 1, r.height()};
|
||||
painter.fill_rectangle(tick_low, Color::white());
|
||||
painter.draw_string({offset_low + 2, r.top()}, style(), label);
|
||||
|
||||
tick_offset += tick_interval;
|
||||
}
|
||||
const Coord offset_high = r.left() + x_center + pixel_offset;
|
||||
const Rect tick_high{offset_high, r.top(), 1, r.height()};
|
||||
painter.fill_rectangle(tick_high, Color::white());
|
||||
painter.draw_string({offset_high - 2 - label_width, r.top()}, style(), label);
|
||||
|
||||
tick_offset += tick_interval;
|
||||
}
|
||||
}
|
||||
|
||||
void FrequencyScale::draw_filter_ranges(Painter& painter, const Rect r) {
|
||||
if( channel_filter_low_frequency != channel_filter_high_frequency ) {
|
||||
const auto x_center = r.width() / 2;
|
||||
if (channel_filter_low_frequency != channel_filter_high_frequency) {
|
||||
const auto x_center = r.width() / 2;
|
||||
|
||||
const auto x_low = x_center + channel_filter_low_frequency * spectrum_bins / spectrum_sampling_rate;
|
||||
const auto x_high = x_center + channel_filter_high_frequency * spectrum_bins / spectrum_sampling_rate;
|
||||
const auto x_low = x_center + channel_filter_low_frequency * spectrum_bins / spectrum_sampling_rate;
|
||||
const auto x_high = x_center + channel_filter_high_frequency * spectrum_bins / spectrum_sampling_rate;
|
||||
|
||||
if( channel_filter_transition ) {
|
||||
const auto trans = channel_filter_transition * spectrum_bins / spectrum_sampling_rate;
|
||||
if (channel_filter_transition) {
|
||||
const auto trans = channel_filter_transition * spectrum_bins / spectrum_sampling_rate;
|
||||
|
||||
const Rect r_all {
|
||||
r.left() + x_low - trans, r.bottom() - filter_band_height,
|
||||
x_high - x_low + trans*2, filter_band_height
|
||||
};
|
||||
painter.fill_rectangle(
|
||||
r_all,
|
||||
Color::yellow()
|
||||
);
|
||||
}
|
||||
const Rect r_all{
|
||||
r.left() + x_low - trans, r.bottom() - filter_band_height,
|
||||
x_high - x_low + trans * 2, filter_band_height};
|
||||
painter.fill_rectangle(
|
||||
r_all,
|
||||
Color::yellow());
|
||||
}
|
||||
|
||||
const Rect r_pass {
|
||||
r.left() + x_low, r.bottom() - filter_band_height,
|
||||
x_high - x_low, filter_band_height
|
||||
};
|
||||
painter.fill_rectangle(
|
||||
r_pass,
|
||||
Color::green()
|
||||
);
|
||||
}
|
||||
const Rect r_pass{
|
||||
r.left() + x_low, r.bottom() - filter_band_height,
|
||||
x_high - x_low, filter_band_height};
|
||||
painter.fill_rectangle(
|
||||
r_pass,
|
||||
Color::green());
|
||||
}
|
||||
}
|
||||
|
||||
void FrequencyScale::on_focus() {
|
||||
_blink = true;
|
||||
on_tick_second();
|
||||
signal_token_tick_second = rtc_time::signal_tick_second += [this]() {
|
||||
this->on_tick_second();
|
||||
};
|
||||
_blink = true;
|
||||
on_tick_second();
|
||||
signal_token_tick_second = rtc_time::signal_tick_second += [this]() {
|
||||
this->on_tick_second();
|
||||
};
|
||||
}
|
||||
|
||||
void FrequencyScale::on_blur() {
|
||||
rtc_time::signal_tick_second -= signal_token_tick_second;
|
||||
_blink = false;
|
||||
set_dirty();
|
||||
rtc_time::signal_tick_second -= signal_token_tick_second;
|
||||
_blink = false;
|
||||
set_dirty();
|
||||
}
|
||||
|
||||
bool FrequencyScale::on_encoder(const EncoderEvent delta) {
|
||||
cursor_position += delta;
|
||||
|
||||
cursor_position = std::min<int32_t>(cursor_position, 119);
|
||||
cursor_position = std::max<int32_t>(cursor_position, -120);
|
||||
|
||||
set_dirty();
|
||||
|
||||
return true;
|
||||
cursor_position += delta;
|
||||
|
||||
cursor_position = std::min<int32_t>(cursor_position, 119);
|
||||
cursor_position = std::max<int32_t>(cursor_position, -120);
|
||||
|
||||
set_dirty();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FrequencyScale::on_key(const KeyEvent key) {
|
||||
if( key == KeyEvent::Select ) {
|
||||
if( on_select ) {
|
||||
on_select((cursor_position * spectrum_sampling_rate) / 240);
|
||||
cursor_position = 0;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
if (key == KeyEvent::Select) {
|
||||
if (on_select) {
|
||||
on_select((cursor_position * spectrum_sampling_rate) / 240);
|
||||
cursor_position = 0;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void FrequencyScale::on_tick_second() {
|
||||
set_dirty();
|
||||
_blink = !_blink;
|
||||
set_dirty();
|
||||
_blink = !_blink;
|
||||
}
|
||||
|
||||
/* WaterfallView *********************************************************/
|
||||
|
||||
void WaterfallView::on_show() {
|
||||
clear();
|
||||
clear();
|
||||
|
||||
const auto screen_r = screen_rect();
|
||||
display.scroll_set_area(screen_r.top(), screen_r.bottom());
|
||||
const auto screen_r = screen_rect();
|
||||
display.scroll_set_area(screen_r.top(), screen_r.bottom());
|
||||
}
|
||||
|
||||
void WaterfallView::on_hide() {
|
||||
/* TODO: Clear region to eliminate brief flash of content at un-shifted
|
||||
* position?
|
||||
*/
|
||||
display.scroll_disable();
|
||||
/* TODO: Clear region to eliminate brief flash of content at un-shifted
|
||||
* position?
|
||||
*/
|
||||
display.scroll_disable();
|
||||
}
|
||||
|
||||
void WaterfallView::paint(Painter& painter) {
|
||||
// Do nothing.
|
||||
(void)painter;
|
||||
// Do nothing.
|
||||
(void)painter;
|
||||
}
|
||||
|
||||
void WaterfallView::on_channel_spectrum(
|
||||
const ChannelSpectrum& spectrum
|
||||
) {
|
||||
/* TODO: static_assert that message.spectrum.db.size() >= pixel_row.size() */
|
||||
const ChannelSpectrum& spectrum) {
|
||||
/* TODO: static_assert that message.spectrum.db.size() >= pixel_row.size() */
|
||||
|
||||
std::array<Color, 240> pixel_row;
|
||||
for(size_t i=0; i<120; i++) {
|
||||
const auto pixel_color = spectrum_rgb3_lut[spectrum.db[256 - 120 + i]];
|
||||
pixel_row[i] = pixel_color;
|
||||
}
|
||||
std::array<Color, 240> pixel_row;
|
||||
for (size_t i = 0; i < 120; i++) {
|
||||
const auto pixel_color = spectrum_rgb3_lut[spectrum.db[256 - 120 + i]];
|
||||
pixel_row[i] = pixel_color;
|
||||
}
|
||||
|
||||
for(size_t i=120; i<240; i++) {
|
||||
const auto pixel_color = spectrum_rgb3_lut[spectrum.db[i - 120]];
|
||||
pixel_row[i] = pixel_color;
|
||||
}
|
||||
for (size_t i = 120; i < 240; i++) {
|
||||
const auto pixel_color = spectrum_rgb3_lut[spectrum.db[i - 120]];
|
||||
pixel_row[i] = pixel_color;
|
||||
}
|
||||
|
||||
const auto draw_y = display.scroll(1);
|
||||
const auto draw_y = display.scroll(1);
|
||||
|
||||
display.draw_pixels(
|
||||
{ { 0, draw_y }, { pixel_row.size(), 1 } },
|
||||
pixel_row
|
||||
);
|
||||
display.draw_pixels(
|
||||
{{0, draw_y}, {pixel_row.size(), 1}},
|
||||
pixel_row);
|
||||
}
|
||||
|
||||
void WaterfallView::clear() {
|
||||
display.fill_rectangle(
|
||||
screen_rect(),
|
||||
Color::black()
|
||||
);
|
||||
display.fill_rectangle(
|
||||
screen_rect(),
|
||||
Color::black());
|
||||
}
|
||||
|
||||
/* WaterfallWidget *******************************************************/
|
||||
|
||||
WaterfallWidget::WaterfallWidget(const bool cursor) {
|
||||
add_children({
|
||||
&waterfall_view,
|
||||
&frequency_scale
|
||||
});
|
||||
|
||||
frequency_scale.set_focusable(cursor);
|
||||
|
||||
// Making the event climb up all the way up to here kinda sucks
|
||||
frequency_scale.on_select = [this](int32_t offset) {
|
||||
if (on_select) on_select(offset);
|
||||
};
|
||||
add_children({&waterfall_view,
|
||||
&frequency_scale});
|
||||
|
||||
frequency_scale.set_focusable(cursor);
|
||||
|
||||
// Making the event climb up all the way up to here kinda sucks
|
||||
frequency_scale.on_select = [this](int32_t offset) {
|
||||
if (on_select) on_select(offset);
|
||||
};
|
||||
}
|
||||
|
||||
void WaterfallWidget::on_show() {
|
||||
baseband::spectrum_streaming_start();
|
||||
baseband::spectrum_streaming_start();
|
||||
}
|
||||
|
||||
void WaterfallWidget::on_hide() {
|
||||
baseband::spectrum_streaming_stop();
|
||||
baseband::spectrum_streaming_stop();
|
||||
}
|
||||
|
||||
void WaterfallWidget::show_audio_spectrum_view(const bool show) {
|
||||
if ((audio_spectrum_view && show) || (!audio_spectrum_view && !show)) return;
|
||||
|
||||
if (show) {
|
||||
audio_spectrum_view = std::make_unique<AudioSpectrumView>(audio_spectrum_view_rect);
|
||||
add_child(audio_spectrum_view.get());
|
||||
update_widgets_rect();
|
||||
} else {
|
||||
audio_spectrum_update = false;
|
||||
remove_child(audio_spectrum_view.get());
|
||||
audio_spectrum_view.reset();
|
||||
update_widgets_rect();
|
||||
}
|
||||
if ((audio_spectrum_view && show) || (!audio_spectrum_view && !show)) return;
|
||||
|
||||
if (show) {
|
||||
audio_spectrum_view = std::make_unique<AudioSpectrumView>(audio_spectrum_view_rect);
|
||||
add_child(audio_spectrum_view.get());
|
||||
update_widgets_rect();
|
||||
} else {
|
||||
audio_spectrum_update = false;
|
||||
remove_child(audio_spectrum_view.get());
|
||||
audio_spectrum_view.reset();
|
||||
update_widgets_rect();
|
||||
}
|
||||
}
|
||||
|
||||
void WaterfallWidget::update_widgets_rect() {
|
||||
if (audio_spectrum_view) {
|
||||
frequency_scale.set_parent_rect({ 0, audio_spectrum_height, screen_rect().width(), scale_height });
|
||||
waterfall_view.set_parent_rect(waterfall_reduced_rect);
|
||||
} else {
|
||||
frequency_scale.set_parent_rect({ 0, 0, screen_rect().width(), scale_height });
|
||||
waterfall_view.set_parent_rect(waterfall_normal_rect);
|
||||
}
|
||||
waterfall_view.on_show();
|
||||
if (audio_spectrum_view) {
|
||||
frequency_scale.set_parent_rect({0, audio_spectrum_height, screen_rect().width(), scale_height});
|
||||
waterfall_view.set_parent_rect(waterfall_reduced_rect);
|
||||
} else {
|
||||
frequency_scale.set_parent_rect({0, 0, screen_rect().width(), scale_height});
|
||||
waterfall_view.set_parent_rect(waterfall_normal_rect);
|
||||
}
|
||||
waterfall_view.on_show();
|
||||
}
|
||||
|
||||
void WaterfallWidget::set_parent_rect(const Rect new_parent_rect) {
|
||||
View::set_parent_rect(new_parent_rect);
|
||||
|
||||
waterfall_normal_rect = { 0, scale_height, new_parent_rect.width(), new_parent_rect.height() - scale_height};
|
||||
waterfall_reduced_rect = { 0, audio_spectrum_height + scale_height, new_parent_rect.width(), new_parent_rect.height() - scale_height - audio_spectrum_height };
|
||||
|
||||
update_widgets_rect();
|
||||
View::set_parent_rect(new_parent_rect);
|
||||
|
||||
waterfall_normal_rect = {0, scale_height, new_parent_rect.width(), new_parent_rect.height() - scale_height};
|
||||
waterfall_reduced_rect = {0, audio_spectrum_height + scale_height, new_parent_rect.width(), new_parent_rect.height() - scale_height - audio_spectrum_height};
|
||||
|
||||
update_widgets_rect();
|
||||
}
|
||||
|
||||
void WaterfallWidget::paint(Painter& painter) {
|
||||
// TODO:
|
||||
(void)painter;
|
||||
// TODO:
|
||||
(void)painter;
|
||||
}
|
||||
|
||||
void WaterfallWidget::on_channel_spectrum(const ChannelSpectrum& spectrum) {
|
||||
waterfall_view.on_channel_spectrum(spectrum);
|
||||
sampling_rate = spectrum.sampling_rate;
|
||||
frequency_scale.set_spectrum_sampling_rate(sampling_rate);
|
||||
frequency_scale.set_channel_filter(
|
||||
spectrum.channel_filter_low_frequency,
|
||||
spectrum.channel_filter_high_frequency,
|
||||
spectrum.channel_filter_transition
|
||||
);
|
||||
waterfall_view.on_channel_spectrum(spectrum);
|
||||
sampling_rate = spectrum.sampling_rate;
|
||||
frequency_scale.set_spectrum_sampling_rate(sampling_rate);
|
||||
frequency_scale.set_channel_filter(
|
||||
spectrum.channel_filter_low_frequency,
|
||||
spectrum.channel_filter_high_frequency,
|
||||
spectrum.channel_filter_transition);
|
||||
}
|
||||
|
||||
void WaterfallWidget::on_audio_spectrum() {
|
||||
audio_spectrum_view->on_audio_spectrum(audio_spectrum_data);
|
||||
audio_spectrum_view->on_audio_spectrum(audio_spectrum_data);
|
||||
}
|
||||
|
||||
} /* namespace spectrum */
|
||||
|
@@ -36,167 +36,161 @@ namespace ui {
|
||||
namespace spectrum {
|
||||
|
||||
class AudioSpectrumView : public View {
|
||||
public:
|
||||
AudioSpectrumView(const Rect parent_rect);
|
||||
|
||||
void paint(Painter& painter) override;
|
||||
|
||||
void on_audio_spectrum(const AudioSpectrum* spectrum);
|
||||
|
||||
private:
|
||||
static constexpr int cursor_band_height = 4;
|
||||
|
||||
int16_t audio_spectrum[128] { 0 };
|
||||
|
||||
Labels labels {
|
||||
{ { 6 * 8, 0 * 16 }, "Hz", Color::light_grey() }
|
||||
};
|
||||
|
||||
NumberField field_frequency {
|
||||
{ 0 * 8, 0 * 16 },
|
||||
5,
|
||||
{ 0, 48000 },
|
||||
48000 / 240,
|
||||
' '
|
||||
};
|
||||
|
||||
Waveform waveform {
|
||||
{ 0, 1 * 16 + cursor_band_height, 30 * 8, 2 * 16 },
|
||||
audio_spectrum,
|
||||
128,
|
||||
0,
|
||||
false,
|
||||
Color::white()
|
||||
};
|
||||
public:
|
||||
AudioSpectrumView(const Rect parent_rect);
|
||||
|
||||
void paint(Painter& painter) override;
|
||||
|
||||
void on_audio_spectrum(const AudioSpectrum* spectrum);
|
||||
|
||||
private:
|
||||
static constexpr int cursor_band_height = 4;
|
||||
|
||||
int16_t audio_spectrum[128]{0};
|
||||
|
||||
Labels labels{
|
||||
{{6 * 8, 0 * 16}, "Hz", Color::light_grey()}};
|
||||
|
||||
NumberField field_frequency{
|
||||
{0 * 8, 0 * 16},
|
||||
5,
|
||||
{0, 48000},
|
||||
48000 / 240,
|
||||
' '};
|
||||
|
||||
Waveform waveform{
|
||||
{0, 1 * 16 + cursor_band_height, 30 * 8, 2 * 16},
|
||||
audio_spectrum,
|
||||
128,
|
||||
0,
|
||||
false,
|
||||
Color::white()};
|
||||
};
|
||||
|
||||
class FrequencyScale : public Widget {
|
||||
public:
|
||||
std::function<void(int32_t offset)> on_select { };
|
||||
|
||||
void on_show() override;
|
||||
void on_focus() override;
|
||||
void on_blur() override;
|
||||
|
||||
bool on_encoder(const EncoderEvent delta) override;
|
||||
bool on_key(const KeyEvent key) override;
|
||||
public:
|
||||
std::function<void(int32_t offset)> on_select{};
|
||||
|
||||
void set_spectrum_sampling_rate(const int new_sampling_rate);
|
||||
void set_channel_filter(const int low_frequency, const int high_frequency, const int transition);
|
||||
void on_show() override;
|
||||
void on_focus() override;
|
||||
void on_blur() override;
|
||||
|
||||
void paint(Painter& painter) override;
|
||||
bool on_encoder(const EncoderEvent delta) override;
|
||||
bool on_key(const KeyEvent key) override;
|
||||
|
||||
private:
|
||||
static constexpr int filter_band_height = 4;
|
||||
void set_spectrum_sampling_rate(const int new_sampling_rate);
|
||||
void set_channel_filter(const int low_frequency, const int high_frequency, const int transition);
|
||||
|
||||
void on_tick_second();
|
||||
|
||||
bool _blink { false };
|
||||
int32_t cursor_position { 0 };
|
||||
SignalToken signal_token_tick_second { };
|
||||
int spectrum_sampling_rate { 0 };
|
||||
const int spectrum_bins = std::tuple_size<decltype(ChannelSpectrum::db)>::value;
|
||||
int channel_filter_low_frequency { 0 };
|
||||
int channel_filter_high_frequency { 0 };
|
||||
int channel_filter_transition { 0 };
|
||||
void paint(Painter& painter) override;
|
||||
|
||||
void clear();
|
||||
void clear_background(Painter& painter, const Rect r);
|
||||
private:
|
||||
static constexpr int filter_band_height = 4;
|
||||
|
||||
void draw_frequency_ticks(Painter& painter, const Rect r);
|
||||
void draw_filter_ranges(Painter& painter, const Rect r);
|
||||
void on_tick_second();
|
||||
|
||||
bool _blink{false};
|
||||
int32_t cursor_position{0};
|
||||
SignalToken signal_token_tick_second{};
|
||||
int spectrum_sampling_rate{0};
|
||||
const int spectrum_bins = std::tuple_size<decltype(ChannelSpectrum::db)>::value;
|
||||
int channel_filter_low_frequency{0};
|
||||
int channel_filter_high_frequency{0};
|
||||
int channel_filter_transition{0};
|
||||
|
||||
void clear();
|
||||
void clear_background(Painter& painter, const Rect r);
|
||||
|
||||
void draw_frequency_ticks(Painter& painter, const Rect r);
|
||||
void draw_filter_ranges(Painter& painter, const Rect r);
|
||||
};
|
||||
|
||||
class WaterfallView : public Widget {
|
||||
public:
|
||||
void on_show() override;
|
||||
void on_hide() override;
|
||||
public:
|
||||
void on_show() override;
|
||||
void on_hide() override;
|
||||
|
||||
void paint(Painter& painter) override;
|
||||
void paint(Painter& painter) override;
|
||||
|
||||
void on_channel_spectrum(const ChannelSpectrum& spectrum);
|
||||
void on_channel_spectrum(const ChannelSpectrum& spectrum);
|
||||
|
||||
private:
|
||||
void clear();
|
||||
private:
|
||||
void clear();
|
||||
};
|
||||
|
||||
class WaterfallWidget : public View {
|
||||
public:
|
||||
std::function<void(int32_t offset)> on_select { };
|
||||
|
||||
WaterfallWidget(const bool cursor = false);
|
||||
public:
|
||||
std::function<void(int32_t offset)> on_select{};
|
||||
|
||||
WaterfallWidget(const WaterfallWidget&) = delete;
|
||||
WaterfallWidget(WaterfallWidget&&) = delete;
|
||||
WaterfallWidget& operator=(const WaterfallWidget&) = delete;
|
||||
WaterfallWidget& operator=(WaterfallWidget&&) = delete;
|
||||
WaterfallWidget(const bool cursor = false);
|
||||
|
||||
void on_show() override;
|
||||
void on_hide() override;
|
||||
WaterfallWidget(const WaterfallWidget&) = delete;
|
||||
WaterfallWidget(WaterfallWidget&&) = delete;
|
||||
WaterfallWidget& operator=(const WaterfallWidget&) = delete;
|
||||
WaterfallWidget& operator=(WaterfallWidget&&) = delete;
|
||||
|
||||
void set_parent_rect(const Rect new_parent_rect) override;
|
||||
|
||||
void show_audio_spectrum_view(const bool show);
|
||||
void on_show() override;
|
||||
void on_hide() override;
|
||||
|
||||
void paint(Painter& painter) override;
|
||||
void set_parent_rect(const Rect new_parent_rect) override;
|
||||
|
||||
private:
|
||||
void update_widgets_rect();
|
||||
|
||||
const Rect audio_spectrum_view_rect { 0 * 8, 0 * 16, 30 * 8, 2 * 16 + 20 };
|
||||
static constexpr Dim audio_spectrum_height = 16 * 2 + 20;
|
||||
static constexpr Dim scale_height = 20;
|
||||
|
||||
WaterfallView waterfall_view { };
|
||||
FrequencyScale frequency_scale { };
|
||||
|
||||
ChannelSpectrumFIFO* channel_fifo { nullptr };
|
||||
AudioSpectrum* audio_spectrum_data { nullptr };
|
||||
bool audio_spectrum_update { false };
|
||||
|
||||
std::unique_ptr<AudioSpectrumView> audio_spectrum_view { };
|
||||
|
||||
int sampling_rate { 0 };
|
||||
int32_t cursor_position { 0 };
|
||||
ui::Rect waterfall_normal_rect { };
|
||||
ui::Rect waterfall_reduced_rect { };
|
||||
void show_audio_spectrum_view(const bool show);
|
||||
|
||||
MessageHandlerRegistration message_handler_channel_spectrum_config {
|
||||
Message::ID::ChannelSpectrumConfig,
|
||||
[this](const Message* const p) {
|
||||
const auto message = *reinterpret_cast<const ChannelSpectrumConfigMessage*>(p);
|
||||
this->channel_fifo = message.fifo;
|
||||
}
|
||||
};
|
||||
MessageHandlerRegistration message_handler_audio_spectrum {
|
||||
Message::ID::AudioSpectrum,
|
||||
[this](const Message* const p) {
|
||||
const auto message = *reinterpret_cast<const AudioSpectrumMessage*>(p);
|
||||
this->audio_spectrum_data = message.data;
|
||||
this->audio_spectrum_update = true;
|
||||
}
|
||||
};
|
||||
MessageHandlerRegistration message_handler_frame_sync {
|
||||
Message::ID::DisplayFrameSync,
|
||||
[this](const Message* const) {
|
||||
if( this->channel_fifo ) {
|
||||
ChannelSpectrum channel_spectrum;
|
||||
while( channel_fifo->out(channel_spectrum) ) {
|
||||
this->on_channel_spectrum(channel_spectrum);
|
||||
}
|
||||
}
|
||||
if (this->audio_spectrum_update) {
|
||||
this->audio_spectrum_update = false;
|
||||
this->on_audio_spectrum();
|
||||
}
|
||||
}
|
||||
};
|
||||
void paint(Painter& painter) override;
|
||||
|
||||
void on_channel_spectrum(const ChannelSpectrum& spectrum);
|
||||
void on_audio_spectrum();
|
||||
private:
|
||||
void update_widgets_rect();
|
||||
|
||||
const Rect audio_spectrum_view_rect{0 * 8, 0 * 16, 30 * 8, 2 * 16 + 20};
|
||||
static constexpr Dim audio_spectrum_height = 16 * 2 + 20;
|
||||
static constexpr Dim scale_height = 20;
|
||||
|
||||
WaterfallView waterfall_view{};
|
||||
FrequencyScale frequency_scale{};
|
||||
|
||||
ChannelSpectrumFIFO* channel_fifo{nullptr};
|
||||
AudioSpectrum* audio_spectrum_data{nullptr};
|
||||
bool audio_spectrum_update{false};
|
||||
|
||||
std::unique_ptr<AudioSpectrumView> audio_spectrum_view{};
|
||||
|
||||
int sampling_rate{0};
|
||||
int32_t cursor_position{0};
|
||||
ui::Rect waterfall_normal_rect{};
|
||||
ui::Rect waterfall_reduced_rect{};
|
||||
|
||||
MessageHandlerRegistration message_handler_channel_spectrum_config{
|
||||
Message::ID::ChannelSpectrumConfig,
|
||||
[this](const Message* const p) {
|
||||
const auto message = *reinterpret_cast<const ChannelSpectrumConfigMessage*>(p);
|
||||
this->channel_fifo = message.fifo;
|
||||
}};
|
||||
MessageHandlerRegistration message_handler_audio_spectrum{
|
||||
Message::ID::AudioSpectrum,
|
||||
[this](const Message* const p) {
|
||||
const auto message = *reinterpret_cast<const AudioSpectrumMessage*>(p);
|
||||
this->audio_spectrum_data = message.data;
|
||||
this->audio_spectrum_update = true;
|
||||
}};
|
||||
MessageHandlerRegistration message_handler_frame_sync{
|
||||
Message::ID::DisplayFrameSync,
|
||||
[this](const Message* const) {
|
||||
if (this->channel_fifo) {
|
||||
ChannelSpectrum channel_spectrum;
|
||||
while (channel_fifo->out(channel_spectrum)) {
|
||||
this->on_channel_spectrum(channel_spectrum);
|
||||
}
|
||||
}
|
||||
if (this->audio_spectrum_update) {
|
||||
this->audio_spectrum_update = false;
|
||||
this->on_audio_spectrum();
|
||||
}
|
||||
}};
|
||||
|
||||
void on_channel_spectrum(const ChannelSpectrum& spectrum);
|
||||
void on_audio_spectrum();
|
||||
};
|
||||
|
||||
} /* namespace spectrum */
|
||||
} /* namespace ui */
|
||||
|
||||
#endif/*__UI_SPECTRUM_H__*/
|
||||
#endif /*__UI_SPECTRUM_H__*/
|
||||
|
@@ -28,127 +28,123 @@ using namespace portapack;
|
||||
namespace ui {
|
||||
|
||||
Tab::Tab() {
|
||||
set_focusable(true);
|
||||
set_focusable(true);
|
||||
};
|
||||
|
||||
void Tab::set(
|
||||
uint32_t index,
|
||||
Dim width,
|
||||
std::string text,
|
||||
Color text_color
|
||||
) {
|
||||
set_parent_rect({ (Coord)(index * width), 0, width, 24 });
|
||||
|
||||
text_ = text.substr(0, (width - 8) / 8);
|
||||
text_color_ = text_color;
|
||||
|
||||
index_ = index;
|
||||
uint32_t index,
|
||||
Dim width,
|
||||
std::string text,
|
||||
Color text_color) {
|
||||
set_parent_rect({(Coord)(index * width), 0, width, 24});
|
||||
|
||||
text_ = text.substr(0, (width - 8) / 8);
|
||||
text_color_ = text_color;
|
||||
|
||||
index_ = index;
|
||||
}
|
||||
|
||||
void Tab::paint(Painter& painter) {
|
||||
const auto rect = screen_rect();
|
||||
const Color color = highlighted() ? Color::black() : Color::grey();
|
||||
const auto rect = screen_rect();
|
||||
const Color color = highlighted() ? Color::black() : Color::grey();
|
||||
|
||||
painter.fill_rectangle({ rect.left(), rect.top(), rect.width() - 8, rect.height() }, color);
|
||||
painter.fill_rectangle({rect.left(), rect.top(), rect.width() - 8, rect.height()}, color);
|
||||
|
||||
if (!highlighted())
|
||||
painter.draw_hline({ rect.left(), rect.top() }, rect.width() - 9, Color::light_grey());
|
||||
|
||||
painter.draw_bitmap(
|
||||
{ rect.right() - 8, rect.top() },
|
||||
bitmap_tab_edge,
|
||||
color,
|
||||
Color::dark_grey()
|
||||
);
|
||||
|
||||
auto text_point = rect.center() - Point(4, 0) - Point(text_.size() * 8 / 2, 16 / 2);
|
||||
|
||||
painter.draw_string(
|
||||
text_point,
|
||||
{ ui::font::fixed_8x16, color, text_color_ },
|
||||
text_
|
||||
);
|
||||
|
||||
if (has_focus())
|
||||
painter.draw_hline(text_point + Point(0, 16), text_.size() * 8, Color::white());
|
||||
if (!highlighted())
|
||||
painter.draw_hline({rect.left(), rect.top()}, rect.width() - 9, Color::light_grey());
|
||||
|
||||
painter.draw_bitmap(
|
||||
{rect.right() - 8, rect.top()},
|
||||
bitmap_tab_edge,
|
||||
color,
|
||||
Color::dark_grey());
|
||||
|
||||
auto text_point = rect.center() - Point(4, 0) - Point(text_.size() * 8 / 2, 16 / 2);
|
||||
|
||||
painter.draw_string(
|
||||
text_point,
|
||||
{ui::font::fixed_8x16, color, text_color_},
|
||||
text_);
|
||||
|
||||
if (has_focus())
|
||||
painter.draw_hline(text_point + Point(0, 16), text_.size() * 8, Color::white());
|
||||
}
|
||||
|
||||
bool Tab::on_key(const KeyEvent key) {
|
||||
if( key == KeyEvent::Select ) {
|
||||
static_cast<TabView*>(parent())->set_selected(index_);
|
||||
return true;
|
||||
}
|
||||
if (key == KeyEvent::Select) {
|
||||
static_cast<TabView*>(parent())->set_selected(index_);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool Tab::on_touch(const TouchEvent event) {
|
||||
switch(event.type) {
|
||||
case TouchEvent::Type::Start:
|
||||
focus();
|
||||
set_dirty();
|
||||
return true;
|
||||
switch (event.type) {
|
||||
case TouchEvent::Type::Start:
|
||||
focus();
|
||||
set_dirty();
|
||||
return true;
|
||||
|
||||
case TouchEvent::Type::End:
|
||||
static_cast<TabView*>(parent())->set_selected(index_);
|
||||
return true;
|
||||
case TouchEvent::Type::End:
|
||||
static_cast<TabView*>(parent())->set_selected(index_);
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void TabView::set_selected(uint32_t index) {
|
||||
Tab * tab;
|
||||
|
||||
if (index >= n_tabs)
|
||||
return;
|
||||
|
||||
// Hide previous view
|
||||
views[current_tab]->hidden(true);
|
||||
|
||||
tab = &tabs[current_tab];
|
||||
tab->set_highlighted(false);
|
||||
tab->set_focusable(true);
|
||||
tab->set_dirty();
|
||||
|
||||
// Show new view
|
||||
views[index]->hidden(false);
|
||||
|
||||
tab = &tabs[index];
|
||||
current_tab = index;
|
||||
tab->set_highlighted(true);
|
||||
tab->set_focusable(false);
|
||||
tab->set_dirty();
|
||||
Tab* tab;
|
||||
|
||||
if (index >= n_tabs)
|
||||
return;
|
||||
|
||||
// Hide previous view
|
||||
views[current_tab]->hidden(true);
|
||||
|
||||
tab = &tabs[current_tab];
|
||||
tab->set_highlighted(false);
|
||||
tab->set_focusable(true);
|
||||
tab->set_dirty();
|
||||
|
||||
// Show new view
|
||||
views[index]->hidden(false);
|
||||
|
||||
tab = &tabs[index];
|
||||
current_tab = index;
|
||||
tab->set_highlighted(true);
|
||||
tab->set_focusable(false);
|
||||
tab->set_dirty();
|
||||
}
|
||||
|
||||
void TabView::on_show() {
|
||||
set_selected(current_tab);
|
||||
set_selected(current_tab);
|
||||
}
|
||||
|
||||
|
||||
void TabView::focus() {
|
||||
views[current_tab]->focus();
|
||||
views[current_tab]->focus();
|
||||
}
|
||||
|
||||
TabView::TabView(std::initializer_list<TabDef> tab_definitions) {
|
||||
size_t i = 0;
|
||||
|
||||
n_tabs = tab_definitions.size();
|
||||
if (n_tabs > MAX_TABS)
|
||||
n_tabs = MAX_TABS;
|
||||
|
||||
size_t tab_width = 240 / n_tabs;
|
||||
|
||||
set_parent_rect({ 0, 0, 30 * 8, 3 * 8 });
|
||||
|
||||
for (auto &tab_definition : tab_definitions) {
|
||||
tabs[i].set(i, tab_width, tab_definition.text, tab_definition.color);
|
||||
views[i] = tab_definition.view;
|
||||
add_child(&tabs[i]);
|
||||
i++;
|
||||
if (i == MAX_TABS) break;
|
||||
}
|
||||
size_t i = 0;
|
||||
|
||||
n_tabs = tab_definitions.size();
|
||||
if (n_tabs > MAX_TABS)
|
||||
n_tabs = MAX_TABS;
|
||||
|
||||
size_t tab_width = 240 / n_tabs;
|
||||
|
||||
set_parent_rect({0, 0, 30 * 8, 3 * 8});
|
||||
|
||||
for (auto& tab_definition : tab_definitions) {
|
||||
tabs[i].set(i, tab_width, tab_definition.text, tab_definition.color);
|
||||
views[i] = tab_definition.view;
|
||||
add_child(&tabs[i]);
|
||||
i++;
|
||||
if (i == MAX_TABS) break;
|
||||
}
|
||||
}
|
||||
|
||||
TabView::~TabView() {
|
||||
|
@@ -29,52 +29,52 @@
|
||||
#include "ui_painter.hpp"
|
||||
|
||||
namespace ui {
|
||||
|
||||
|
||||
#define MAX_TABS 5
|
||||
|
||||
class Tab : public Widget {
|
||||
public:
|
||||
Tab();
|
||||
public:
|
||||
Tab();
|
||||
|
||||
void paint(Painter& painter) override;
|
||||
|
||||
bool on_key(const KeyEvent key) override;
|
||||
bool on_touch(const TouchEvent event) override;
|
||||
|
||||
void set(uint32_t index, Dim width, std::string text, Color text_color);
|
||||
void paint(Painter& painter) override;
|
||||
|
||||
private:
|
||||
std::string text_ { };
|
||||
Color text_color_ { };
|
||||
uint32_t index_ { };
|
||||
bool on_key(const KeyEvent key) override;
|
||||
bool on_touch(const TouchEvent event) override;
|
||||
|
||||
void set(uint32_t index, Dim width, std::string text, Color text_color);
|
||||
|
||||
private:
|
||||
std::string text_{};
|
||||
Color text_color_{};
|
||||
uint32_t index_{};
|
||||
};
|
||||
|
||||
class TabView : public View {
|
||||
public:
|
||||
struct TabDef {
|
||||
std::string text;
|
||||
ui::Color color;
|
||||
View* view;
|
||||
};
|
||||
|
||||
TabView(std::initializer_list<TabDef> tab_definitions);
|
||||
~TabView();
|
||||
|
||||
void focus() override;
|
||||
void on_show() override;
|
||||
|
||||
void set_selected(uint32_t index);
|
||||
uint32_t selected() {
|
||||
return current_tab;
|
||||
};
|
||||
public:
|
||||
struct TabDef {
|
||||
std::string text;
|
||||
ui::Color color;
|
||||
View* view;
|
||||
};
|
||||
|
||||
private:
|
||||
size_t n_tabs { };
|
||||
std::array<Tab, MAX_TABS> tabs { };
|
||||
std::array<View*, MAX_TABS> views { };
|
||||
uint32_t current_tab { 0 };
|
||||
TabView(std::initializer_list<TabDef> tab_definitions);
|
||||
~TabView();
|
||||
|
||||
void focus() override;
|
||||
void on_show() override;
|
||||
|
||||
void set_selected(uint32_t index);
|
||||
uint32_t selected() {
|
||||
return current_tab;
|
||||
};
|
||||
|
||||
private:
|
||||
size_t n_tabs{};
|
||||
std::array<Tab, MAX_TABS> tabs{};
|
||||
std::array<View*, MAX_TABS> views{};
|
||||
uint32_t current_tab{0};
|
||||
};
|
||||
|
||||
} /* namespace ui */
|
||||
|
||||
#endif/*__UI_TABVIEW_H__*/
|
||||
#endif /*__UI_TABVIEW_H__*/
|
||||
|
@@ -30,71 +30,66 @@ using namespace portapack;
|
||||
namespace ui {
|
||||
|
||||
void text_prompt(
|
||||
NavigationView& nav,
|
||||
std::string& str,
|
||||
size_t max_length,
|
||||
std::function<void(std::string&)> on_done
|
||||
) {
|
||||
text_prompt(nav, str, str.length(), max_length, on_done);
|
||||
NavigationView& nav,
|
||||
std::string& str,
|
||||
size_t max_length,
|
||||
std::function<void(std::string&)> on_done) {
|
||||
text_prompt(nav, str, str.length(), max_length, on_done);
|
||||
}
|
||||
|
||||
void text_prompt(
|
||||
NavigationView& nav,
|
||||
std::string& str,
|
||||
uint32_t cursor_pos,
|
||||
size_t max_length,
|
||||
std::function<void(std::string&)> on_done
|
||||
) {
|
||||
//if (persistent_memory::ui_config_textentry() == 0) {
|
||||
auto te_view = nav.push<AlphanumView>(str, max_length);
|
||||
te_view->set_cursor(cursor_pos);
|
||||
te_view->on_changed = [on_done](std::string& value) {
|
||||
if (on_done)
|
||||
on_done(value);
|
||||
};
|
||||
/*} else {
|
||||
auto te_view = nav.push<HandWriteView>(str, max_length);
|
||||
te_view->on_changed = [on_done](std::string * value) {
|
||||
if (on_done)
|
||||
on_done(value);
|
||||
};
|
||||
}*/
|
||||
NavigationView& nav,
|
||||
std::string& str,
|
||||
uint32_t cursor_pos,
|
||||
size_t max_length,
|
||||
std::function<void(std::string&)> on_done) {
|
||||
// if (persistent_memory::ui_config_textentry() == 0) {
|
||||
auto te_view = nav.push<AlphanumView>(str, max_length);
|
||||
te_view->set_cursor(cursor_pos);
|
||||
te_view->on_changed = [on_done](std::string& value) {
|
||||
if (on_done)
|
||||
on_done(value);
|
||||
};
|
||||
/*} else {
|
||||
auto te_view = nav.push<HandWriteView>(str, max_length);
|
||||
te_view->on_changed = [on_done](std::string * value) {
|
||||
if (on_done)
|
||||
on_done(value);
|
||||
};
|
||||
}*/
|
||||
}
|
||||
|
||||
/* TextEntryView ***********************************************************/
|
||||
|
||||
void TextEntryView::char_delete() {
|
||||
text_input.char_delete();
|
||||
text_input.char_delete();
|
||||
}
|
||||
|
||||
void TextEntryView::char_add(const char c) {
|
||||
text_input.char_add(c);
|
||||
text_input.char_add(c);
|
||||
}
|
||||
|
||||
void TextEntryView::set_cursor(uint32_t pos) {
|
||||
text_input.set_cursor(pos);
|
||||
text_input.set_cursor(pos);
|
||||
}
|
||||
|
||||
void TextEntryView::focus() {
|
||||
text_input.focus();
|
||||
text_input.focus();
|
||||
}
|
||||
|
||||
TextEntryView::TextEntryView(
|
||||
NavigationView& nav,
|
||||
std::string& str,
|
||||
size_t max_length
|
||||
) : text_input{ str, max_length, { 0, 0 } }
|
||||
{
|
||||
add_children({
|
||||
&text_input,
|
||||
&button_ok
|
||||
});
|
||||
NavigationView& nav,
|
||||
std::string& str,
|
||||
size_t max_length)
|
||||
: text_input{str, max_length, {0, 0}} {
|
||||
add_children({&text_input,
|
||||
&button_ok});
|
||||
|
||||
button_ok.on_select = [this, &str, &nav](Button&) {
|
||||
if (on_changed)
|
||||
on_changed(str);
|
||||
nav.pop();
|
||||
};
|
||||
button_ok.on_select = [this, &str, &nav](Button&) {
|
||||
if (on_changed)
|
||||
on_changed(str);
|
||||
nav.pop();
|
||||
};
|
||||
}
|
||||
|
||||
} /* namespace ui */
|
||||
|
@@ -29,30 +29,29 @@
|
||||
namespace ui {
|
||||
|
||||
class TextEntryView : public View {
|
||||
public:
|
||||
std::function<void(std::string&)> on_changed { };
|
||||
|
||||
void focus() override;
|
||||
std::string title() const override { return "Text entry"; };
|
||||
public:
|
||||
std::function<void(std::string&)> on_changed{};
|
||||
|
||||
void set_cursor(uint32_t pos);
|
||||
|
||||
protected:
|
||||
TextEntryView(NavigationView& nav, std::string& str, size_t max_length);
|
||||
|
||||
TextEntryView(const TextEntryView&) = delete;
|
||||
TextEntryView(TextEntryView&&) = delete;
|
||||
TextEntryView& operator=(const TextEntryView&) = delete;
|
||||
TextEntryView& operator=(TextEntryView&&) = delete;
|
||||
void focus() override;
|
||||
std::string title() const override { return "Text entry"; };
|
||||
|
||||
void char_add(const char c);
|
||||
void char_delete();
|
||||
|
||||
TextField text_input;
|
||||
Button button_ok {
|
||||
{ 10 * 8, 33 * 8, 9 * 8, 32 },
|
||||
"OK"
|
||||
};
|
||||
void set_cursor(uint32_t pos);
|
||||
|
||||
protected:
|
||||
TextEntryView(NavigationView& nav, std::string& str, size_t max_length);
|
||||
|
||||
TextEntryView(const TextEntryView&) = delete;
|
||||
TextEntryView(TextEntryView&&) = delete;
|
||||
TextEntryView& operator=(const TextEntryView&) = delete;
|
||||
TextEntryView& operator=(TextEntryView&&) = delete;
|
||||
|
||||
void char_add(const char c);
|
||||
void char_delete();
|
||||
|
||||
TextField text_input;
|
||||
Button button_ok{
|
||||
{10 * 8, 33 * 8, 9 * 8, 32},
|
||||
"OK"};
|
||||
};
|
||||
|
||||
// Show the TextEntry view to receive keyboard input.
|
||||
@@ -60,18 +59,18 @@ protected:
|
||||
// by reference and its lifetime must be ensured by the
|
||||
// caller until the TextEntry view is dismissed.
|
||||
void text_prompt(
|
||||
NavigationView& nav,
|
||||
std::string& str,
|
||||
size_t max_length,
|
||||
std::function<void(std::string&)> on_done = nullptr);
|
||||
NavigationView& nav,
|
||||
std::string& str,
|
||||
size_t max_length,
|
||||
std::function<void(std::string&)> on_done = nullptr);
|
||||
|
||||
void text_prompt(
|
||||
NavigationView& nav,
|
||||
std::string& str,
|
||||
uint32_t cursor_pos,
|
||||
size_t max_length,
|
||||
std::function<void(std::string&)> on_done = nullptr);
|
||||
NavigationView& nav,
|
||||
std::string& str,
|
||||
uint32_t cursor_pos,
|
||||
size_t max_length,
|
||||
std::function<void(std::string&)> on_done = nullptr);
|
||||
|
||||
} /* namespace ui */
|
||||
|
||||
#endif/*__UI_TEXTENTRY_H__*/
|
||||
#endif /*__UI_TEXTENTRY_H__*/
|
||||
|
@@ -36,157 +36,156 @@ namespace ui {
|
||||
/* TransmitterView *******************************************************/
|
||||
|
||||
void TransmitterView::paint(Painter& painter) {
|
||||
size_t c;
|
||||
Point pos = { 0, screen_pos().y() };
|
||||
|
||||
for (c = 0; c < 20; c++) {
|
||||
painter.draw_bitmap(
|
||||
pos,
|
||||
bitmap_stripes,
|
||||
ui::Color(191, 191, 0),
|
||||
ui::Color::black()
|
||||
);
|
||||
if (c != 9)
|
||||
pos += { 24, 0 };
|
||||
else
|
||||
pos = { 0, screen_pos().y() + 32 + 8 };
|
||||
}
|
||||
size_t c;
|
||||
Point pos = {0, screen_pos().y()};
|
||||
|
||||
for (c = 0; c < 20; c++) {
|
||||
painter.draw_bitmap(
|
||||
pos,
|
||||
bitmap_stripes,
|
||||
ui::Color(191, 191, 0),
|
||||
ui::Color::black());
|
||||
if (c != 9)
|
||||
pos += {24, 0};
|
||||
else
|
||||
pos = {0, screen_pos().y() + 32 + 8};
|
||||
}
|
||||
}
|
||||
|
||||
void TransmitterView::on_tuning_frequency_changed(rf::Frequency f) {
|
||||
transmitter_model.set_tuning_frequency(f);
|
||||
transmitter_model.set_tuning_frequency(f);
|
||||
}
|
||||
|
||||
void TransmitterView::on_channel_bandwidth_changed(uint32_t channel_bandwidth) {
|
||||
transmitter_model.set_channel_bandwidth(channel_bandwidth);
|
||||
transmitter_model.set_channel_bandwidth(channel_bandwidth);
|
||||
}
|
||||
|
||||
void TransmitterView::on_tx_gain_changed(int32_t tx_gain) {
|
||||
transmitter_model.set_tx_gain(tx_gain);
|
||||
update_gainlevel_styles();
|
||||
transmitter_model.set_tx_gain(tx_gain);
|
||||
update_gainlevel_styles();
|
||||
}
|
||||
|
||||
void TransmitterView::on_tx_amp_changed(bool rf_amp) {
|
||||
transmitter_model.set_rf_amp(rf_amp);
|
||||
update_gainlevel_styles();
|
||||
transmitter_model.set_rf_amp(rf_amp);
|
||||
update_gainlevel_styles();
|
||||
}
|
||||
|
||||
void TransmitterView::update_gainlevel_styles() {
|
||||
const Style *new_style_ptr = NULL;
|
||||
int8_t tot_gain = transmitter_model.tx_gain() + (transmitter_model.rf_amp() ? 14 : 0);
|
||||
|
||||
if(tot_gain > POWER_THRESHOLD_HIGH) {
|
||||
new_style_ptr = &style_power_high;
|
||||
} else if(tot_gain > POWER_THRESHOLD_MED) {
|
||||
new_style_ptr = &style_power_med;
|
||||
} else if(tot_gain > POWER_THRESHOLD_LOW) {
|
||||
new_style_ptr = &style_power_low;
|
||||
}
|
||||
const Style* new_style_ptr = NULL;
|
||||
int8_t tot_gain = transmitter_model.tx_gain() + (transmitter_model.rf_amp() ? 14 : 0);
|
||||
|
||||
field_gain.set_style(new_style_ptr);
|
||||
text_gain.set_style(new_style_ptr);
|
||||
field_amp.set_style(new_style_ptr);
|
||||
text_amp.set_style(new_style_ptr);
|
||||
if (tot_gain > POWER_THRESHOLD_HIGH) {
|
||||
new_style_ptr = &style_power_high;
|
||||
} else if (tot_gain > POWER_THRESHOLD_MED) {
|
||||
new_style_ptr = &style_power_med;
|
||||
} else if (tot_gain > POWER_THRESHOLD_LOW) {
|
||||
new_style_ptr = &style_power_low;
|
||||
}
|
||||
|
||||
field_gain.set_style(new_style_ptr);
|
||||
text_gain.set_style(new_style_ptr);
|
||||
field_amp.set_style(new_style_ptr);
|
||||
text_amp.set_style(new_style_ptr);
|
||||
}
|
||||
|
||||
void TransmitterView::set_transmitting(const bool transmitting) {
|
||||
if (transmitting) {
|
||||
button_start.set_text("STOP");
|
||||
button_start.set_style(&style_stop);
|
||||
} else {
|
||||
button_start.set_text("START");
|
||||
button_start.set_style(&style_start);
|
||||
}
|
||||
|
||||
transmitting_ = transmitting;
|
||||
if (transmitting) {
|
||||
button_start.set_text("STOP");
|
||||
button_start.set_style(&style_stop);
|
||||
} else {
|
||||
button_start.set_text("START");
|
||||
button_start.set_style(&style_start);
|
||||
}
|
||||
|
||||
transmitting_ = transmitting;
|
||||
}
|
||||
|
||||
void TransmitterView::on_show() {
|
||||
field_frequency.set_value(transmitter_model.tuning_frequency());
|
||||
field_frequency_step.set_by_value(receiver_model.frequency_step());
|
||||
field_frequency.set_value(transmitter_model.tuning_frequency());
|
||||
field_frequency_step.set_by_value(receiver_model.frequency_step());
|
||||
|
||||
field_gain.set_value(transmitter_model.tx_gain());
|
||||
field_amp.set_value(transmitter_model.rf_amp() ? 14 : 0);
|
||||
field_gain.set_value(transmitter_model.tx_gain());
|
||||
field_amp.set_value(transmitter_model.rf_amp() ? 14 : 0);
|
||||
|
||||
update_gainlevel_styles();
|
||||
update_gainlevel_styles();
|
||||
}
|
||||
|
||||
void TransmitterView::focus() {
|
||||
button_start.focus();
|
||||
button_start.focus();
|
||||
}
|
||||
|
||||
TransmitterView::TransmitterView(
|
||||
const Coord y, const uint64_t frequency_step, const uint32_t channel_bandwidth, const bool lock
|
||||
) : lock_ { lock }
|
||||
{
|
||||
set_parent_rect({ 0, y, 30 * 8, 6 * 8 });
|
||||
|
||||
add_children({
|
||||
&field_frequency,
|
||||
&field_frequency_step,
|
||||
&text_gain,
|
||||
&field_gain,
|
||||
&button_start,
|
||||
&text_amp,
|
||||
&field_amp,
|
||||
});
|
||||
|
||||
set_transmitting(false);
|
||||
|
||||
if (lock_) {
|
||||
field_frequency.set_focusable(false);
|
||||
field_frequency.set_style(&style_locked);
|
||||
} else {
|
||||
if (channel_bandwidth) {
|
||||
add_children({
|
||||
&text_bw,
|
||||
&field_bw
|
||||
});
|
||||
|
||||
field_bw.on_change = [this](int32_t v) {
|
||||
on_channel_bandwidth_changed(v * 1000);
|
||||
};
|
||||
field_bw.set_value(channel_bandwidth);
|
||||
}
|
||||
}
|
||||
const Coord y,
|
||||
const uint64_t frequency_step,
|
||||
const uint32_t channel_bandwidth,
|
||||
const bool lock)
|
||||
: lock_{lock} {
|
||||
set_parent_rect({0, y, 30 * 8, 6 * 8});
|
||||
|
||||
//field_frequency.set_value(transmitter_model.tuning_frequency());
|
||||
field_frequency.set_step(frequency_step);
|
||||
field_frequency.on_change = [this](rf::Frequency f) {
|
||||
on_tuning_frequency_changed(f);
|
||||
};
|
||||
field_frequency.on_edit = [this]() {
|
||||
if (on_edit_frequency)
|
||||
on_edit_frequency();
|
||||
};
|
||||
|
||||
field_frequency_step.on_change = [this](size_t, OptionsField::value_t v) {
|
||||
this->field_frequency.set_step(v);
|
||||
};
|
||||
add_children({
|
||||
&field_frequency,
|
||||
&field_frequency_step,
|
||||
&text_gain,
|
||||
&field_gain,
|
||||
&button_start,
|
||||
&text_amp,
|
||||
&field_amp,
|
||||
});
|
||||
|
||||
field_gain.on_change = [this](uint32_t tx_gain) {
|
||||
on_tx_gain_changed(tx_gain);
|
||||
};
|
||||
|
||||
field_amp.on_change = [this](uint32_t rf_amp) {
|
||||
on_tx_amp_changed((bool) rf_amp);
|
||||
};
|
||||
set_transmitting(false);
|
||||
|
||||
button_start.on_select = [this](Button&){
|
||||
if (transmitting_) {
|
||||
if (on_stop)
|
||||
on_stop();
|
||||
} else {
|
||||
if (on_start)
|
||||
on_start();
|
||||
}
|
||||
};
|
||||
if (lock_) {
|
||||
field_frequency.set_focusable(false);
|
||||
field_frequency.set_style(&style_locked);
|
||||
} else {
|
||||
if (channel_bandwidth) {
|
||||
add_children({&text_bw,
|
||||
&field_bw});
|
||||
|
||||
field_bw.on_change = [this](int32_t v) {
|
||||
on_channel_bandwidth_changed(v * 1000);
|
||||
};
|
||||
field_bw.set_value(channel_bandwidth);
|
||||
}
|
||||
}
|
||||
|
||||
// field_frequency.set_value(transmitter_model.tuning_frequency());
|
||||
field_frequency.set_step(frequency_step);
|
||||
field_frequency.on_change = [this](rf::Frequency f) {
|
||||
on_tuning_frequency_changed(f);
|
||||
};
|
||||
field_frequency.on_edit = [this]() {
|
||||
if (on_edit_frequency)
|
||||
on_edit_frequency();
|
||||
};
|
||||
|
||||
field_frequency_step.on_change = [this](size_t, OptionsField::value_t v) {
|
||||
this->field_frequency.set_step(v);
|
||||
};
|
||||
|
||||
field_gain.on_change = [this](uint32_t tx_gain) {
|
||||
on_tx_gain_changed(tx_gain);
|
||||
};
|
||||
|
||||
field_amp.on_change = [this](uint32_t rf_amp) {
|
||||
on_tx_amp_changed((bool)rf_amp);
|
||||
};
|
||||
|
||||
button_start.on_select = [this](Button&) {
|
||||
if (transmitting_) {
|
||||
if (on_stop)
|
||||
on_stop();
|
||||
} else {
|
||||
if (on_start)
|
||||
on_start();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
TransmitterView::~TransmitterView() {
|
||||
audio::output::stop();
|
||||
transmitter_model.disable();
|
||||
baseband::shutdown();
|
||||
audio::output::stop();
|
||||
transmitter_model.disable();
|
||||
baseband::shutdown();
|
||||
}
|
||||
|
||||
/* TransmitterView2 *******************************************************/
|
||||
@@ -196,82 +195,81 @@ TransmitterView::~TransmitterView() {
|
||||
// We use short compact char lines , in Replay / GPS Simul / Playlist App , called (x pos , y pos,SHORT_UI )
|
||||
|
||||
void TransmitterView2::paint(Painter& painter) {
|
||||
// Not using TransmitterView2, but if we delete it,we got , top line 1 a blanking rect.
|
||||
(void) painter; // Avoid warning: unused parameter .
|
||||
// Not using TransmitterView2, but if we delete it,we got , top line 1 a blanking rect.
|
||||
(void)painter; // Avoid warning: unused parameter .
|
||||
}
|
||||
|
||||
void TransmitterView2::on_tx_gain_changed(int32_t tx_gain) {
|
||||
transmitter_model.set_tx_gain(tx_gain);
|
||||
update_gainlevel_styles();
|
||||
transmitter_model.set_tx_gain(tx_gain);
|
||||
update_gainlevel_styles();
|
||||
}
|
||||
|
||||
void TransmitterView2::on_tx_amp_changed(bool rf_amp) {
|
||||
transmitter_model.set_rf_amp(rf_amp);
|
||||
update_gainlevel_styles();
|
||||
transmitter_model.set_rf_amp(rf_amp);
|
||||
update_gainlevel_styles();
|
||||
}
|
||||
|
||||
void TransmitterView2::update_gainlevel_styles() {
|
||||
const Style *new_style_ptr = NULL;
|
||||
int8_t tot_gain = transmitter_model.tx_gain() + (transmitter_model.rf_amp() ? 14 : 0);
|
||||
|
||||
if(tot_gain > POWER_THRESHOLD_HIGH) {
|
||||
new_style_ptr = &style_power_high;
|
||||
} else if(tot_gain > POWER_THRESHOLD_MED) {
|
||||
new_style_ptr = &style_power_med;
|
||||
} else if(tot_gain > POWER_THRESHOLD_LOW) {
|
||||
new_style_ptr = &style_power_low;
|
||||
}
|
||||
const Style* new_style_ptr = NULL;
|
||||
int8_t tot_gain = transmitter_model.tx_gain() + (transmitter_model.rf_amp() ? 14 : 0);
|
||||
|
||||
field_gain.set_style(new_style_ptr);
|
||||
text_gain_amp.set_style(new_style_ptr);
|
||||
field_amp.set_style(new_style_ptr);
|
||||
if (tot_gain > POWER_THRESHOLD_HIGH) {
|
||||
new_style_ptr = &style_power_high;
|
||||
} else if (tot_gain > POWER_THRESHOLD_MED) {
|
||||
new_style_ptr = &style_power_med;
|
||||
} else if (tot_gain > POWER_THRESHOLD_LOW) {
|
||||
new_style_ptr = &style_power_low;
|
||||
}
|
||||
|
||||
field_gain_short_UI.set_style(new_style_ptr);
|
||||
text_gain_amp_short_UI.set_style(new_style_ptr);
|
||||
field_amp_short_UI.set_style(new_style_ptr);
|
||||
field_gain.set_style(new_style_ptr);
|
||||
text_gain_amp.set_style(new_style_ptr);
|
||||
field_amp.set_style(new_style_ptr);
|
||||
|
||||
field_gain_short_UI.set_style(new_style_ptr);
|
||||
text_gain_amp_short_UI.set_style(new_style_ptr);
|
||||
field_amp_short_UI.set_style(new_style_ptr);
|
||||
}
|
||||
|
||||
void TransmitterView2::on_show() {
|
||||
field_gain.set_value(transmitter_model.tx_gain());
|
||||
field_amp.set_value(transmitter_model.rf_amp() ? 14 : 0);
|
||||
field_gain.set_value(transmitter_model.tx_gain());
|
||||
field_amp.set_value(transmitter_model.rf_amp() ? 14 : 0);
|
||||
|
||||
field_gain_short_UI.set_value(transmitter_model.tx_gain());
|
||||
field_amp_short_UI.set_value(transmitter_model.rf_amp() ? 14 : 0);
|
||||
|
||||
update_gainlevel_styles();
|
||||
field_gain_short_UI.set_value(transmitter_model.tx_gain());
|
||||
field_amp_short_UI.set_value(transmitter_model.rf_amp() ? 14 : 0);
|
||||
|
||||
update_gainlevel_styles();
|
||||
}
|
||||
|
||||
TransmitterView2::TransmitterView2( const Coord x, const Coord y, bool short_UI)
|
||||
{
|
||||
set_parent_rect({ x , y, 20 * 8, 1 * 8 }); // set_parent_rect({ 0, y, 30 * 8, 6 * 8 });
|
||||
TransmitterView2::TransmitterView2(const Coord x, const Coord y, bool short_UI) {
|
||||
set_parent_rect({x, y, 20 * 8, 1 * 8}); // set_parent_rect({ 0, y, 30 * 8, 6 * 8 });
|
||||
|
||||
add_children({
|
||||
&(short_UI ? text_gain_amp_short_UI : text_gain_amp),
|
||||
&(short_UI ? field_gain_short_UI : field_gain),
|
||||
&(short_UI ? field_amp_short_UI : field_amp),
|
||||
});
|
||||
add_children({
|
||||
&(short_UI ? text_gain_amp_short_UI : text_gain_amp),
|
||||
&(short_UI ? field_gain_short_UI : field_gain),
|
||||
&(short_UI ? field_amp_short_UI : field_amp),
|
||||
});
|
||||
|
||||
if (short_UI) {
|
||||
field_gain_short_UI.on_change = [this](uint32_t tx_gain) {
|
||||
on_tx_gain_changed(tx_gain);
|
||||
};
|
||||
field_amp_short_UI.on_change = [this](uint32_t rf_amp) {
|
||||
on_tx_amp_changed((bool) rf_amp);
|
||||
};
|
||||
} else {
|
||||
field_gain.on_change = [this](uint32_t tx_gain) {
|
||||
on_tx_gain_changed(tx_gain);
|
||||
};
|
||||
field_amp.on_change = [this](uint32_t rf_amp) {
|
||||
on_tx_amp_changed((bool) rf_amp);
|
||||
};
|
||||
}
|
||||
if (short_UI) {
|
||||
field_gain_short_UI.on_change = [this](uint32_t tx_gain) {
|
||||
on_tx_gain_changed(tx_gain);
|
||||
};
|
||||
field_amp_short_UI.on_change = [this](uint32_t rf_amp) {
|
||||
on_tx_amp_changed((bool)rf_amp);
|
||||
};
|
||||
} else {
|
||||
field_gain.on_change = [this](uint32_t tx_gain) {
|
||||
on_tx_gain_changed(tx_gain);
|
||||
};
|
||||
field_amp.on_change = [this](uint32_t rf_amp) {
|
||||
on_tx_amp_changed((bool)rf_amp);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
TransmitterView2::~TransmitterView2() {
|
||||
audio::output::stop();
|
||||
transmitter_model.disable();
|
||||
baseband::shutdown();
|
||||
audio::output::stop();
|
||||
transmitter_model.disable();
|
||||
baseband::shutdown();
|
||||
}
|
||||
|
||||
} /* namespace ui */
|
||||
|
@@ -37,212 +37,195 @@
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
|
||||
#define POWER_THRESHOLD_HIGH 47
|
||||
#define POWER_THRESHOLD_MED 38
|
||||
#define POWER_THRESHOLD_LOW 17
|
||||
#define POWER_THRESHOLD_HIGH 47
|
||||
#define POWER_THRESHOLD_MED 38
|
||||
#define POWER_THRESHOLD_LOW 17
|
||||
|
||||
namespace ui {
|
||||
|
||||
class TXGainField : public NumberField {
|
||||
public:
|
||||
std::function<void(void)> on_show_options { };
|
||||
public:
|
||||
std::function<void(void)> on_show_options{};
|
||||
|
||||
TXGainField(Point parent_pos);
|
||||
TXGainField(Point parent_pos);
|
||||
};
|
||||
|
||||
class TransmitterView : public View {
|
||||
public:
|
||||
std::function<void(void)> on_edit_frequency { };
|
||||
std::function<void(void)> on_start { };
|
||||
std::function<void(void)> on_stop { };
|
||||
|
||||
TransmitterView(const Coord y, const uint64_t frequency_step, const uint32_t channel_bandwidth, const bool lock);
|
||||
TransmitterView(
|
||||
const Coord y, const uint32_t frequency_step, const uint32_t channel_bandwidth
|
||||
) : TransmitterView { y, frequency_step, channel_bandwidth, false }
|
||||
{
|
||||
}
|
||||
|
||||
~TransmitterView();
|
||||
|
||||
void on_show() override;
|
||||
void paint(Painter& painter) override;
|
||||
void focus() override;
|
||||
|
||||
void set_transmitting(const bool transmitting);
|
||||
public:
|
||||
std::function<void(void)> on_edit_frequency{};
|
||||
std::function<void(void)> on_start{};
|
||||
std::function<void(void)> on_stop{};
|
||||
|
||||
private:
|
||||
const Style style_start {
|
||||
.font = font::fixed_8x16,
|
||||
.background = Color::black(),
|
||||
.foreground = Color::green(),
|
||||
};
|
||||
const Style style_stop {
|
||||
.font = font::fixed_8x16,
|
||||
.background = Color::black(),
|
||||
.foreground = Color::red(),
|
||||
};
|
||||
const Style style_locked {
|
||||
.font = font::fixed_8x16,
|
||||
.background = Color::black(),
|
||||
.foreground = Color::dark_grey(),
|
||||
};
|
||||
const Style style_power_low {
|
||||
.font = font::fixed_8x16,
|
||||
.background = Color::black(),
|
||||
.foreground = Color::yellow(),
|
||||
};
|
||||
const Style style_power_med {
|
||||
.font = font::fixed_8x16,
|
||||
.background = Color::black(),
|
||||
.foreground = Color::orange(),
|
||||
};
|
||||
const Style style_power_high {
|
||||
.font = font::fixed_8x16,
|
||||
.background = Color::black(),
|
||||
.foreground = Color::red(),
|
||||
};
|
||||
TransmitterView(const Coord y, const uint64_t frequency_step, const uint32_t channel_bandwidth, const bool lock);
|
||||
TransmitterView(
|
||||
const Coord y,
|
||||
const uint32_t frequency_step,
|
||||
const uint32_t channel_bandwidth)
|
||||
: TransmitterView{y, frequency_step, channel_bandwidth, false} {
|
||||
}
|
||||
|
||||
bool lock_ { false };
|
||||
bool transmitting_ { false };
|
||||
|
||||
FrequencyField field_frequency {
|
||||
{ 0, 1 * 8 }
|
||||
};
|
||||
|
||||
Text text_gain {
|
||||
{ 0, 3 * 8, 5 * 8, 1 * 16 },
|
||||
"Gain:"
|
||||
};
|
||||
|
||||
NumberField field_gain {
|
||||
{ 5 * 8, 3 * 8 },
|
||||
2,
|
||||
{ max2837::tx::gain_db_range.minimum, max2837::tx::gain_db_range.maximum },
|
||||
max2837::tx::gain_db_step,
|
||||
' '
|
||||
};
|
||||
~TransmitterView();
|
||||
|
||||
Text text_bw {
|
||||
{ 18 * 8, 1 * 8, 3 * 8, 1 * 16 },
|
||||
"kHz"
|
||||
};
|
||||
NumberField field_bw {
|
||||
{ 15 * 8, 1 * 8 },
|
||||
3,
|
||||
{ 1, 150 },
|
||||
1,
|
||||
' '
|
||||
};
|
||||
|
||||
Text text_amp {
|
||||
{ 11 * 8, 3 * 8, 5 * 8, 1 * 16 },
|
||||
"Amp:"
|
||||
};
|
||||
void on_show() override;
|
||||
void paint(Painter& painter) override;
|
||||
void focus() override;
|
||||
|
||||
NumberField field_amp {
|
||||
{ 16 * 8, 3 * 8 },
|
||||
2,
|
||||
{ 0, 14 },
|
||||
14,
|
||||
' '
|
||||
};
|
||||
void set_transmitting(const bool transmitting);
|
||||
|
||||
Button button_start {
|
||||
{ 21 * 8, 1 * 8, 9 * 8, 32 },
|
||||
"START"
|
||||
};
|
||||
|
||||
FrequencyStepView field_frequency_step {
|
||||
{ 10 * 8 - 4, 1 * 8 },
|
||||
};
|
||||
private:
|
||||
const Style style_start{
|
||||
.font = font::fixed_8x16,
|
||||
.background = Color::black(),
|
||||
.foreground = Color::green(),
|
||||
};
|
||||
const Style style_stop{
|
||||
.font = font::fixed_8x16,
|
||||
.background = Color::black(),
|
||||
.foreground = Color::red(),
|
||||
};
|
||||
const Style style_locked{
|
||||
.font = font::fixed_8x16,
|
||||
.background = Color::black(),
|
||||
.foreground = Color::dark_grey(),
|
||||
};
|
||||
const Style style_power_low{
|
||||
.font = font::fixed_8x16,
|
||||
.background = Color::black(),
|
||||
.foreground = Color::yellow(),
|
||||
};
|
||||
const Style style_power_med{
|
||||
.font = font::fixed_8x16,
|
||||
.background = Color::black(),
|
||||
.foreground = Color::orange(),
|
||||
};
|
||||
const Style style_power_high{
|
||||
.font = font::fixed_8x16,
|
||||
.background = Color::black(),
|
||||
.foreground = Color::red(),
|
||||
};
|
||||
|
||||
void on_tuning_frequency_changed(rf::Frequency f);
|
||||
void on_channel_bandwidth_changed(uint32_t channel_bandwidth);
|
||||
void on_tx_gain_changed(int32_t tx_gain);
|
||||
void on_tx_amp_changed(bool rf_amp);
|
||||
bool lock_{false};
|
||||
bool transmitting_{false};
|
||||
|
||||
void update_gainlevel_styles(void);
|
||||
FrequencyField field_frequency{
|
||||
{0, 1 * 8}};
|
||||
|
||||
Text text_gain{
|
||||
{0, 3 * 8, 5 * 8, 1 * 16},
|
||||
"Gain:"};
|
||||
|
||||
NumberField field_gain{
|
||||
{5 * 8, 3 * 8},
|
||||
2,
|
||||
{max2837::tx::gain_db_range.minimum, max2837::tx::gain_db_range.maximum},
|
||||
max2837::tx::gain_db_step,
|
||||
' '};
|
||||
|
||||
Text text_bw{
|
||||
{18 * 8, 1 * 8, 3 * 8, 1 * 16},
|
||||
"kHz"};
|
||||
NumberField field_bw{
|
||||
{15 * 8, 1 * 8},
|
||||
3,
|
||||
{1, 150},
|
||||
1,
|
||||
' '};
|
||||
|
||||
Text text_amp{
|
||||
{11 * 8, 3 * 8, 5 * 8, 1 * 16},
|
||||
"Amp:"};
|
||||
|
||||
NumberField field_amp{
|
||||
{16 * 8, 3 * 8},
|
||||
2,
|
||||
{0, 14},
|
||||
14,
|
||||
' '};
|
||||
|
||||
Button button_start{
|
||||
{21 * 8, 1 * 8, 9 * 8, 32},
|
||||
"START"};
|
||||
|
||||
FrequencyStepView field_frequency_step{
|
||||
{10 * 8 - 4, 1 * 8},
|
||||
};
|
||||
|
||||
void on_tuning_frequency_changed(rf::Frequency f);
|
||||
void on_channel_bandwidth_changed(uint32_t channel_bandwidth);
|
||||
void on_tx_gain_changed(int32_t tx_gain);
|
||||
void on_tx_amp_changed(bool rf_amp);
|
||||
|
||||
void update_gainlevel_styles(void);
|
||||
};
|
||||
|
||||
|
||||
class TransmitterView2 : public View {
|
||||
public:
|
||||
|
||||
TransmitterView2(const Coord x, const Coord y, bool short_UI);
|
||||
public:
|
||||
TransmitterView2(const Coord x, const Coord y, bool short_UI);
|
||||
|
||||
~TransmitterView2();
|
||||
|
||||
void on_show() override;
|
||||
void paint(Painter& painter) override;
|
||||
|
||||
private:
|
||||
const Style style_power_low {
|
||||
.font = font::fixed_8x16,
|
||||
.background = Color::black(),
|
||||
.foreground = Color::yellow(),
|
||||
};
|
||||
const Style style_power_med {
|
||||
.font = font::fixed_8x16,
|
||||
.background = Color::black(),
|
||||
.foreground = Color::orange(),
|
||||
};
|
||||
const Style style_power_high {
|
||||
.font = font::fixed_8x16,
|
||||
.background = Color::black(),
|
||||
.foreground = Color::red(),
|
||||
};
|
||||
~TransmitterView2();
|
||||
|
||||
Text text_gain_amp {
|
||||
{ 0, 3 * 8, 5 * 8, 1 * 16 },
|
||||
"Gain: Amp:"
|
||||
};
|
||||
|
||||
NumberField field_gain {
|
||||
{ 5 * 8, 3 * 8 },
|
||||
2,
|
||||
{ max2837::tx::gain_db_range.minimum, max2837::tx::gain_db_range.maximum },
|
||||
max2837::tx::gain_db_step,
|
||||
' '
|
||||
};
|
||||
void on_show() override;
|
||||
void paint(Painter& painter) override;
|
||||
|
||||
NumberField field_amp {
|
||||
{ 12 * 8, 3 * 8 },
|
||||
2,
|
||||
{ 0, 14 },
|
||||
14,
|
||||
' '
|
||||
};
|
||||
private:
|
||||
const Style style_power_low{
|
||||
.font = font::fixed_8x16,
|
||||
.background = Color::black(),
|
||||
.foreground = Color::yellow(),
|
||||
};
|
||||
const Style style_power_med{
|
||||
.font = font::fixed_8x16,
|
||||
.background = Color::black(),
|
||||
.foreground = Color::orange(),
|
||||
};
|
||||
const Style style_power_high{
|
||||
.font = font::fixed_8x16,
|
||||
.background = Color::black(),
|
||||
.foreground = Color::red(),
|
||||
};
|
||||
|
||||
Text text_gain_amp_short_UI {
|
||||
{ 0, (3 * 8)-1, 5 * 8, 1 * 16 },
|
||||
"Gain A:"
|
||||
};
|
||||
Text text_gain_amp{
|
||||
{0, 3 * 8, 5 * 8, 1 * 16},
|
||||
"Gain: Amp:"};
|
||||
|
||||
NumberField field_gain_short_UI {
|
||||
{ (4 * 8)+2 , 3 * 8 },
|
||||
2,
|
||||
{ max2837::tx::gain_db_range.minimum, max2837::tx::gain_db_range.maximum },
|
||||
max2837::tx::gain_db_step,
|
||||
' '
|
||||
};
|
||||
NumberField field_gain{
|
||||
{5 * 8, 3 * 8},
|
||||
2,
|
||||
{max2837::tx::gain_db_range.minimum, max2837::tx::gain_db_range.maximum},
|
||||
max2837::tx::gain_db_step,
|
||||
' '};
|
||||
|
||||
NumberField field_amp_short_UI {
|
||||
{ (9 * 8)-2, 3 * 8 },
|
||||
2,
|
||||
{ 0, 14 },
|
||||
14,
|
||||
' '
|
||||
};
|
||||
NumberField field_amp{
|
||||
{12 * 8, 3 * 8},
|
||||
2,
|
||||
{0, 14},
|
||||
14,
|
||||
' '};
|
||||
|
||||
void on_tx_gain_changed(int32_t tx_gain);
|
||||
void on_tx_amp_changed(bool rf_amp);
|
||||
Text text_gain_amp_short_UI{
|
||||
{0, (3 * 8) - 1, 5 * 8, 1 * 16},
|
||||
"Gain A:"};
|
||||
|
||||
void update_gainlevel_styles(void);
|
||||
NumberField field_gain_short_UI{
|
||||
{(4 * 8) + 2, 3 * 8},
|
||||
2,
|
||||
{max2837::tx::gain_db_range.minimum, max2837::tx::gain_db_range.maximum},
|
||||
max2837::tx::gain_db_step,
|
||||
' '};
|
||||
|
||||
NumberField field_amp_short_UI{
|
||||
{(9 * 8) - 2, 3 * 8},
|
||||
2,
|
||||
{0, 14},
|
||||
14,
|
||||
' '};
|
||||
|
||||
void on_tx_gain_changed(int32_t tx_gain);
|
||||
void on_tx_amp_changed(bool rf_amp);
|
||||
|
||||
void update_gainlevel_styles(void);
|
||||
};
|
||||
|
||||
|
||||
|
||||
} /* namespace ui */
|
||||
|
||||
#endif/*__UI_TRANSMITTER_H__*/
|
||||
#endif /*__UI_TRANSMITTER_H__*/
|
||||
|
@@ -40,211 +40,198 @@ namespace tv {
|
||||
/* TimeScopeView******************************************************/
|
||||
|
||||
TimeScopeView::TimeScopeView(
|
||||
const Rect parent_rect
|
||||
) : View { parent_rect }
|
||||
{
|
||||
set_focusable(true);
|
||||
|
||||
add_children({
|
||||
//&labels,
|
||||
//&field_frequency,
|
||||
&waveform
|
||||
});
|
||||
|
||||
/*field_frequency.on_change = [this](int32_t) {
|
||||
set_dirty();
|
||||
};
|
||||
field_frequency.set_value(10);*/
|
||||
const Rect parent_rect)
|
||||
: View{parent_rect} {
|
||||
set_focusable(true);
|
||||
|
||||
add_children({//&labels,
|
||||
//&field_frequency,
|
||||
&waveform});
|
||||
|
||||
/*field_frequency.on_change = [this](int32_t) {
|
||||
set_dirty();
|
||||
};
|
||||
field_frequency.set_value(10);*/
|
||||
}
|
||||
|
||||
void TimeScopeView::paint(Painter& painter) {
|
||||
const auto r = screen_rect();
|
||||
const auto r = screen_rect();
|
||||
|
||||
painter.fill_rectangle(r, Color::black());
|
||||
|
||||
// Cursor
|
||||
/*
|
||||
const Rect r_cursor {
|
||||
field_frequency.value() / (48000 / 240), r.bottom() - 32 - cursor_band_height,
|
||||
1, cursor_band_height
|
||||
};
|
||||
painter.fill_rectangle(
|
||||
r_cursor,
|
||||
Color::red()
|
||||
);*/
|
||||
painter.fill_rectangle(r, Color::black());
|
||||
|
||||
// Cursor
|
||||
/*
|
||||
const Rect r_cursor {
|
||||
field_frequency.value() / (48000 / 240), r.bottom() - 32 - cursor_band_height,
|
||||
1, cursor_band_height
|
||||
};
|
||||
painter.fill_rectangle(
|
||||
r_cursor,
|
||||
Color::red()
|
||||
);*/
|
||||
}
|
||||
|
||||
void TimeScopeView::on_audio_spectrum(const AudioSpectrum* spectrum) {
|
||||
for (size_t i = 0; i < spectrum->db.size(); i++)
|
||||
audio_spectrum[i] = ((int16_t)spectrum->db[i] - 127) * 256;
|
||||
waveform.set_dirty();
|
||||
for (size_t i = 0; i < spectrum->db.size(); i++)
|
||||
audio_spectrum[i] = ((int16_t)spectrum->db[i] - 127) * 256;
|
||||
waveform.set_dirty();
|
||||
}
|
||||
|
||||
/* TVView *********************************************************/
|
||||
|
||||
void TVView::on_show() {
|
||||
clear();
|
||||
clear();
|
||||
|
||||
const auto screen_r = screen_rect();
|
||||
display.scroll_set_area(screen_r.top(), screen_r.bottom());
|
||||
const auto screen_r = screen_rect();
|
||||
display.scroll_set_area(screen_r.top(), screen_r.bottom());
|
||||
}
|
||||
|
||||
void TVView::on_hide() {
|
||||
/* TODO: Clear region to eliminate brief flash of content at un-shifted
|
||||
* position?
|
||||
*/
|
||||
display.scroll_disable();
|
||||
/* TODO: Clear region to eliminate brief flash of content at un-shifted
|
||||
* position?
|
||||
*/
|
||||
display.scroll_disable();
|
||||
}
|
||||
|
||||
void TVView::paint(Painter& painter) {
|
||||
// Do nothing.
|
||||
(void)painter;
|
||||
// Do nothing.
|
||||
(void)painter;
|
||||
}
|
||||
|
||||
void TVView::on_adjust_xcorr(uint8_t xcorr){
|
||||
x_correction = xcorr;
|
||||
void TVView::on_adjust_xcorr(uint8_t xcorr) {
|
||||
x_correction = xcorr;
|
||||
}
|
||||
|
||||
void TVView::on_channel_spectrum(
|
||||
const ChannelSpectrum& spectrum
|
||||
) {
|
||||
//portapack has limitations
|
||||
// 1.screen resolution (less than 240x320) 2.samples each call back (128 or 256)
|
||||
// 3.memory size (for ui::Color, the buffer size
|
||||
//spectrum.db[i] is 256 long
|
||||
//768x625 ->128x625 ->128x312 -> 128x104
|
||||
//originally @6MHz sample rate, the PAL should be 768x625
|
||||
//I reduced sample rate to 2MHz(3 times less samples), then calculate mag (effectively decimate by 2)
|
||||
//the resolution is now changed to 128x625. The total decimation factor is 6, which changes how many samples in a line
|
||||
//However 625 is too large for the screen, also interlaced scanning is harder to realize in portapack than normal computer.
|
||||
//So I decided to simply drop half of the lines, once y is larger than 625/2=312.5 or 312, I recognize it as a new frame.
|
||||
//then the resolution is changed to 128x312
|
||||
//128x312 is now able to put into a 240x320 screen, but the buffer for a whole frame is 128x312=39936, which is too large
|
||||
//according to my test, I can only make a buffer with a length of 13312 of type ui::Color. which is 1/3 of what I wanted.
|
||||
//So now the resolution is changed to 128x104, the height is shrinked to 1/3 of the original height.
|
||||
//I was expecting to see 1/3 height of original video.
|
||||
const ChannelSpectrum& spectrum) {
|
||||
// portapack has limitations
|
||||
// 1.screen resolution (less than 240x320) 2.samples each call back (128 or 256)
|
||||
// 3.memory size (for ui::Color, the buffer size
|
||||
// spectrum.db[i] is 256 long
|
||||
// 768x625 ->128x625 ->128x312 -> 128x104
|
||||
// originally @6MHz sample rate, the PAL should be 768x625
|
||||
// I reduced sample rate to 2MHz(3 times less samples), then calculate mag (effectively decimate by 2)
|
||||
// the resolution is now changed to 128x625. The total decimation factor is 6, which changes how many samples in a line
|
||||
// However 625 is too large for the screen, also interlaced scanning is harder to realize in portapack than normal computer.
|
||||
// So I decided to simply drop half of the lines, once y is larger than 625/2=312.5 or 312, I recognize it as a new frame.
|
||||
// then the resolution is changed to 128x312
|
||||
// 128x312 is now able to put into a 240x320 screen, but the buffer for a whole frame is 128x312=39936, which is too large
|
||||
// according to my test, I can only make a buffer with a length of 13312 of type ui::Color. which is 1/3 of what I wanted.
|
||||
// So now the resolution is changed to 128x104, the height is shrinked to 1/3 of the original height.
|
||||
// I was expecting to see 1/3 height of original video.
|
||||
|
||||
//Look how nice is that! I am now able to meet the requirements of 1 and 3 for portapack. Also the length of a line is 128
|
||||
//Each call back gives me 256 samples which is exactly 2 lines. What a coincidence!
|
||||
// Look how nice is that! I am now able to meet the requirements of 1 and 3 for portapack. Also the length of a line is 128
|
||||
// Each call back gives me 256 samples which is exactly 2 lines. What a coincidence!
|
||||
|
||||
//After some experiment, I did some improvements.
|
||||
//1.I found that instead of 1/3 of the frame is shown, I got 3 whole frames shrinked into one window.
|
||||
//So I made the height twice simply by painting 2 identical lines in the place of original lines
|
||||
//2.I found sometimes there is an horizontal offset, so I added x_correction to move the frame back to center manually
|
||||
//3.I changed video_buffer's type, from ui::Color to uint_8, since I don't need 3 digit to represent a grey scale value.
|
||||
//I was hoping that by doing this, I can have a longer buffer like 39936, then the frame will looks better vertically
|
||||
//however this is useless until now.
|
||||
// After some experiment, I did some improvements.
|
||||
// 1.I found that instead of 1/3 of the frame is shown, I got 3 whole frames shrinked into one window.
|
||||
// So I made the height twice simply by painting 2 identical lines in the place of original lines
|
||||
// 2.I found sometimes there is an horizontal offset, so I added x_correction to move the frame back to center manually
|
||||
// 3.I changed video_buffer's type, from ui::Color to uint_8, since I don't need 3 digit to represent a grey scale value.
|
||||
// I was hoping that by doing this, I can have a longer buffer like 39936, then the frame will looks better vertically
|
||||
// however this is useless until now.
|
||||
|
||||
for(size_t i=0; i<256; i++)
|
||||
{
|
||||
//video_buffer[i+count*256] = spectrum_rgb4_lut[spectrum.db[i]];
|
||||
video_buffer_int[i+count*256] = 255 - spectrum.db[i];
|
||||
}
|
||||
count = count + 1;
|
||||
if (count == 52 -1)
|
||||
{
|
||||
ui::Color line_buffer[128];
|
||||
Coord line;
|
||||
uint32_t bmp_px;
|
||||
for (size_t i = 0; i < 256; i++) {
|
||||
// video_buffer[i+count*256] = spectrum_rgb4_lut[spectrum.db[i]];
|
||||
video_buffer_int[i + count * 256] = 255 - spectrum.db[i];
|
||||
}
|
||||
count = count + 1;
|
||||
if (count == 52 - 1) {
|
||||
ui::Color line_buffer[128];
|
||||
Coord line;
|
||||
uint32_t bmp_px;
|
||||
|
||||
/*for (line = 0; line < 104; line++)
|
||||
{
|
||||
for (bmp_px = 0; bmp_px < 128; bmp_px++)
|
||||
{
|
||||
//line_buffer[bmp_px] = video_buffer[bmp_px+line*128];
|
||||
line_buffer[bmp_px] = spectrum_rgb4_lut[video_buffer_int[bmp_px+line*128 + x_correction]];
|
||||
}
|
||||
/*for (line = 0; line < 104; line++)
|
||||
{
|
||||
for (bmp_px = 0; bmp_px < 128; bmp_px++)
|
||||
{
|
||||
//line_buffer[bmp_px] = video_buffer[bmp_px+line*128];
|
||||
line_buffer[bmp_px] = spectrum_rgb4_lut[video_buffer_int[bmp_px+line*128 + x_correction]];
|
||||
}
|
||||
|
||||
display.render_line({ 0, line + 100 }, 128, line_buffer);
|
||||
}*/
|
||||
for (line = 0; line < 208; line=line+2)
|
||||
{
|
||||
for (bmp_px = 0; bmp_px < 128; bmp_px++)
|
||||
{
|
||||
//line_buffer[bmp_px] = video_buffer[bmp_px+line*128];
|
||||
line_buffer[bmp_px] = spectrum_rgb4_lut[video_buffer_int[bmp_px+line/2*128 + x_correction]];
|
||||
}
|
||||
|
||||
display.render_line({ 0, line + 100 }, 128, line_buffer);
|
||||
display.render_line({ 0, line + 101 }, 128, line_buffer);
|
||||
}
|
||||
count = 0;
|
||||
}
|
||||
display.render_line({ 0, line + 100 }, 128, line_buffer);
|
||||
}*/
|
||||
for (line = 0; line < 208; line = line + 2) {
|
||||
for (bmp_px = 0; bmp_px < 128; bmp_px++) {
|
||||
// line_buffer[bmp_px] = video_buffer[bmp_px+line*128];
|
||||
line_buffer[bmp_px] = spectrum_rgb4_lut[video_buffer_int[bmp_px + line / 2 * 128 + x_correction]];
|
||||
}
|
||||
|
||||
display.render_line({0, line + 100}, 128, line_buffer);
|
||||
display.render_line({0, line + 101}, 128, line_buffer);
|
||||
}
|
||||
count = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void TVView::clear() {
|
||||
display.fill_rectangle(
|
||||
screen_rect(),
|
||||
Color::black()
|
||||
);
|
||||
display.fill_rectangle(
|
||||
screen_rect(),
|
||||
Color::black());
|
||||
}
|
||||
|
||||
/* TVWidget *******************************************************/
|
||||
|
||||
TVWidget::TVWidget() {
|
||||
add_children({
|
||||
&tv_view,
|
||||
&field_xcorr
|
||||
});
|
||||
field_xcorr.set_value(10);
|
||||
add_children({&tv_view,
|
||||
&field_xcorr});
|
||||
field_xcorr.set_value(10);
|
||||
}
|
||||
|
||||
void TVWidget::on_show() {
|
||||
baseband::spectrum_streaming_start();
|
||||
baseband::spectrum_streaming_start();
|
||||
}
|
||||
|
||||
void TVWidget::on_hide() {
|
||||
baseband::spectrum_streaming_stop();
|
||||
baseband::spectrum_streaming_stop();
|
||||
}
|
||||
|
||||
void TVWidget::show_audio_spectrum_view(const bool show) {
|
||||
if ((audio_spectrum_view && show) || (!audio_spectrum_view && !show)) return;
|
||||
|
||||
if (show) {
|
||||
audio_spectrum_view = std::make_unique<TimeScopeView>(audio_spectrum_view_rect);
|
||||
add_child(audio_spectrum_view.get());
|
||||
update_widgets_rect();
|
||||
} else {
|
||||
audio_spectrum_update = false;
|
||||
remove_child(audio_spectrum_view.get());
|
||||
audio_spectrum_view.reset();
|
||||
update_widgets_rect();
|
||||
}
|
||||
if ((audio_spectrum_view && show) || (!audio_spectrum_view && !show)) return;
|
||||
|
||||
if (show) {
|
||||
audio_spectrum_view = std::make_unique<TimeScopeView>(audio_spectrum_view_rect);
|
||||
add_child(audio_spectrum_view.get());
|
||||
update_widgets_rect();
|
||||
} else {
|
||||
audio_spectrum_update = false;
|
||||
remove_child(audio_spectrum_view.get());
|
||||
audio_spectrum_view.reset();
|
||||
update_widgets_rect();
|
||||
}
|
||||
}
|
||||
|
||||
void TVWidget::update_widgets_rect() {
|
||||
if (audio_spectrum_view) {
|
||||
tv_view.set_parent_rect(tv_reduced_rect);
|
||||
} else {
|
||||
tv_view.set_parent_rect(tv_normal_rect);
|
||||
}
|
||||
tv_view.on_show();
|
||||
if (audio_spectrum_view) {
|
||||
tv_view.set_parent_rect(tv_reduced_rect);
|
||||
} else {
|
||||
tv_view.set_parent_rect(tv_normal_rect);
|
||||
}
|
||||
tv_view.on_show();
|
||||
}
|
||||
|
||||
void TVWidget::set_parent_rect(const Rect new_parent_rect) {
|
||||
View::set_parent_rect(new_parent_rect);
|
||||
|
||||
tv_normal_rect = { 0, scale_height, new_parent_rect.width(), new_parent_rect.height() - scale_height};
|
||||
tv_reduced_rect = { 0, audio_spectrum_height + scale_height, new_parent_rect.width(), new_parent_rect.height() - scale_height - audio_spectrum_height };
|
||||
|
||||
update_widgets_rect();
|
||||
View::set_parent_rect(new_parent_rect);
|
||||
|
||||
tv_normal_rect = {0, scale_height, new_parent_rect.width(), new_parent_rect.height() - scale_height};
|
||||
tv_reduced_rect = {0, audio_spectrum_height + scale_height, new_parent_rect.width(), new_parent_rect.height() - scale_height - audio_spectrum_height};
|
||||
|
||||
update_widgets_rect();
|
||||
}
|
||||
|
||||
void TVWidget::paint(Painter& painter) {
|
||||
// TODO:
|
||||
(void)painter;
|
||||
// TODO:
|
||||
(void)painter;
|
||||
}
|
||||
|
||||
void TVWidget::on_channel_spectrum(const ChannelSpectrum& spectrum) {
|
||||
tv_view.on_channel_spectrum(spectrum);
|
||||
tv_view.on_adjust_xcorr(field_xcorr.value());
|
||||
sampling_rate = spectrum.sampling_rate;
|
||||
|
||||
tv_view.on_channel_spectrum(spectrum);
|
||||
tv_view.on_adjust_xcorr(field_xcorr.value());
|
||||
sampling_rate = spectrum.sampling_rate;
|
||||
}
|
||||
|
||||
void TVWidget::on_audio_spectrum() {
|
||||
audio_spectrum_view->on_audio_spectrum(audio_spectrum_data);
|
||||
audio_spectrum_view->on_audio_spectrum(audio_spectrum_data);
|
||||
}
|
||||
|
||||
} /* namespace tv */
|
||||
|
@@ -37,140 +37,135 @@ namespace ui {
|
||||
namespace tv {
|
||||
|
||||
class TimeScopeView : public View {
|
||||
public:
|
||||
TimeScopeView(const Rect parent_rect);
|
||||
|
||||
void paint(Painter& painter) override;
|
||||
|
||||
void on_audio_spectrum(const AudioSpectrum* spectrum);
|
||||
|
||||
private:
|
||||
static constexpr int cursor_band_height = 4;
|
||||
|
||||
int16_t audio_spectrum[128] { 0 };
|
||||
|
||||
/*Labels labels {
|
||||
{ { 6 * 8, 0 * 16 }, "Hz", Color::light_grey() }
|
||||
};*/
|
||||
/*
|
||||
NumberField field_frequency {
|
||||
{ 0 * 8, 0 * 16 },
|
||||
5,
|
||||
{ 0, 48000 },
|
||||
48000 / 240,
|
||||
' '
|
||||
};*/
|
||||
|
||||
Waveform waveform {
|
||||
{ 0, 1 * 16 + cursor_band_height, 30 * 8, 2 * 16 },
|
||||
audio_spectrum,
|
||||
128,
|
||||
0,
|
||||
false,
|
||||
Color::white()
|
||||
};
|
||||
public:
|
||||
TimeScopeView(const Rect parent_rect);
|
||||
|
||||
void paint(Painter& painter) override;
|
||||
|
||||
void on_audio_spectrum(const AudioSpectrum* spectrum);
|
||||
|
||||
private:
|
||||
static constexpr int cursor_band_height = 4;
|
||||
|
||||
int16_t audio_spectrum[128]{0};
|
||||
|
||||
/*Labels labels {
|
||||
{ { 6 * 8, 0 * 16 }, "Hz", Color::light_grey() }
|
||||
};*/
|
||||
/*
|
||||
NumberField field_frequency {
|
||||
{ 0 * 8, 0 * 16 },
|
||||
5,
|
||||
{ 0, 48000 },
|
||||
48000 / 240,
|
||||
' '
|
||||
};*/
|
||||
|
||||
Waveform waveform{
|
||||
{0, 1 * 16 + cursor_band_height, 30 * 8, 2 * 16},
|
||||
audio_spectrum,
|
||||
128,
|
||||
0,
|
||||
false,
|
||||
Color::white()};
|
||||
};
|
||||
|
||||
class TVView : public Widget {
|
||||
public:
|
||||
void on_show() override;
|
||||
void on_hide() override;
|
||||
public:
|
||||
void on_show() override;
|
||||
void on_hide() override;
|
||||
|
||||
void paint(Painter& painter) override;
|
||||
void on_channel_spectrum(const ChannelSpectrum& spectrum);
|
||||
void on_adjust_xcorr(uint8_t xcorr);
|
||||
//ui::Color video_buffer[13312];
|
||||
uint8_t video_buffer_int[13312+128] { 0 }; //128 is for the over length caused by x_correction
|
||||
uint32_t count=0;
|
||||
uint8_t x_correction=0;
|
||||
private:
|
||||
void clear();
|
||||
|
||||
void paint(Painter& painter) override;
|
||||
void on_channel_spectrum(const ChannelSpectrum& spectrum);
|
||||
void on_adjust_xcorr(uint8_t xcorr);
|
||||
// ui::Color video_buffer[13312];
|
||||
uint8_t video_buffer_int[13312 + 128]{0}; // 128 is for the over length caused by x_correction
|
||||
uint32_t count = 0;
|
||||
uint8_t x_correction = 0;
|
||||
|
||||
private:
|
||||
void clear();
|
||||
};
|
||||
|
||||
class TVWidget : public View {
|
||||
public:
|
||||
std::function<void(int32_t offset)> on_select { };
|
||||
|
||||
TVWidget();
|
||||
public:
|
||||
std::function<void(int32_t offset)> on_select{};
|
||||
|
||||
TVWidget(const TVWidget&) = delete;
|
||||
TVWidget(TVWidget&&) = delete;
|
||||
TVWidget& operator=(const TVWidget&) = delete;
|
||||
TVWidget& operator=(TVWidget&&) = delete;
|
||||
TVWidget();
|
||||
|
||||
void on_show() override;
|
||||
void on_hide() override;
|
||||
TVWidget(const TVWidget&) = delete;
|
||||
TVWidget(TVWidget&&) = delete;
|
||||
TVWidget& operator=(const TVWidget&) = delete;
|
||||
TVWidget& operator=(TVWidget&&) = delete;
|
||||
|
||||
void set_parent_rect(const Rect new_parent_rect) override;
|
||||
|
||||
void show_audio_spectrum_view(const bool show);
|
||||
void on_show() override;
|
||||
void on_hide() override;
|
||||
|
||||
void paint(Painter& painter) override;
|
||||
NumberField field_xcorr {
|
||||
{ 0 * 8, 0 * 16 },
|
||||
5,
|
||||
{ 0, 128 },
|
||||
1,
|
||||
' '
|
||||
};
|
||||
void set_parent_rect(const Rect new_parent_rect) override;
|
||||
|
||||
private:
|
||||
void update_widgets_rect();
|
||||
|
||||
const Rect audio_spectrum_view_rect { 0 * 8, 0 * 16, 30 * 8, 2 * 16 + 20 };
|
||||
static constexpr Dim audio_spectrum_height = 16 * 2 + 20;
|
||||
static constexpr Dim scale_height = 20;
|
||||
|
||||
TVView tv_view { };
|
||||
void show_audio_spectrum_view(const bool show);
|
||||
|
||||
ChannelSpectrumFIFO* channel_fifo { nullptr };
|
||||
AudioSpectrum* audio_spectrum_data { nullptr };
|
||||
bool audio_spectrum_update { false };
|
||||
|
||||
std::unique_ptr<TimeScopeView> audio_spectrum_view { };
|
||||
|
||||
int sampling_rate { 0 };
|
||||
int32_t cursor_position { 0 };
|
||||
ui::Rect tv_normal_rect { };
|
||||
ui::Rect tv_reduced_rect { };
|
||||
void paint(Painter& painter) override;
|
||||
NumberField field_xcorr{
|
||||
{0 * 8, 0 * 16},
|
||||
5,
|
||||
{0, 128},
|
||||
1,
|
||||
' '};
|
||||
|
||||
MessageHandlerRegistration message_handler_channel_spectrum_config {
|
||||
Message::ID::ChannelSpectrumConfig,
|
||||
[this](const Message* const p) {
|
||||
const auto message = *reinterpret_cast<const ChannelSpectrumConfigMessage*>(p);
|
||||
this->channel_fifo = message.fifo;
|
||||
}
|
||||
};
|
||||
MessageHandlerRegistration message_handler_audio_spectrum {
|
||||
Message::ID::AudioSpectrum,
|
||||
[this](const Message* const p) {
|
||||
const auto message = *reinterpret_cast<const AudioSpectrumMessage*>(p);
|
||||
this->audio_spectrum_data = message.data;
|
||||
this->audio_spectrum_update = true;
|
||||
}
|
||||
};
|
||||
MessageHandlerRegistration message_handler_frame_sync {
|
||||
Message::ID::DisplayFrameSync,
|
||||
[this](const Message* const) {
|
||||
if( this->channel_fifo ) {
|
||||
ChannelSpectrum channel_spectrum;
|
||||
while( channel_fifo->out(channel_spectrum) ) {
|
||||
this->on_channel_spectrum(channel_spectrum);
|
||||
}
|
||||
}
|
||||
if (this->audio_spectrum_update) {
|
||||
this->audio_spectrum_update = false;
|
||||
this->on_audio_spectrum();
|
||||
}
|
||||
}
|
||||
};
|
||||
private:
|
||||
void update_widgets_rect();
|
||||
|
||||
void on_channel_spectrum(const ChannelSpectrum& spectrum);
|
||||
void on_audio_spectrum();
|
||||
const Rect audio_spectrum_view_rect{0 * 8, 0 * 16, 30 * 8, 2 * 16 + 20};
|
||||
static constexpr Dim audio_spectrum_height = 16 * 2 + 20;
|
||||
static constexpr Dim scale_height = 20;
|
||||
|
||||
TVView tv_view{};
|
||||
|
||||
ChannelSpectrumFIFO* channel_fifo{nullptr};
|
||||
AudioSpectrum* audio_spectrum_data{nullptr};
|
||||
bool audio_spectrum_update{false};
|
||||
|
||||
std::unique_ptr<TimeScopeView> audio_spectrum_view{};
|
||||
|
||||
int sampling_rate{0};
|
||||
int32_t cursor_position{0};
|
||||
ui::Rect tv_normal_rect{};
|
||||
ui::Rect tv_reduced_rect{};
|
||||
|
||||
MessageHandlerRegistration message_handler_channel_spectrum_config{
|
||||
Message::ID::ChannelSpectrumConfig,
|
||||
[this](const Message* const p) {
|
||||
const auto message = *reinterpret_cast<const ChannelSpectrumConfigMessage*>(p);
|
||||
this->channel_fifo = message.fifo;
|
||||
}};
|
||||
MessageHandlerRegistration message_handler_audio_spectrum{
|
||||
Message::ID::AudioSpectrum,
|
||||
[this](const Message* const p) {
|
||||
const auto message = *reinterpret_cast<const AudioSpectrumMessage*>(p);
|
||||
this->audio_spectrum_data = message.data;
|
||||
this->audio_spectrum_update = true;
|
||||
}};
|
||||
MessageHandlerRegistration message_handler_frame_sync{
|
||||
Message::ID::DisplayFrameSync,
|
||||
[this](const Message* const) {
|
||||
if (this->channel_fifo) {
|
||||
ChannelSpectrum channel_spectrum;
|
||||
while (channel_fifo->out(channel_spectrum)) {
|
||||
this->on_channel_spectrum(channel_spectrum);
|
||||
}
|
||||
}
|
||||
if (this->audio_spectrum_update) {
|
||||
this->audio_spectrum_update = false;
|
||||
this->on_audio_spectrum();
|
||||
}
|
||||
}};
|
||||
|
||||
void on_channel_spectrum(const ChannelSpectrum& spectrum);
|
||||
void on_audio_spectrum();
|
||||
};
|
||||
|
||||
} /* namespace tv */
|
||||
} /* namespace ui */
|
||||
|
||||
#endif/*__UI_TV_H__*/
|
||||
#endif /*__UI_TV_H__*/
|
||||
|
Reference in New Issue
Block a user