mirror of
https://github.com/eried/portapack-mayhem.git
synced 2024-10-01 01:26:06 -04:00
Search cleanup, more binder support (#1467)
This commit is contained in:
parent
951890eaff
commit
78713cc2af
@ -153,28 +153,14 @@ FrequencySaveView::FrequencySaveView(
|
||||
add_children(
|
||||
{&labels,
|
||||
&big_display,
|
||||
&button_clear,
|
||||
&button_edit,
|
||||
&button_save,
|
||||
&text_description});
|
||||
&field_description});
|
||||
|
||||
entry_.type = freqman_type::Single;
|
||||
entry_.frequency_a = value;
|
||||
entry_.description = to_string_timestamp(rtc_time::now());
|
||||
refresh_ui();
|
||||
|
||||
button_clear.on_select = [this, &nav](Button&) {
|
||||
entry_.description = "";
|
||||
refresh_ui();
|
||||
};
|
||||
|
||||
button_edit.on_select = [this, &nav](Button&) {
|
||||
temp_buffer_ = entry_.description;
|
||||
text_prompt(nav_, temp_buffer_, desc_edit_max, [this](std::string& new_desc) {
|
||||
entry_.description = new_desc;
|
||||
refresh_ui();
|
||||
});
|
||||
};
|
||||
bind(field_description, entry_.description, nav);
|
||||
|
||||
button_save.on_select = [this, &nav](Button&) {
|
||||
db_.insert_entry(db_.entry_count(), entry_);
|
||||
@ -182,9 +168,13 @@ FrequencySaveView::FrequencySaveView(
|
||||
};
|
||||
}
|
||||
|
||||
void FrequencySaveView::focus() {
|
||||
refresh_ui();
|
||||
FreqManBaseView::focus();
|
||||
}
|
||||
|
||||
void FrequencySaveView::refresh_ui() {
|
||||
big_display.set(entry_.frequency_a);
|
||||
text_description.set(entry_.description);
|
||||
}
|
||||
|
||||
/* FrequencyLoadView *************************************/
|
||||
|
@ -87,9 +87,9 @@ class FrequencySaveView : public FreqManBaseView {
|
||||
public:
|
||||
FrequencySaveView(NavigationView& nav, const rf::Frequency value);
|
||||
std::string title() const override { return "Save freq"; }
|
||||
void focus() override;
|
||||
|
||||
private:
|
||||
std::string temp_buffer_{};
|
||||
freqman_entry entry_{};
|
||||
|
||||
void refresh_ui();
|
||||
@ -101,15 +101,9 @@ class FrequencySaveView : public FreqManBaseView {
|
||||
Labels labels{
|
||||
{{0 * 8, 6 * 16}, "Description:", Color::white()}};
|
||||
|
||||
Text text_description{{0 * 8, 7 * 16, 30 * 8, 1 * 16}};
|
||||
|
||||
Button button_clear{
|
||||
{4 * 8, 10 * 16, 10 * 8, 2 * 16},
|
||||
"Clear"};
|
||||
|
||||
Button button_edit{
|
||||
{16 * 8, 10 * 16, 10 * 8, 2 * 16},
|
||||
"Edit"};
|
||||
TextField field_description{
|
||||
{0 * 8, 7 * 16, 30 * 8, 1 * 16},
|
||||
""};
|
||||
|
||||
Button button_save{
|
||||
{0 * 8, 17 * 16, 15 * 8, 2 * 16},
|
||||
|
@ -23,7 +23,9 @@
|
||||
#include "ui_search.hpp"
|
||||
|
||||
#include "baseband_api.hpp"
|
||||
#include "binder.hpp"
|
||||
#include "string_format.hpp"
|
||||
#include "ui_freqman.hpp"
|
||||
|
||||
using namespace portapack;
|
||||
|
||||
@ -47,8 +49,61 @@ void RecentEntriesTable<SearchRecentEntries>::draw(
|
||||
painter.draw_string(target_rect.location(), style, to_string_short_freq(entry.frequency) + " " + entry.time + " " + str_duration);
|
||||
}
|
||||
|
||||
void SearchView::focus() {
|
||||
field_frequency_min.focus();
|
||||
/* SearchView ********************************************/
|
||||
|
||||
SearchView::SearchView(
|
||||
NavigationView& nav)
|
||||
: nav_(nav) {
|
||||
baseband::run_image(portapack::spi_flash::image_tag_wideband_spectrum);
|
||||
|
||||
add_children({&labels,
|
||||
&field_frequency_min,
|
||||
&field_frequency_max,
|
||||
&field_lna,
|
||||
&field_vga,
|
||||
&field_threshold,
|
||||
&text_mean,
|
||||
&text_slices,
|
||||
&text_rate,
|
||||
&text_infos,
|
||||
&vu_max,
|
||||
&progress_timers,
|
||||
&check_snap,
|
||||
&options_snap,
|
||||
&big_display,
|
||||
&recent_entries_view});
|
||||
|
||||
baseband::set_spectrum(SEARCH_SLICE_WIDTH, 31);
|
||||
|
||||
recent_entries_view.set_parent_rect({0, 28 * 8, screen_width, 12 * 8});
|
||||
recent_entries_view.on_select = [this, &nav](const SearchRecentEntry& entry) {
|
||||
nav.push<FrequencySaveView>(entry.frequency);
|
||||
};
|
||||
|
||||
text_mean.set_style(&Styles::grey);
|
||||
text_slices.set_style(&Styles::grey);
|
||||
text_rate.set_style(&Styles::grey);
|
||||
progress_timers.set_style(&Styles::grey);
|
||||
big_display.set_style(&Styles::grey);
|
||||
|
||||
field_frequency_min.set_step(100'000);
|
||||
bind(field_frequency_min, settings_.freq_min, nav, [this](auto) {
|
||||
on_range_changed();
|
||||
});
|
||||
|
||||
field_frequency_max.set_step(100'000);
|
||||
bind(field_frequency_max, settings_.freq_max, nav, [this](auto) {
|
||||
on_range_changed();
|
||||
});
|
||||
|
||||
bind(field_threshold, settings_.power_threshold);
|
||||
bind(check_snap, settings_.snap_search);
|
||||
bind(options_snap, settings_.snap_step);
|
||||
|
||||
progress_timers.set_max(DETECT_DELAY);
|
||||
|
||||
on_range_changed();
|
||||
receiver_model.enable();
|
||||
}
|
||||
|
||||
SearchView::~SearchView() {
|
||||
@ -56,6 +111,18 @@ SearchView::~SearchView() {
|
||||
baseband::shutdown();
|
||||
}
|
||||
|
||||
void SearchView::on_show() {
|
||||
baseband::spectrum_streaming_start();
|
||||
}
|
||||
|
||||
void SearchView::on_hide() {
|
||||
baseband::spectrum_streaming_stop();
|
||||
}
|
||||
|
||||
void SearchView::focus() {
|
||||
field_frequency_min.focus();
|
||||
}
|
||||
|
||||
void SearchView::do_detection() {
|
||||
uint8_t power_max = 0;
|
||||
int32_t bin_max = -1;
|
||||
@ -83,7 +150,7 @@ void SearchView::do_detection() {
|
||||
if (power > overall_power_max)
|
||||
overall_power_max = power;
|
||||
|
||||
if ((power >= mean_power + power_threshold) && (power > power_max)) {
|
||||
if ((power >= mean_power + settings_.power_threshold) && (power > power_max)) {
|
||||
power_max = power;
|
||||
bin_max = slices[slice].max_index;
|
||||
slice_max = slice;
|
||||
@ -104,7 +171,7 @@ void SearchView::do_detection() {
|
||||
}
|
||||
|
||||
// Check range
|
||||
if ((resolved_frequency >= f_min) && (resolved_frequency <= f_max)) {
|
||||
if ((resolved_frequency >= settings_.freq_min) && (resolved_frequency <= settings_.freq_max)) {
|
||||
duration = 0;
|
||||
|
||||
auto& entry = ::on_packet(recent, resolved_frequency);
|
||||
@ -159,16 +226,47 @@ void SearchView::do_detection() {
|
||||
}
|
||||
}
|
||||
|
||||
void SearchView::add_spectrum_pixel(Color color) {
|
||||
// Is avoiding floats really necessary ?
|
||||
bin_skip_acc += bin_skip_frac;
|
||||
if (bin_skip_acc < 0x10000)
|
||||
return;
|
||||
void SearchView::do_timers() {
|
||||
if (timing_div >= 60) {
|
||||
// ~1Hz
|
||||
|
||||
bin_skip_acc -= 0x10000;
|
||||
timing_div = 0;
|
||||
|
||||
if (pixel_index < 240)
|
||||
spectrum_row[pixel_index++] = color;
|
||||
// Update scan rate
|
||||
text_rate.set(to_string_dec_uint(search_counter, 3));
|
||||
search_counter = 0;
|
||||
}
|
||||
|
||||
if (timing_div % 12 == 0) {
|
||||
// ~5Hz
|
||||
|
||||
// Update power levels
|
||||
text_mean.set(to_string_dec_uint(mean_power, 3));
|
||||
|
||||
vu_max.set_value(overall_power_max);
|
||||
vu_max.set_mark(mean_power + settings_.power_threshold);
|
||||
}
|
||||
|
||||
if (timing_div % 6 == 0) {
|
||||
// ~10Hz
|
||||
|
||||
// Update timing indicator
|
||||
if (locked) {
|
||||
progress_timers.set_max(RELEASE_DELAY);
|
||||
progress_timers.set_value(RELEASE_DELAY - release_timer);
|
||||
} else {
|
||||
progress_timers.set_max(DETECT_DELAY);
|
||||
progress_timers.set_value(detect_timer);
|
||||
}
|
||||
|
||||
// Increment timers
|
||||
if (detect_timer < DETECT_DELAY) detect_timer++;
|
||||
if (release_timer < RELEASE_DELAY) release_timer++;
|
||||
|
||||
if (locked) duration++;
|
||||
}
|
||||
|
||||
timing_div++;
|
||||
}
|
||||
|
||||
void SearchView::on_channel_spectrum(const ChannelSpectrum& spectrum) {
|
||||
@ -221,22 +319,14 @@ void SearchView::on_channel_spectrum(const ChannelSpectrum& spectrum) {
|
||||
baseband::spectrum_streaming_start();
|
||||
}
|
||||
|
||||
void SearchView::on_show() {
|
||||
baseband::spectrum_streaming_start();
|
||||
}
|
||||
|
||||
void SearchView::on_hide() {
|
||||
baseband::spectrum_streaming_stop();
|
||||
}
|
||||
|
||||
void SearchView::on_range_changed() {
|
||||
rf::Frequency slices_span, center_frequency;
|
||||
rf::Frequency slices_span;
|
||||
rf::Frequency center_frequency;
|
||||
int64_t offset;
|
||||
size_t slice;
|
||||
|
||||
f_min = field_frequency_min.value();
|
||||
f_max = field_frequency_max.value();
|
||||
search_span = abs(f_max - f_min);
|
||||
// TODO: enforce min < max?
|
||||
search_span = abs(settings_.freq_max - settings_.freq_min);
|
||||
|
||||
if (search_span > SEARCH_SLICE_WIDTH) {
|
||||
// ex: 100M~115M (15M span):
|
||||
@ -253,14 +343,14 @@ void SearchView::on_range_changed() {
|
||||
// offset = 0 + 2.5/2 = 1.25M
|
||||
offset = ((search_span - slices_span) / 2) + (SEARCH_SLICE_WIDTH / 2);
|
||||
// slice_start = 100M + 1.25M = 101.25M
|
||||
center_frequency = std::min(f_min, f_max) + offset;
|
||||
center_frequency = std::min(settings_.freq_min, settings_.freq_max) + offset;
|
||||
|
||||
for (slice = 0; slice < slices_nb; slice++) {
|
||||
slices[slice].center_frequency = center_frequency;
|
||||
center_frequency += SEARCH_SLICE_WIDTH;
|
||||
}
|
||||
} else {
|
||||
slices[0].center_frequency = (f_max + f_min) / 2;
|
||||
slices[0].center_frequency = (settings_.freq_max + settings_.freq_min) / 2;
|
||||
receiver_model.set_target_frequency(slices[0].center_frequency);
|
||||
|
||||
slices_nb = 1;
|
||||
@ -272,139 +362,16 @@ void SearchView::on_range_changed() {
|
||||
slice_counter = 0;
|
||||
}
|
||||
|
||||
void SearchView::on_lna_changed(int32_t v_db) {
|
||||
receiver_model.set_lna(v_db);
|
||||
}
|
||||
void SearchView::add_spectrum_pixel(Color color) {
|
||||
// Is avoiding floats really necessary?
|
||||
bin_skip_acc += bin_skip_frac;
|
||||
if (bin_skip_acc < 0x10000)
|
||||
return;
|
||||
|
||||
void SearchView::on_vga_changed(int32_t v_db) {
|
||||
receiver_model.set_vga(v_db);
|
||||
}
|
||||
bin_skip_acc -= 0x10000;
|
||||
|
||||
void SearchView::do_timers() {
|
||||
if (timing_div >= 60) {
|
||||
// ~1Hz
|
||||
|
||||
timing_div = 0;
|
||||
|
||||
// Update scan rate
|
||||
text_rate.set(to_string_dec_uint(search_counter, 3));
|
||||
search_counter = 0;
|
||||
}
|
||||
|
||||
if (timing_div % 12 == 0) {
|
||||
// ~5Hz
|
||||
|
||||
// Update power levels
|
||||
text_mean.set(to_string_dec_uint(mean_power, 3));
|
||||
|
||||
vu_max.set_value(overall_power_max);
|
||||
vu_max.set_mark(mean_power + power_threshold);
|
||||
}
|
||||
|
||||
if (timing_div % 6 == 0) {
|
||||
// ~10Hz
|
||||
|
||||
// Update timing indicator
|
||||
if (locked) {
|
||||
progress_timers.set_max(RELEASE_DELAY);
|
||||
progress_timers.set_value(RELEASE_DELAY - release_timer);
|
||||
} else {
|
||||
progress_timers.set_max(DETECT_DELAY);
|
||||
progress_timers.set_value(detect_timer);
|
||||
}
|
||||
|
||||
// Increment timers
|
||||
if (detect_timer < DETECT_DELAY) detect_timer++;
|
||||
if (release_timer < RELEASE_DELAY) release_timer++;
|
||||
|
||||
if (locked) duration++;
|
||||
}
|
||||
|
||||
timing_div++;
|
||||
}
|
||||
|
||||
SearchView::SearchView(
|
||||
NavigationView& nav)
|
||||
: nav_(nav) {
|
||||
baseband::run_image(portapack::spi_flash::image_tag_wideband_spectrum);
|
||||
|
||||
add_children({&labels,
|
||||
&field_frequency_min,
|
||||
&field_frequency_max,
|
||||
&field_lna,
|
||||
&field_vga,
|
||||
&field_threshold,
|
||||
&text_mean,
|
||||
&text_slices,
|
||||
&text_rate,
|
||||
&text_infos,
|
||||
&vu_max,
|
||||
&progress_timers,
|
||||
&check_snap,
|
||||
&options_snap,
|
||||
&big_display,
|
||||
&recent_entries_view});
|
||||
|
||||
baseband::set_spectrum(SEARCH_SLICE_WIDTH, 31);
|
||||
|
||||
recent_entries_view.set_parent_rect({0, 28 * 8, 240, 12 * 8});
|
||||
recent_entries_view.on_select = [this, &nav](const SearchRecentEntry& entry) {
|
||||
nav.push<FrequencyKeypadView>(entry.frequency);
|
||||
};
|
||||
|
||||
text_mean.set_style(&Styles::grey);
|
||||
text_slices.set_style(&Styles::grey);
|
||||
text_rate.set_style(&Styles::grey);
|
||||
progress_timers.set_style(&Styles::grey);
|
||||
big_display.set_style(&Styles::grey);
|
||||
|
||||
check_snap.set_value(true);
|
||||
options_snap.set_selected_index(1); // 12.5kHz
|
||||
|
||||
field_threshold.set_value(80);
|
||||
field_threshold.on_change = [this](int32_t value) {
|
||||
power_threshold = value;
|
||||
};
|
||||
|
||||
field_frequency_min.set_value(receiver_model.target_frequency() - 1000000);
|
||||
field_frequency_min.set_step(100000);
|
||||
field_frequency_min.on_change = [this](rf::Frequency) {
|
||||
this->on_range_changed();
|
||||
};
|
||||
field_frequency_min.on_edit = [this, &nav]() {
|
||||
auto new_view = nav.push<FrequencyKeypadView>(receiver_model.target_frequency() - 1000000);
|
||||
new_view->on_changed = [this](rf::Frequency f) {
|
||||
this->field_frequency_min.set_value(f);
|
||||
};
|
||||
};
|
||||
|
||||
field_frequency_max.set_value(receiver_model.target_frequency() + 1000000);
|
||||
field_frequency_max.set_step(100000);
|
||||
field_frequency_max.on_change = [this](rf::Frequency) {
|
||||
this->on_range_changed();
|
||||
};
|
||||
field_frequency_max.on_edit = [this, &nav]() {
|
||||
auto new_view = nav.push<FrequencyKeypadView>(receiver_model.target_frequency() + 1000000);
|
||||
new_view->on_changed = [this](rf::Frequency f) {
|
||||
this->field_frequency_max.set_value(f);
|
||||
};
|
||||
};
|
||||
|
||||
field_lna.set_value(receiver_model.lna());
|
||||
field_lna.on_change = [this](int32_t v) {
|
||||
this->on_lna_changed(v);
|
||||
};
|
||||
|
||||
field_vga.set_value(receiver_model.vga());
|
||||
field_vga.on_change = [this](int32_t v_db) {
|
||||
this->on_vga_changed(v_db);
|
||||
};
|
||||
|
||||
progress_timers.set_max(DETECT_DELAY);
|
||||
|
||||
on_range_changed();
|
||||
|
||||
receiver_model.enable();
|
||||
if (pixel_index < screen_width)
|
||||
spectrum_row[pixel_index++] = color;
|
||||
}
|
||||
|
||||
} /* namespace ui */
|
||||
|
@ -20,14 +20,13 @@
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "app_settings.hpp"
|
||||
#include "receiver_model.hpp"
|
||||
#include "recent_entries.hpp"
|
||||
#include "radio_state.hpp"
|
||||
|
||||
#include "spectrum_color_lut.hpp"
|
||||
|
||||
#include "ui_receiver.hpp"
|
||||
#include "ui_styles.hpp"
|
||||
#include "recent_entries.hpp"
|
||||
|
||||
namespace ui {
|
||||
|
||||
@ -95,6 +94,26 @@ class SearchView : public View {
|
||||
SEARCH_SLICE_WIDTH /* sampling rate */,
|
||||
ReceiverModel::Mode::SpectrumAnalysis};
|
||||
|
||||
// Settings
|
||||
struct SearchSettings {
|
||||
uint32_t power_threshold = 80;
|
||||
rf::Frequency freq_min = 100'000'000;
|
||||
rf::Frequency freq_max = 400'000'000;
|
||||
bool snap_search = true;
|
||||
uint32_t snap_step = 12'500;
|
||||
};
|
||||
SearchSettings settings_{};
|
||||
app_settings::SettingsManager app_settings_{
|
||||
"rx_search"sv,
|
||||
app_settings::Mode::RX,
|
||||
{
|
||||
{"power_threshold"sv, &settings_.power_threshold},
|
||||
{"freq_min"sv, &settings_.freq_min},
|
||||
{"freq_max"sv, &settings_.freq_max},
|
||||
{"snap_search"sv, &settings_.snap_search},
|
||||
{"snap_step"sv, &settings_.snap_step},
|
||||
}};
|
||||
|
||||
struct slice_t {
|
||||
rf::Frequency center_frequency;
|
||||
uint8_t max_power;
|
||||
@ -103,33 +122,36 @@ class SearchView : public View {
|
||||
int16_t index;
|
||||
} slices[32];
|
||||
|
||||
uint32_t bin_skip_acc{0}, bin_skip_frac{};
|
||||
uint32_t pixel_index{0};
|
||||
std::array<Color, 240> spectrum_row = {0};
|
||||
ChannelSpectrumFIFO* fifo{nullptr};
|
||||
rf::Frequency f_min{0}, f_max{0};
|
||||
uint8_t detect_timer{0}, release_timer{0}, timing_div{0};
|
||||
uint8_t overall_power_max{0};
|
||||
uint32_t mean_power{0}, mean_acc{0};
|
||||
uint32_t duration{0};
|
||||
uint32_t power_threshold{80}; // Todo: Put this in persistent / settings
|
||||
rf::Frequency slice_start{0};
|
||||
uint8_t slices_nb{0};
|
||||
uint8_t slice_counter{0};
|
||||
int16_t last_bin{0};
|
||||
uint32_t last_slice{0};
|
||||
Coord last_tick_pos{0};
|
||||
rf::Frequency search_span{0}, resolved_frequency{0};
|
||||
uint16_t locked_bin{0};
|
||||
uint8_t search_counter{0};
|
||||
bool locked{false};
|
||||
uint32_t bin_skip_acc = 0;
|
||||
uint32_t bin_skip_frac = 0;
|
||||
uint32_t pixel_index = 0;
|
||||
std::array<Color, 240> spectrum_row{};
|
||||
ChannelSpectrumFIFO* fifo = nullptr;
|
||||
|
||||
uint8_t detect_timer = 0;
|
||||
uint8_t release_timer = 0;
|
||||
uint8_t timing_div = 0;
|
||||
uint8_t overall_power_max{0};
|
||||
uint32_t mean_power = 0;
|
||||
uint32_t mean_acc = 0;
|
||||
uint32_t duration = 0;
|
||||
|
||||
rf::Frequency slice_start = 0;
|
||||
uint8_t slices_nb = 0;
|
||||
uint8_t slice_counter = 0;
|
||||
int16_t last_bin = 0;
|
||||
uint32_t last_slice = 0;
|
||||
Coord last_tick_pos = 0;
|
||||
rf::Frequency search_span = 0;
|
||||
rf::Frequency resolved_frequency = 0;
|
||||
uint16_t locked_bin = 0;
|
||||
uint8_t search_counter = 0;
|
||||
bool locked = false;
|
||||
|
||||
void do_detection();
|
||||
void do_timers();
|
||||
void on_channel_spectrum(const ChannelSpectrum& spectrum);
|
||||
void on_range_changed();
|
||||
void do_detection();
|
||||
void on_lna_changed(int32_t v_db);
|
||||
void on_vga_changed(int32_t v_db);
|
||||
void do_timers();
|
||||
void add_spectrum_pixel(Color color);
|
||||
|
||||
const RecentEntriesColumns columns{{{"Frequency", 9},
|
||||
@ -150,6 +172,7 @@ class SearchView : public View {
|
||||
{1 * 8, 1 * 16}};
|
||||
FrequencyField field_frequency_max{
|
||||
{11 * 8, 1 * 16}};
|
||||
|
||||
LNAGainField field_lna{
|
||||
{22 * 8, 1 * 16}};
|
||||
VGAGainField field_vga{
|
||||
@ -191,10 +214,10 @@ class SearchView : public View {
|
||||
{17 * 8, 15 * 8}, // Position
|
||||
7, // Length
|
||||
{ // Options
|
||||
{"25kHz ", 25000},
|
||||
{"12.5kHz", 12500},
|
||||
{"8.33kHz", 8333},
|
||||
{"2.5kHz", 2500},
|
||||
{"25kHz ", 25'000},
|
||||
{"12.5kHz", 12'500},
|
||||
{"8.33kHz", 8'333},
|
||||
{"2.5kHz", 2'500},
|
||||
{"500Hz", 500}}};
|
||||
|
||||
BigFrequency big_display{
|
||||
@ -207,6 +230,7 @@ class SearchView : public View {
|
||||
const auto message = *reinterpret_cast<const ChannelSpectrumConfigMessage*>(p);
|
||||
this->fifo = message.fifo;
|
||||
}};
|
||||
|
||||
MessageHandlerRegistration message_handler_frame_sync{
|
||||
Message::ID::DisplayFrameSync,
|
||||
[this](const Message* const) {
|
||||
|
@ -52,6 +52,15 @@ struct NoOp {
|
||||
* adequate lifetime of the referenced instances.
|
||||
*/
|
||||
|
||||
template <typename T, typename Fn = NoOp>
|
||||
void bind(Checkbox& check, T& value, Fn fn = Fn{}) {
|
||||
check.set_value(value);
|
||||
check.on_select = [&value, fn](Checkbox&, bool b) {
|
||||
value = b;
|
||||
fn(value);
|
||||
};
|
||||
}
|
||||
|
||||
template <typename T, typename Fn = NoOp>
|
||||
void bind(NumberField& field, T& value, Fn fn = Fn{}) {
|
||||
field.set_value(value);
|
||||
|
@ -28,6 +28,7 @@
|
||||
#include <cstddef>
|
||||
#include <algorithm>
|
||||
|
||||
#include "irq_controls.hpp"
|
||||
#include "string_format.hpp"
|
||||
|
||||
using namespace portapack;
|
||||
@ -1730,9 +1731,15 @@ bool TextEdit::on_key(const KeyEvent key) {
|
||||
cursor_pos_--;
|
||||
else if (key == KeyEvent::Right && cursor_pos_ < text_.length())
|
||||
cursor_pos_++;
|
||||
else if (key == KeyEvent::Select)
|
||||
else if (key == KeyEvent::Select) {
|
||||
if (key_is_long_pressed(key)) {
|
||||
// Delete text to the cursor.
|
||||
text_ = text_.substr(cursor_pos_);
|
||||
set_cursor(0);
|
||||
} else {
|
||||
insert_mode_ = !insert_mode_;
|
||||
else
|
||||
}
|
||||
} else
|
||||
return false;
|
||||
|
||||
set_dirty();
|
||||
@ -1760,6 +1767,19 @@ bool TextEdit::on_touch(const TouchEvent event) {
|
||||
return true;
|
||||
}
|
||||
|
||||
void TextEdit::on_focus() {
|
||||
// Enable long press on "Select".
|
||||
SwitchesState config;
|
||||
config[toUType(Switch::Sel)] = true;
|
||||
set_switches_long_press_config(config);
|
||||
}
|
||||
|
||||
void TextEdit::on_blur() {
|
||||
// Reset long press.
|
||||
SwitchesState config{};
|
||||
set_switches_long_press_config(config);
|
||||
}
|
||||
|
||||
/* TextField *************************************************************/
|
||||
|
||||
TextField::TextField(Rect parent_rect, std::string text)
|
||||
|
@ -689,6 +689,9 @@ class TextEdit : public Widget {
|
||||
bool on_encoder(const EncoderEvent delta) override;
|
||||
bool on_touch(const TouchEvent event) override;
|
||||
|
||||
void on_focus() override;
|
||||
void on_blur() override;
|
||||
|
||||
protected:
|
||||
std::string& text_;
|
||||
size_t max_length_;
|
||||
|
Loading…
Reference in New Issue
Block a user