mirror of
https://github.com/eried/portapack-mayhem.git
synced 2025-02-02 09:35:20 -05:00
Add Binder helper methods (#1465)
* Add binder, fix focus on remote btn delete * Use binder for freqman edit
This commit is contained in:
parent
fb00bfac3f
commit
951890eaff
@ -23,6 +23,7 @@
|
|||||||
|
|
||||||
#include "ui_freqman.hpp"
|
#include "ui_freqman.hpp"
|
||||||
|
|
||||||
|
#include "binder.hpp"
|
||||||
#include "event_m0.hpp"
|
#include "event_m0.hpp"
|
||||||
#include "portapack.hpp"
|
#include "portapack.hpp"
|
||||||
#include "rtc_time.hpp"
|
#include "rtc_time.hpp"
|
||||||
@ -376,69 +377,26 @@ FrequencyEditView::FrequencyEditView(
|
|||||||
field_step.options().insert(
|
field_step.options().insert(
|
||||||
field_step.options().begin(), {"None", -1});
|
field_step.options().begin(), {"None", -1});
|
||||||
|
|
||||||
field_type.set_by_value((int32_t)entry_.type);
|
bind(field_type, entry_.type, [this](auto) {
|
||||||
field_type.on_change = [this](size_t, auto value) {
|
|
||||||
entry_.type = static_cast<freqman_type>(value);
|
|
||||||
refresh_ui();
|
refresh_ui();
|
||||||
};
|
});
|
||||||
|
|
||||||
// TODO: this pattern should be able to be wrapped up.
|
bind(field_freq_a, entry_.frequency_a, nav, [this](auto) {
|
||||||
field_freq_a.set_value(entry_.frequency_a);
|
|
||||||
field_freq_a.on_change = [this](rf::Frequency f) {
|
|
||||||
entry_.frequency_a = f;
|
|
||||||
refresh_ui();
|
refresh_ui();
|
||||||
};
|
});
|
||||||
field_freq_a.on_edit = [this]() {
|
|
||||||
auto freq_view = nav_.push<FrequencyKeypadView>(field_freq_a.value());
|
|
||||||
freq_view->on_changed = [this](rf::Frequency f) {
|
|
||||||
field_freq_a.set_value(f);
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
field_freq_b.set_value(entry_.frequency_b);
|
bind(field_freq_b, entry_.frequency_b, nav, [this](auto) {
|
||||||
field_freq_b.on_change = [this](rf::Frequency f) {
|
|
||||||
entry_.frequency_b = f;
|
|
||||||
refresh_ui();
|
refresh_ui();
|
||||||
};
|
});
|
||||||
field_freq_b.on_edit = [this]() {
|
|
||||||
auto freq_view = nav_.push<FrequencyKeypadView>(field_freq_b.value());
|
|
||||||
freq_view->on_changed = [this](rf::Frequency f) {
|
|
||||||
field_freq_b.set_value(f);
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
field_modulation.set_by_value((int32_t)entry_.modulation);
|
bind(field_modulation, entry_.modulation, [this](auto) {
|
||||||
field_modulation.on_change = [this](size_t, auto value) {
|
|
||||||
entry_.modulation = static_cast<freqman_index_t>(value);
|
|
||||||
populate_bandwidth_options();
|
populate_bandwidth_options();
|
||||||
};
|
});
|
||||||
|
|
||||||
field_bandwidth.set_by_value((int32_t)entry_.bandwidth);
|
bind(field_bandwidth, entry_.bandwidth);
|
||||||
field_bandwidth.on_change = [this](size_t, auto value) {
|
bind(field_step, entry_.step);
|
||||||
entry_.bandwidth = static_cast<freqman_index_t>(value);
|
bind(field_tone, entry_.tone);
|
||||||
};
|
bind(field_description, entry_.description, nav_);
|
||||||
|
|
||||||
field_step.set_by_value((int32_t)entry_.step);
|
|
||||||
field_step.on_change = [this](size_t, auto value) {
|
|
||||||
entry_.step = static_cast<freqman_index_t>(value);
|
|
||||||
};
|
|
||||||
|
|
||||||
field_tone.set_by_value((int32_t)entry_.tone);
|
|
||||||
field_tone.on_change = [this](size_t, auto value) {
|
|
||||||
entry_.tone = static_cast<freqman_index_t>(value);
|
|
||||||
};
|
|
||||||
|
|
||||||
field_description.set_text(entry_.description);
|
|
||||||
field_description.on_change = [this](TextField& tf) {
|
|
||||||
entry_.description = tf.get_text();
|
|
||||||
};
|
|
||||||
field_description.on_select = [this](TextField& tf) {
|
|
||||||
temp_buffer_ = tf.get_text();
|
|
||||||
text_prompt(nav_, temp_buffer_, FreqManBaseView::desc_edit_max,
|
|
||||||
[this, &tf](std::string& new_desc) {
|
|
||||||
tf.set_text(new_desc);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
button_save.on_select = [this](Button&) {
|
button_save.on_select = [this](Button&) {
|
||||||
if (on_save)
|
if (on_save)
|
||||||
|
@ -21,6 +21,7 @@
|
|||||||
|
|
||||||
#include "ui_remote.hpp"
|
#include "ui_remote.hpp"
|
||||||
|
|
||||||
|
#include "binder.hpp"
|
||||||
#include "convert.hpp"
|
#include "convert.hpp"
|
||||||
#include "file_reader.hpp"
|
#include "file_reader.hpp"
|
||||||
#include "io_convert.hpp"
|
#include "io_convert.hpp"
|
||||||
@ -239,19 +240,9 @@ RemoteEntryEditView::RemoteEntryEditView(
|
|||||||
&button_done,
|
&button_done,
|
||||||
});
|
});
|
||||||
|
|
||||||
// TODO: It's time to make field bindings and clean this mess up.
|
bind(field_name, entry_.name, nav, [this](auto& v) {
|
||||||
field_name.on_change = [this](TextField& tf) {
|
button_preview.set_text(v);
|
||||||
entry_.name = tf.get_text();
|
});
|
||||||
button_preview.set_text(entry_.name);
|
|
||||||
};
|
|
||||||
field_name.on_select = [this, &nav](TextField& tf) {
|
|
||||||
temp_buffer_ = tf.get_text();
|
|
||||||
text_prompt(nav, temp_buffer_, text_edit_max,
|
|
||||||
[this, &tf](std::string& str) {
|
|
||||||
tf.set_text(str);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
field_name.set_text(entry_.name);
|
|
||||||
|
|
||||||
field_path.on_select = [this, &nav](TextField&) {
|
field_path.on_select = [this, &nav](TextField&) {
|
||||||
auto open_view = nav.push<FileLoadView>(".C*");
|
auto open_view = nav.push<FileLoadView>(".C*");
|
||||||
@ -262,31 +253,16 @@ RemoteEntryEditView::RemoteEntryEditView(
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
field_freq.on_edit = [this, &nav]() {
|
bind(field_freq, entry_.metadata.center_frequency, nav);
|
||||||
auto freq_view = nav.push<FrequencyKeypadView>(entry_.metadata.center_frequency);
|
bind(field_icon_index, entry_.icon, [this](auto v) {
|
||||||
freq_view->on_changed = [this](rf::Frequency f) {
|
|
||||||
entry_.metadata.center_frequency = f;
|
|
||||||
field_freq.set_value(f);
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
field_icon_index.on_change = [this](int32_t v) {
|
|
||||||
entry_.icon = v;
|
|
||||||
button_preview.set_bitmap(RemoteIcons::get(v));
|
button_preview.set_bitmap(RemoteIcons::get(v));
|
||||||
};
|
});
|
||||||
field_icon_index.set_value(entry.icon);
|
bind(field_fg_color_index, entry_.fg_color, [this](auto v) {
|
||||||
|
|
||||||
field_fg_color_index.on_change = [this](int32_t v) {
|
|
||||||
entry_.fg_color = v;
|
|
||||||
button_preview.set_color(RemoteColors::get(v));
|
button_preview.set_color(RemoteColors::get(v));
|
||||||
};
|
});
|
||||||
field_fg_color_index.set_value(entry_.fg_color);
|
bind(field_bg_color_index, entry_.bg_color, [this](auto) {
|
||||||
|
|
||||||
field_bg_color_index.on_change = [this](int32_t v) {
|
|
||||||
entry_.bg_color = v;
|
|
||||||
button_preview.set_dirty();
|
button_preview.set_dirty();
|
||||||
};
|
});
|
||||||
field_bg_color_index.set_value(entry_.bg_color);
|
|
||||||
|
|
||||||
button_delete.on_select = [this, &nav]() {
|
button_delete.on_select = [this, &nav]() {
|
||||||
nav.display_modal(
|
nav.display_modal(
|
||||||
@ -485,6 +461,7 @@ void RemoteView::edit_button(RemoteButton& btn) {
|
|||||||
nav_.set_on_pop([this]() {
|
nav_.set_on_pop([this]() {
|
||||||
refresh_ui();
|
refresh_ui();
|
||||||
set_needs_save();
|
set_needs_save();
|
||||||
|
focus(); // Need to refocus after refreshing the buttons.
|
||||||
});
|
});
|
||||||
|
|
||||||
edit_view->on_delete = [this](RemoteEntryModel& to_delete) {
|
edit_view->on_delete = [this](RemoteEntryModel& to_delete) {
|
||||||
|
@ -195,7 +195,6 @@ class RemoteEntryEditView : public View {
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
RemoteEntryModel& entry_;
|
RemoteEntryModel& entry_;
|
||||||
std::string temp_buffer_{};
|
|
||||||
|
|
||||||
void refresh_ui();
|
void refresh_ui();
|
||||||
void load_path(std::filesystem::path&& path);
|
void load_path(std::filesystem::path&& path);
|
||||||
|
108
firmware/application/binder.hpp
Normal file
108
firmware/application/binder.hpp
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2023 Kyle Reed
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __BINDER_H__
|
||||||
|
#define __BINDER_H__
|
||||||
|
|
||||||
|
#include "ui.hpp"
|
||||||
|
#include "ui_navigation.hpp"
|
||||||
|
#include "ui_widget.hpp"
|
||||||
|
|
||||||
|
namespace ui {
|
||||||
|
|
||||||
|
/* Default no-op binding callback. */
|
||||||
|
struct NoOp {
|
||||||
|
NoOp() {}
|
||||||
|
template <typename T>
|
||||||
|
void operator()(T) const {}
|
||||||
|
};
|
||||||
|
|
||||||
|
/* The gist of the bind functions is to bind a control
|
||||||
|
* to a value.
|
||||||
|
* bind(control, value)
|
||||||
|
*
|
||||||
|
* An optional callback can be specified which will be called
|
||||||
|
* when the control's value is changed.
|
||||||
|
* bind(control, value, [](auto new_val) { ... });
|
||||||
|
*
|
||||||
|
* Some controls need a reference to the NavigationView so
|
||||||
|
* edit dialogs (like text_prompt) can be launched.
|
||||||
|
* bind(control, value, nav, [](auto new_val) { ... });
|
||||||
|
*
|
||||||
|
* Be careful with lifetime of captured objects. Most things
|
||||||
|
* are captured by reference so the caller will need to ensure
|
||||||
|
* adequate lifetime of the referenced instances.
|
||||||
|
*/
|
||||||
|
|
||||||
|
template <typename T, typename Fn = NoOp>
|
||||||
|
void bind(NumberField& field, T& value, Fn fn = Fn{}) {
|
||||||
|
field.set_value(value);
|
||||||
|
field.on_change = [&value, fn](int32_t v) {
|
||||||
|
value = v;
|
||||||
|
fn(value);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, typename Fn = NoOp>
|
||||||
|
void bind(OptionsField& field, T& value, Fn fn = Fn{}) {
|
||||||
|
field.set_by_value(static_cast<int32_t>(value));
|
||||||
|
field.on_change = [&value, fn](size_t, auto v) {
|
||||||
|
value = static_cast<T>(v);
|
||||||
|
fn(value);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, typename Fn = NoOp>
|
||||||
|
void bind(FrequencyField& field, T& value, NavigationView& nav, Fn fn = Fn{}) {
|
||||||
|
field.set_value(value);
|
||||||
|
field.on_change = [&value, fn](rf::Frequency f) {
|
||||||
|
value = f;
|
||||||
|
fn(value);
|
||||||
|
};
|
||||||
|
field.on_edit = [&field, &value, &nav]() {
|
||||||
|
auto freq_view = nav.push<FrequencyKeypadView>(value);
|
||||||
|
freq_view->on_changed = [&field](rf::Frequency f) {
|
||||||
|
field.set_value(f);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, typename Fn = NoOp>
|
||||||
|
void bind(TextField& field, T& value, NavigationView& nav, Fn fn = Fn{}) {
|
||||||
|
field.set_text(value);
|
||||||
|
field.on_change = [&value, fn](TextField& tf) {
|
||||||
|
value = tf.get_text();
|
||||||
|
fn(value);
|
||||||
|
};
|
||||||
|
// text_prompt needs a mutable working buffer.
|
||||||
|
// Capture a new string and make the lambda mutable so it can be modified.
|
||||||
|
field.on_select = [&nav, buf = std::string{}](TextField& tf) mutable {
|
||||||
|
buf = tf.get_text();
|
||||||
|
text_prompt(nav, buf, /*max_length*/ 255,
|
||||||
|
[&tf](std::string& str) {
|
||||||
|
tf.set_text(str);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace ui
|
||||||
|
|
||||||
|
#endif /*__BINDER_H__*/
|
Loading…
x
Reference in New Issue
Block a user