Multi screen support, with dyn alignment (#2801)

This commit is contained in:
Totoo
2025-10-03 19:10:10 +02:00
committed by GitHub
parent 23cabb8b8a
commit 371b6b5079
161 changed files with 4042 additions and 4157 deletions
+6 -6
View File
@@ -67,18 +67,18 @@ class ACARSAppView : public View {
uint32_t packet_counter{0};
RFAmpField field_rf_amp{
{13 * 8, 0 * 16}};
{13 * 8, UI_POS_Y(0)}};
LNAGainField field_lna{
{15 * 8, 0 * 16}};
{15 * 8, UI_POS_Y(0)}};
VGAGainField field_vga{
{18 * 8, 0 * 16}};
{18 * 8, UI_POS_Y(0)}};
RSSI rssi{
{21 * 8, 0, 6 * 8, 4}};
{UI_POS_X(21), 0, UI_POS_WIDTH_REMAINING(24), 4}};
Channel channel{
{21 * 8, 5, 6 * 8, 4}};
{UI_POS_X(21), 5, UI_POS_WIDTH_REMAINING(24), 4}};
RxFrequencyField field_frequency{
{0 * 8, 0 * 8},
{UI_POS_X(0), 0 * 8},
nav_};
Checkbox check_log{
{16 * 8, 1 * 16},
+2 -2
View File
@@ -62,7 +62,7 @@ class ADSBPositionView : public OptionTabView {
GeoPos::HIDDEN};
Button button_set_map{
{8 * 8, 6 * 16, 14 * 8, 2 * 16},
{UI_POS_X_CENTER(14), 6 * 16, 14 * 8, 2 * 16},
"Set from map"};
};
@@ -244,7 +244,7 @@ class ADSBTxView : public View {
"-"};
TransmitterView tx_view{
16 * 16,
(int16_t)UI_POS_Y_BOTTOM(4),
1000000,
0};
+9 -9
View File
@@ -73,31 +73,31 @@ class AFSKRxView : public View {
bool logging{false};
RFAmpField field_rf_amp{
{13 * 8, 0 * 16}};
{13 * 8, UI_POS_Y(0)}};
LNAGainField field_lna{
{15 * 8, 0 * 16}};
{15 * 8, UI_POS_Y(0)}};
VGAGainField field_vga{
{18 * 8, 0 * 16}};
{18 * 8, UI_POS_Y(0)}};
RSSI rssi{
{21 * 8, 0, 6 * 8, 4}};
{UI_POS_X(21), 0, UI_POS_WIDTH_REMAINING(24), 4}};
Channel channel{
{21 * 8, 5, 6 * 8, 4}};
{UI_POS_X(21), 5, UI_POS_WIDTH_REMAINING(24), 4}};
AudioVolumeField field_volume{
{screen_width - 2 * 8, 0 * 16}};
{screen_width - 2 * 8, UI_POS_Y(0)}};
RxFrequencyField field_frequency{
{0 * 8, 0 * 16},
{UI_POS_X(0), UI_POS_Y(0)},
nav_};
Checkbox check_log{
{0 * 8, 1 * 16},
{UI_POS_X(0), 1 * 16},
3,
LanguageHelper::currentMessages[LANG_LOG],
false};
Text text_debug{
{0 * 8, 12 + 2 * 16, screen_width, 16},
{UI_POS_X(0), 12 + 2 * 16, screen_width, 16},
LanguageHelper::currentMessages[LANG_DEBUG]};
Button button_modem_setup{
+7 -7
View File
@@ -58,8 +58,8 @@ class AnalogTvView : public View {
app_settings::SettingsManager settings_{
"rx_tv", app_settings::Mode::RX};
const Rect options_view_rect{0 * 8, 1 * 16, screen_width, 1 * 16};
const Rect nbfm_view_rect{0 * 8, 1 * 16, 18 * 8, 1 * 16};
const Rect options_view_rect{UI_POS_X(0), 1 * 16, screen_width, 1 * 16};
const Rect nbfm_view_rect{UI_POS_X(0), 1 * 16, 18 * 8, 1 * 16};
RSSI rssi{
{21 * 8, 0, 6 * 8, 4}};
@@ -71,17 +71,17 @@ class AnalogTvView : public View {
{21 * 8, 10, 6 * 8, 4}};
RxFrequencyField field_frequency{
{5 * 8, 0 * 16},
{5 * 8, UI_POS_Y(0)},
nav_};
LNAGainField field_lna{
{15 * 8, 0 * 16}};
{15 * 8, UI_POS_Y(0)}};
VGAGainField field_vga{
{18 * 8, 0 * 16}};
{18 * 8, UI_POS_Y(0)}};
OptionsField options_modulation{
{0 * 8, 0 * 16},
{UI_POS_X(0), UI_POS_Y(0)},
4,
{
{"TV ", toUType(ReceiverModel::Mode::WidebandFMAudio)},
@@ -90,7 +90,7 @@ class AnalogTvView : public View {
}};
AudioVolumeField field_volume{
{27 * 8, 0 * 16}};
{27 * 8, UI_POS_Y(0)}};
std::unique_ptr<Widget> options_widget{};
@@ -89,7 +89,7 @@ class WhipCalcView : public View {
{0, 6 * 16, screen_width, 160}};
Button button_exit{
{72, 17 * 16, 96, 32},
{UI_POS_X_RIGHT(12), UI_POS_Y_BOTTOM(3), 96, 32},
"Back"};
};
@@ -50,7 +50,7 @@ AppManagerView::AppManagerView(NavigationView& nav)
&button_set_cancel_autostart,
&button_clean_autostart});
menu_view.set_parent_rect({0, 2 * 8, screen_width, 24 * 8});
menu_view.set_parent_rect({0, 2 * 8, screen_width, UI_POS_HEIGHT_REMAINING(7)});
menu_view.on_highlight = [this]() {
if (menu_view.highlighted_index() >= app_list_index) {
@@ -43,28 +43,28 @@ class AppManagerView : public View {
uint16_t app_list_index{0};
Labels labels{
{{0 * 8, 0 * 16}, "App list:", Theme::getInstance()->fg_light->foreground}};
{{UI_POS_X(0), UI_POS_Y(0)}, "App list:", Theme::getInstance()->fg_light->foreground}};
MenuView menu_view{};
Text text_app_info{
{0, 27 * 8, screen_width, 16},
{0, UI_POS_Y_BOTTOM(6), screen_width, UI_POS_HEIGHT(1)},
"Highlight an app"};
Button button_hide_unhide{
{0, 29 * 8, screen_width / 2 - 1, 32},
{0, UI_POS_Y_BOTTOM(5), screen_width / 2 - 1, UI_POS_HEIGHT(2)},
"Hide/Show"};
Button button_clean_hide{
{screen_width / 2 + 2, 29 * 8, screen_width / 2 - 2, 32},
{screen_width / 2 + 2, UI_POS_Y_BOTTOM(5), screen_width / 2 - 2, UI_POS_HEIGHT(2)},
"Clean Hidden"};
Button button_set_cancel_autostart{
{0, screen_height - 32 - 16, screen_width / 2 - 1, 32},
{0, screen_height - 32 - 16, screen_width / 2 - 1, UI_POS_HEIGHT(2)},
"Set Autostart"};
Button button_clean_autostart{
{screen_width / 2 + 2, screen_height - 32 - 16, screen_width / 2 - 2, 32},
{screen_width / 2 + 2, screen_height - 32 - 16, screen_width / 2 - 2, UI_POS_HEIGHT(2)},
"Del Autostart"};
std::string get_app_info(uint16_t index, bool is_display_name);
+5 -5
View File
@@ -44,13 +44,13 @@ class AudioTestView : public View {
bool beep{false};
Labels labels{
{{7 * 8, 3 * 16}, "Audio Beep Test", Theme::getInstance()->fg_light->foreground},
{{0 * 8, 6 * 16}, "Sample Rate (Hz):", Theme::getInstance()->fg_light->foreground},
{{UI_POS_X_CENTER(16), 3 * 16}, "Audio Beep Test", Theme::getInstance()->fg_light->foreground},
{{UI_POS_X(0), 6 * 16}, "Sample Rate (Hz):", Theme::getInstance()->fg_light->foreground},
{{25 * 8, 7 * 16}, "Step:", Theme::getInstance()->fg_light->foreground},
{{0 * 8, 8 * 16}, "Frequency (Hz):", Theme::getInstance()->fg_light->foreground},
{{0 * 8, 10 * 16}, "Duration (ms):", Theme::getInstance()->fg_light->foreground},
{{UI_POS_X(0), 8 * 16}, "Frequency (Hz):", Theme::getInstance()->fg_light->foreground},
{{UI_POS_X(0), 10 * 16}, "Duration (ms):", Theme::getInstance()->fg_light->foreground},
{{25 * 8, 10 * 16}, "0=con", Theme::getInstance()->fg_light->foreground},
{{0 * 8, 12 * 16}, "Volume:", Theme::getInstance()->fg_light->foreground}};
{{UI_POS_X(0), 12 * 16}, "Volume:", Theme::getInstance()->fg_light->foreground}};
OptionsField options_sample_rate{
{18 * 8, 6 * 16},
+158
View File
@@ -0,0 +1,158 @@
/*
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2016 Furrtek
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#include "bht.hpp"
#include "portapack_persistent_memory.hpp"
namespace ui::external_app::bht_tx {
size_t gen_message_ep(uint8_t city_code, size_t family_code_ep, uint32_t relay_number, uint32_t relay_state) {
size_t c;
const encoder_def_t* um3750_def;
uint8_t bits[12];
std::string ep_fragments;
// Pre-allocate to avoid fragmentation.
ep_fragments.reserve(384);
// Repeated 2x 26 times
// Whole frame + space = 128ms, data only = 64ms
um3750_def = &encoder_defs[ENCODER_UM3750];
// City code is bit-reversed
for (c = 0; c < 8; c++)
bits[c] = (city_code >> c) & 1;
bits[8] = (family_code_ep >> 1) & 1;
bits[9] = family_code_ep & 1;
bits[10] = relay_number & 1;
bits[11] = relay_state ? 1 : 0;
// Text for display
// for (c = 0; c < 12; c++)
// ep_message[c] = bits[c] + '0';
c = 0;
for (auto ch : um3750_def->word_format) {
if (ch == 'S')
ep_fragments += um3750_def->sync;
else
ep_fragments += um3750_def->bit_format[bits[c++]];
}
// Return bitstream length
return make_bitstream(ep_fragments);
}
std::string gen_message_xy(const std::string& ascii_code) {
std::string local_code = ascii_code;
uint8_t ccir_message[XY_TONE_COUNT];
uint8_t translate;
uint32_t c;
// Replace repeats with E code
for (c = 1; c < XY_TONE_COUNT; c++)
if (local_code[c] == local_code[c - 1]) local_code[c] = 'E';
for (c = 0; c < XY_TONE_COUNT; c++) {
if (local_code[c] <= '9')
translate = local_code[c] - '0';
else
translate = local_code[c] - 'A' + 10;
ccir_message[c] = (translate < 16) ? translate : 0; // Sanitize
}
// Copy for baseband
memcpy(shared_memory.bb_data.tones_data.message, ccir_message, XY_TONE_COUNT);
// Return as text for display
return local_code;
}
std::string gen_message_xy(size_t header_code_a, size_t header_code_b, size_t city_code, size_t family_code, bool subfamily_wc, size_t subfamily_code, bool id_wc, size_t receiver_code, size_t relay_state_A, size_t relay_state_B, size_t relay_state_C, size_t relay_state_D) {
uint8_t ccir_message[XY_TONE_COUNT];
size_t c;
// Header
ccir_message[0] = (header_code_a / 10);
ccir_message[1] = (header_code_a % 10);
ccir_message[2] = (header_code_b / 10);
ccir_message[3] = (header_code_b % 10);
// Addresses
ccir_message[4] = (city_code / 10);
ccir_message[5] = (city_code % 10);
ccir_message[6] = family_code;
if (subfamily_wc)
ccir_message[7] = 0xA; // Wildcard
else
ccir_message[7] = subfamily_code;
if (id_wc) {
ccir_message[8] = 0xA; // Wildcard
ccir_message[9] = 0xA; // Wildcard
} else {
ccir_message[8] = (receiver_code / 10);
ccir_message[9] = (receiver_code % 10);
}
ccir_message[10] = 0xB;
// Relay states
ccir_message[11] = relay_state_A;
ccir_message[12] = relay_state_B;
ccir_message[13] = relay_state_C;
ccir_message[14] = relay_state_D;
ccir_message[15] = 0xB;
// End
for (c = 16; c < XY_TONE_COUNT; c++)
ccir_message[c] = 0;
// Replace repeats with E code
for (c = 1; c < XY_TONE_COUNT; c++)
if (ccir_message[c] == ccir_message[c - 1]) ccir_message[c] = 0xE;
// Copy for baseband
memcpy(shared_memory.bb_data.tones_data.message, ccir_message, XY_TONE_COUNT);
// Return as text for display
return ccir_to_ascii(ccir_message);
}
std::string ccir_to_ascii(uint8_t* ccir) {
std::string ascii;
for (size_t c = 0; c < XY_TONE_COUNT; c++) {
if (ccir[c] > 9)
ascii += (char)(ccir[c] - 10 + 'A');
else
ascii += (char)(ccir[c] + '0');
}
return ascii;
}
} // namespace ui::external_app::bht_tx
+53
View File
@@ -0,0 +1,53 @@
/*
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2016 Furrtek
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#include "ui.hpp"
#include "ui_navigation.hpp"
#include "tonesets.hpp"
#include "encoders.hpp"
using namespace encoders;
namespace ui::external_app::bht_tx {
#define XY_TONE_DURATION ((TONES_SAMPLERATE * 0.1) - 1) // 100ms
#define XY_SILENCE (TONES_SAMPLERATE * 0.4) // 400ms
#define XY_TONE_COUNT 20
#define XY_MAX_CITY 99
#define EPAR_BIT_DURATION (OOK_SAMPLERATE / 580)
#define EPAR_REPEAT_COUNT 26
#define EPAR_MAX_CITY 255
struct bht_city {
std::string name;
uint8_t freq_index;
bool recent;
};
size_t gen_message_ep(uint8_t city_code, size_t family_code_ep, uint32_t relay_state_A, uint32_t relay_state_B);
std::string gen_message_xy(const std::string& code);
std::string gen_message_xy(size_t header_code_a, size_t header_code_b, size_t city_code, size_t family_code, bool subfamily_wc, size_t subfamily_code, bool id_wc, size_t receiver_code, size_t relay_state_A, size_t relay_state_B, size_t relay_state_C, size_t relay_state_D);
std::string ccir_to_ascii(uint8_t* ccir);
} // namespace ui::external_app::bht_tx
+83
View File
@@ -0,0 +1,83 @@
/*
* Copyright (C) 2024 EPIRB Decoder Implementation
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#include "ui.hpp"
#include "ui_bht_tx.hpp"
#include "ui_navigation.hpp"
#include "external_app.hpp"
namespace ui::external_app::bht_tx {
void initialize_app(ui::NavigationView& nav) {
nav.push<BHTView>();
}
} // namespace ui::external_app::bht_tx
extern "C" {
__attribute__((section(".external_app.app_bht_tx.application_information"), used)) application_information_t _application_information_bht_tx = {
/*.memory_location = */ (uint8_t*)0x00000000,
/*.externalAppEntry = */ ui::external_app::bht_tx::initialize_app,
/*.header_version = */ CURRENT_HEADER_VERSION,
/*.app_version = */ VERSION_MD5,
/*.app_name = */ "BHT TX",
/*.bitmap_data = */ {
0x00,
0x00,
0xE0,
0x07,
0xF8,
0x08,
0x9C,
0x07,
0x0C,
0x00,
0x8E,
0x0A,
0x46,
0x12,
0x26,
0x22,
0x06,
0x02,
0x06,
0x00,
0x06,
0x00,
0x06,
0x00,
0x06,
0x00,
0x06,
0x00,
0x06,
0x00,
0x00,
0x00,
},
/*.icon_color = */ ui::Color::green().v,
/*.menu_location = */ app_location_t::TX,
/*.desired_menu_position = */ -1,
/*.m4_app_tag = portapack::spi_flash::image_tag_tones */ {'P', 'T', 'O', 'N'},
/*.m4_app_offset = */ 0x00000000, // will be filled at compile time
};
}
+341
View File
@@ -0,0 +1,341 @@
/*
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2016 Furrtek
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#include "ui_bht_tx.hpp"
#include "string_format.hpp"
#include "baseband_api.hpp"
#include "portapack_persistent_memory.hpp"
using namespace portapack;
namespace ui::external_app::bht_tx {
void BHTView::focus() {
tx_view.focus();
}
void BHTView::start_tx() {
baseband::shutdown();
transmitter_model.set_baseband_bandwidth(1750000);
if (target_system == XYLOS) {
baseband::run_prepared_image(portapack::memory::map::m4_code.base());
view_xylos.generate_message();
// if (tx_mode == SINGLE) {
progressbar.set_max(XY_TONE_COUNT);
/*} else if (tx_mode == SCAN) {
progressbar.set_max(XY_TONE_COUNT * view_xylos.get_scan_remaining());
}*/
transmitter_model.set_sampling_rate(TONES_SAMPLERATE);
transmitter_model.enable();
// Setup tones
for (size_t c = 0; c < ccir_deltas.size(); c++)
baseband::set_tone(c, ccir_deltas[c], XY_TONE_DURATION);
baseband::set_tones_config(transmitter_model.channel_bandwidth(), XY_SILENCE, XY_TONE_COUNT, false, false);
} else if (target_system == EPAR) {
baseband::run_image(portapack::spi_flash::image_tag_ook);
auto bitstream_length = view_EPAR.generate_message();
// if (tx_mode == SINGLE) {
progressbar.set_max(2 * EPAR_REPEAT_COUNT);
/*} else if (tx_mode == SCAN) {
progressbar.set_max(2 * EPAR_REPEAT_COUNT * view_EPAR.get_scan_remaining());
}*/
transmitter_model.set_sampling_rate(OOK_SAMPLERATE);
transmitter_model.enable();
baseband::set_ook_data(
bitstream_length,
EPAR_BIT_DURATION,
EPAR_REPEAT_COUNT,
encoder_defs[ENCODER_UM3750].pause_symbols);
}
}
void BHTView::stop_tx() {
transmitter_model.disable();
baseband::shutdown();
tx_mode = IDLE;
tx_view.set_transmitting(false);
progressbar.set_value(0);
}
void BHTView::on_tx_progress(const uint32_t progress, const bool done) {
if (target_system == XYLOS) {
if (done) {
if (tx_mode == SINGLE) {
if (checkbox_flashing.value()) {
// TODO: Thread !
chThdSleepMilliseconds(field_speed.value() * 1000); // Dirty :(
view_xylos.flip_relays();
start_tx();
} else
stop_tx();
} else if (tx_mode == SCAN) {
if (view_xylos.increment_address())
start_tx();
else
stop_tx(); // Reached end of scan range
}
} else
progressbar.set_value(progress);
} else if (target_system == EPAR) {
if (done) {
if (!view_EPAR.half) {
view_EPAR.half = true;
start_tx(); // Start second half of transmission
} else {
view_EPAR.half = false;
if (tx_mode == SINGLE) {
if (checkbox_flashing.value()) {
// TODO: Thread !
chThdSleepMilliseconds(field_speed.value() * 1000); // Dirty :(
view_EPAR.flip_relays();
start_tx();
} else
stop_tx();
} else if (tx_mode == SCAN) {
if (view_EPAR.increment_address())
start_tx();
else
stop_tx(); // Reached end of scan range
}
}
} else
progressbar.set_value(progress);
}
}
BHTView::~BHTView() {
transmitter_model.disable();
baseband::shutdown();
}
BHTView::BHTView(NavigationView& nav) {
add_children({&tab_view,
&labels,
&view_xylos,
&view_EPAR,
&checkbox_scan,
&checkbox_flashing,
&field_speed,
&progressbar,
&tx_view});
field_speed.set_value(1);
tx_view.on_edit_frequency = [this, &nav]() {
auto new_view = nav.push<FrequencyKeypadView>(transmitter_model.target_frequency());
new_view->on_changed = [this](rf::Frequency f) {
transmitter_model.set_target_frequency(f);
};
};
tx_view.on_start = [this]() {
if (tx_mode == IDLE) {
if (checkbox_scan.value()) {
tx_mode = SCAN;
} else {
tx_mode = SINGLE;
}
progressbar.set_value(0);
tx_view.set_transmitting(true);
target_system = (target_system_t)tab_view.selected();
view_EPAR.half = false;
start_tx();
}
};
tx_view.on_stop = [this]() {
stop_tx();
};
}
bool EPARView::increment_address() {
auto city_code = field_city.value();
if (city_code < EPAR_MAX_CITY) {
field_city.set_value(city_code + 1);
return true;
} else
return false;
}
uint32_t EPARView::get_scan_remaining() {
return EPAR_MAX_CITY - field_city.value();
}
void EPARView::flip_relays() {
// Invert first relay's state
relay_states[0].set_selected_index(relay_states[0].selected_index() ^ 1);
}
size_t EPARView::generate_message() {
// R2, then R1
return gen_message_ep(field_city.value(), field_group.selected_index_value(),
half ? 0 : 1, relay_states[half].selected_index());
}
EPARView::EPARView(
Rect parent_rect)
: View(parent_rect) {
hidden(true);
add_children({&labels,
&field_city,
&field_group});
field_city.set_value(0);
field_group.set_selected_index(2);
field_city.on_change = [this](int32_t) { generate_message(); };
field_group.on_change = [this](size_t, int32_t) { generate_message(); };
const auto relay_state_fn = [this](size_t, OptionsField::value_t) {
generate_message();
};
size_t n = 0;
for (auto& relay_state : relay_states) {
relay_state.set_parent_rect({static_cast<Coord>(90 + (n * 36)),
80,
24, 24});
relay_state.set_options(relay_options);
relay_state.on_change = relay_state_fn; // NB: set after set_options to avoid startup call.
add_child(&relay_state);
n++;
}
}
void EPARView::focus() {
field_city.focus();
}
void XylosView::flip_relays() {
// Invert first relay's state if not ignored
size_t rs = relay_states[0].selected_index();
if (rs > 0)
relay_states[0].set_selected_index(rs ^ 3);
}
bool XylosView::increment_address() {
auto city_code = field_city.value();
if (city_code < XY_MAX_CITY) {
field_city.set_value(city_code + 1);
return true;
} else
return false;
}
uint32_t XylosView::get_scan_remaining() {
return XY_MAX_CITY - field_city.value();
}
void XylosView::generate_message() {
gen_message_xy(field_header_a.value(), field_header_b.value(), field_city.value(), field_family.value(),
checkbox_wcsubfamily.value(), field_subfamily.value(), checkbox_wcid.value(), field_receiver.value(),
relay_states[0].selected_index(), relay_states[1].selected_index(),
relay_states[2].selected_index(), relay_states[3].selected_index());
}
XylosView::XylosView(
Rect parent_rect)
: View(parent_rect) {
hidden(true);
add_children({
&labels,
&field_header_a,
&field_header_b,
&field_city,
&field_family,
&field_subfamily,
&checkbox_wcsubfamily,
&field_receiver,
&checkbox_wcid,
//&button_seq,
});
field_header_a.set_value(0);
field_header_b.set_value(0);
field_city.set_value(10);
field_family.set_value(1);
field_subfamily.set_value(1);
field_receiver.set_value(1);
const auto field_fn = [this](int32_t) {
generate_message();
};
field_header_a.on_change = field_fn;
field_header_b.on_change = field_fn;
field_city.on_change = field_fn;
field_family.on_change = field_fn;
field_subfamily.on_change = field_fn;
field_receiver.on_change = field_fn;
checkbox_wcsubfamily.on_select = [this](Checkbox&, bool v) {
field_subfamily.set_focusable(!v);
generate_message();
};
checkbox_wcid.on_select = [this](Checkbox&, bool v) {
field_receiver.set_focusable(!v);
generate_message();
};
checkbox_wcsubfamily.set_value(true);
checkbox_wcid.set_value(true);
const auto relay_state_fn = [this](size_t, OptionsField::value_t) {
generate_message();
};
size_t n = 0;
for (auto& relay_state : relay_states) {
relay_state.set_parent_rect({static_cast<Coord>(54 + (n * 36)),
134,
24, 24});
relay_state.set_options(relay_options);
relay_state.on_change = relay_state_fn; // NB: set after set_options to avoid startup call.
add_child(&relay_state);
n++;
}
}
void XylosView::focus() {
field_city.focus();
}
} // namespace ui::external_app::bht_tx
+237
View File
@@ -0,0 +1,237 @@
/*
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2016 Furrtek
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#include "ui.hpp"
#include "ui_widget.hpp"
#include "ui_tabview.hpp"
#include "ui_navigation.hpp"
#include "ui_transmitter.hpp"
#include "bht.hpp"
#include "bitmap.hpp"
#include "message.hpp"
#include "transmitter_model.hpp"
#include "encoders.hpp"
#include "app_settings.hpp"
#include "radio_state.hpp"
#include "portapack.hpp"
namespace ui::external_app::bht_tx {
class XylosView : public View {
public:
XylosView(Rect parent_rect);
void focus() override;
void flip_relays();
void generate_message();
bool increment_address();
uint32_t get_scan_remaining();
private:
Labels labels{
{{8 * 8, 1 * 8}, "Header:", Theme::getInstance()->fg_light->foreground},
{{4 * 8, 3 * 8}, "City code:", Theme::getInstance()->fg_light->foreground},
{{7 * 8, 5 * 8}, "Family:", Theme::getInstance()->fg_light->foreground},
{{2 * 8, 7 * 8 + 2}, "Subfamily:", Theme::getInstance()->fg_light->foreground},
{{2 * 8, 11 * 8}, "Receiver ID:", Theme::getInstance()->fg_light->foreground},
{{2 * 8, 14 * 8}, "Relay:", Theme::getInstance()->fg_light->foreground}};
NumberField field_header_a{
{16 * 8, 1 * 8},
2,
{0, 99},
1,
'0'};
NumberField field_header_b{
{18 * 8, 1 * 8},
2,
{0, 99},
1,
'0'};
NumberField field_city{
{16 * 8, 3 * 8},
2,
{0, XY_MAX_CITY},
1,
' '};
NumberField field_family{
{16 * 8, 5 * 8},
1,
{0, 9},
1,
' '};
NumberField field_subfamily{
{16 * 8, 7 * 8 + 2},
1,
{0, 9},
1,
' '};
Checkbox checkbox_wcsubfamily{
{20 * 8, 6 * 8 + 6},
3,
"All"};
NumberField field_receiver{
{16 * 8, 11 * 8},
2,
{0, 99},
1,
'0'};
Checkbox checkbox_wcid{
{20 * 8, 10 * 8 + 4},
3,
"All"};
std::array<ImageOptionsField, 4> relay_states{};
ImageOptionsField::options_t relay_options = {
{&bitmap_bulb_ignore, 0},
{&bitmap_bulb_off, 1},
{&bitmap_bulb_on, 2}};
};
class EPARView : public View {
public:
EPARView(Rect parent_rect);
void focus() override;
void flip_relays();
size_t generate_message();
bool increment_address();
uint32_t get_scan_remaining();
bool half{false};
private:
Labels labels{
{{4 * 8, 1 * 8}, "City code:", Theme::getInstance()->fg_light->foreground},
{{8 * 8, 3 * 8}, "Group:", Theme::getInstance()->fg_light->foreground},
{{8 * 8, 7 * 8}, "Relay:", Theme::getInstance()->fg_light->foreground}};
NumberField field_city{
{16 * 8, 1 * 8},
3,
{0, EPAR_MAX_CITY},
1,
'0'};
OptionsField field_group{
{16 * 8, 3 * 8},
2,
{{"A ", 2}, // See receiver PCB
{"B ", 1},
{"C ", 0},
{"TP", 3}}};
std::array<ImageOptionsField, 2> relay_states{};
ImageOptionsField::options_t relay_options = {
{&bitmap_bulb_off, 0},
{&bitmap_bulb_on, 1}};
};
class BHTView : public View {
public:
BHTView(NavigationView& nav);
~BHTView();
void focus() override;
std::string title() const override { return "BHT TX"; };
private:
TxRadioState radio_state_{};
app_settings::SettingsManager settings_{
"tx_bht", app_settings::Mode::TX};
void on_tx_progress(const uint32_t progress, const bool done);
void start_tx();
void stop_tx();
enum target_system_t {
XYLOS = 0,
EPAR = 1
};
target_system_t target_system = {};
enum tx_modes {
IDLE = 0,
SINGLE,
SCAN
};
tx_modes tx_mode = IDLE;
Rect view_rect = {0, 3 * 8, screen_width, 176};
XylosView view_xylos{view_rect};
EPARView view_EPAR{view_rect};
TabView tab_view{
{"Xylos", Theme::getInstance()->fg_cyan->foreground, &view_xylos},
{"EPAR", Theme::getInstance()->fg_green->foreground, &view_EPAR}};
Labels labels{
{{29 * 8, 14 * 16 + 4}, "s", Theme::getInstance()->fg_light->foreground}};
Checkbox checkbox_scan{
{1 * 8, 25 * 8},
4,
"Scan"};
Checkbox checkbox_flashing{
{16 * 8, 25 * 8},
8,
"Flashing"};
NumberField field_speed{
{26 * 8, 25 * 8 + 4},
2,
{1, 99},
1,
' '};
ProgressBar progressbar{
{UI_POS_X(0), UI_POS_Y_BOTTOM(5), screen_width, 16},
};
TransmitterView tx_view{
(int16_t)UI_POS_Y_BOTTOM(4),
10000,
12};
MessageHandlerRegistration message_handler_tx_progress{
Message::ID::TXProgress,
[this](const Message* const p) {
const auto message = *reinterpret_cast<const TXProgressMessage*>(p);
this->on_tx_progress(message.progress, message.done);
}};
};
} // namespace ui::external_app::bht_tx
+24 -24
View File
@@ -70,7 +70,7 @@ void BlackjackView::frame_sync() {
}
void BlackjackView::clear_screen() {
painter.fill_rectangle({0, 0, 240, 320}, Color::black());
painter.fill_rectangle({0, 0, screen_width, screen_height}, Color::black());
}
void BlackjackView::init_deck() {
@@ -265,24 +265,24 @@ void BlackjackView::draw_menu_static() {
auto style_text = *ui::Theme::getInstance()->fg_light;
auto style_rules = *ui::Theme::getInstance()->fg_cyan;
painter.draw_string({84, 20}, style_title, "BLACKJACK");
painter.draw_string({UI_POS_X_CENTER(10), 20}, style_title, "BLACKJACK");
// Draw rules
painter.draw_string({70, 55}, style_rules, "-- RULES --");
painter.draw_string({61, 75}, style_text, "Get close to 21");
painter.draw_string({61, 90}, style_text, "without going over");
painter.draw_string({61, 110}, style_text, "Dealer hits on 16");
painter.draw_string({61, 125}, style_text, "Dealer stays on 17");
painter.draw_string({61, 145}, style_text, "Blackjack pays 1:1");
painter.draw_string({UI_POS_X_CENTER(12), 55}, style_rules, "-- RULES --");
painter.draw_string({UI_POS_X_CENTER(16), 75}, style_text, "Get close to 21");
painter.draw_string({UI_POS_X_CENTER(19), 90}, style_text, "without going over");
painter.draw_string({UI_POS_X_CENTER(18), 110}, style_text, "Dealer hits on 16");
painter.draw_string({UI_POS_X_CENTER(19), 125}, style_text, "Dealer stays on 17");
painter.draw_string({UI_POS_X_CENTER(19), 145}, style_text, "Blackjack pays 1:1");
// Controls
painter.draw_string({65, 175}, style_rules, "-- CONTROLS --");
painter.draw_string({61, 195}, style_text, "SELECT: Start/Hit");
painter.draw_string({61, 210}, style_text, "LEFT: Stats");
painter.draw_string({61, 225}, style_text, "RIGHT: Exit/Stay");
painter.draw_string({UI_POS_X_CENTER(15), 175}, style_rules, "-- CONTROLS --");
painter.draw_string({UI_POS_X_CENTER(18), 195}, style_text, "SELECT: Start/Hit");
painter.draw_string({UI_POS_X_CENTER(12), 210}, style_text, "LEFT: Stats");
painter.draw_string({UI_POS_X_CENTER(17), 225}, style_text, "RIGHT: Exit/Stay");
// Draw high score
painter.draw_string({61, 250}, style_text, "High Score: $" + std::to_string(high_score));
painter.draw_string({UI_POS_X_CENTER(17), 250}, style_text, "High Score: $" + std::to_string(high_score));
}
void BlackjackView::draw_menu() {
@@ -293,10 +293,10 @@ void BlackjackView::draw_menu() {
if (blink_state) {
auto style = *ui::Theme::getInstance()->fg_yellow;
painter.draw_string({55, 280}, style, "* PRESS SELECT *");
painter.draw_string({UI_POS_X_CENTER(17), 280}, style, "* PRESS SELECT *");
} else {
// Clear just the text area
painter.fill_rectangle({55, 278, 130, 20}, Color::black());
painter.fill_rectangle({UI_POS_X_CENTER(17), 278, 130, 20}, Color::black());
}
}
}
@@ -308,27 +308,27 @@ void BlackjackView::draw_stats() {
auto style_text = *ui::Theme::getInstance()->fg_light;
auto style_value = *ui::Theme::getInstance()->fg_yellow;
painter.draw_string({75, 30}, style_title, "STATISTICS");
painter.draw_string({UI_POS_X_CENTER(11), 30}, style_title, "STATISTICS");
painter.draw_string({30, 80}, style_text, "Wins:");
painter.draw_string({150, 80}, style_value, std::to_string(wins));
painter.draw_string({UI_POS_X_CENTER(5), 80}, style_value, std::to_string(wins));
painter.draw_string({30, 100}, style_text, "Losses:");
painter.draw_string({150, 100}, style_value, std::to_string(losses));
painter.draw_string({UI_POS_X_CENTER(5), 100}, style_value, std::to_string(losses));
// Win percentage
uint32_t total = wins + losses;
if (total > 0) {
uint32_t win_pct = (wins * 100) / total;
painter.draw_string({30, 120}, style_text, "Win %:");
painter.draw_string({150, 120}, style_value, std::to_string(win_pct) + "%");
painter.draw_string({UI_POS_X_CENTER(5), 120}, style_value, std::to_string(win_pct) + "%");
}
painter.draw_string({30, 160}, style_text, "High Score:");
painter.draw_string({150, 160}, style_value, "$" + std::to_string(high_score));
painter.draw_string({UI_POS_X_CENTER(5), 160}, style_value, "$" + std::to_string(high_score));
painter.draw_string({30, 180}, style_text, "Cash:");
painter.draw_string({150, 180}, style_value, "$" + std::to_string(cash));
painter.draw_string({UI_POS_X_CENTER(5), 180}, style_value, "$" + std::to_string(cash));
painter.draw_string({40, 250}, style_text, "SELECT: Back");
}
@@ -416,7 +416,7 @@ void BlackjackView::draw_game() {
}
// Draw result
painter.draw_string({60, 270}, *style_result, result);
painter.draw_string({UI_POS_X_CENTER(result.length()), 270}, *style_result, result);
// Draw compact bet selector in top right area
auto style_bet = *ui::Theme::getInstance()->fg_cyan;
@@ -620,8 +620,8 @@ void BlackjackView::draw_card(int x, int y, uint8_t card, bool hidden) {
void BlackjackView::draw_hand(int x, int y, uint8_t* cards, uint8_t count, bool is_dealer) {
// Calculate total width needed
const int card_width = 60;
const int overlap = 40; // Amount of overlap when cards need to fit
const int max_width = 230 - x; // Available width on screen
const int overlap = 40; // Amount of overlap when cards need to fit
const int max_width = screen_width - 10 - x; // Available width on screen
int spacing;
if (count == 1) {
+1 -1
View File
@@ -141,7 +141,7 @@ class BlackjackView : public View {
{"highscore"sv, &high_score}}};
Button dummy{
{240, 0, 0, 0},
{screen_width, 0, 0, 0},
""};
MessageHandlerRegistration message_handler_frame_sync{
+3 -3
View File
@@ -104,10 +104,10 @@ class BLESpamView : public View {
4'000'000 /* sampling rate */
};
TxFrequencyField field_frequency{
{0 * 8, 0 * 16},
{UI_POS_X(0), UI_POS_Y(0)},
nav_};
TransmitterView2 tx_view{
{11 * 8, 0 * 16},
{11 * 8, UI_POS_Y(0)},
/*short_ui*/ true};
app_settings::SettingsManager settings_{
"tx_blespam", app_settings::Mode::TX};
@@ -122,7 +122,7 @@ class BLESpamView : public View {
{0, 70, screen_width, 220}};
#endif
OptionsField options_atkmode{
{0 * 8, 2 * 8},
{UI_POS_X(0), 2 * 8},
10,
{{"Android", 0},
{"iOs", 1},
+11 -11
View File
@@ -333,11 +333,11 @@ void BreakoutView::show_menu() {
auto style_blue = *ui::Theme::getInstance()->fg_blue;
auto style_cyan = *ui::Theme::getInstance()->fg_cyan;
painter.draw_string({50, 40}, style_yellow, "*** BREAKOUT ***");
painter.draw_string({20, 100}, style_blue, "========================");
painter.draw_string({30, 130}, style_cyan, "ROTARY: MOVE PADDLE");
painter.draw_string({30, 160}, style_cyan, "SELECT: START/LAUNCH");
painter.draw_string({20, 190}, style_blue, "========================");
painter.draw_string({UI_POS_X_CENTER(17), 40}, style_yellow, "*** BREAKOUT ***");
painter.draw_string({UI_POS_X_CENTER(25), 100}, style_blue, "========================");
painter.draw_string({UI_POS_X_CENTER(20), 130}, style_cyan, "ROTARY: MOVE PADDLE");
painter.draw_string({UI_POS_X_CENTER(21), 160}, style_cyan, "SELECT: START/LAUNCH");
painter.draw_string({UI_POS_X_CENTER(25), 190}, style_blue, "========================");
}
auto style_red = *ui::Theme::getInstance()->fg_red;
@@ -346,10 +346,10 @@ void BreakoutView::show_menu() {
menu_blink_counter = 0;
menu_blink_state = !menu_blink_state;
painter.fill_rectangle({56, 228, 128, 20}, Color::black());
painter.fill_rectangle({UI_POS_X_CENTER(17), 228, 128, 20}, Color::black());
if (menu_blink_state) {
painter.draw_string({56, 230}, style_red, "* PRESS SELECT *");
painter.draw_string({UI_POS_X_CENTER(17), 230}, style_red, "* PRESS SELECT *");
}
}
}
@@ -362,8 +362,8 @@ void BreakoutView::show_game_over() {
auto style_red = *ui::Theme::getInstance()->fg_red;
auto style_yellow = *ui::Theme::getInstance()->fg_yellow;
painter.draw_string({90, 100}, style_red, "GAME OVER");
painter.draw_string({50, 150}, style_yellow, "SCORE: " + std::to_string(score));
painter.draw_string({UI_POS_X_CENTER(10), 100}, style_red, "GAME OVER");
painter.draw_string({UI_POS_X_CENTER(11), 150}, style_yellow, "SCORE: " + std::to_string(score));
}
auto style_green = *ui::Theme::getInstance()->fg_green;
@@ -372,10 +372,10 @@ void BreakoutView::show_game_over() {
gameover_blink_counter = 0;
gameover_blink_state = !gameover_blink_state;
painter.fill_rectangle({72, 198, 96, 20}, Color::black());
painter.fill_rectangle({UI_POS_X_CENTER(13), 198, 96, 20}, Color::black());
if (gameover_blink_state) {
painter.draw_string({72, 200}, style_green, "PRESS SELECT");
painter.draw_string({UI_POS_X_CENTER(13), 200}, style_green, "PRESS SELECT");
}
}
}
+17 -17
View File
@@ -48,25 +48,25 @@ class CalculatorView : public View {
private:
NavigationView& nav_;
Button button_F{{0 * 8, 11 * 16, 6 * 8, 16 + 8}, "F"};
Button button_7{{8 * 8, 11 * 16, 6 * 8, 16 + 8}, "7"};
Button button_8{{16 * 8, 11 * 16, 6 * 8, 16 + 8}, "8"};
Button button_9{{24 * 8, 11 * 16, 6 * 8, 16 + 8}, "9"};
Button button_E{{0 * 8, 13 * 16, 6 * 8, 16 + 8}, "E"};
Button button_4{{8 * 8, 13 * 16, 6 * 8, 16 + 8}, "4"};
Button button_5{{16 * 8, 13 * 16, 6 * 8, 16 + 8}, "5"};
Button button_6{{24 * 8, 13 * 16, 6 * 8, 16 + 8}, "6"};
Button button_N{{0 * 8, 15 * 16, 6 * 8, 16 + 8}, "N"};
Button button_1{{8 * 8, 15 * 16, 6 * 8, 16 + 8}, "1"};
Button button_2{{16 * 8, 15 * 16, 6 * 8, 16 + 8}, "2"};
Button button_3{{24 * 8, 15 * 16, 6 * 8, 16 + 8}, "3"};
Button button_C{{0 * 8, 17 * 16, 6 * 8, 16 + 8}, "C"};
Button button_0{{8 * 8, 17 * 16, 6 * 8, 16 + 8}, "0"};
Button button_P{{16 * 8, 17 * 16, 6 * 8, 16 + 8}, "."};
Button button_D{{24 * 8, 17 * 16, 6 * 8, 16 + 8}, "D"};
Button button_F{{UI_POS_X_TABLE(4, 0), UI_POS_Y_BOTTOM(9), UI_POS_WIDTH(6), UI_POS_HEIGHT(1.5)}, "F"};
Button button_7{{UI_POS_X_TABLE(4, 1), UI_POS_Y_BOTTOM(9), UI_POS_WIDTH(6), UI_POS_HEIGHT(1.5)}, "7"};
Button button_8{{UI_POS_X_TABLE(4, 2), UI_POS_Y_BOTTOM(9), UI_POS_WIDTH(6), UI_POS_HEIGHT(1.5)}, "8"};
Button button_9{{UI_POS_X_TABLE(4, 3), UI_POS_Y_BOTTOM(9), UI_POS_WIDTH(6), UI_POS_HEIGHT(1.5)}, "9"};
Button button_E{{UI_POS_X_TABLE(4, 0), UI_POS_Y_BOTTOM(7), UI_POS_WIDTH(6), UI_POS_HEIGHT(1.5)}, "E"};
Button button_4{{UI_POS_X_TABLE(4, 1), UI_POS_Y_BOTTOM(7), UI_POS_WIDTH(6), UI_POS_HEIGHT(1.5)}, "4"};
Button button_5{{UI_POS_X_TABLE(4, 2), UI_POS_Y_BOTTOM(7), UI_POS_WIDTH(6), UI_POS_HEIGHT(1.5)}, "5"};
Button button_6{{UI_POS_X_TABLE(4, 3), UI_POS_Y_BOTTOM(7), UI_POS_WIDTH(6), UI_POS_HEIGHT(1.5)}, "6"};
Button button_N{{UI_POS_X_TABLE(4, 0), UI_POS_Y_BOTTOM(5), UI_POS_WIDTH(6), UI_POS_HEIGHT(1.5)}, "N"};
Button button_1{{UI_POS_X_TABLE(4, 1), UI_POS_Y_BOTTOM(5), UI_POS_WIDTH(6), UI_POS_HEIGHT(1.5)}, "1"};
Button button_2{{UI_POS_X_TABLE(4, 2), UI_POS_Y_BOTTOM(5), UI_POS_WIDTH(6), UI_POS_HEIGHT(1.5)}, "2"};
Button button_3{{UI_POS_X_TABLE(4, 3), UI_POS_Y_BOTTOM(5), UI_POS_WIDTH(6), UI_POS_HEIGHT(1.5)}, "3"};
Button button_C{{UI_POS_X_TABLE(4, 0), UI_POS_Y_BOTTOM(3), UI_POS_WIDTH(6), UI_POS_HEIGHT(1.5)}, "C"};
Button button_0{{UI_POS_X_TABLE(4, 1), UI_POS_Y_BOTTOM(3), UI_POS_WIDTH(6), UI_POS_HEIGHT(1.5)}, "0"};
Button button_P{{UI_POS_X_TABLE(4, 2), UI_POS_Y_BOTTOM(3), UI_POS_WIDTH(6), UI_POS_HEIGHT(1.5)}, "."};
Button button_D{{UI_POS_X_TABLE(4, 3), UI_POS_Y_BOTTOM(3), UI_POS_WIDTH(6), UI_POS_HEIGHT(1.5)}, "D"};
Console console{
{0 * 8, 0 * 16, screen_width, 10 * 16}};
{UI_POS_X(0), UI_POS_Y(0), screen_width, UI_POS_HEIGHT_REMAINING(10)}};
void on_button_press(uint8_t button);
void update_button_labels();
+1 -1
View File
@@ -88,7 +88,7 @@ class CoasterPagerView : public View {
""};
TransmitterView tx_view{
16 * 16,
(int16_t)UI_POS_Y_BOTTOM(4),
10000,
12};
+9 -9
View File
@@ -53,38 +53,38 @@ class CVSSpamView : public View {
std::vector<std::filesystem::path> file_list{};
MenuView menu_view{
{0, 0, screen_width, 180},
{0, 0, screen_width, UI_POS_HEIGHT_REMAINING(12)},
true};
Text text_empty{
{7 * 8, 12 * 8, 16 * 8, 16},
{UI_POS_X_CENTER(16), 12 * 8, UI_POS_WIDTH(16), UI_POS_HEIGHT(1)},
"Empty directory!"};
Button button_prev_page{
{0, 180, 50, 32},
{0, UI_POS_Y_BOTTOM(10), UI_POS_WIDTH(4), UI_POS_HEIGHT(2)},
"<"};
Button button_next_page{
{190, 180, 50, 32},
{UI_POS_X_RIGHT(3), UI_POS_Y_BOTTOM(10), UI_POS_WIDTH(4), UI_POS_HEIGHT(2)},
">"};
Text page_info{
{95, 188, 50, 16}};
{UI_POS_X_CENTER(7), UI_POS_Y_BOTTOM(9.5), UI_POS_WIDTH(7), UI_POS_HEIGHT(1)}};
Button button_send{
{0, 212, 75, 36},
{0, UI_POS_Y_BOTTOM(8), UI_POS_WIDTH(9), UI_POS_HEIGHT(2)},
"CALL"};
Button button_chaos{
{82, 212, 75, 36},
{UI_POS_X_CENTER(9), UI_POS_Y_BOTTOM(8), UI_POS_WIDTH(9), UI_POS_HEIGHT(2)},
"CHAOS"};
Button button_stop{
{165, 212, 75, 36},
{UI_POS_X_RIGHT(9), UI_POS_Y_BOTTOM(8), UI_POS_WIDTH(9), UI_POS_HEIGHT(2)},
LanguageHelper::currentMessages[LANG_STOP]};
ProgressBar progressbar{
{0, 256, screen_width, 44}};
{0, UI_POS_Y_BOTTOM(6), screen_width, UI_POS_HEIGHT(5)}};
MessageHandlerRegistration message_handler_fifo_signal{
Message::ID::RequestSignal,
+2 -2
View File
@@ -44,11 +44,11 @@ class DebugDumpView : public View {
NavigationView& nav_;
Text dump_output{
{0 * 8, 19 * 8, screen_width, 16},
{UI_POS_X(0), 19 * 8, screen_width, 16},
""};
Button button_exit{
{22 * 8, 34 * 8, 8 * 8, 32},
{UI_POS_X_CENTER(8), UI_POS_Y_BOTTOM(4), 8 * 8, 32},
"Exit"};
};
@@ -77,7 +77,9 @@ class DetectorRxView : public View {
}};
Labels labels{
{{UI_POS_X(0), UI_POS_Y(0)}, "LNA: VGA: AMP: VOL: ", Theme::getInstance()->fg_light->foreground}};
{{UI_POS_X(0), UI_POS_Y(0)}, "LNA: VGA: AMP: ", Theme::getInstance()->fg_light->foreground},
{{UI_POS_X_RIGHT(6), UI_POS_Y(0)}, "VOL: ", Theme::getInstance()->fg_light->foreground},
};
LNAGainField field_lna{
{UI_POS_X(4), UI_POS_Y(0)}};
@@ -89,7 +91,7 @@ class DetectorRxView : public View {
{UI_POS_X(18), UI_POS_Y(0)}};
AudioVolumeField field_volume{
{UI_POS_X(24), UI_POS_Y(0)}};
{UI_POS_X_RIGHT(2), UI_POS_Y(0)}};
OptionsField field_mode{
{UI_POS_X(0), UI_POS_Y(1)},
+12 -12
View File
@@ -178,12 +178,12 @@ void DinoGameView::show_menu() {
auto style_title = *ui::Theme::getInstance()->fg_light;
// Draw title
painter.draw_string({80, 60}, style_title, "DINO GAME");
painter.draw_string({UI_POS_X_CENTER(10), 60}, style_title, "DINO GAME");
// Draw instructions
painter.draw_string({45, 130}, style, "SELECT: Jump/Start");
painter.draw_string({65, 150}, style, "DOWN: Duck");
painter.draw_string({50, 170}, style, "Avoid obstacles!");
painter.draw_string({UI_POS_X_CENTER(19), 130}, style, "SELECT: Jump/Start");
painter.draw_string({UI_POS_X_CENTER(11), 150}, style, "DOWN: Duck");
painter.draw_string({UI_POS_X_CENTER(17), 170}, style, "Avoid obstacles!");
// Draw high score
draw_high_score();
@@ -209,9 +209,9 @@ void DinoGameView::show_menu() {
blink_counter = 0;
blink_state = !blink_state;
painter.fill_rectangle({55, 258, 130, 20}, Color::black());
painter.fill_rectangle({UI_POS_X_CENTER(17), 258, 130, 20}, Color::black());
if (blink_state) {
painter.draw_string({55, 260}, style_prompt, "* PRESS SELECT *");
painter.draw_string({UI_POS_X_CENTER(17), 260}, style_prompt, "* PRESS SELECT *");
}
}
}
@@ -234,19 +234,19 @@ void DinoGameView::show_game_over() {
auto style_score = *ui::Theme::getInstance()->fg_medium;
// Game over text
painter.draw_string({85, 70}, style, "GAME OVER");
painter.draw_string({UI_POS_X_CENTER(10), 70}, style, "GAME OVER");
// Show final score
std::string score_text = "SCORE: " + score_to_string(score);
int score_x = (240 - score_text.length() * 8) / 2;
int score_x = (screen_width - score_text.length() * 8) / 2;
painter.draw_string({score_x, 90}, style_score, score_text);
painter.draw_string({65, 110}, style, "SELECT TO RETRY");
painter.draw_string({UI_POS_X_CENTER(16), 110}, style, "SELECT TO RETRY");
// Update high score
if (score > highScore) {
highScore = score;
painter.draw_string({55, 130}, style, "NEW HIGH SCORE!");
painter.draw_string({UI_POS_X_CENTER(16), 130}, style, "NEW HIGH SCORE!");
}
}
}
@@ -743,8 +743,8 @@ void DinoGameView::draw_current_score() {
void DinoGameView::draw_high_score() {
auto style = *ui::Theme::getInstance()->fg_light;
painter.fill_rectangle({152, 28, 100, 14}, Color::black());
painter.draw_string({152, 30}, style, "HI " + score_to_string(highScore));
painter.fill_rectangle({UI_POS_X_RIGHT(9), 28, UI_POS_WIDTH(9), 18}, Color::black());
painter.draw_string({UI_POS_X_RIGHT(9), 30}, style, "HI " + score_to_string(highScore));
}
void DinoGameView::jump() {
+1 -1
View File
@@ -229,7 +229,7 @@ class DinoGameView : public View {
bool easy_mode = false;
Button button_difficulty{
{70, 195, 100, 20},
{UI_POS_X_CENTER(13), 195, 13 * 8, 20},
"Mode: HARD"};
app_settings::SettingsManager settings_{
+13 -13
View File
@@ -197,11 +197,11 @@ class EPIRBAppView : public ui::View {
static constexpr auto header_height = 4 * 16;
ui::Text label_frequency{
{0 * 8, 0 * 16, 4 * 8, 1 * 16},
{UI_POS_X(0), UI_POS_Y(0), 4 * 8, 1 * 16},
"Freq"};
ui::OptionsField options_frequency{
{5 * 8, 0 * 16},
{5 * 8, UI_POS_Y(0)},
7,
{
{"406.028", 406028000},
@@ -212,26 +212,26 @@ class EPIRBAppView : public ui::View {
}};
ui::RFAmpField field_rf_amp{
{13 * 8, 0 * 16}};
{13 * 8, UI_POS_Y(0)}};
ui::LNAGainField field_lna{
{15 * 8, 0 * 16}};
{15 * 8, UI_POS_Y(0)}};
ui::VGAGainField field_vga{
{18 * 8, 0 * 16}};
{18 * 8, UI_POS_Y(0)}};
ui::RSSI rssi{
{21 * 8, 0, 6 * 8, 4}};
ui::AudioVolumeField field_volume{
{screen_width - 2 * 8, 0 * 16}};
{UI_POS_X(21), 0, UI_POS_WIDTH_REMAINING(24), 4}};
ui::Channel channel{
{21 * 8, 5, 6 * 8, 4}};
{UI_POS_X(21), 5, UI_POS_WIDTH_REMAINING(24), 4}};
ui::AudioVolumeField field_volume{
{screen_width - 2 * 8, UI_POS_Y(0)}};
// Status display
ui::Text label_status{
{0 * 8, 1 * 16, 15 * 8, 1 * 16},
{UI_POS_X(0), 1 * 16, 15 * 8, 1 * 16},
"Listening..."};
ui::Text label_beacons_count{
@@ -239,12 +239,12 @@ class EPIRBAppView : public ui::View {
"Beacons: 0"};
ui::Text label_packet_stats{
{0 * 8, 3 * 16, 29 * 8, 1 * 16},
{UI_POS_X(0), 3 * 16, 29 * 8, 1 * 16},
""};
// Latest beacon info display
ui::Text label_latest{
{0 * 8, 2 * 16, 8 * 8, 1 * 16},
{UI_POS_X(0), 2 * 16, 8 * 8, 1 * 16},
"Latest:"};
ui::Text text_latest_info{
+5 -2
View File
@@ -175,8 +175,11 @@ void RecentEntriesTable<ui::external_app::ert_app::ERTRecentEntries>::draw(
const Entry& entry,
const Rect& target_rect,
Painter& painter,
const Style& style) {
std::string line = ert::format::id(entry.id) + " " + ert::format::commodity_type(entry.commodity_type) + " " + ert::format::consumption(entry.last_consumption) + " ";
const Style& style,
RecentEntriesColumns& columns) {
std::string lid = ert::format::id(entry.id);
lid.resize(columns.at(0).second, ' ');
std::string line = lid + " " + ert::format::commodity_type(entry.commodity_type) + " " + ert::format::consumption(entry.last_consumption) + " ";
line += (entry.packet_type == ert::Packet::Type::SCM) ? ert::format::tamper_flags_scm(entry.last_tamper_flags) : ert::format::tamper_flags(entry.last_tamper_flags);
line += (entry.received_count > 99) ? " ++" : to_string_dec_uint(entry.received_count, 3);
+8 -8
View File
@@ -136,8 +136,8 @@ class ERTAppView : public View {
app_settings::SettingsManager settings_{
"rx_ert", app_settings::Mode::RX};
const RecentEntriesColumns columns{{
{"ID", 10},
RecentEntriesColumns columns{{
{"ID", 0},
{"Ty", 2},
{"Consumpt", 8},
{"Tamp", 4},
@@ -148,24 +148,24 @@ class ERTAppView : public View {
static constexpr auto header_height = 1 * 16;
RxFrequencyField field_frequency{
{0 * 8, 0 * 16},
{UI_POS_X(0), UI_POS_Y(0)},
nav_};
RFAmpField field_rf_amp{
{13 * 8, 0 * 16}};
{13 * 8, UI_POS_Y(0)}};
LNAGainField field_lna{
{15 * 8, 0 * 16}};
{15 * 8, UI_POS_Y(0)}};
VGAGainField field_vga{
{18 * 8, 0 * 16}};
{18 * 8, UI_POS_Y(0)}};
RSSI rssi{
{21 * 8, 0, 6 * 8, 4},
{UI_POS_X(21), 0, UI_POS_WIDTH_REMAINING(24), 4},
};
AudioVolumeField field_volume{
{screen_width - 2 * 8, 0 * 16}};
{screen_width - 2 * 8, UI_POS_Y(0)}};
MessageHandlerRegistration message_handler_packet{
Message::ID::ERTPacket,
+6
View File
@@ -249,6 +249,11 @@ set(EXTCPPSRC
#game2048
external/game2048/main.cpp
external/game2048/ui_game2048.cpp
#bht_tx
external/bht_tx/main.cpp
external/bht_tx/ui_bht_tx.cpp
external/bht_tx/bht.cpp
)
set(EXTAPPLIST
@@ -312,4 +317,5 @@ set(EXTAPPLIST
epirb_rx
soundboard
game2048
bht_tx
)
+9
View File
@@ -83,6 +83,7 @@ MEMORY
ram_external_app_epirb_rx (rwx) : org = 0xADEA0000, len = 32k
ram_external_app_soundboard (rwx) : org = 0xADEB0000, len = 32k
ram_external_app_game2048 (rwx) : org = 0xADEC0000, len = 32k
ram_external_app_bht_tx (rwx) : org = 0xADED0000, len = 32k
}
@@ -449,5 +450,13 @@ SECTIONS
*(*ui*external_app*game2048*);
} > ram_external_app_game2048
.external_app_bht_tx : ALIGN(4) SUBALIGN(4)
{
KEEP(*(.external_app.app_bht_tx.application_information));
*(*ui*external_app*bht_tx*);
} > ram_external_app_bht_tx
}
+9 -9
View File
@@ -56,16 +56,16 @@ class ExtSensorsView : public View {
uint16_t prev_scan_int = 0;
Labels labels{
{{0 * 8, 3 * 16}, "GPS:", Theme::getInstance()->fg_light->foreground},
{{0 * 8, 5 * 16}, "ORI:", Theme::getInstance()->fg_light->foreground},
{{0 * 8, 7 * 16}, "ENV:", Theme::getInstance()->fg_light->foreground}};
{{UI_POS_X(0), 3 * 16}, "GPS:", Theme::getInstance()->fg_light->foreground},
{{UI_POS_X(0), 5 * 16}, "ORI:", Theme::getInstance()->fg_light->foreground},
{{UI_POS_X(0), 7 * 16}, "ENV:", Theme::getInstance()->fg_light->foreground}};
Text text_info{{0 * 8, 0 * 8, screen_width, 16 * 1}, "Connect a compatible module..."};
Text text_gps{{5 * 8, 3 * 16, 24 * 8, 16}, "-"};
Text text_orientation{{5 * 8, 5 * 16, 24 * 8, 16}, "-"};
Text text_envl1{{5 * 8, 7 * 16, 24 * 8, 16}, "-"};
Text text_envl2{{1 * 8, 9 * 16, 24 * 8, 16}, "-"};
Text text_envl3{{1 * 8, 11 * 16, 24 * 8, 16}, "-"};
Text text_info{{UI_POS_X(0), 0 * 8, screen_width, 16 * 1}, "Connect a compatible module..."};
Text text_gps{{5 * 8, 3 * 16, UI_POS_WIDTH_REMAINING(6), UI_POS_HEIGHT(1)}, "-"};
Text text_orientation{{5 * 8, 5 * 16, UI_POS_WIDTH_REMAINING(6), UI_POS_HEIGHT(1)}, "-"};
Text text_envl1{{5 * 8, 7 * 16, UI_POS_WIDTH_REMAINING(6), UI_POS_HEIGHT(1)}, "-"};
Text text_envl2{{1 * 8, 9 * 16, UI_POS_WIDTH_REMAINING(6), UI_POS_HEIGHT(1)}, "-"};
Text text_envl3{{1 * 8, 11 * 16, UI_POS_WIDTH_REMAINING(6), UI_POS_HEIGHT(1)}, "-"};
Console console{
{1, 13 * 16, screen_width - 1, screen_height - 13 * 16}};
+2 -2
View File
@@ -49,10 +49,10 @@ class FlipperTxView : public View {
};
TxFrequencyField field_frequency{
{0 * 8, 0 * 16},
{UI_POS_X(0), UI_POS_Y(0)},
nav_};
TransmitterView2 tx_view{
{11 * 8, 0 * 16},
{11 * 8, UI_POS_Y(0)},
/*short_ui*/ true};
app_settings::SettingsManager settings_{
+16 -16
View File
@@ -121,18 +121,18 @@ class FmRadioView : public View {
{"theme"sv, &current_theme}}};
RFAmpField field_rf_amp{
{13 * 8, 0 * 16}};
{13 * 8, UI_POS_Y(0)}};
LNAGainField field_lna{
{15 * 8, 0 * 16}};
{15 * 8, UI_POS_Y(0)}};
VGAGainField field_vga{
{18 * 8, 0 * 16}};
{18 * 8, UI_POS_Y(0)}};
RSSI rssi{
{21 * 8, 0, 6 * 8, 4}};
{UI_POS_X(21), 0, UI_POS_WIDTH_REMAINING(24), 4}};
AudioVolumeField field_volume{
{screen_width - 2 * 8, 0 * 16}};
{screen_width - 2 * 8, UI_POS_Y(0)}};
RxFrequencyField field_frequency{
{0 * 8, 0 * 16},
{UI_POS_X(0), UI_POS_Y(0)},
nav_};
OptionsField field_bw{
@@ -171,16 +171,16 @@ class FmRadioView : public View {
GraphEq gr{{2, FMR_BTNGRID_TOP, UI_POS_MAXWIDTH - 4, UI_POS_MAXHEIGHT - FMR_BTNGRID_TOP}, true};
Button btn_fav_0{{2, FMR_BTNGRID_TOP + 0 * 34, 10 * 8, 28}, "---"};
Button btn_fav_1{{2 + 15 * 8, FMR_BTNGRID_TOP + 0 * 34, 10 * 8, 28}, "---"};
Button btn_fav_2{{2, FMR_BTNGRID_TOP + 1 * 34, 10 * 8, 28}, "---"};
Button btn_fav_3{{2 + 15 * 8, FMR_BTNGRID_TOP + 1 * 34, 10 * 8, 28}, "---"};
Button btn_fav_4{{2, FMR_BTNGRID_TOP + 2 * 34, 10 * 8, 28}, "---"};
Button btn_fav_5{{2 + 15 * 8, FMR_BTNGRID_TOP + 2 * 34, 10 * 8, 28}, "---"};
Button btn_fav_6{{2, FMR_BTNGRID_TOP + 3 * 34, 10 * 8, 28}, "---"};
Button btn_fav_7{{2 + 15 * 8, FMR_BTNGRID_TOP + 3 * 34, 10 * 8, 28}, "---"};
Button btn_fav_8{{2, FMR_BTNGRID_TOP + 4 * 34, 10 * 8, 28}, "---"};
Button btn_fav_9{{2 + 15 * 8, FMR_BTNGRID_TOP + 4 * 34, 10 * 8, 28}, "---"};
Button btn_fav_0{{UI_POS_X_CENTER(10) - UI_POS_WIDTH(8), FMR_BTNGRID_TOP + 0 * 34, 10 * 8, 28}, "---"};
Button btn_fav_1{{UI_POS_X_CENTER(10) + UI_POS_WIDTH(8), FMR_BTNGRID_TOP + 0 * 34, 10 * 8, 28}, "---"};
Button btn_fav_2{{UI_POS_X_CENTER(10) - UI_POS_WIDTH(8), FMR_BTNGRID_TOP + 1 * 34, 10 * 8, 28}, "---"};
Button btn_fav_3{{UI_POS_X_CENTER(10) + UI_POS_WIDTH(8), FMR_BTNGRID_TOP + 1 * 34, 10 * 8, 28}, "---"};
Button btn_fav_4{{UI_POS_X_CENTER(10) - UI_POS_WIDTH(8), FMR_BTNGRID_TOP + 2 * 34, 10 * 8, 28}, "---"};
Button btn_fav_5{{UI_POS_X_CENTER(10) + UI_POS_WIDTH(8), FMR_BTNGRID_TOP + 2 * 34, 10 * 8, 28}, "---"};
Button btn_fav_6{{UI_POS_X_CENTER(10) - UI_POS_WIDTH(8), FMR_BTNGRID_TOP + 3 * 34, 10 * 8, 28}, "---"};
Button btn_fav_7{{UI_POS_X_CENTER(10) + UI_POS_WIDTH(8), FMR_BTNGRID_TOP + 3 * 34, 10 * 8, 28}, "---"};
Button btn_fav_8{{UI_POS_X_CENTER(10) - UI_POS_WIDTH(8), FMR_BTNGRID_TOP + 4 * 34, 10 * 8, 28}, "---"};
Button btn_fav_9{{UI_POS_X_CENTER(10) + UI_POS_WIDTH(8), FMR_BTNGRID_TOP + 4 * 34, 10 * 8, 28}, "---"};
Button btn_fav_save{{2, FMR_BTNGRID_TOP + 6 * 34, 7 * 8, 1 * 28}, "Save"};
bool save_fav = false;
@@ -49,7 +49,7 @@ class DebugFontsView : public View {
void paint_zoomed_text(Painter& painter);
NumberField field_cursor{
{0 * 8, 0 * 8},
{UI_POS_X(0), 0 * 8},
4,
{0, 1000},
1,
@@ -61,7 +61,7 @@ class DebugFontsView : public View {
1,
' '};
Text text_address{
{screen_width / 2, 0 * 16, screen_width / 2, 16},
{screen_width / 2, UI_POS_Y(0), screen_width / 2, 16},
"0x20",
};
NavigationView& nav_;
+7 -7
View File
@@ -54,23 +54,23 @@ class FoxhuntRxView : public View {
"rx_foxhunt", app_settings::Mode::RX};
RFAmpField field_rf_amp{
{13 * 8, 0 * 16}};
{13 * 8, UI_POS_Y(0)}};
LNAGainField field_lna{
{15 * 8, 0 * 16}};
{15 * 8, UI_POS_Y(0)}};
VGAGainField field_vga{
{18 * 8, 0 * 16}};
{18 * 8, UI_POS_Y(0)}};
RSSI rssi{
{21 * 8, 0, 6 * 8, 4}};
{UI_POS_X(21), 0, UI_POS_WIDTH_REMAINING(24), 4}};
AudioVolumeField field_volume{
{screen_width - 2 * 8, 0 * 16}};
{screen_width - 2 * 8, UI_POS_Y(0)}};
RxFrequencyField field_frequency{
{0 * 8, 0 * 16},
{UI_POS_X(0), UI_POS_Y(0)},
nav_};
// Power: -XXX db
Text freq_stats_db{
{0 * 8, 2 * 16 + 4, 14 * 8, 14},
{UI_POS_X(0), 2 * 16 + 4, 14 * 8, 14},
};
RSSIGraph rssi_graph{
{0, 50, screen_width, 30},
+11 -10
View File
@@ -27,6 +27,7 @@ namespace ui::external_app::game2048 {
Game2048View::Game2048View(NavigationView& nav)
: nav_(nav), score(0), best_score(0), game_state(STATE_PLAYING) {
add_children({&dummy});
TILE_SIZE = (screen_width - 2 * BOARD_START_X - (GRID_SIZE + 1) * TILE_MARGIN) / GRID_SIZE;
init_game();
}
@@ -301,11 +302,11 @@ void Game2048View::draw_game() {
void Game2048View::show_game_over() {
if (show_you_won_you_lost_slow_down_cunter++ % 1000 != 0) return;
// slow down otherwies it repaint even faster than it finished paint full stuff
painter.fill_rectangle({40, 100, 160, 100}, Color::black());
painter.draw_rectangle({40, 100, 160, 100}, Color::white());
painter.draw_string({80, 115}, *Theme::getInstance()->bg_darkest, "GAME OVER");
painter.draw_string({70, 135}, *Theme::getInstance()->bg_darkest, "Press SELECT");
painter.draw_string({75, 155}, *Theme::getInstance()->bg_darkest, "to restart");
painter.fill_rectangle({UI_POS_X_CENTER(20), 100, 160, 100}, Color::black());
painter.draw_rectangle({UI_POS_X_CENTER(20), 100, 160, 100}, Color::white());
painter.draw_string({UI_POS_X_CENTER(10), 115}, *Theme::getInstance()->bg_darkest, "GAME OVER");
painter.draw_string({UI_POS_X_CENTER(13), 135}, *Theme::getInstance()->bg_darkest, "Press SELECT");
painter.draw_string({UI_POS_X_CENTER(11), 155}, *Theme::getInstance()->bg_darkest, "to restart");
need_repaint = false;
need_repaint_bg_frame = true;
}
@@ -313,11 +314,11 @@ void Game2048View::show_game_over() {
void Game2048View::show_you_won() {
if (show_you_won_you_lost_slow_down_cunter++ % 1000 != 0) return;
// slow down otherwies it repaint even faster than it finished paint full stuff
painter.fill_rectangle({40, 100, 160, 100}, Color::black());
painter.draw_rectangle({40, 100, 160, 100}, Color::white());
painter.draw_string({80, 115}, *Theme::getInstance()->bg_darkest, "YOU WON!");
painter.draw_string({70, 135}, *Theme::getInstance()->bg_darkest, "Press SELECT");
painter.draw_string({75, 155}, *Theme::getInstance()->bg_darkest, "to restart");
painter.fill_rectangle({UI_POS_X_CENTER(20), 100, 160, 100}, Color::black());
painter.draw_rectangle({UI_POS_X_CENTER(20), 100, 160, 100}, Color::white());
painter.draw_string({UI_POS_X_CENTER(9), 115}, *Theme::getInstance()->bg_darkest, "YOU WON!");
painter.draw_string({UI_POS_X_CENTER(13), 135}, *Theme::getInstance()->bg_darkest, "Press SELECT");
painter.draw_string({UI_POS_X_CENTER(11), 155}, *Theme::getInstance()->bg_darkest, "to restart");
need_repaint = false;
need_repaint_bg_frame = true;
}
+1 -1
View File
@@ -33,7 +33,6 @@
namespace ui::external_app::game2048 {
#define GRID_SIZE 4
#define TILE_SIZE 50
#define TILE_MARGIN 5
#define BOARD_START_X 15
#define BOARD_START_Y 35
@@ -63,6 +62,7 @@ class Game2048View : public View {
Painter painter{};
int grid[GRID_SIZE][GRID_SIZE];
int TILE_SIZE = 50;
int score;
int best_score;
GameState game_state;
+5 -5
View File
@@ -68,12 +68,12 @@ class gfxEQView : public View {
ColorTheme{Color(64, 64, 64), Color(255, 0, 0)},
ColorTheme{Color(255, 192, 0), Color(0, 64, 128)}};
ButtonWithEncoder button_frequency{{0 * 8, 0 * 16 + 4, 11 * 8, 1 * 8}, ""};
RFAmpField field_rf_amp{{13 * 8, 0 * 16}};
LNAGainField field_lna{{15 * 8, 0 * 16}};
VGAGainField field_vga{{18 * 8, 0 * 16}};
ButtonWithEncoder button_frequency{{UI_POS_X(0), UI_POS_Y(0) + 4, 11 * 8, 1 * 8}, ""};
RFAmpField field_rf_amp{{13 * 8, UI_POS_Y(0)}};
LNAGainField field_lna{{15 * 8, UI_POS_Y(0)}};
VGAGainField field_vga{{18 * 8, UI_POS_Y(0)}};
Button button_mood{{21 * 8, 0, 6 * 8, 16}, "MOOD"};
AudioVolumeField field_volume{{screen_width - 2 * 8, 0 * 16}};
AudioVolumeField field_volume{{screen_width - 2 * 8, UI_POS_Y(0)}};
GraphEq gr{{2, UI_POS_DEFAULT_HEIGHT, UI_POS_MAXWIDTH - 4, UI_POS_HEIGHT_REMAINING(2)}, false};
rf::Frequency frequency_value{93100000};
+5 -5
View File
@@ -82,24 +82,24 @@ class GpsSimAppView : public View {
bool ready_signal{false};
Button button_open{
{0 * 8, 0 * 16, 10 * 8, 2 * 16},
{UI_POS_X(0), UI_POS_Y(0), 10 * 8, 2 * 16},
"Open file"};
Text text_filename{
{11 * 8, 0 * 16, 12 * 8, 16},
{11 * 8, UI_POS_Y(0), 12 * 8, 16},
"-"};
Text text_sample_rate{
{24 * 8, 0 * 16, 6 * 8, 16},
{24 * 8, UI_POS_Y(0), 6 * 8, 16},
"-"};
Text text_duration{
{11 * 8, 1 * 16, 6 * 8, 16},
"-"};
ProgressBar progressbar{
{18 * 8, 1 * 16, 12 * 8, 16}};
{18 * 8, 1 * 16, UI_POS_WIDTH_REMAINING(18), 16}};
TxFrequencyField field_frequency{
{0 * 8, 2 * 16},
{UI_POS_X(0), 2 * 16},
nav_};
TransmitterView2 tx_view{
+27 -27
View File
@@ -88,55 +88,55 @@ class HopperView : public View {
false};
NewButton button_load_list{
{0 * 8, 9 * 16 + 4, 4 * 8, 32},
{UI_POS_X(0), UI_POS_Y_BOTTOM(10), 4 * 8, 32},
{},
&bitmap_icon_load,
Color::dark_blue(),
/*vcenter*/ true};
NewButton button_save_list{
{4 * 8, 9 * 16 + 4, 4 * 8, 32},
{4 * 8, UI_POS_Y_BOTTOM(10), 4 * 8, 32},
{},
&bitmap_icon_save,
Color::dark_blue(),
/*vcenter*/ true};
NewButton button_add_freq{
{8 * 8 + 4, 9 * 16 + 4, 4 * 8, 32},
{8 * 8 + 4, UI_POS_Y_BOTTOM(10), 4 * 8, 32},
{},
&bitmap_icon_add,
Color::dark_green(),
/*vcenter*/ true};
NewButton button_delete_freq{
{12 * 8 + 4, 9 * 16 + 4, 4 * 8, 32},
{12 * 8 + 4, UI_POS_Y_BOTTOM(10), 4 * 8, 32},
{},
&bitmap_icon_trash,
Color::dark_red(),
/*vcenter*/ true};
NewButton button_clear{
{screen_width - 4 * 8, 9 * 16 + 4, 4 * 8, 32},
{screen_width - 4 * 8, UI_POS_Y_BOTTOM(10), 4 * 8, 32},
{},
&bitmap_icon_tools_wipesd,
Color::red(),
/*vcenter*/ true};
Labels labels{
{{2 * 8, 23 * 8}, "Type:", Theme::getInstance()->fg_light->foreground},
{{1 * 8, 25 * 8}, "Speed:", Theme::getInstance()->fg_light->foreground},
{{3 * 8, 27 * 8}, "Hop:", Theme::getInstance()->fg_light->foreground},
{{4 * 8, 29 * 8}, "TX:", Theme::getInstance()->fg_light->foreground},
{{1 * 8, 31 * 8}, "Sle3p:", Theme::getInstance()->fg_light->foreground}, // euquiq: Token of appreciation to TheSle3p, which made this ehnancement a reality with his bounty.
{{0 * 8, 33 * 8}, "Jitter:", Theme::getInstance()->fg_light->foreground}, // Maybe the repository curator can keep the "mystype" for some versions.
{{11 * 8, 29 * 8}, "Secs.", Theme::getInstance()->fg_light->foreground},
{{11 * 8, 31 * 8}, "Secs.", Theme::getInstance()->fg_light->foreground},
{{11 * 8, 33 * 8}, "/60", Theme::getInstance()->fg_light->foreground},
{{2 * 8, 35 * 8}, "Gain:", Theme::getInstance()->fg_light->foreground},
{{11 * 8, 35 * 8}, "A:", Theme::getInstance()->fg_light->foreground}};
{{2 * 8, UI_POS_Y_BOTTOM(8)}, "Type:", Theme::getInstance()->fg_light->foreground},
{{1 * 8, UI_POS_Y_BOTTOM(7)}, "Speed:", Theme::getInstance()->fg_light->foreground},
{{3 * 8, UI_POS_Y_BOTTOM(6)}, "Hop:", Theme::getInstance()->fg_light->foreground},
{{4 * 8, UI_POS_Y_BOTTOM(5)}, "TX:", Theme::getInstance()->fg_light->foreground},
{{1 * 8, UI_POS_Y_BOTTOM(4)}, "Sle3p:", Theme::getInstance()->fg_light->foreground}, // euquiq: Token of appreciation to TheSle3p, which made this ehnancement a reality with his bounty.
{{UI_POS_X(0), UI_POS_Y_BOTTOM(3)}, "Jitter:", Theme::getInstance()->fg_light->foreground}, // Maybe the repository curator can keep the "mystype" for some versions.
{{11 * 8, UI_POS_Y_BOTTOM(5)}, "Secs.", Theme::getInstance()->fg_light->foreground},
{{11 * 8, UI_POS_Y_BOTTOM(4)}, "Secs.", Theme::getInstance()->fg_light->foreground},
{{11 * 8, UI_POS_Y_BOTTOM(3)}, "/60", Theme::getInstance()->fg_light->foreground},
{{2 * 8, UI_POS_Y_BOTTOM(2)}, "Gain:", Theme::getInstance()->fg_light->foreground},
{{11 * 8, UI_POS_Y_BOTTOM(2)}, "A:", Theme::getInstance()->fg_light->foreground}};
OptionsField options_type{
{7 * 8, 23 * 8},
{7 * 8, UI_POS_Y_BOTTOM(8)},
8,
{
{"Rand FSK", 0},
@@ -153,14 +153,14 @@ class HopperView : public View {
}};
Text text_range_number{
{16 * 8, 23 * 8, 2 * 8, 16},
{16 * 8, UI_POS_Y_BOTTOM(8), 2 * 8, 16},
"--"};
Text text_range_total{
{18 * 8, 23 * 8, 3 * 8, 16},
{18 * 8, UI_POS_Y_BOTTOM(8), 3 * 8, 16},
"/--"};
OptionsField options_speed{
{7 * 8, 25 * 8},
{7 * 8, UI_POS_Y_BOTTOM(7)},
6,
{{"10Hz ", 10},
{"100Hz ", 100},
@@ -169,7 +169,7 @@ class HopperView : public View {
{"100kHz", 100000}}};
OptionsField options_hop{
{7 * 8, 27 * 8},
{7 * 8, UI_POS_Y_BOTTOM(6)},
6,
{{"0ms !!", 0},
{"1ms ", 1},
@@ -182,7 +182,7 @@ class HopperView : public View {
{"10s ", 10000}}};
NumberField field_timetx{
{7 * 8, 29 * 8},
{7 * 8, UI_POS_Y_BOTTOM(5)},
3,
{1, 180},
1,
@@ -190,7 +190,7 @@ class HopperView : public View {
};
NumberField field_timepause{
{8 * 8, 31 * 8},
{8 * 8, UI_POS_Y_BOTTOM(4)},
2,
{1, 60},
1,
@@ -198,7 +198,7 @@ class HopperView : public View {
};
NumberField field_jitter{
{8 * 8, 33 * 8},
{8 * 8, UI_POS_Y_BOTTOM(3)},
2,
{1, 60},
1,
@@ -206,7 +206,7 @@ class HopperView : public View {
};
NumberField field_gain{
{8 * 8, 35 * 8},
{8 * 8, UI_POS_Y_BOTTOM(2)},
2,
{0, 47},
1,
@@ -214,7 +214,7 @@ class HopperView : public View {
};
NumberField field_amp{
{13 * 8, 35 * 8},
{13 * 8, UI_POS_Y_BOTTOM(2)},
1,
{0, 1},
1,
@@ -222,7 +222,7 @@ class HopperView : public View {
};
Button button_transmit{
{148, 216, 80, 80},
{148, UI_POS_Y_BOTTOM(6), 80, 80},
LanguageHelper::currentMessages[LANG_START]};
MessageHandlerRegistration message_handler_retune{
+2 -2
View File
@@ -57,7 +57,7 @@ class RangeView : public View {
"Load range"};
Button button_start{
{0 * 8, 11 * 8, 11 * 8, 28},
{UI_POS_X(0), 11 * 8, 11 * 8, 28},
""};
Button button_stop{
@@ -144,7 +144,7 @@ class JammerView : public View {
{{3 * 8, 27 * 8}, "Hop:", Theme::getInstance()->fg_light->foreground},
{{4 * 8, 29 * 8}, "TX:", Theme::getInstance()->fg_light->foreground},
{{1 * 8, 31 * 8}, "Sleep:", Theme::getInstance()->fg_light->foreground},
{{0 * 8, 33 * 8}, "Jitter:", Theme::getInstance()->fg_light->foreground},
{{UI_POS_X(0), 33 * 8}, "Jitter:", Theme::getInstance()->fg_light->foreground},
{{11 * 8, 29 * 8}, "Secs.", Theme::getInstance()->fg_light->foreground},
{{11 * 8, 31 * 8}, "Secs.", Theme::getInstance()->fg_light->foreground},
{{11 * 8, 33 * 8}, "/60", Theme::getInstance()->fg_light->foreground},
+3 -3
View File
@@ -106,14 +106,14 @@ class KeyfobView : public View {
SymField::Type::Hex};
Text text_status{
{2 * 8, 13 * 16, 128, 16},
{2 * 8, UI_POS_Y_BOTTOM(7), 128, 16},
"Ready"};
ProgressBar progressbar{
{2 * 8, 13 * 16 + 20, 208, 16}};
{2 * 8, UI_POS_Y_BOTTOM(6), UI_POS_WIDTH_REMAINING(4), 16}};
TransmitterView tx_view{
16 * 16,
(int16_t)UI_POS_Y_BOTTOM(4),
0,
15,
true};
+2 -2
View File
@@ -145,10 +145,10 @@ class LCRView : public View {
{2 * 8, 27 * 8 + 4, 26 * 8, 16},
LanguageHelper::currentMessages[LANG_READY]};
ProgressBar progress{
{2 * 8, 29 * 8 + 4, 26 * 8, 16}};
{2 * 8, 29 * 8 + 4, UI_POS_WIDTH_REMAINING(4), 16}};
TransmitterView tx_view{
16 * 16,
(int16_t)UI_POS_Y_BOTTOM(4),
10000,
12};
+10 -10
View File
@@ -80,21 +80,21 @@ class LevelView : public View {
}};
Labels labels{
{{0 * 8, 0 * 16}, "LNA: VGA: AMP: VOL: ", Theme::getInstance()->fg_light->foreground},
{{0 * 8, 1 * 16}, "BW: MODE: S: ", Theme::getInstance()->fg_light->foreground},
{{UI_POS_X(0), UI_POS_Y(0)}, "LNA: VGA: AMP: VOL: ", Theme::getInstance()->fg_light->foreground},
{{UI_POS_X(0), 1 * 16}, "BW: MODE: S: ", Theme::getInstance()->fg_light->foreground},
};
LNAGainField field_lna{
{4 * 8, 0 * 16}};
{4 * 8, UI_POS_Y(0)}};
VGAGainField field_vga{
{11 * 8, 0 * 16}};
{11 * 8, UI_POS_Y(0)}};
RFAmpField field_rf_amp{
{18 * 8, 0 * 16}};
{18 * 8, UI_POS_Y(0)}};
AudioVolumeField field_volume{
{24 * 8, 0 * 16}};
{24 * 8, UI_POS_Y(0)}};
OptionsField field_bw{
{3 * 8, 1 * 16},
@@ -112,7 +112,7 @@ class LevelView : public View {
{}};
ButtonWithEncoder button_frequency{
{0 * 8, 2 * 16 + 8, 15 * 8, 1 * 8},
{UI_POS_X(0), 2 * 16 + 8, 15 * 8, 1 * 8},
""};
OptionsField field_audio_mode{
@@ -135,12 +135,12 @@ class LevelView : public View {
// RSSI: XX/XX/XXX
Text freq_stats_rssi{
{0 * 8, 3 * 16 + 4, 15 * 8, 1 * 16},
{UI_POS_X(0), 3 * 16 + 4, 15 * 8, 1 * 16},
};
// Power: -XXX db
Text freq_stats_db{
{0 * 8, 4 * 16 + 4, 15 * 8, 1 * 16},
{UI_POS_X(0), 4 * 16 + 4, 15 * 8, 1 * 16},
};
OptionsField peak_mode{
@@ -169,7 +169,7 @@ class LevelView : public View {
// RxSat: XX%
Text freq_stats_rx{
{0 * 8, 5 * 16 + 4, 10 * 8, 1 * 16},
{UI_POS_X(0), 5 * 16 + 4, 10 * 8, 1 * 16},
};
Text text_ctcss{
+4 -4
View File
@@ -93,8 +93,8 @@ class LGEView : public View {
{{2 * 8, 3 * 8}, "Room:", Theme::getInstance()->fg_light->foreground},
{{14 * 8, 3 * 8}, "Text:", Theme::getInstance()->fg_light->foreground},
{{2 * 8, 5 * 8}, "Team:", Theme::getInstance()->fg_light->foreground},
{{0 * 8, 7 * 8}, "Player:", Theme::getInstance()->fg_light->foreground},
{{0 * 8, 10 * 8}, "Vest:", Theme::getInstance()->fg_light->foreground},
{{UI_POS_X(0), 7 * 8}, "Player:", Theme::getInstance()->fg_light->foreground},
{{UI_POS_X(0), 10 * 8}, "Vest:", Theme::getInstance()->fg_light->foreground},
{{4 * 8, 12 * 8}, "ID:", Theme::getInstance()->fg_light->foreground},
{{3 * 8, 14 * 8}, "Pow: /10", Theme::getInstance()->fg_light->foreground},
{{2 * 8, 16 * 8}, "Time: x100ms", Theme::getInstance()->fg_light->foreground}};
@@ -171,10 +171,10 @@ class LGEView : public View {
'0'};
Console console{
{0, 18 * 8, screen_width, 7 * 16}};
{0, 18 * 8, screen_width, UI_POS_Y_BOTTOM(4) - 18 * 8}};
TransmitterView tx_view{
16 * 16,
(int16_t)UI_POS_Y_BOTTOM(4),
10000,
12};
@@ -72,7 +72,7 @@ class McuTemperatureView : public View {
private:
Text text_title{
{76, 16, screen_width, 16},
{UI_POS_X_CENTER(11), 16, UI_POS_WIDTH(11), 16},
"Temperature",
};
@@ -81,7 +81,7 @@ class McuTemperatureView : public View {
};
Button button_done{
{72, screen_height - 56, 96, 24},
{UI_POS_X_CENTER(12), UI_POS_Y_BOTTOM(3), UI_POS_WIDTH(12), 24},
"Done"};
};
+6 -6
View File
@@ -60,13 +60,13 @@ class MetronomeView : public View {
uint32_t current_beat_{0};
Labels labels{
{{0 * 8, 1 * 16}, "BPM:", Theme::getInstance()->fg_light->foreground},
{{0 * 8, 2 * 16}, "Accent Beep Tune:", Theme::getInstance()->fg_light->foreground},
{{0 * 8, 3 * 16}, "Unaccent Beep Tune:", Theme::getInstance()->fg_light->foreground},
{{0 * 8, 4 * 16}, "Rhythm:", Theme::getInstance()->fg_light->foreground},
{{UI_POS_X(0), 1 * 16}, "BPM:", Theme::getInstance()->fg_light->foreground},
{{UI_POS_X(0), 2 * 16}, "Accent Beep Tune:", Theme::getInstance()->fg_light->foreground},
{{UI_POS_X(0), 3 * 16}, "Unaccent Beep Tune:", Theme::getInstance()->fg_light->foreground},
{{UI_POS_X(0), 4 * 16}, "Rhythm:", Theme::getInstance()->fg_light->foreground},
{{(sizeof("Rhythm:") + 1) * 8 + 4 * 8, 4 * 16}, "/", Theme::getInstance()->fg_light->foreground},
{{0 * 8, 5 * 16}, "Beep Flash Duration:", Theme::getInstance()->fg_light->foreground},
{{0 * 8, 6 * 16}, "Volume:", Theme::getInstance()->fg_light->foreground}};
{{UI_POS_X(0), 5 * 16}, "Beep Flash Duration:", Theme::getInstance()->fg_light->foreground},
{{UI_POS_X(0), 6 * 16}, "Volume:", Theme::getInstance()->fg_light->foreground}};
NumberField field_bpm{
{(sizeof("BPM:") + 1) * 8, 1 * 16},
+2 -2
View File
@@ -171,10 +171,10 @@ class MorseView : public View {
"Set message"};
ProgressBar progressbar{
{2 * 8, 28 * 8, 208, 16}};
{2 * 8, 28 * 8, UI_POS_WIDTH_REMAINING(4), 16}};
TransmitterView tx_view{
16 * 16,
(int16_t)UI_POS_Y_BOTTOM(4),
10000,
12};
+2 -2
View File
@@ -87,7 +87,7 @@ class NoaaAptRxView : public View {
VGAGainField field_vga{
{UI_POS_X(18), UI_POS_Y(0)}};
RSSI rssi{
{UI_POS_X(21), UI_POS_Y(0), UI_POS_WIDTH(6), 4}};
{UI_POS_X(21), UI_POS_Y(0), UI_POS_WIDTH_REMAINING(24), 4}};
AudioVolumeField field_volume{
{UI_POS_X_RIGHT(2), UI_POS_Y(0)}};
@@ -114,7 +114,7 @@ class NoaaAptRxView : public View {
};
Button button_ss{
{UI_POS_X_RIGHT(6), UI_POS_Y(1), UI_POS_WIDTH(5), UI_POS_DEFAULT_HEIGHT},
{UI_POS_X_RIGHT(6), UI_POS_Y(1), UI_POS_WIDTH(6), UI_POS_HEIGHT(2)},
LanguageHelper::currentMessages[LANG_START]};
MessageHandlerRegistration message_handler_stats{
+6 -6
View File
@@ -62,18 +62,18 @@ class NRFRxView : public View {
uint32_t prev_value{0};
RFAmpField field_rf_amp{
{13 * 8, 0 * 16}};
{13 * 8, UI_POS_Y(0)}};
LNAGainField field_lna{
{15 * 8, 0 * 16}};
{15 * 8, UI_POS_Y(0)}};
VGAGainField field_vga{
{18 * 8, 0 * 16}};
{18 * 8, UI_POS_Y(0)}};
RSSI rssi{
{21 * 8, 0, 6 * 8, 4}};
{21 * 8, 0, UI_POS_WIDTH_REMAINING(22), 4}};
Channel channel{
{21 * 8, 5, 6 * 8, 4}};
{21 * 8, 5, UI_POS_WIDTH_REMAINING(22), 4}};
RxFrequencyField field_frequency{
{0 * 8, 0 * 16},
{UI_POS_X(0), UI_POS_Y(0)},
nav_};
Button button_modem_setup{
+15 -15
View File
@@ -93,8 +93,8 @@ class OOKEditorAppView : public View {
{{"ook_editor_tx_freq"sv, &ook_editor_tx_freq}}};
// UI components for frequency and transmitter view.
FrequencyField field_frequency{{0 * 8, 0 * 16}};
TransmitterView2 tx_view{{20 * 7, 0 * 16}, true};
FrequencyField field_frequency{{UI_POS_X(0), UI_POS_Y(0)}};
TransmitterView2 tx_view{{UI_POS_X_RIGHT(9), UI_POS_Y(0)}, true};
// Labels for various fields such as sample rate and repeat count.
Labels label_step{{{170, 20}, "Step:", Theme::getInstance()->fg_light->foreground}};
@@ -120,17 +120,17 @@ class OOKEditorAppView : public View {
NumberField field_repeat{{210, 40}, 3, {1, 999}, 1, '0', false};
// Text field to display the payload data.
Text text_payload{{0 * 8, 100, screen_width, 16}, ""};
Text text_payload{{UI_POS_X(0), 100, screen_width, 16}, ""};
// Buttons for setting configurations, opening files, and starting transmission.
Button button_set{{0, 125, 60, 28}, LanguageHelper::currentMessages[LANG_SET]};
Button button_open{{68, 125, 80, 28}, LanguageHelper::currentMessages[LANG_OPEN_FILE]};
Button button_save{{154, 125, 80, 28}, LanguageHelper::currentMessages[LANG_SAVE_FILE]};
Button button_bug_key{{0, 125 + 28 + 3, screen_width, 28}, "Bug Key"};
Button button_send_stop{{80, 273, 80, 32}, LanguageHelper::currentMessages[LANG_SEND]};
Button button_send_stop{{UI_POS_X_CENTER(10), 273, UI_POS_WIDTH(10), 32}, LanguageHelper::currentMessages[LANG_SEND]};
// Progress bar to display transmission progress.
ProgressBar progressbar{{2 * 8, 250, 208, 16}};
ProgressBar progressbar{{2 * 8, 250, UI_POS_WIDTH_REMAINING(4), 16}};
// Waveform display using waveform buffer and yellow theme color.
Waveform waveform{{0, 208, screen_width, 32}, waveform_buffer, 0, 0, true, Theme::getInstance()->fg_yellow->foreground};
@@ -167,11 +167,11 @@ class OOKEditorBugKeyView : public View {
std::string build_payload();
Labels labels{
{{0 * 8, 0 * 16}, "Primary Step", Theme::getInstance()->fg_light->foreground},
{{(screen_width / 2), 0 * 16}, "Secondary Step", Theme::getInstance()->fg_light->foreground}};
{{UI_POS_X(0), UI_POS_Y(0)}, "Primary Step", Theme::getInstance()->fg_light->foreground},
{{(screen_width / 2), UI_POS_Y(0)}, "Secondary Step", Theme::getInstance()->fg_light->foreground}};
NumberField field_primary_step{
{0 * 8, 1 * 16},
{UI_POS_X(0), 1 * 16},
3,
{0, 550},
1,
@@ -185,30 +185,30 @@ class OOKEditorBugKeyView : public View {
' '};
Console console{
{0, 3 * 16, screen_width, screen_height - 10 * 16}};
{0, UI_POS_Y(3), screen_width, UI_POS_HEIGHT_REMAINING(10)}};
Button button_insert_low_level_long{
{0 * 8, 13 * 16, screen_width / 2, 2 * 16},
{UI_POS_X(0), UI_POS_Y_BOTTOM(7), screen_width / 2, UI_POS_HEIGHT(2)},
"00"};
Button button_insert_low_level_short{
{0 * 8, 15 * 16, screen_width / 2, 2 * 16},
{UI_POS_X(0), UI_POS_Y_BOTTOM(5), screen_width / 2, UI_POS_HEIGHT(2)},
"0"};
Button button_insert_high_level_long{
{(screen_width / 2), 13 * 16, screen_width / 2, 2 * 16},
{(screen_width / 2), UI_POS_Y_BOTTOM(7), screen_width / 2, UI_POS_HEIGHT(2)},
"11"};
Button button_insert_high_level_short{
{(screen_width / 2), 15 * 16, screen_width / 2, 2 * 16},
{(screen_width / 2), UI_POS_Y_BOTTOM(5), screen_width / 2, UI_POS_HEIGHT(2)},
"1"};
Button button_delete{
{1, 17 * 16, screen_width / 2 - 4, 2 * 16},
{1, UI_POS_Y_BOTTOM(3), screen_width / 2 - 4, UI_POS_HEIGHT(2)},
"<Backspace"};
Button button_save{
{1 + screen_width / 2 + 1, 17 * 16, screen_width / 2 - 4, 2 * 16},
{1 + screen_width / 2 + 1, UI_POS_Y_BOTTOM(3), screen_width / 2 - 4, UI_POS_HEIGHT(2)},
"Save"};
};
+6 -6
View File
@@ -45,18 +45,18 @@ class OOKBruteView : public View {
};
TxFrequencyField field_frequency{
{0 * 8, 0 * 16},
{UI_POS_X(0), UI_POS_Y(0)},
nav_};
TransmitterView2 tx_view{
{11 * 8, 0 * 16},
{11 * 8, UI_POS_Y(0)},
/*short_ui*/ true};
app_settings::SettingsManager settings_{
"tx_ookbrute", app_settings::Mode::TX};
Labels labels{
{{0 * 8, 2 * 16}, "Start Position:", Theme::getInstance()->fg_light->foreground},
{{0 * 8, 7 * 16}, "Stop Position:", Theme::getInstance()->fg_light->foreground},
{{0 * 8, 13 * 16}, "Encoder Type:", Theme::getInstance()->fg_light->foreground}};
{{UI_POS_X(0), 2 * 16}, "Start Position:", Theme::getInstance()->fg_light->foreground},
{{UI_POS_X(0), 7 * 16}, "Stop Position:", Theme::getInstance()->fg_light->foreground},
{{UI_POS_X(0), 13 * 16}, "Encoder Type:", Theme::getInstance()->fg_light->foreground}};
Button button_startstop{
{8, screen_height - 48 - 16, screen_width - 2 * 8, 48},
@@ -71,7 +71,7 @@ class OOKBruteView : public View {
"Input Stop Pos"};
NumberField field_start{
{0 * 8, 3 * 16},
{UI_POS_X(0), 3 * 16},
8,
{0, 2500},
1,
@@ -52,7 +52,7 @@ PlaylistEditorView::PlaylistEditorView(NavigationView& nav)
&button_insert,
&button_save_playlist});
menu_view.set_parent_rect({0, 2 * 8, screen_width, 24 * 8});
menu_view.set_parent_rect({0, 2 * 8, screen_width, UI_POS_HEIGHT_REMAINING(9)});
menu_view.on_highlight = [this]() {
text_hint.set("Edit:" +
@@ -48,40 +48,40 @@ class PlaylistEditorView : public View {
bool ever_opened = false;
Labels labels{
{{0 * 8, 0 * 16}, "PPL file:", Theme::getInstance()->fg_light->foreground}};
{{UI_POS_X(0), UI_POS_Y(0)}, "PPL file:", Theme::getInstance()->fg_light->foreground}};
Button button_new{
{(sizeof("PPL file:") + 1) * 8, 0 * 16, 8 * 5, 16},
{(sizeof("PPL file:") + 1) * 8, UI_POS_Y(0), 8 * 5, 16},
"New"};
Text text_current_ppl_file{
{sizeof("PPL file:") * 8, 0 * 16, screen_width - (int)sizeof("PPL file:") * 8, 16},
{sizeof("PPL file:") * 8, UI_POS_Y(0), screen_width - (int)sizeof("PPL file:") * 8, 16},
""};
MenuView menu_view{};
Text text_hint{
{0, 27 * 8, screen_width, 16},
{0, UI_POS_Y_BOTTOM(6), screen_width, 16},
"Open a PPL file"};
Text text_ppl_name{
{0, 27 * 8, screen_width, 16},
{0, UI_POS_Y_BOTTOM(6), screen_width, 16},
"Highlight an app"};
Button button_open_playlist{
{0, 29 * 8, screen_width / 2 - 1, 32},
{0, UI_POS_Y_BOTTOM(5), screen_width / 2 - 1, 32},
"Open PPL"};
Button button_edit{
{screen_width / 2 + 2, 29 * 8, screen_width / 2 - 2, 32},
{screen_width / 2 + 2, UI_POS_Y_BOTTOM(5), screen_width / 2 - 2, 32},
"Edit Item"};
Button button_insert{
{0, screen_height - 32 - 16, screen_width / 2 - 1, 32},
{0, UI_POS_Y_BOTTOM(3), screen_width / 2 - 1, 32},
"Ins. After"};
Button button_save_playlist{
{screen_width / 2 + 2, screen_height - 32 - 16, screen_width / 2 - 2, 32},
{screen_width / 2 + 2, UI_POS_Y_BOTTOM(3), screen_width / 2 - 2, 32},
"Save PPL"};
void open_file();
@@ -122,7 +122,7 @@ class PlaylistItemEditView : public View {
std::string build_item() const;
Labels labels{
{{0 * 8, 1 * 16}, "Path:", Theme::getInstance()->fg_light->foreground},
{{UI_POS_X(0), 1 * 16}, "Path:", Theme::getInstance()->fg_light->foreground},
{{2 * 8, 5 * 16}, "Delay(ms):", Theme::getInstance()->fg_light->foreground}};
TextField field_path{
@@ -98,9 +98,9 @@ class RandomPasswordView : public View {
"rx_passgen", app_settings::Mode::RX};
Labels labels{
{{0 * 8, 0 * 16}, "------------seeds-------------", Theme::getInstance()->fg_light->foreground},
{{0 * 8, 3 * 16}, "-----------password-----------", Theme::getInstance()->fg_light->foreground},
{{0 * 8, 7 * 16 - 2}, "digits:", Theme::getInstance()->fg_light->foreground},
{{UI_POS_X_CENTER(30), UI_POS_Y(0)}, "------------seeds-------------", Theme::getInstance()->fg_light->foreground},
{{UI_POS_X_CENTER(30), 3 * 16}, "-----------password-----------", Theme::getInstance()->fg_light->foreground},
{{UI_POS_X(0), 7 * 16 - 2}, "digits:", Theme::getInstance()->fg_light->foreground},
{{screen_width / 2, 7 * 16 - 2}, "method:", Theme::getInstance()->fg_light->foreground},
};
@@ -112,12 +112,12 @@ class RandomPasswordView : public View {
{18 * 8, 1 * 16}};
RSSI rssi{
{21 * 8, 1 * 16 + 0, 6 * 8, 4}};
{UI_POS_X(21), 1 * 16 + 0, UI_POS_WIDTH_REMAINING(21), 4}};
Channel channel{
{21 * 8, 1 * 16 + 5, 6 * 8, 4}};
{UI_POS_X(21), 1 * 16 + 5, UI_POS_WIDTH_REMAINING(21), 4}};
RxFrequencyField field_frequency{
{0 * 8, 1 * 16},
{UI_POS_X(0), 1 * 16},
nav_};
Button button_modem_setup{
@@ -180,7 +180,7 @@ class RandomPasswordView : public View {
LanguageHelper::currentMessages[LANG_SAVE]};
Button button_flood{
{0 * 8, 15 * 16 + 18, screen_width / 2, 22},
{UI_POS_X(0), 15 * 16 + 18, screen_width / 2, 22},
LanguageHelper::currentMessages[LANG_FLOOD]};
Button button_send{
@@ -188,7 +188,7 @@ class RandomPasswordView : public View {
"Send pwd"};
Button button_refresh{
{0 * 8, 17 * 16 + 10, screen_width / 2, 22},
{UI_POS_X(0), 17 * 16 + 10, screen_width / 2, 22},
"Generate"};
Button button_show_qr{
+10 -10
View File
@@ -208,7 +208,7 @@ class RemoteEntryEditView : public View {
{{2 * 8, 5 * 16}, "Icon:", Theme::getInstance()->fg_light->foreground},
{{2 * 8, 6 * 16}, "FG Color:", Theme::getInstance()->fg_light->foreground},
{{2 * 8, 7 * 16}, "BG Color:", Theme::getInstance()->fg_light->foreground},
{{8 * 8, 9 * 16}, "Button preview", Theme::getInstance()->fg_light->foreground},
{{UI_POS_X_CENTER(14), 9 * 16}, "Button preview", Theme::getInstance()->fg_light->foreground},
};
TextField field_name{{8 * 8, 1 * 16, 20 * 8, 1 * 16}, {}};
@@ -244,7 +244,7 @@ class RemoteEntryEditView : public View {
/*loop*/ true};
RemoteButton button_preview{
{10 * 8, 11 * 16 - 8, 10 * 8, 50},
{UI_POS_X_CENTER(10), 11 * 16 - 8, 10 * 8, 50},
&entry_};
NewButton button_delete{
@@ -253,7 +253,7 @@ class RemoteEntryEditView : public View {
&bitmap_icon_trash,
Color::red()};
Button button_done{{11 * 8, 16 * 16, 8 * 8, 2 * 16}, "Done"};
Button button_done{{UI_POS_X_CENTER(10), 16 * 16, 10 * 8, 2 * 16}, "Done"};
};
/* App that allows for buttons to be bound to captures for playback. */
@@ -328,39 +328,39 @@ class RemoteAppView : public View {
bool ready_signal_{}; // Used to signal ReplayThread ready.
TextField field_title{
{0 * 8, 0 * 16 + 2, screen_width, 1 * 16},
{UI_POS_X(0), UI_POS_Y(0) + 2, screen_width, 1 * 16},
{}};
TransmitterView2 tx_view{
{0 * 8, 17 * 16},
{UI_POS_X(0), UI_POS_Y_BOTTOM(3)},
/*short_ui*/ true};
Checkbox check_loop{
{10 * 8, 17 * 16},
{10 * 8, UI_POS_Y_BOTTOM(3)},
4,
"Loop",
/*small*/ true};
TextField field_filename{
{0 * 8, 18 * 16, 17 * 8, 1 * 16},
{UI_POS_X(0), UI_POS_Y_BOTTOM(2), 17 * 8, 1 * 16},
{}};
NewButton button_add{
{17 * 8 + 4, 17 * 16, 4 * 8, 2 * 16},
{17 * 8 + 4, UI_POS_Y_BOTTOM(3), 4 * 8, 2 * 16},
"",
&bitmap_icon_add,
Color::orange(),
/*vcenter*/ true};
NewButton button_new{
{22 * 8, 17 * 16, 4 * 8, 2 * 16},
{22 * 8, UI_POS_Y_BOTTOM(3), 4 * 8, 2 * 16},
"",
&bitmap_icon_new_file,
Color::dark_blue(),
/*vcenter*/ true};
NewButton button_open{
{26 * 8, 17 * 16, 4 * 8, 2 * 16},
{26 * 8, UI_POS_Y_BOTTOM(3), 4 * 8, 2 * 16},
"",
&bitmap_icon_load,
Color::dark_blue(),
+37 -33
View File
@@ -173,33 +173,37 @@ class ScannerView : public View {
};
Labels labels{
{{0 * 8, 0 * 16}, "LNA: VGA: AMP: VOL:", Theme::getInstance()->fg_light->foreground},
{{0 * 8, 1 * 16}, "BW: SQ: Wsa: Wsl:", Theme::getInstance()->fg_light->foreground},
{{0 * 8, 10 * 16}, "SRCH START SEARCH END SWITCH", Theme::getInstance()->fg_light->foreground},
{{UI_POS_X(0), UI_POS_Y(0)}, "LNA: VGA: AMP:", Theme::getInstance()->fg_light->foreground},
{{UI_POS_X_RIGHT(6), UI_POS_Y(0)}, "VOL:", Theme::getInstance()->fg_light->foreground},
{{UI_POS_X(0), UI_POS_Y(1)}, "BW: SQ: Wsa: Wsl:", Theme::getInstance()->fg_light->foreground},
{{UI_POS_X_RIGHT(13), UI_POS_Y(1)}, "Wsa: Wsl:", Theme::getInstance()->fg_light->foreground},
{{UI_POS_X(0), UI_POS_Y(10)}, "SRCH START", Theme::getInstance()->fg_light->foreground},
{{UI_POS_X_CENTER(10) + 8, UI_POS_Y(10)}, "SEARCH END", Theme::getInstance()->fg_light->foreground},
{{UI_POS_X_RIGHT(6), UI_POS_Y(10)}, "SWITCH", Theme::getInstance()->fg_light->foreground},
{{0 * 8, (26 * 8) + 4}, "MODE:", Theme::getInstance()->fg_light->foreground},
{{UI_POS_X(0), (26 * 8) + 4}, "MODE:", Theme::getInstance()->fg_light->foreground},
{{11 * 8, (26 * 8) + 4}, "STEP:", Theme::getInstance()->fg_light->foreground},
};
LNAGainField field_lna{
{4 * 8, 0 * 16}};
{UI_POS_X(4), UI_POS_Y(0)}};
VGAGainField field_vga{
{11 * 8, 0 * 16}};
{UI_POS_X(11), UI_POS_Y(0)}};
RFAmpField field_rf_amp{
{18 * 8, 0 * 16}};
{UI_POS_X(18), UI_POS_Y(0)}};
AudioVolumeField field_volume{
{24 * 8, 0 * 16}};
{UI_POS_X_RIGHT(2), UI_POS_Y(0)}};
OptionsField field_bw{
{3 * 8, 1 * 16},
{UI_POS_X(3), UI_POS_Y(1)},
6,
{}};
NumberField field_squelch{
{13 * 8, 1 * 16},
{UI_POS_X(13), UI_POS_Y(1)},
3,
{-90, 20},
1,
@@ -208,7 +212,7 @@ class ScannerView : public View {
NumberField field_browse_wait{
// Signal-Active wait timer - time to wait before moving on even when signal locked
{21 * 8, 1 * 16},
{UI_POS_X_RIGHT(9), UI_POS_Y(1)},
2,
{0, 99},
1,
@@ -217,7 +221,7 @@ class ScannerView : public View {
NumberField field_lock_wait{
// Signal-Lost wait timer - time to wait before moving on after losing signal lock
{screen_width - 2 * 8, 1 * 16},
{UI_POS_X_RIGHT(2), UI_POS_Y(1)},
2,
{0, 99},
1,
@@ -225,20 +229,20 @@ class ScannerView : public View {
};
RSSI rssi{
{0 * 16, 2 * 16, 15 * 16, 8},
{UI_POS_X(0), UI_POS_Y(2), UI_POS_WIDTH_REMAINING(6), 8},
};
TextField field_current_index{
{0, 3 * 16, 3 * 8, 16},
{UI_POS_X(0), UI_POS_Y(3), 3 * 8, 16},
{},
};
Text text_max_index{
{4 * 8, 3 * 16, 18 * 8, 16},
{UI_POS_X(4), UI_POS_Y(3), 18 * 8, 16},
};
Text text_current_desc{
{0, 4 * 16, screen_width - 6 * 8, 16},
{UI_POS_X(0), UI_POS_Y(4), UI_POS_WIDTH_REMAINING(6), UI_POS_HEIGHT(1)},
};
BigFrequency big_display{
@@ -246,15 +250,15 @@ class ScannerView : public View {
0};
Button button_manual_start{
{0 * 8, 11 * 16, 11 * 8, 28},
{UI_POS_X(0), UI_POS_Y(11), UI_POS_WIDTH(10), 28},
""};
Button button_manual_end{
{12 * 8, 11 * 16, 11 * 8, 28},
{UI_POS_X_CENTER(10) + 8, UI_POS_Y(11), UI_POS_WIDTH(10), 28},
""};
Button button_manual_search{
{24 * 8, 11 * 16, 6 * 8, 28},
{UI_POS_X_RIGHT(6), UI_POS_Y(11), UI_POS_WIDTH(6), 28},
""};
OptionsField field_mode{
@@ -273,34 +277,34 @@ class ScannerView : public View {
{0, (15 * 16) - 4, 72, 28},
"<PAUSE>"};
Button button_audio_app{
{UI_POS_X_CENTER(9), (15 * 16) - 4, UI_POS_WIDTH(9), 28},
"AUDIO"};
Button button_add{
{UI_POS_X_RIGHT(9), (15 * 16) - 4, UI_POS_WIDTH(9), 28},
"ADD FQ"};
Button button_dir{
{0, (35 * 8) - 4, 72, 28},
"REVERSE"};
Button button_audio_app{
{84, (15 * 16) - 4, 72, 28},
"AUDIO"};
Button button_mic_app{
{84, (35 * 8) - 4, 72, 28},
{UI_POS_X_CENTER(9), (35 * 8) - 4, UI_POS_WIDTH(9), 28},
"MIC TX"};
Button button_add{
{168, (15 * 16) - 4, 72, 28},
"ADD FQ"};
Button button_remove{
{UI_POS_X_RIGHT(9), (35 * 8) - 4, UI_POS_WIDTH(9), 28},
"DEL FQ"};
Button button_load{
{24 * 8, 3 * 16 - 10, 6 * 8, 22},
{UI_POS_X_RIGHT(6), UI_POS_Y(3) - 10, UI_POS_WIDTH(6), 22},
"LOAD"};
Button button_clear{
{24 * 8, 4 * 16, 6 * 8, 22},
{UI_POS_X_RIGHT(6), UI_POS_Y(4), UI_POS_WIDTH(6), 22},
"MCLR"};
Button button_remove{
{168, (35 * 8) - 4, 72, 28},
"DEL FQ"};
std::unique_ptr<ScannerThread> scan_thread{};
MessageHandlerRegistration message_handler_retune{
@@ -62,19 +62,19 @@ class ShoppingCartLock : public View {
true};
Text text_empty{
{40, 70, 160, 16},
{UI_POS_X_CENTER(20), 70, 160, 16},
"RocketGod was here"};
Button button_lock{
{40, 165, 160, 35},
{UI_POS_X_CENTER(20), 165, 160, 35},
LanguageHelper::currentMessages[LANG_LOCK]};
Button button_unlock{
{40, 205, 160, 35},
{UI_POS_X_CENTER(20), 205, 160, 35},
LanguageHelper::currentMessages[LANG_UNLOCK]};
Button button_stop{
{40, 245, 160, 35},
{UI_POS_X_CENTER(20), 245, 160, 35},
LanguageHelper::currentMessages[LANG_STOP]};
MessageHandlerRegistration message_handler_fifo_signal{
+9 -9
View File
@@ -175,7 +175,7 @@ void SnakeView::erase_food() {
void SnakeView::draw_score() {
auto style = *ui::Theme::getInstance()->fg_blue;
painter.draw_string({5, 5}, style, "Score: " + std::to_string(score));
painter.draw_string({UI_POS_X_CENTER(11), 5}, style, "Score: " + std::to_string(score));
}
void SnakeView::draw_borders() {
@@ -189,11 +189,11 @@ void SnakeView::show_menu() {
auto style_yellow = *ui::Theme::getInstance()->fg_yellow;
auto style_green = *ui::Theme::getInstance()->fg_green;
auto style_blue = *ui::Theme::getInstance()->fg_blue;
painter.draw_string({50, 40}, style_yellow, "* * * SNAKE * * *");
painter.draw_string({0, 120}, style_blue, "USE THE D-PAD TO MOVE");
painter.draw_string({0, 150}, style_blue, "EAT THE RED SQUARES TO GROW");
painter.draw_string({0, 180}, style_blue, "DON'T HIT THE WALLS OR SELF");
painter.draw_string({15, 240}, style_green, "** PRESS SELECT TO START **");
painter.draw_string({UI_POS_X_CENTER(18), 40}, style_yellow, "* * * SNAKE * * *");
painter.draw_string({UI_POS_X_CENTER(22), 120}, style_blue, "USE THE D-PAD TO MOVE");
painter.draw_string({UI_POS_X_CENTER(28), 150}, style_blue, "EAT THE RED SQUARES TO GROW");
painter.draw_string({UI_POS_X_CENTER(28), 180}, style_blue, "DON'T HIT THE WALLS OR SELF");
painter.draw_string({UI_POS_X_CENTER(28), 240}, style_green, "** PRESS SELECT TO START **");
}
void SnakeView::show_game_over() {
@@ -202,9 +202,9 @@ void SnakeView::show_game_over() {
auto style_red = *ui::Theme::getInstance()->fg_red;
auto style_yellow = *ui::Theme::getInstance()->fg_yellow;
auto style_green = *ui::Theme::getInstance()->fg_green;
painter.draw_string({75, 90}, style_red, "GAME OVER");
painter.draw_string({74, 150}, style_yellow, "SCORE: " + std::to_string(score));
painter.draw_string({20, 220}, style_green, "PRESS SELECT TO RESTART");
painter.draw_string({UI_POS_X_CENTER(10), 90}, style_red, "GAME OVER");
painter.draw_string({UI_POS_X_CENTER(11), 150}, style_yellow, "SCORE: " + std::to_string(score));
painter.draw_string({UI_POS_X_CENTER(24), 220}, style_green, "PRESS SELECT TO RESTART");
wait(1);
}
+14 -14
View File
@@ -95,25 +95,25 @@ class SoundBoardView : public View {
void update_config();
Labels labels{
{{24 * 8, 180}, "Vol:", Theme::getInstance()->fg_light->foreground},
{{0, 180}, "Key:", Theme::getInstance()->fg_light->foreground}};
{{24 * 8, UI_POS_Y_BOTTOM(9)}, "Vol:", Theme::getInstance()->fg_light->foreground},
{{0, UI_POS_Y_BOTTOM(9)}, "Key:", Theme::getInstance()->fg_light->foreground}};
Button button_next_page{
{30 * 7, 25 * 8, 10 * 3, 2 * 14},
{UI_POS_X_RIGHT(4), UI_POS_Y_BOTTOM(8), 10 * 3, 2 * 14},
"=>"};
Button button_prev_page{
{17 * 10, 25 * 8, 10 * 3, 2 * 14},
{UI_POS_X_RIGHT(8), UI_POS_Y_BOTTOM(8), 10 * 3, 2 * 14},
"<="};
Text page_info{
{0, 29 * 8, screen_width, 16}};
{0, UI_POS_Y_BOTTOM(6), screen_width, 16}};
MenuView menu_view{
{0, 0, screen_width, 175},
{0, 0, screen_width, UI_POS_Y_BOTTOM(9)},
true};
Text text_empty{
{7 * 8, 12 * 8, 16 * 8, 16},
{UI_POS_X_CENTER(16), 12 * 8, 16 * 8, 16},
"Empty directory !",
};
@@ -126,31 +126,31 @@ class SoundBoardView : public View {
};*/
OptionsField options_tone_key{
{4 * 8, 180},
{4 * 8, UI_POS_Y_BOTTOM(9)},
18,
{}};
AudioVolumeField field_volume{
{screen_width - 2 * 8, 180}};
{screen_width - 2 * 8, UI_POS_Y_BOTTOM(9)}};
Text text_volume_disabled{
{screen_width - 2 * 8, 180, 3 * 8, 16},
{screen_width - 2 * 8, UI_POS_Y_BOTTOM(9), 3 * 8, 16},
"--"};
Checkbox check_loop{
{0, 25 * 8 + 4},
{0, UI_POS_Y_BOTTOM(8)},
4,
"Loop"};
Checkbox check_random{
{10 * 7, 25 * 8 + 4},
{10 * 7, UI_POS_Y_BOTTOM(8)},
6,
"Random"};
ProgressBar progressbar{
{0 * 8, 31 * 8 + 2, screen_width, 4}};
{UI_POS_X(0), UI_POS_Y_BOTTOM(4.5) + 2, screen_width, 4}};
TransmitterView tx_view{
16 * 16,
(int16_t)UI_POS_Y_BOTTOM(4),
5000,
12};
@@ -183,20 +183,23 @@ void SpectrumPainterView::focus() {
void SpectrumPainterView::paint(Painter& painter) {
View::paint(painter);
size_t c;
Point pos = {0, screen_pos().y() + 8 + footer_location};
for (c = 0; c < 20; c++) {
uint16_t c;
Point pos = {0, UI_POS_Y_BOTTOM(1)};
Point pos2 = {0, UI_POS_Y_BOTTOM(4)};
// 24*8 pxstripes
for (c = 0; c < screen_width / 24 + 1; c++) {
painter.draw_bitmap(
pos,
bitmap_stripes,
Theme::getInstance()->fg_yellow->foreground,
Theme::getInstance()->fg_yellow->background);
if (c != 9)
pos += {24, 0};
else
pos = {0, screen_pos().y() + 8 + footer_location + 32 + 8};
painter.draw_bitmap(
pos2,
bitmap_stripes,
Theme::getInstance()->fg_yellow->foreground,
Theme::getInstance()->fg_yellow->background);
pos += {24, 0};
pos2 += {24, 0};
}
}
} // namespace ui::external_app::spainter
@@ -89,57 +89,57 @@ class SpectrumPainterView : public View {
static constexpr int32_t footer_location = 15 * 16 + 8;
ProgressBar progressbar{
{4, footer_location - 16, screen_width - 8, 16}};
{4, UI_POS_Y_BOTTOM(6) - 3, screen_width - 8, 16}};
Labels labels{
{{10 * 8, footer_location + 1 * 16}, "GAIN A:", Theme::getInstance()->fg_light->foreground},
{{1 * 8, footer_location + 2 * 16}, "BW: Du: P:", Theme::getInstance()->fg_light->foreground},
{{10 * 8, UI_POS_Y_BOTTOM(4)}, "GAIN A:", Theme::getInstance()->fg_light->foreground},
{{1 * 8, UI_POS_Y_BOTTOM(3)}, "BW: Du: P:", Theme::getInstance()->fg_light->foreground},
};
TxFrequencyField field_frequency{
{0 * 8, footer_location + 1 * 16},
{UI_POS_X(0), UI_POS_Y_BOTTOM(4)},
nav_};
NumberField field_rfgain{
{14 * 8, footer_location + 1 * 16},
{14 * 8, UI_POS_Y_BOTTOM(4)},
2,
{0, 47},
1,
' '};
NumberField field_rfamp{
{19 * 8, footer_location + 1 * 16},
{19 * 8, UI_POS_Y_BOTTOM(4)},
2,
{0, 14},
14,
' '};
Checkbox check_loop{
{21 * 8, footer_location + 1 * 16},
{21 * 8, UI_POS_Y_BOTTOM(4)},
4,
LanguageHelper::currentMessages[LANG_LOOP],
true};
ImageButton button_play{
{screen_width - 2 * 8, footer_location + 1 * 16, 2 * 8, 1 * 16},
{screen_width - 2 * 8, UI_POS_Y_BOTTOM(4), 2 * 8, 1 * 16},
&bitmap_play,
Theme::getInstance()->fg_green->foreground,
Theme::getInstance()->fg_green->background};
OptionsField option_bandwidth{
{4 * 8, footer_location + 2 * 16},
{4 * 8, UI_POS_Y_BOTTOM(3)},
5,
{}};
NumberField field_duration{
{13 * 8, footer_location + 2 * 16},
{13 * 8, UI_POS_Y_BOTTOM(3)},
3,
{1, 999},
1,
' '};
NumberField field_pause{
{19 * 8, footer_location + 2 * 16},
{19 * 8, UI_POS_Y_BOTTOM(3)},
2,
{0, 99},
1,
@@ -58,7 +58,7 @@ class SpectrumInputImageView : public View {
uint32_t data_start{0};
Button button_load_image{
{0 * 8, 11 * 16 - 4, screen_width, 28},
{UI_POS_X(0), UI_POS_Y_BOTTOM(10), screen_width, 28},
"Load Image ..."};
bool drawBMP_scaled(const ui::Rect r, const std::string file);
@@ -53,43 +53,43 @@ class SpectrumInputTextView : public View {
void on_set_text(NavigationView& nav);
Text text_message_0{
{0 * 8, 0 * 16, screen_width, 16},
{UI_POS_X(0), UI_POS_Y(0), screen_width, 16},
""};
Text text_message_1{
{0 * 8, 1 * 16, screen_width, 16},
{UI_POS_X(0), 1 * 16, screen_width, 16},
""};
Text text_message_2{
{0 * 8, 2 * 16, screen_width, 16},
{UI_POS_X(0), 2 * 16, screen_width, 16},
""};
Text text_message_3{
{0 * 8, 3 * 16, screen_width, 16},
{UI_POS_X(0), 3 * 16, screen_width, 16},
""};
Text text_message_4{
{0 * 8, 4 * 16, screen_width, 16},
{UI_POS_X(0), 4 * 16, screen_width, 16},
""};
Text text_message_5{
{0 * 8, 5 * 16, screen_width, 16},
{UI_POS_X(0), 5 * 16, screen_width, 16},
""};
Text text_message_6{
{0 * 8, 6 * 16, screen_width, 16},
{UI_POS_X(0), 6 * 16, screen_width, 16},
""};
Text text_message_7{
{0 * 8, 7 * 16, screen_width, 16},
{UI_POS_X(0), 7 * 16, screen_width, 16},
""};
Text text_message_8{
{0 * 8, 8 * 16, screen_width, 16},
{UI_POS_X(0), 8 * 16, screen_width, 16},
""};
Text text_message_9{
{0 * 8, 9 * 16, screen_width, 16},
{UI_POS_X(0), 9 * 16, screen_width, 16},
""};
std::array<Text*, 10> text_message{{
@@ -106,7 +106,7 @@ class SpectrumInputTextView : public View {
}};
Button button_message{
{0 * 8, 11 * 16 - 4, screen_width, 28},
{UI_POS_X(0), UI_POS_Y_BOTTOM(10), screen_width, 28},
"Set message"};
};
+2 -2
View File
@@ -93,10 +93,10 @@ class SSTVTXView : public View {
{}};
ProgressBar progressbar{
{16, 25 * 8, 208, 16}};
{16, 25 * 8, UI_POS_WIDTH_REMAINING(4), 16}};
TransmitterView tx_view{
16 * 16,
(int16_t)UI_POS_Y_BOTTOM(4),
10000,
12};
+5 -5
View File
@@ -57,8 +57,8 @@ class StopwatchView : public View {
/* m m s s ms ms ms*/
Labels labels{
{{0 * 8, 1 * 16}, "TOTAL:", Theme::getInstance()->fg_light->foreground},
{{0 * 8, 7 * 16}, "LAP:", Theme::getInstance()->fg_light->foreground},
{{UI_POS_X(0), 1 * 16}, "TOTAL:", Theme::getInstance()->fg_light->foreground},
{{UI_POS_X(0), 7 * 16}, "LAP:", Theme::getInstance()->fg_light->foreground},
};
OptionsField options_ms_display_level{
@@ -71,15 +71,15 @@ class StopwatchView : public View {
Painter painter;
Button button_run_stop{
{72, 210, 96, 24},
{UI_POS_X_CENTER(12), 210, 96, 24},
"START"};
Button button_reset_lap{
{72, screen_height - 80, 96, 24},
{UI_POS_X_CENTER(12), screen_height - 80, 96, 24},
"RESET"};
Button button_done{
{72, screen_height - 50, 96, 24},
{UI_POS_X_CENTER(12), screen_height - 50, 96, 24},
"EXIT"};
MessageHandlerRegistration message_handler_frame_sync{
+6 -2
View File
@@ -198,8 +198,12 @@ void RecentEntriesTable<ui::external_app::tpmsrx::TPMSRecentEntries>::draw(
const Entry& entry,
const Rect& target_rect,
Painter& painter,
const Style& style) {
std::string line = ui::external_app::tpmsrx::format::type(entry.type) + " " + ui::external_app::tpmsrx::format::id(entry.id);
const Style& style,
RecentEntriesColumns& columns) {
std::string line = ui::external_app::tpmsrx::format::type(entry.type) + " ";
std::string lid = ui::external_app::tpmsrx::format::id(entry.id);
lid.resize(columns.at(1).second, ' ');
line += lid;
if (entry.last_pressure.is_valid()) {
line += " " + ui::external_app::tpmsrx::format::pressure(entry.last_pressure.value());
+12 -14
View File
@@ -134,19 +134,17 @@ class TPMSAppView : public View {
ui::Rect view_normal_rect{};
RSSI rssi{
{21 * 8, 0, 6 * 8, 4},
{UI_POS_X(21), 0, UI_POS_WIDTH_REMAINING(24), 4},
};
AudioVolumeField field_volume{
{screen_width - 2 * 8, 0 * 16}};
Channel channel{
{21 * 8, 5, 6 * 8, 4},
{UI_POS_X(21), 5, UI_POS_WIDTH_REMAINING(24), 4},
};
AudioVolumeField field_volume{
{UI_POS_X_RIGHT(2), UI_POS_Y(0)}};
// "315 MHz" TPMS sensors transmit at either 314.9 or 315 MHz but we should pick up either
OptionsField options_band{
{0 * 8, 0 * 16},
{UI_POS_X(0), UI_POS_Y(0)},
5,
{
{"314.9", 314900000},
@@ -155,32 +153,32 @@ class TPMSAppView : public View {
}};
OptionsField options_pressure{
{6 * 8, 0 * 16},
{6 * 8, UI_POS_Y(0)},
3,
{{"kPa", 0},
{"PSI", 1}}};
OptionsField options_temperature{
{10 * 8, 0 * 16},
{10 * 8, UI_POS_Y(0)},
2,
{{STR_DEGREES_C, 0},
{STR_DEGREES_F, 1}}};
RFAmpField field_rf_amp{
{13 * 8, 0 * 16}};
{13 * 8, UI_POS_Y(0)}};
LNAGainField field_lna{
{15 * 8, 0 * 16}};
{15 * 8, UI_POS_Y(0)}};
VGAGainField field_vga{
{18 * 8, 0 * 16}};
{18 * 8, UI_POS_Y(0)}};
TPMSRecentEntries recent{};
std::unique_ptr<TPMSLogger> logger{};
const RecentEntriesColumns columns{{
RecentEntriesColumns columns{{
{"Tp", 2},
{"ID", 8},
{"ID", 0},
{"Pres", 4},
{"Temp", 4},
{"Cnt", 3},
+6 -6
View File
@@ -69,11 +69,11 @@ class TunerView : public View {
bool current_note_playing{false};
Labels labels{
{{0 * 8, 1 * 16}, "Instrument:", Theme::getInstance()->fg_light->foreground},
{{0 * 8, 2 * 16}, "Note:", Theme::getInstance()->fg_light->foreground},
{{0 * 8, 3 * 16}, "Note Frequency:", Theme::getInstance()->fg_light->foreground},
{{0 * 8, 4 * 16}, "Note Octave Shift:", Theme::getInstance()->fg_light->foreground},
{{0 * 8, 5 * 16}, "Volume:", Theme::getInstance()->fg_light->foreground}};
{{UI_POS_X(0), 1 * 16}, "Instrument:", Theme::getInstance()->fg_light->foreground},
{{UI_POS_X(0), 2 * 16}, "Note:", Theme::getInstance()->fg_light->foreground},
{{UI_POS_X(0), 3 * 16}, "Note Frequency:", Theme::getInstance()->fg_light->foreground},
{{UI_POS_X(0), 4 * 16}, "Note Octave Shift:", Theme::getInstance()->fg_light->foreground},
{{UI_POS_X(0), 5 * 16}, "Volume:", Theme::getInstance()->fg_light->foreground}};
Text text_note_frequency{
{(sizeof("Note Frequency:") + 1) * 8, 3 * 16, (int)screen_width - (int)(sizeof("Note Frequency:") + 1) * 8, 16},
@@ -102,7 +102,7 @@ class TunerView : public View {
{}};
NewButton button_play_stop{
{0 * 8, 16 * 16, screen_width, screen_height - 16 * 16},
{UI_POS_X(0), 16 * 16, screen_width, screen_height - 16 * 16},
{},
&bitmap_icon_replay,
Theme::getInstance()->fg_red->foreground};
@@ -48,16 +48,16 @@ class WardriveMapView : public View {
const std::filesystem::path flippersub_dir = u"subghz";
NavigationView& nav_;
Text text_info{{0 * 8, 0 * 8, 20 * 8, 16 * 1}, "0 / 30"};
Text text_notfound{{0 * 8, 0 * 8, screen_width, 16 * 1}, "No GeoTagged captures found"};
Text text_info{{UI_POS_X(0), 0 * 8, 20 * 8, 16 * 1}, "0 / 30"};
Text text_notfound{{UI_POS_X(0), 0 * 8, screen_width, 16 * 1}, "No GeoTagged captures found"};
GeoPos geopos{
{0, 20},
GeoPos::alt_unit::METERS,
GeoPos::spd_unit::HIDDEN};
GeoMap geomap{{0, 75, screen_width, screen_height - 75}};
Button btn_back{{22 * 8, 0 * 8, 3 * 8, 16}, "<-"};
Button btn_next{{26 * 8, 0 * 8, 3 * 8, 16}, "->"};
Button btn_back{{UI_POS_X_RIGHT(8), 0 * 8, 3 * 8, 16}, "<-"};
Button btn_next{{UI_POS_X_RIGHT(4), 0 * 8, 3 * 8, 16}, "->"};
void on_gps(const GPSPosDataMessage* msg);
void on_orientation(const OrientationDataMessage* msg);
+18 -12
View File
@@ -49,26 +49,26 @@ void ViewWavView::refresh_waveform() {
uint8_t bits_per_sample = wav_reader->bits_per_sample();
for (size_t i = 0; i < 240; i++) {
for (size_t i = 0; i < screen_width; i++) {
wav_reader->data_seek(position + (i * scale));
if (bits_per_sample == 8) {
uint8_t sample;
wav_reader->read(&sample, 1);
waveform_buffer[i] = (sample - 0x80) * 256;
} else {
wav_reader->read(&waveform_buffer[i], 2);
wav_reader->read(&waveform_buffer.data()[i], 2);
}
}
waveform.set_dirty();
// Window
uint64_t w_start = (position * 240) / wav_reader->sample_count();
uint64_t w_width = (scale * 240) / (wav_reader->sample_count() / 240);
display.fill_rectangle({0, 10 * 16 + 1, 240, 16}, Theme::getInstance()->bg_darkest->background);
uint64_t w_start = (position * screen_width) / wav_reader->sample_count();
uint64_t w_width = (scale * screen_width) / (wav_reader->sample_count() / screen_width);
display.fill_rectangle({0, 10 * 16 + 1, screen_width, 16}, Theme::getInstance()->bg_darkest->background);
display.fill_rectangle({(Coord)w_start, 21 * 8, (Dim)w_width + 1, 8}, Theme::getInstance()->bg_darkest->foreground);
display.draw_line({0, 10 * 16 + 1}, {(Coord)w_start, 21 * 8}, Theme::getInstance()->bg_darkest->foreground);
display.draw_line({239, 10 * 16 + 1}, {(Coord)(w_start + w_width), 21 * 8}, Theme::getInstance()->bg_darkest->foreground);
display.draw_line({screen_width - 1, 10 * 16 + 1}, {(Coord)(w_start + w_width), 21 * 8}, Theme::getInstance()->bg_darkest->foreground);
}
void ViewWavView::refresh_measurements() {
@@ -82,11 +82,11 @@ void ViewWavView::refresh_measurements() {
void ViewWavView::paint(Painter& painter) {
// Waveform limits
painter.draw_hline({0, 6 * 16 - 1}, 240, Theme::getInstance()->bg_medium->background);
painter.draw_hline({0, 10 * 16}, 240, Theme::getInstance()->bg_medium->background);
painter.draw_hline({0, 6 * 16 - 1}, screen_width, Theme::getInstance()->bg_medium->background);
painter.draw_hline({0, 10 * 16}, screen_width, Theme::getInstance()->bg_medium->background);
// Overall amplitude view, 0~127 to 0~255 color index
for (size_t i = 0; i < 240; i++)
for (size_t i = 0; i < screen_width; i++)
painter.draw_vline({(Coord)i, 11 * 16}, 8, spectrum_rgb2_lut[amplitude_buffer[i] << 1]);
}
@@ -128,10 +128,10 @@ void ViewWavView::load_wav(std::filesystem::path file_path) {
text_title.set(wav_reader->title());
// Fill amplitude buffer, world's worst downsampling
uint64_t skip = wav_reader->sample_count() / (240 * subsampling_factor);
uint64_t skip = wav_reader->sample_count() / (screen_width * subsampling_factor);
uint8_t bits_per_sample = wav_reader->bits_per_sample();
for (size_t i = 0; i < 240; i++) {
for (size_t i = 0; i < screen_width; i++) {
average = 0;
for (size_t s = 0; s < subsampling_factor; s++) {
@@ -163,7 +163,7 @@ void ViewWavView::reset_controls() {
field_pos_seconds.set_range(0, wav_reader->ms_duration() / 1000);
field_pos_milliseconds.set_range(0, (wav_reader->ms_duration() < 1000) ? wav_reader->ms_duration() % 1000 : 999);
field_pos_samples.set_range(0, wav_reader->sample_count() - 1);
field_scale.set_range(1, std::min(99999ul, wav_reader->sample_count() / 240));
field_scale.set_range(1, std::min(99999ul, wav_reader->sample_count() / screen_width));
}
bool ViewWavView::is_active() {
@@ -262,6 +262,12 @@ void ViewWavView::on_playback_progress(const uint32_t progress) {
ViewWavView::ViewWavView(
NavigationView& nav)
: nav_(nav) {
waveform_buffer.resize(screen_width);
amplitude_buffer.resize(screen_width);
waveform.set_length(screen_width);
waveform.set_data((int16_t*)waveform_buffer.data());
for (auto& v : waveform_buffer) v = 0;
for (auto& v : amplitude_buffer) v = 0;
baseband::run_prepared_image(portapack::memory::map::m4_code.base());
wav_reader = std::make_unique<WAVFileReader>();
+23 -22
View File
@@ -73,8 +73,8 @@ class ViewWavView : public View {
std::unique_ptr<WAVFileReader> wav_reader{};
int16_t waveform_buffer[240]{};
uint8_t amplitude_buffer[240]{};
std::vector<int16_t> waveform_buffer{};
std::vector<uint8_t> amplitude_buffer{};
int32_t scale{1};
uint64_t ns_per_pixel{};
uint64_t position{};
@@ -83,53 +83,54 @@ class ViewWavView : public View {
bool waveform_update_needed{false};
Labels labels{
{{0 * 8, 0 * 16}, "File:", Theme::getInstance()->fg_light->foreground},
{{UI_POS_X(0), UI_POS_Y(0)}, "File:", Theme::getInstance()->fg_light->foreground},
{{2 * 8, 1 * 16}, "-bit mono", Theme::getInstance()->fg_light->foreground},
{{0 * 8, 2 * 16}, "Title:", Theme::getInstance()->fg_light->foreground},
{{0 * 8, 3 * 16}, "Duration:", Theme::getInstance()->fg_light->foreground},
{{0 * 8, 12 * 16}, "Position: . s Scale:", Theme::getInstance()->fg_light->foreground},
{{0 * 8, 13 * 16}, " Sample:", Theme::getInstance()->fg_light->foreground},
{{0 * 8, 14 * 16}, "Cursor A:", Theme::getInstance()->fg_darkcyan->foreground},
{{0 * 8, 15 * 16}, "Cursor B:", Theme::getInstance()->fg_magenta->foreground},
{{0 * 8, 16 * 16}, "Delta:", Theme::getInstance()->fg_light->foreground},
{{24 * 8, 18 * 16}, "Vol:", Theme::getInstance()->fg_light->foreground}};
{{UI_POS_X(0), 2 * 16}, "Title:", Theme::getInstance()->fg_light->foreground},
{{UI_POS_X(0), 3 * 16}, "Duration:", Theme::getInstance()->fg_light->foreground},
{{UI_POS_X(0), 12 * 16}, "Position: . s Scale:", Theme::getInstance()->fg_light->foreground},
{{UI_POS_X(0), 13 * 16}, " Sample:", Theme::getInstance()->fg_light->foreground},
{{UI_POS_X(0), 14 * 16}, "Cursor A:", Theme::getInstance()->fg_darkcyan->foreground},
{{UI_POS_X(0), 15 * 16}, "Cursor B:", Theme::getInstance()->fg_magenta->foreground},
{{UI_POS_X(0), 16 * 16}, "Delta:", Theme::getInstance()->fg_light->foreground},
{{UI_POS_X_RIGHT(6), UI_POS_Y_BOTTOM(2)}, "Vol:", Theme::getInstance()->fg_light->foreground}};
Text text_filename{
{5 * 8, 0 * 16, 18 * 8, 16},
{UI_POS_X(5), UI_POS_Y(0), UI_POS_WIDTH_REMAINING(12), UI_POS_HEIGHT(1)},
""};
Text text_samplerate{
{12 * 8, 1 * 16, 10 * 8, 16},
{12 * 8, 1 * 16, 10 * 8, UI_POS_HEIGHT(1)},
""};
Text text_title{
{6 * 8, 2 * 16, 17 * 8, 16},
{6 * 8, 2 * 16, 17 * 8, UI_POS_HEIGHT(1)},
""};
Text text_duration{
{9 * 8, 3 * 16, 20 * 8, 16},
{9 * 8, 3 * 16, 20 * 8, UI_POS_HEIGHT(1)},
""};
Text text_bits_per_sample{
{0 * 8, 1 * 16, 2 * 8, 16},
{UI_POS_X(0), 1 * 16, 2 * 8, UI_POS_HEIGHT(1)},
"16"};
Button button_open{
{24 * 8, 8, 6 * 8, 2 * 16},
{UI_POS_X_RIGHT(6), 8, UI_POS_WIDTH(6), UI_POS_HEIGHT(2)},
"Open"};
ImageButton button_play{
{24 * 8, 17 * 16, 2 * 8, 1 * 16},
{UI_POS_X_RIGHT(6), UI_POS_Y_BOTTOM(3), UI_POS_WIDTH(2), UI_POS_HEIGHT(1)},
&bitmap_play,
Theme::getInstance()->fg_green->foreground,
Theme::getInstance()->fg_green->background};
AudioVolumeField field_volume{
{screen_width - 2 * 8, 18 * 16}};
{UI_POS_X_RIGHT(2), UI_POS_Y_BOTTOM(2)}};
Waveform waveform{
{0, 5 * 16, screen_width, 64},
waveform_buffer,
{0, UI_POS_Y(5), screen_width, UI_POS_HEIGHT(4)},
waveform_buffer.data(),
240,
0,
false,
Theme::getInstance()->bg_darkest->foreground};
ProgressBar progressbar{
{0 * 8, 11 * 16, screen_width, 4}};
{UI_POS_X(0), 11 * 16, screen_width, 4}};
NumberField field_pos_seconds{
{9 * 8, 12 * 16},
+8 -8
View File
@@ -84,18 +84,18 @@ class WeFaxRxView : public View {
}};
RFAmpField field_rf_amp{
{13 * 8, 0 * 16}};
{13 * 8, UI_POS_Y(0)}};
LNAGainField field_lna{
{15 * 8, 0 * 16}};
{15 * 8, UI_POS_Y(0)}};
VGAGainField field_vga{
{18 * 8, 0 * 16}};
{18 * 8, UI_POS_Y(0)}};
RSSI rssi{
{21 * 8, 0, 6 * 8, 4}};
{UI_POS_X(21), 0, UI_POS_WIDTH_REMAINING(24), 4}};
AudioVolumeField field_volume{
{screen_width - 2 * 8, 0 * 16}};
{screen_width - 2 * 8, UI_POS_Y(0)}};
RxFrequencyField field_frequency{
{0 * 8, 0 * 16},
{UI_POS_X(0), UI_POS_Y(0)},
nav_};
Labels labels{
@@ -124,11 +124,11 @@ class WeFaxRxView : public View {
}};
Text txt_status{
{0 * 8, 2 * 16, 20 * 8, 16},
{UI_POS_X(0), 2 * 16, 20 * 8, 16},
};
Button button_ss{
{190, 2 * 16, 5 * 8, 16},
{UI_POS_X_RIGHT(6), UI_POS_Y(2), UI_POS_WIDTH(6), UI_POS_HEIGHT(1)},
LanguageHelper::currentMessages[LANG_START]};
MessageHandlerRegistration message_handler_stats{