Add Binder helper methods (#1465)

* Add binder, fix focus on remote btn delete
* Use binder for freqman edit
This commit is contained in:
Kyle Reed 2023-09-29 09:55:27 -07:00 committed by GitHub
parent fb00bfac3f
commit 951890eaff
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 133 additions and 91 deletions

View File

@ -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)

View File

@ -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) {

View File

@ -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);

View 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__*/