mirror of
https://github.com/eried/portapack-mayhem.git
synced 2025-06-25 15:20:31 -04:00
SymField rewrite (#1444)
* First WIP symfield * Cleanup widget code * Rebase and format * Fix 'to_integer' bug, fix siggen UI. * to_string_hex fix, unit tests for string code * Pass instance in callback * Fix on_change callbacks * Fix keyfob (not active) * to_byte_array, coaster tx cleanup * Add to_byte_array tests * Changes in ui_numbers * Fix ui_encoders * Format * Fix modemsetup view's symfields * Remove header * Format
This commit is contained in:
parent
70e0f2913f
commit
af424aa5f8
30 changed files with 607 additions and 371 deletions
|
@ -1813,7 +1813,7 @@ void NumberField::set_value(int32_t new_value, bool trigger_change) {
|
|||
new_value = range.second + new_value + 1;
|
||||
}
|
||||
|
||||
new_value = clip_value(new_value);
|
||||
new_value = clip(new_value, range.first, range.second);
|
||||
|
||||
if (new_value != value()) {
|
||||
value_ = new_value;
|
||||
|
@ -1868,169 +1868,182 @@ bool NumberField::on_touch(const TouchEvent event) {
|
|||
return true;
|
||||
}
|
||||
|
||||
int32_t NumberField::clip_value(int32_t value) {
|
||||
if (value > range.second) {
|
||||
value = range.second;
|
||||
}
|
||||
if (value < range.first) {
|
||||
value = range.first;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
/* SymField **************************************************************/
|
||||
|
||||
SymField::SymField(
|
||||
Point parent_pos,
|
||||
size_t length,
|
||||
symfield_type type)
|
||||
: Widget{{parent_pos, {static_cast<ui::Dim>(8 * length), 16}}},
|
||||
length_{length},
|
||||
type_{type} {
|
||||
uint32_t c;
|
||||
Type type,
|
||||
bool explicit_edits)
|
||||
: Widget{{parent_pos, {char_width * (int)length, 16}}},
|
||||
type_{type},
|
||||
explicit_edits_{explicit_edits} {
|
||||
if (length == 0)
|
||||
length = 1;
|
||||
|
||||
// Auto-init
|
||||
if (type == SYMFIELD_OCT) {
|
||||
for (c = 0; c < length; c++)
|
||||
set_symbol_list(c, "01234567");
|
||||
} else if (type == SYMFIELD_DEC) {
|
||||
for (c = 0; c < length; c++)
|
||||
set_symbol_list(c, "0123456789");
|
||||
} else if (type == SYMFIELD_HEX) {
|
||||
for (c = 0; c < length; c++)
|
||||
set_symbol_list(c, "0123456789ABCDEF");
|
||||
} else if (type == SYMFIELD_ALPHANUM) {
|
||||
for (c = 0; c < length; c++)
|
||||
set_symbol_list(c, " 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ");
|
||||
selected_ = length - 1;
|
||||
value_.resize(length);
|
||||
|
||||
switch (type) {
|
||||
case Type::Oct:
|
||||
set_symbol_list("01234567");
|
||||
break;
|
||||
|
||||
case Type::Dec:
|
||||
set_symbol_list("0123456789");
|
||||
break;
|
||||
|
||||
case Type::Hex:
|
||||
set_symbol_list("0123456789ABCDEF");
|
||||
break;
|
||||
|
||||
case Type::Alpha:
|
||||
set_symbol_list(" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ");
|
||||
break;
|
||||
|
||||
default:
|
||||
set_symbol_list("01");
|
||||
break;
|
||||
}
|
||||
|
||||
set_focusable(true);
|
||||
}
|
||||
|
||||
uint16_t SymField::concatenate_4_octal_u16() {
|
||||
// input array 4 octal digits , return 12 bits, same order, trippled A4-A2-A1|B4-B2-B1|C4-C2-C1|D4-D2-D1
|
||||
uint32_t mul = 1;
|
||||
uint16_t v = 0;
|
||||
|
||||
if (type_ == SYMFIELD_OCT) {
|
||||
for (uint32_t c = 0; c < length_; c++) {
|
||||
v += values_[(length_ - 1 - c)] * mul;
|
||||
mul *= 8; // shift 3 bits to the right every new octal squawk digit
|
||||
}
|
||||
return v;
|
||||
} else
|
||||
return 0;
|
||||
SymField::SymField(
|
||||
Point parent_pos,
|
||||
size_t length,
|
||||
std::string symbol_list,
|
||||
bool explicit_edits)
|
||||
: SymField{parent_pos, length, Type::Custom, explicit_edits} {
|
||||
set_symbol_list(std::move(symbol_list));
|
||||
}
|
||||
|
||||
uint32_t SymField::value_dec_u32() {
|
||||
uint32_t mul = 1, v = 0;
|
||||
|
||||
if (type_ == SYMFIELD_DEC) {
|
||||
for (uint32_t c = 0; c < length_; c++) {
|
||||
v += values_[(length_ - 1 - c)] * mul;
|
||||
mul *= 10;
|
||||
}
|
||||
return v;
|
||||
} else
|
||||
char SymField::get_symbol(size_t index) const {
|
||||
if (index >= value_.length())
|
||||
return 0;
|
||||
|
||||
return value_[index];
|
||||
}
|
||||
|
||||
uint64_t SymField::value_hex_u64() {
|
||||
void SymField::set_symbol(size_t index, char symbol) {
|
||||
if (index >= value_.length())
|
||||
return;
|
||||
|
||||
set_symbol_internal(index, ensure_valid(symbol));
|
||||
}
|
||||
|
||||
size_t SymField::get_offset(size_t index) const {
|
||||
if (index >= value_.length())
|
||||
return 0;
|
||||
|
||||
// NB: Linear search - symbol lists are small.
|
||||
return symbols_.find(value_[index]);
|
||||
}
|
||||
|
||||
void SymField::set_offset(size_t index, size_t offset) {
|
||||
if (index >= value_.length() || offset >= symbols_.length())
|
||||
return;
|
||||
|
||||
set_symbol_internal(index, symbols_[offset]);
|
||||
}
|
||||
|
||||
void SymField::set_symbol_list(std::string symbol_list) {
|
||||
if (symbol_list.length() == 0)
|
||||
return;
|
||||
|
||||
symbols_ = std::move(symbol_list);
|
||||
ensure_all_symbols();
|
||||
}
|
||||
|
||||
void SymField::set_value(uint64_t value) {
|
||||
auto v = value;
|
||||
uint8_t radix = get_radix();
|
||||
|
||||
for (int i = value_.length() - 1; i >= 0; --i) {
|
||||
uint8_t temp = v % radix;
|
||||
value_[i] = uint_to_char(temp, radix);
|
||||
v /= radix;
|
||||
}
|
||||
}
|
||||
|
||||
void SymField::set_value(std::string_view value) {
|
||||
// Is new value too long?
|
||||
// TODO: Truncate instead? Which end?
|
||||
if (value.length() > value_.length())
|
||||
return;
|
||||
|
||||
// Right-align string in field.
|
||||
auto left_padding = value_.length() - value.length();
|
||||
value_ = std::string(static_cast<size_t>(left_padding), '\0') + std::string{value};
|
||||
ensure_all_symbols();
|
||||
}
|
||||
|
||||
uint64_t SymField::to_integer() const {
|
||||
uint64_t v = 0;
|
||||
uint64_t mul = 1;
|
||||
uint8_t radix = get_radix();
|
||||
|
||||
if (type_ != SYMFIELD_DEF) {
|
||||
for (uint32_t c = 0; c < length_; c++)
|
||||
v += (uint64_t)(values_[c]) << (4 * (length_ - 1 - c));
|
||||
return v;
|
||||
} else
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::string SymField::value_string() {
|
||||
std::string return_string{""};
|
||||
|
||||
if (type_ == SYMFIELD_ALPHANUM) {
|
||||
for (uint32_t c = 0; c < length_; c++) {
|
||||
return_string += symbol_list_[0][values_[c]];
|
||||
}
|
||||
for (int i = value_.length() - 1; i >= 0; --i) {
|
||||
auto temp = char_to_uint(value_[i], radix);
|
||||
v += temp * mul;
|
||||
mul *= radix;
|
||||
}
|
||||
|
||||
return return_string;
|
||||
return v;
|
||||
}
|
||||
|
||||
uint32_t SymField::get_sym(const uint32_t index) {
|
||||
if (index >= length_) return 0;
|
||||
|
||||
return values_[index];
|
||||
}
|
||||
|
||||
void SymField::set_sym(const uint32_t index, const uint32_t new_value) {
|
||||
if (index >= length_) return;
|
||||
|
||||
uint32_t clipped_value = clip_value(index, new_value);
|
||||
|
||||
if (clipped_value != values_[index]) {
|
||||
values_[index] = clipped_value;
|
||||
if (on_change) {
|
||||
on_change();
|
||||
}
|
||||
set_dirty();
|
||||
}
|
||||
}
|
||||
|
||||
void SymField::set_length(const uint32_t new_length) {
|
||||
if ((new_length <= 32) && (new_length != length_)) {
|
||||
prev_length_ = length_;
|
||||
length_ = new_length;
|
||||
|
||||
// Clip eventual garbage from previous shorter word
|
||||
for (size_t n = 0; n < length_; n++)
|
||||
set_sym(n, values_[n]);
|
||||
|
||||
erase_prev_ = true;
|
||||
set_dirty();
|
||||
}
|
||||
}
|
||||
|
||||
void SymField::set_symbol_list(const uint32_t index, const std::string symbol_list) {
|
||||
if (index >= length_) return;
|
||||
|
||||
symbol_list_[index] = symbol_list;
|
||||
|
||||
// Re-clip symbol's value
|
||||
set_sym(index, values_[index]);
|
||||
const std::string& SymField::to_string() const {
|
||||
return value_;
|
||||
}
|
||||
|
||||
void SymField::paint(Painter& painter) {
|
||||
Point pt_draw = screen_pos();
|
||||
Point p = screen_pos();
|
||||
|
||||
if (erase_prev_) {
|
||||
painter.fill_rectangle({pt_draw, {(int)prev_length_ * 8, 16}}, Color::black());
|
||||
erase_prev_ = false;
|
||||
}
|
||||
for (size_t n = 0; n < value_.length(); n++) {
|
||||
auto c = value_[n];
|
||||
MutableStyle paint_style{style()};
|
||||
|
||||
for (size_t n = 0; n < length_; n++) {
|
||||
const auto text = symbol_list_[n].substr(values_[n], 1);
|
||||
// Only highlight while focused.
|
||||
if (has_focus()) {
|
||||
if (explicit_edits_) {
|
||||
// Invert the whole field on focus if explicit edits is enabled.
|
||||
paint_style.invert();
|
||||
} else if (n == selected_) {
|
||||
// Otherwise only highlight the selected symbol.
|
||||
paint_style.invert();
|
||||
}
|
||||
|
||||
const auto paint_style = (has_focus() && (n == selected_)) ? style().invert() : style();
|
||||
if (editing_ && n == selected_) {
|
||||
// Use 'bg_blue' style to indicate in editing mode.
|
||||
paint_style.foreground = Color::white();
|
||||
paint_style.background = Color::blue();
|
||||
}
|
||||
}
|
||||
|
||||
painter.draw_string(
|
||||
pt_draw,
|
||||
paint_style,
|
||||
text);
|
||||
|
||||
pt_draw += {8, 0};
|
||||
painter.draw_char(p, paint_style, c);
|
||||
p += {8, 0};
|
||||
}
|
||||
}
|
||||
|
||||
bool SymField::on_key(const KeyEvent key) {
|
||||
bool SymField::on_key(KeyEvent key) {
|
||||
// If explicit edits are enabled, only Select is handled when not in edit mode.
|
||||
if (explicit_edits_ && !editing_) {
|
||||
switch (key) {
|
||||
case KeyEvent::Select:
|
||||
editing_ = true;
|
||||
set_dirty();
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
switch (key) {
|
||||
case KeyEvent::Select:
|
||||
if (on_select) {
|
||||
on_select(*this);
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
editing_ = !editing_;
|
||||
set_dirty();
|
||||
return true;
|
||||
|
||||
case KeyEvent::Left:
|
||||
if (selected_ > 0) {
|
||||
|
@ -2041,13 +2054,27 @@ bool SymField::on_key(const KeyEvent key) {
|
|||
break;
|
||||
|
||||
case KeyEvent::Right:
|
||||
if (selected_ < (length_ - 1)) {
|
||||
if (selected_ < (value_.length() - 1)) {
|
||||
selected_++;
|
||||
set_dirty();
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
|
||||
case KeyEvent::Up:
|
||||
if (editing_) {
|
||||
on_encoder(1);
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
|
||||
case KeyEvent::Down:
|
||||
if (editing_) {
|
||||
on_encoder(-1);
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -2055,29 +2082,66 @@ bool SymField::on_key(const KeyEvent key) {
|
|||
return false;
|
||||
}
|
||||
|
||||
bool SymField::on_encoder(const EncoderEvent delta) {
|
||||
int32_t new_value = (int)values_[selected_] + delta;
|
||||
bool SymField::on_encoder(EncoderEvent delta) {
|
||||
if (explicit_edits_ && !editing_)
|
||||
return false;
|
||||
|
||||
if (new_value >= 0)
|
||||
set_sym(selected_, values_[selected_] + delta);
|
||||
// TODO: Wrapping or carrying might be nice.
|
||||
int offset = get_offset(selected_) + delta;
|
||||
|
||||
offset = clip<int>(offset, 0, symbols_.length() - 1);
|
||||
set_offset(selected_, offset);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SymField::on_touch(const TouchEvent event) {
|
||||
if (event.type == TouchEvent::Type::Start) {
|
||||
bool SymField::on_touch(TouchEvent event) {
|
||||
if (event.type == TouchEvent::Type::Start)
|
||||
focus();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int32_t SymField::clip_value(const uint32_t index, const uint32_t value) {
|
||||
size_t symbol_count = symbol_list_[index].length() - 1;
|
||||
char SymField::ensure_valid(char symbol) const {
|
||||
// NB: Linear search - symbol lists are small.
|
||||
auto pos = symbols_.find(symbol);
|
||||
return pos != std::string::npos ? symbol : symbols_[0];
|
||||
}
|
||||
|
||||
if (value > symbol_count)
|
||||
return symbol_count;
|
||||
else
|
||||
return value;
|
||||
void SymField::ensure_all_symbols() {
|
||||
auto temp = value_;
|
||||
|
||||
for (auto& c : value_)
|
||||
c = ensure_valid(c);
|
||||
|
||||
if (temp != value_) {
|
||||
if (on_change)
|
||||
on_change(*this);
|
||||
set_dirty();
|
||||
}
|
||||
}
|
||||
|
||||
void SymField::set_symbol_internal(size_t index, char symbol) {
|
||||
if (value_[index] == symbol)
|
||||
return;
|
||||
|
||||
value_[index] = symbol;
|
||||
if (on_change)
|
||||
on_change(*this);
|
||||
set_dirty();
|
||||
}
|
||||
|
||||
uint8_t SymField::get_radix() const {
|
||||
switch (type_) {
|
||||
case Type::Oct:
|
||||
return 8;
|
||||
case Type::Dec:
|
||||
return 10;
|
||||
case Type::Hex:
|
||||
return 16;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Waveform **************************************************************/
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue