mirror of
https://github.com/portapack-mayhem/mayhem-firmware.git
synced 2025-08-14 17:17:42 +00:00
Fileman copy/paste support (#970)
* Add copy/paste UI instead of file save
This commit is contained in:
@@ -1535,6 +1535,137 @@ bool OptionsField::on_touch(const TouchEvent event) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/* TextField ***********************************************************/
|
||||
|
||||
TextField::TextField(
|
||||
std::string& str,
|
||||
size_t max_length,
|
||||
Point position,
|
||||
uint32_t length
|
||||
) : Widget{ { position, { 8 * static_cast<int>(length), 16 } } },
|
||||
text_{ str },
|
||||
max_length_{ std::max<size_t>(max_length, str.length()) },
|
||||
char_count_{ std::max<uint32_t>(length, 1) },
|
||||
cursor_pos_{ text_.length() },
|
||||
insert_mode_{ true }
|
||||
{
|
||||
set_focusable(true);
|
||||
}
|
||||
|
||||
const std::string& TextField::value() const {
|
||||
return text_;
|
||||
}
|
||||
|
||||
void TextField::set_cursor(uint32_t pos) {
|
||||
cursor_pos_ = std::min<size_t>(pos, text_.length());
|
||||
set_dirty();
|
||||
}
|
||||
|
||||
void TextField::set_insert_mode() {
|
||||
insert_mode_ = true;
|
||||
}
|
||||
|
||||
void TextField::set_overwrite_mode() {
|
||||
insert_mode_ = false;
|
||||
}
|
||||
|
||||
void TextField::char_add(char c) {
|
||||
// Don't add if inserting and at max_length and
|
||||
// don't overwrite if past the end of the text.
|
||||
if ((text_.length() >= max_length_ && insert_mode_) ||
|
||||
(cursor_pos_ >= text_.length() && !insert_mode_))
|
||||
return;
|
||||
|
||||
if (insert_mode_)
|
||||
text_.insert(cursor_pos_, 1, c);
|
||||
else
|
||||
text_[cursor_pos_] = c;
|
||||
|
||||
cursor_pos_++;
|
||||
set_dirty();
|
||||
}
|
||||
|
||||
void TextField::char_delete() {
|
||||
if (cursor_pos_ == 0)
|
||||
return;
|
||||
|
||||
cursor_pos_--;
|
||||
text_.erase(cursor_pos_, 1);
|
||||
set_dirty();
|
||||
}
|
||||
|
||||
void TextField::paint(Painter& painter) {
|
||||
constexpr int char_width = 8;
|
||||
|
||||
auto rect = screen_rect();
|
||||
auto text_style = has_focus() ? style().invert() : style();
|
||||
auto offset = 0;
|
||||
|
||||
// Does the string need to be shifted?
|
||||
if (cursor_pos_ >= char_count_)
|
||||
offset = cursor_pos_ - char_count_ + 1;
|
||||
|
||||
// Clear the control.
|
||||
painter.fill_rectangle(rect, text_style.background);
|
||||
|
||||
// Draw the text starting at the offset.
|
||||
for (uint32_t i = 0; i < char_count_ && i + offset < text_.length(); i++) {
|
||||
painter.draw_char(
|
||||
{ rect.location().x() + (static_cast<int>(i) * char_width), rect.location().y() },
|
||||
text_style,
|
||||
text_[i + offset]
|
||||
);
|
||||
}
|
||||
|
||||
// Determine cursor position on screen (either the cursor position or the last char).
|
||||
int32_t cursor_x = char_width * (offset > 0 ? char_count_ - 1 : cursor_pos_);
|
||||
Point cursor_point{ screen_pos().x() + cursor_x, screen_pos().y() };
|
||||
auto cursor_style = text_style.invert();
|
||||
|
||||
// Invert the cursor character when in overwrite mode.
|
||||
if (!insert_mode_ && (cursor_pos_) < text_.length())
|
||||
painter.draw_char(cursor_point, cursor_style, text_[cursor_pos_]);
|
||||
|
||||
// Draw the cursor.
|
||||
Rect cursor_box{ cursor_point, { char_width, 16 } };
|
||||
painter.draw_rectangle(cursor_box, cursor_style.background);
|
||||
}
|
||||
|
||||
bool TextField::on_key(const KeyEvent key) {
|
||||
if (key == KeyEvent::Left && cursor_pos_ > 0)
|
||||
cursor_pos_--;
|
||||
else if (key == KeyEvent::Right && cursor_pos_ < text_.length())
|
||||
cursor_pos_++;
|
||||
else if (key == KeyEvent::Select)
|
||||
insert_mode_ = !insert_mode_;
|
||||
else
|
||||
return false;
|
||||
|
||||
set_dirty();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TextField::on_encoder(const EncoderEvent delta) {
|
||||
int32_t new_pos = cursor_pos_ + delta;
|
||||
|
||||
// Let the encoder wrap around the ends of the text.
|
||||
if (new_pos < 0)
|
||||
new_pos = text_.length();
|
||||
else if (static_cast<size_t>(new_pos) > text_.length())
|
||||
new_pos = 0;
|
||||
|
||||
set_cursor(new_pos);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TextField::on_touch(const TouchEvent event) {
|
||||
if (event.type == TouchEvent::Type::Start)
|
||||
focus();
|
||||
|
||||
set_dirty();
|
||||
return true;
|
||||
}
|
||||
|
||||
/* NumberField ***********************************************************/
|
||||
|
||||
NumberField::NumberField(
|
||||
|
@@ -414,7 +414,6 @@ private:
|
||||
bool instant_exec_ { false };
|
||||
};
|
||||
|
||||
|
||||
class ButtonWithEncoder : public Widget {
|
||||
public:
|
||||
std::function<void(ButtonWithEncoder&)> on_select { };
|
||||
@@ -457,8 +456,6 @@ private:
|
||||
bool instant_exec_ { false };
|
||||
};
|
||||
|
||||
|
||||
|
||||
class NewButton : public Widget {
|
||||
public:
|
||||
std::function<void(void)> on_select { };
|
||||
@@ -610,6 +607,52 @@ private:
|
||||
size_t selected_index_ { 0 };
|
||||
};
|
||||
|
||||
// A TextField is bound to a string reference and allows the string
|
||||
// to be manipulated. The field itself does not provide the UI for
|
||||
// setting the value. It provides the UI of rendering the text,
|
||||
// a cursor, and an API to edit the string content.
|
||||
class TextField : public Widget {
|
||||
public:
|
||||
TextField(std::string& str, Point position, uint32_t length = 30)
|
||||
: TextField{str, 64, position, length} { }
|
||||
|
||||
// Str: the string containing the content to edit.
|
||||
// Max_length: max length the string is allowed to use.
|
||||
// Position: the top-left corner of the control.
|
||||
// Length: the number of characters to display.
|
||||
// - Characters are 8 pixels wide.
|
||||
// - The screen can show 30 characters max.
|
||||
// - The control is 16 pixels tall.
|
||||
TextField(std::string& str, size_t max_length, Point position, uint32_t length = 30);
|
||||
|
||||
TextField(const TextField&) = delete;
|
||||
TextField(TextField&&) = delete;
|
||||
TextField& operator=(const TextField&) = delete;
|
||||
TextField& operator=(TextField&&) = delete;
|
||||
|
||||
const std::string& value() const;
|
||||
|
||||
void set_cursor(uint32_t pos);
|
||||
void set_insert_mode();
|
||||
void set_overwrite_mode();
|
||||
|
||||
void char_add(char c);
|
||||
void char_delete();
|
||||
|
||||
void paint(Painter& painter) override;
|
||||
|
||||
bool on_key(const KeyEvent key) override;
|
||||
bool on_encoder(const EncoderEvent delta) override;
|
||||
bool on_touch(const TouchEvent event) override;
|
||||
|
||||
protected:
|
||||
std::string& text_;
|
||||
size_t max_length_;
|
||||
uint32_t char_count_;
|
||||
uint32_t cursor_pos_;
|
||||
bool insert_mode_;
|
||||
};
|
||||
|
||||
class NumberField : public Widget {
|
||||
public:
|
||||
std::function<void(NumberField&)> on_select { };
|
||||
|
Reference in New Issue
Block a user