mirror of
https://github.com/portapack-mayhem/mayhem-firmware.git
synced 2025-08-23 14:57:29 +00:00
Added bias-T status icon
Merged radio settings in one screen
This commit is contained in:
123
firmware/application/ui/ui_alphanum.cpp
Normal file
123
firmware/application/ui/ui_alphanum.cpp
Normal file
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
|
||||
* Copyright (C) 2016 Furrtek
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "ui_alphanum.hpp"
|
||||
|
||||
#include "portapack.hpp"
|
||||
#include "hackrf_hal.hpp"
|
||||
#include "portapack_shared_memory.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
namespace ui {
|
||||
|
||||
void AlphanumView::paint(Painter&) {
|
||||
draw_cursor();
|
||||
}
|
||||
|
||||
AlphanumView::AlphanumView(
|
||||
NavigationView& nav,
|
||||
std::string * str,
|
||||
size_t max_length
|
||||
) : TextEntryView(nav, str, max_length)
|
||||
{
|
||||
size_t n;
|
||||
|
||||
add_children({
|
||||
&button_mode,
|
||||
&text_raw,
|
||||
&field_raw
|
||||
});
|
||||
|
||||
const auto button_fn = [this](Button& button) {
|
||||
this->on_button(button);
|
||||
};
|
||||
|
||||
n = 0;
|
||||
for (auto& button : buttons) {
|
||||
button.on_select = button_fn;
|
||||
button.set_parent_rect({
|
||||
static_cast<Coord>((n % 5) * (240 / 5)),
|
||||
static_cast<Coord>((n / 5) * 38 + 24),
|
||||
240 / 5, 38
|
||||
});
|
||||
add_child(&button);
|
||||
n++;
|
||||
}
|
||||
|
||||
set_mode(mode);
|
||||
|
||||
button_mode.on_select = [this](Button&) {
|
||||
set_mode(mode + 1);
|
||||
};
|
||||
|
||||
field_raw.set_value('0');
|
||||
field_raw.on_select = [this](NumberField&) {
|
||||
char_add(field_raw.value());
|
||||
update_text();
|
||||
};
|
||||
|
||||
button_ok.on_select = [this, &nav](Button&) {
|
||||
if (on_changed)
|
||||
on_changed(_str);
|
||||
nav.pop();
|
||||
};
|
||||
|
||||
update_text();
|
||||
}
|
||||
|
||||
void AlphanumView::set_mode(const uint32_t new_mode) {
|
||||
size_t n = 0;
|
||||
|
||||
if (new_mode < 3)
|
||||
mode = new_mode;
|
||||
else
|
||||
mode = 0;
|
||||
|
||||
const char * key_list = key_sets[mode].second;
|
||||
|
||||
for (auto& button : buttons) {
|
||||
const std::string label {
|
||||
key_list[n]
|
||||
};
|
||||
button.set_text(label);
|
||||
n++;
|
||||
}
|
||||
|
||||
if (mode < 2)
|
||||
button_mode.set_text(key_sets[mode + 1].first);
|
||||
else
|
||||
button_mode.set_text(key_sets[0].first);
|
||||
}
|
||||
|
||||
void AlphanumView::on_button(Button& button) {
|
||||
const auto c = button.text()[0];
|
||||
|
||||
if (c == '<')
|
||||
char_delete();
|
||||
else
|
||||
char_add(c);
|
||||
|
||||
update_text();
|
||||
}
|
||||
|
||||
}
|
88
firmware/application/ui/ui_alphanum.hpp
Normal file
88
firmware/application/ui/ui_alphanum.hpp
Normal file
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
|
||||
* Copyright (C) 2016 Furrtek
|
||||
*
|
||||
* 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 __ALPHANUM_H__
|
||||
#define __ALPHANUM_H__
|
||||
|
||||
#include "ui.hpp"
|
||||
#include "ui_widget.hpp"
|
||||
#include "ui_painter.hpp"
|
||||
#include "ui_textentry.hpp"
|
||||
#include "ui_menu.hpp"
|
||||
|
||||
namespace ui {
|
||||
|
||||
class AlphanumView : public TextEntryView {
|
||||
public:
|
||||
AlphanumView(NavigationView& nav, std::string * str, size_t max_length);
|
||||
|
||||
AlphanumView(const AlphanumView&) = delete;
|
||||
AlphanumView(AlphanumView&&) = delete;
|
||||
AlphanumView& operator=(const AlphanumView&) = delete;
|
||||
AlphanumView& operator=(AlphanumView&&) = delete;
|
||||
|
||||
void paint(Painter& painter) override;
|
||||
|
||||
private:
|
||||
const char * const keys_upper = "ABCDEFGHIJKLMNOPQRSTUVWXYZ' .<";
|
||||
const char * const keys_lower = "abcdefghijklmnopqrstuvwxyz' .<";
|
||||
const char * const keys_digit = "0123456789!\"#'()*+-/:;=>?@[\\]<";
|
||||
|
||||
const std::pair<std::string, const char *> key_sets[3] = {
|
||||
{ "Upper", keys_upper },
|
||||
{ "Lower", keys_lower },
|
||||
{ "Digit", keys_digit }
|
||||
};
|
||||
|
||||
uint32_t mode = 0; // Uppercase
|
||||
|
||||
void set_mode(const uint32_t new_mode);
|
||||
void on_button(Button& button);
|
||||
|
||||
std::array<Button, 30> buttons { };
|
||||
|
||||
Button button_mode {
|
||||
{ 21 * 8, 33 * 8, 8 * 8, 32 },
|
||||
""
|
||||
};
|
||||
|
||||
Text text_raw {
|
||||
{ 1 * 8, 33 * 8, 4 * 8, 16 },
|
||||
"Raw:"
|
||||
};
|
||||
NumberField field_raw {
|
||||
{ 5 * 8, 33 * 8 },
|
||||
3,
|
||||
{ 1, 255 },
|
||||
1,
|
||||
'0'
|
||||
};
|
||||
|
||||
Button button_ok {
|
||||
{ 10 * 8, 33 * 8, 9 * 8, 32 },
|
||||
"OK"
|
||||
};
|
||||
};
|
||||
|
||||
} /* namespace ui */
|
||||
|
||||
#endif/*__ALPHANUM_H__*/
|
72
firmware/application/ui/ui_audio.cpp
Normal file
72
firmware/application/ui/ui_audio.cpp
Normal file
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "ui_audio.hpp"
|
||||
|
||||
#include "utility.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
namespace ui {
|
||||
|
||||
void Audio::paint(Painter& painter) {
|
||||
const auto r = screen_rect();
|
||||
|
||||
constexpr int db_min = -96;
|
||||
constexpr int db_max = 0;
|
||||
constexpr int db_delta = db_max - db_min;
|
||||
const range_t<int> x_rms_range { 0, r.width() - 1 };
|
||||
const auto x_rms = x_rms_range.clip((rms_db_ - db_min) * r.width() / db_delta);
|
||||
const range_t<int> x_max_range { x_rms + 1, r.width() };
|
||||
const auto x_max = x_max_range.clip((max_db_ - db_min) * r.width() / db_delta);
|
||||
|
||||
const Rect r0 { r.left(), r.top(), x_rms, r.height() };
|
||||
painter.fill_rectangle(
|
||||
r0,
|
||||
Color::green()
|
||||
);
|
||||
|
||||
const Rect r1 { r.left() + x_rms, r.top(), 1, r.height() };
|
||||
painter.fill_rectangle(
|
||||
r1,
|
||||
Color::black()
|
||||
);
|
||||
|
||||
const Rect r2 { r.left() + x_rms + 1, r.top(), x_max - (x_rms + 1), r.height() };
|
||||
painter.fill_rectangle(
|
||||
r2,
|
||||
Color::red()
|
||||
);
|
||||
|
||||
const Rect r3 { r.left() + x_max, r.top(), r.width() - x_max, r.height() };
|
||||
painter.fill_rectangle(
|
||||
r3,
|
||||
Color::black()
|
||||
);
|
||||
}
|
||||
|
||||
void Audio::on_statistics_update(const AudioStatistics& statistics) {
|
||||
rms_db_ = statistics.rms_db;
|
||||
max_db_ = statistics.max_db;
|
||||
set_dirty();
|
||||
}
|
||||
|
||||
} /* namespace ui */
|
63
firmware/application/ui/ui_audio.hpp
Normal file
63
firmware/application/ui/ui_audio.hpp
Normal file
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
|
||||
*
|
||||
* 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 __UI_AUDIO_H__
|
||||
#define __UI_AUDIO_H__
|
||||
|
||||
#include "ui.hpp"
|
||||
#include "ui_widget.hpp"
|
||||
#include "ui_painter.hpp"
|
||||
|
||||
#include "event_m0.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace ui {
|
||||
|
||||
class Audio : public Widget {
|
||||
public:
|
||||
Audio(
|
||||
const Rect parent_rect
|
||||
) : Widget { parent_rect },
|
||||
rms_db_ { -120 },
|
||||
max_db_ { -120 }
|
||||
{
|
||||
}
|
||||
|
||||
void paint(Painter& painter) override;
|
||||
|
||||
private:
|
||||
int32_t rms_db_;
|
||||
int32_t max_db_;
|
||||
|
||||
MessageHandlerRegistration message_handler_statistics {
|
||||
Message::ID::AudioStatistics,
|
||||
[this](const Message* const p) {
|
||||
this->on_statistics_update(static_cast<const AudioStatisticsMessage*>(p)->statistics);
|
||||
}
|
||||
};
|
||||
|
||||
void on_statistics_update(const AudioStatistics& statistics);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif/*__UI_AUDIO_H__*/
|
63
firmware/application/ui/ui_channel.cpp
Normal file
63
firmware/application/ui/ui_channel.cpp
Normal file
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "ui_channel.hpp"
|
||||
|
||||
#include "utility.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
namespace ui {
|
||||
|
||||
void Channel::paint(Painter& painter) {
|
||||
const auto r = screen_rect();
|
||||
|
||||
constexpr int db_min = -96;
|
||||
constexpr int db_max = 0;
|
||||
constexpr int db_delta = db_max - db_min;
|
||||
const range_t<int> x_max_range { 0, r.width() - 1 };
|
||||
const auto x_max = x_max_range.clip((max_db_ - db_min) * r.width() / db_delta);
|
||||
|
||||
const Rect r0 { r.left(), r.top(), x_max, r.height() };
|
||||
painter.fill_rectangle(
|
||||
r0,
|
||||
Color::blue()
|
||||
);
|
||||
|
||||
const Rect r1 { r.left() + x_max, r.top(), 1, r.height() };
|
||||
painter.fill_rectangle(
|
||||
r1,
|
||||
Color::white()
|
||||
);
|
||||
|
||||
const Rect r2 { r.left() + x_max + 1, r.top(), r.width() - (x_max + 1), r.height() };
|
||||
painter.fill_rectangle(
|
||||
r2,
|
||||
Color::black()
|
||||
);
|
||||
}
|
||||
|
||||
void Channel::on_statistics_update(const ChannelStatistics& statistics) {
|
||||
max_db_ = statistics.max_db;
|
||||
set_dirty();
|
||||
}
|
||||
|
||||
} /* namespace ui */
|
63
firmware/application/ui/ui_channel.hpp
Normal file
63
firmware/application/ui/ui_channel.hpp
Normal file
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
|
||||
*
|
||||
* 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 __UI_CHANNEL_H__
|
||||
#define __UI_CHANNEL_H__
|
||||
|
||||
#include "ui.hpp"
|
||||
#include "ui_widget.hpp"
|
||||
#include "ui_painter.hpp"
|
||||
|
||||
#include "event_m0.hpp"
|
||||
|
||||
#include "message.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace ui {
|
||||
|
||||
class Channel : public Widget {
|
||||
public:
|
||||
Channel(
|
||||
const Rect parent_rect
|
||||
) : Widget { parent_rect },
|
||||
max_db_ { -120 }
|
||||
{
|
||||
}
|
||||
|
||||
void paint(Painter& painter) override;
|
||||
|
||||
private:
|
||||
int32_t max_db_;
|
||||
|
||||
MessageHandlerRegistration message_handler_stats {
|
||||
Message::ID::ChannelStatistics,
|
||||
[this](const Message* const p) {
|
||||
this->on_statistics_update(static_cast<const ChannelStatisticsMessage*>(p)->statistics);
|
||||
}
|
||||
};
|
||||
|
||||
void on_statistics_update(const ChannelStatistics& statistics);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif/*__UI_CHANNEL_H__*/
|
138
firmware/application/ui/ui_font_fixed_8x16.cpp
Normal file
138
firmware/application/ui/ui_font_fixed_8x16.cpp
Normal file
@@ -0,0 +1,138 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "ui_font_fixed_8x16.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace ui {
|
||||
namespace font {
|
||||
|
||||
namespace {
|
||||
|
||||
const uint8_t fixed_8x16_glyph_data[] = {
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x08, 0x08, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x24, 0x24, 0x24, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x48, 0x48, 0x48, 0xff, 0x24, 0x24, 0xff, 0x12, 0x12, 0x12, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x10, 0x78, 0x14, 0x14, 0x14, 0x18, 0x30, 0x50, 0x50, 0x50, 0x3c, 0x10, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x46, 0x29, 0x29, 0x19, 0x16, 0x68, 0x98, 0x94, 0x94, 0x62, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x18, 0x24, 0x24, 0x14, 0x88, 0x54, 0x72, 0x22, 0x62, 0x9c, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x40, 0x20, 0x10, 0x10, 0x08, 0x08, 0x08, 0x08, 0x10, 0x10, 0x20, 0x40, 0x00,
|
||||
0x00, 0x00, 0x00, 0x02, 0x04, 0x08, 0x08, 0x10, 0x10, 0x10, 0x10, 0x08, 0x08, 0x04, 0x02, 0x00,
|
||||
0x00, 0x00, 0x00, 0x08, 0x2a, 0x1c, 0x2a, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x10, 0x10, 0xfe, 0x10, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x20, 0x20, 0x10, 0x10, 0x08, 0x08, 0x04, 0x04, 0x02, 0x02, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x18, 0x24, 0x42, 0x42, 0x5a, 0x5a, 0x42, 0x42, 0x24, 0x18, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x0c, 0x0a, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x3e, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x1e, 0x20, 0x20, 0x20, 0x20, 0x10, 0x08, 0x04, 0x02, 0x3e, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x1e, 0x20, 0x20, 0x10, 0x0c, 0x10, 0x20, 0x20, 0x10, 0x0e, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x30, 0x30, 0x28, 0x28, 0x24, 0x24, 0x22, 0x7e, 0x20, 0x20, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x7c, 0x04, 0x04, 0x04, 0x3c, 0x40, 0x40, 0x40, 0x40, 0x3c, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x38, 0x04, 0x02, 0x02, 0x3a, 0x46, 0x42, 0x42, 0x44, 0x38, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x7e, 0x40, 0x20, 0x20, 0x10, 0x10, 0x10, 0x08, 0x08, 0x04, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x3c, 0x42, 0x42, 0x24, 0x18, 0x24, 0x42, 0x42, 0x42, 0x3c, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x1c, 0x22, 0x42, 0x42, 0x62, 0x5c, 0x40, 0x40, 0x20, 0x1c, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x00, 0x00, 0x00, 0x08, 0x08, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x00, 0x00, 0x00, 0x08, 0x08, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x20, 0x18, 0x04, 0x18, 0x20, 0x40, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x08, 0x30, 0x40, 0x30, 0x08, 0x04, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x38, 0x44, 0x40, 0x40, 0x30, 0x08, 0x08, 0x00, 0x08, 0x08, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x1c, 0x22, 0x41, 0x59, 0x55, 0x55, 0x55, 0x39, 0x01, 0x02, 0x3c, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x08, 0x14, 0x14, 0x14, 0x14, 0x22, 0x3e, 0x22, 0x41, 0x41, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x3e, 0x42, 0x42, 0x42, 0x3e, 0x42, 0x42, 0x42, 0x42, 0x3e, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x38, 0x44, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x44, 0x38, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x1e, 0x22, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x22, 0x1e, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x7e, 0x02, 0x02, 0x02, 0x7e, 0x02, 0x02, 0x02, 0x02, 0x7e, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x7e, 0x02, 0x02, 0x02, 0x7e, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x38, 0x44, 0x02, 0x02, 0x02, 0x72, 0x42, 0x42, 0x44, 0x38, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x42, 0x42, 0x42, 0x42, 0x7e, 0x42, 0x42, 0x42, 0x42, 0x42, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x3e, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x3e, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x1e, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x82, 0x42, 0x22, 0x12, 0x0a, 0x0e, 0x12, 0x22, 0x42, 0x82, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x7e, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x42, 0x66, 0x66, 0x5a, 0x5a, 0x42, 0x42, 0x42, 0x42, 0x42, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x42, 0x46, 0x46, 0x4a, 0x4a, 0x52, 0x52, 0x62, 0x62, 0x42, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x18, 0x24, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x24, 0x18, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x3e, 0x42, 0x42, 0x3e, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x18, 0x24, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x24, 0x18, 0x10, 0x60, 0x00,
|
||||
0x00, 0x00, 0x00, 0x3e, 0x42, 0x42, 0x42, 0x3e, 0x12, 0x22, 0x22, 0x42, 0x82, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x3c, 0x42, 0x02, 0x02, 0x0c, 0x30, 0x40, 0x40, 0x42, 0x3c, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x7f, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x3c, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x41, 0x41, 0x22, 0x22, 0x22, 0x14, 0x14, 0x14, 0x14, 0x08, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x41, 0x41, 0x41, 0x49, 0x49, 0x55, 0x55, 0x55, 0x22, 0x22, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x41, 0x22, 0x22, 0x14, 0x08, 0x08, 0x14, 0x22, 0x22, 0x41, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x41, 0x22, 0x22, 0x14, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x7e, 0x40, 0x20, 0x10, 0x10, 0x08, 0x08, 0x04, 0x02, 0x7e, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x38, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x38, 0x00,
|
||||
0x00, 0x00, 0x00, 0x02, 0x02, 0x04, 0x04, 0x08, 0x08, 0x10, 0x10, 0x20, 0x20, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x0e, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x0e, 0x00,
|
||||
0x00, 0x00, 0x00, 0x10, 0x28, 0x44, 0x44, 0x82, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff,
|
||||
0x00, 0x00, 0x00, 0x08, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x42, 0x40, 0x7c, 0x42, 0x62, 0x5c, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x02, 0x02, 0x02, 0x1a, 0x26, 0x42, 0x42, 0x42, 0x26, 0x1a, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x04, 0x02, 0x02, 0x02, 0x04, 0x78, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x40, 0x40, 0x40, 0x58, 0x64, 0x42, 0x42, 0x42, 0x64, 0x58, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x24, 0x42, 0x7e, 0x02, 0x04, 0x78, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x70, 0x08, 0x08, 0x7e, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x22, 0x22, 0x22, 0x1c, 0x02, 0x3e, 0x42, 0x42, 0x3c,
|
||||
0x00, 0x00, 0x00, 0x02, 0x02, 0x02, 0x3a, 0x46, 0x42, 0x42, 0x42, 0x42, 0x42, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x1c, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x1c, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x0e,
|
||||
0x00, 0x00, 0x00, 0x02, 0x02, 0x02, 0x22, 0x12, 0x0a, 0x0e, 0x12, 0x22, 0x42, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x0e, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x38, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x37, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3a, 0x46, 0x42, 0x42, 0x42, 0x42, 0x42, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x42, 0x42, 0x42, 0x42, 0x42, 0x3c, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1a, 0x26, 0x42, 0x42, 0x42, 0x26, 0x1a, 0x02, 0x02, 0x02,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x58, 0x64, 0x42, 0x42, 0x42, 0x64, 0x58, 0x40, 0x40, 0x40,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x74, 0x0c, 0x04, 0x04, 0x04, 0x04, 0x04, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x02, 0x02, 0x3c, 0x40, 0x40, 0x3e, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x3c, 0x08, 0x08, 0x08, 0x08, 0x08, 0x70, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x42, 0x42, 0x42, 0x42, 0x62, 0x5c, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x22, 0x22, 0x36, 0x14, 0x14, 0x08, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x41, 0x49, 0x55, 0x55, 0x22, 0x22, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x24, 0x18, 0x18, 0x18, 0x24, 0x42, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x22, 0x22, 0x14, 0x14, 0x14, 0x08, 0x08, 0x04, 0x03,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x20, 0x10, 0x08, 0x08, 0x04, 0x7e, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x10, 0x08, 0x08, 0x08, 0x08, 0x04, 0x08, 0x08, 0x08, 0x08, 0x08, 0x10, 0x00,
|
||||
0x00, 0x00, 0x00, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00,
|
||||
0x00, 0x00, 0x00, 0x08, 0x10, 0x10, 0x10, 0x10, 0x20, 0x10, 0x10, 0x10, 0x10, 0x10, 0x08, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8e, 0x71, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
const ui::Font fixed_8x16 {
|
||||
8, 16,
|
||||
fixed_8x16_glyph_data,
|
||||
0x20, 95,
|
||||
};
|
||||
|
||||
} /* namespace font */
|
||||
} /* namespace ui */
|
35
firmware/application/ui/ui_font_fixed_8x16.hpp
Normal file
35
firmware/application/ui/ui_font_fixed_8x16.hpp
Normal file
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
|
||||
*
|
||||
* 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 __UI_FONT_FIXED_8X16_H__
|
||||
#define __UI_FONT_FIXED_8X16_H__
|
||||
|
||||
#include "ui_text.hpp"
|
||||
|
||||
namespace ui {
|
||||
namespace font {
|
||||
|
||||
extern const ui::Font fixed_8x16;
|
||||
|
||||
} /* namespace font */
|
||||
} /* namspace ui */
|
||||
|
||||
#endif/*__UI_FONT_FIXED_8X16_H__*/
|
354
firmware/application/ui/ui_geomap.cpp
Normal file
354
firmware/application/ui/ui_geomap.cpp
Normal file
@@ -0,0 +1,354 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
|
||||
* Copyright (C) 2017 Furrtek
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "ui_geomap.hpp"
|
||||
|
||||
#include "portapack.hpp"
|
||||
|
||||
#include <cstring>
|
||||
#include <stdio.h>
|
||||
|
||||
using namespace portapack;
|
||||
|
||||
#include "string_format.hpp"
|
||||
|
||||
namespace ui {
|
||||
|
||||
GeoPos::GeoPos(
|
||||
const Point pos,
|
||||
const alt_unit altitude_unit
|
||||
) : altitude_unit_(altitude_unit) {
|
||||
|
||||
set_parent_rect({pos, { 30 * 8, 3 * 16 }});
|
||||
|
||||
add_children({
|
||||
&labels_position,
|
||||
&field_altitude,
|
||||
&text_alt_unit,
|
||||
&field_lat_degrees,
|
||||
&field_lat_minutes,
|
||||
&field_lat_seconds,
|
||||
&text_lat_decimal,
|
||||
&field_lon_degrees,
|
||||
&field_lon_minutes,
|
||||
&field_lon_seconds,
|
||||
&text_lon_decimal
|
||||
});
|
||||
|
||||
// Defaults
|
||||
set_altitude(0);
|
||||
set_lat(0);
|
||||
set_lon(0);
|
||||
|
||||
const auto changed_fn = [this](int32_t) {
|
||||
float lat_value = lat();
|
||||
float lon_value = lon();
|
||||
double integer_part;
|
||||
double fractional_part;
|
||||
|
||||
fractional_part = modf(lat_value, &integer_part) * 100000;
|
||||
if (fractional_part < 0)
|
||||
fractional_part = -fractional_part;
|
||||
text_lat_decimal.set(to_string_dec_int(integer_part) + "." + to_string_dec_uint(fractional_part, 5));
|
||||
fractional_part = modf(lon_value, &integer_part) * 100000;
|
||||
if (fractional_part < 0)
|
||||
fractional_part = -fractional_part;
|
||||
text_lon_decimal.set(to_string_dec_int(integer_part) + "." + to_string_dec_uint(fractional_part, 5));
|
||||
|
||||
if (on_change && report_change)
|
||||
on_change(altitude(), lat_value, lon_value);
|
||||
};
|
||||
|
||||
field_altitude.on_change = changed_fn;
|
||||
field_lat_degrees.on_change = changed_fn;
|
||||
field_lat_minutes.on_change = changed_fn;
|
||||
field_lat_seconds.on_change = changed_fn;
|
||||
field_lon_degrees.on_change = changed_fn;
|
||||
field_lon_minutes.on_change = changed_fn;
|
||||
field_lon_seconds.on_change = changed_fn;
|
||||
|
||||
text_alt_unit.set(altitude_unit_ ? "m" : "ft");
|
||||
}
|
||||
|
||||
void GeoPos::set_read_only(bool v) {
|
||||
for(auto child : children_)
|
||||
child->set_focusable(!v);
|
||||
}
|
||||
|
||||
// Stupid hack to avoid an event loop
|
||||
void GeoPos::set_report_change(bool v) {
|
||||
report_change = v;
|
||||
}
|
||||
|
||||
void GeoPos::focus() {
|
||||
field_altitude.focus();
|
||||
}
|
||||
|
||||
void GeoPos::set_altitude(int32_t altitude) {
|
||||
field_altitude.set_value(altitude);
|
||||
}
|
||||
|
||||
void GeoPos::set_lat(float lat) {
|
||||
field_lat_degrees.set_value(lat);
|
||||
field_lat_minutes.set_value((uint32_t)abs(lat / (1.0 / 60)) % 60);
|
||||
field_lat_seconds.set_value((uint32_t)abs(lat / (1.0 / 3600)) % 60);
|
||||
}
|
||||
|
||||
void GeoPos::set_lon(float lon) {
|
||||
field_lon_degrees.set_value(lon);
|
||||
field_lon_minutes.set_value((uint32_t)abs(lon / (1.0 / 60)) % 60);
|
||||
field_lon_seconds.set_value((uint32_t)abs(lon / (1.0 / 3600)) % 60);
|
||||
}
|
||||
|
||||
float GeoPos::lat() {
|
||||
return field_lat_degrees.value() + (field_lat_minutes.value() / 60.0) + (field_lat_seconds.value() / 3600.0);
|
||||
};
|
||||
|
||||
float GeoPos::lon() {
|
||||
return field_lon_degrees.value() + (field_lon_minutes.value() / 60.0) + (field_lon_seconds.value() / 3600.0);
|
||||
};
|
||||
|
||||
int32_t GeoPos::altitude() {
|
||||
return field_altitude.value();
|
||||
};
|
||||
|
||||
GeoMap::GeoMap(
|
||||
Rect parent_rect
|
||||
) : Widget { parent_rect }
|
||||
{
|
||||
//set_focusable(true);
|
||||
}
|
||||
|
||||
void GeoMap::paint(Painter& painter) {
|
||||
Coord line;
|
||||
std::array<ui::Color, 240> map_line_buffer;
|
||||
const auto r = screen_rect();
|
||||
|
||||
// Ony redraw map if it moved by at least 1 pixel
|
||||
if ((x_pos != prev_x_pos) || (y_pos != prev_y_pos)) {
|
||||
for (line = 0; line < r.height(); line++) {
|
||||
map_file.seek(4 + ((x_pos + (map_width * (y_pos + line))) << 1));
|
||||
map_file.read(map_line_buffer.data(), r.width() << 1);
|
||||
display.draw_pixels({ 0, r.top() + line, r.width(), 1 }, map_line_buffer);
|
||||
}
|
||||
|
||||
prev_x_pos = x_pos;
|
||||
prev_y_pos = y_pos;
|
||||
}
|
||||
|
||||
if (mode_ == PROMPT) {
|
||||
// Cross
|
||||
display.fill_rectangle({ r.center() - Point(16, 1), { 32, 2 } }, Color::red());
|
||||
display.fill_rectangle({ r.center() - Point(1, 16), { 2, 32 } }, Color::red());
|
||||
} else {
|
||||
draw_bearing({ 120, 32 + 144 }, angle_, 16, Color::red());
|
||||
painter.draw_string({ 120 - ((int)tag_.length() * 8 / 2), 32 + 144 - 32 }, style(), tag_);
|
||||
}
|
||||
}
|
||||
|
||||
bool GeoMap::on_touch(const TouchEvent event) {
|
||||
if ((event.type == TouchEvent::Type::Start) && (mode_ == PROMPT)) {
|
||||
set_highlighted(true);
|
||||
if (on_move) {
|
||||
Point p = event.point - screen_rect().center();
|
||||
on_move(p.x() / 2.0 * lon_ratio, p.y() / 2.0 * lat_ratio);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void GeoMap::move(const float lon, const float lat) {
|
||||
lon_ = lon;
|
||||
lat_ = lat;
|
||||
|
||||
Rect map_rect = screen_rect();
|
||||
|
||||
// Map is in Equidistant "Plate Carrée" projection
|
||||
x_pos = map_center_x - (map_rect.width() / 2) + (lon_ / lon_ratio);
|
||||
y_pos = map_center_y - (map_rect.height() / 2) + (lat_ / lat_ratio) + 16;
|
||||
|
||||
// Cap position
|
||||
if (x_pos > (map_width - map_rect.width()))
|
||||
x_pos = map_width - map_rect.width();
|
||||
if (y_pos > (map_height + map_rect.height()))
|
||||
y_pos = map_height - map_rect.height();
|
||||
}
|
||||
|
||||
bool GeoMap::init() {
|
||||
auto result = map_file.open("ADSB/world_map.bin");
|
||||
if (result.is_valid())
|
||||
return false;
|
||||
|
||||
map_file.read(&map_width, 2);
|
||||
map_file.read(&map_height, 2);
|
||||
|
||||
map_center_x = map_width >> 1;
|
||||
map_center_y = map_height >> 1;
|
||||
|
||||
lon_ratio = 180.0 / map_center_x;
|
||||
lat_ratio = -90.0 / map_center_y;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void GeoMap::set_mode(GeoMapMode mode) {
|
||||
mode_ = mode;
|
||||
}
|
||||
|
||||
void GeoMap::draw_bearing(const Point origin, const uint32_t angle, uint32_t size, const Color color) {
|
||||
Point arrow_a, arrow_b, arrow_c;
|
||||
|
||||
for (size_t thickness = 0; thickness < 3; thickness++) {
|
||||
arrow_a = polar_to_point(angle, size) + origin;
|
||||
arrow_b = polar_to_point(angle + 180 - 30, size) + origin;
|
||||
arrow_c = polar_to_point(angle + 180 + 30, size) + origin;
|
||||
|
||||
display.draw_line(arrow_a, arrow_b, color);
|
||||
display.draw_line(arrow_b, arrow_c, color);
|
||||
display.draw_line(arrow_c, arrow_a, color);
|
||||
|
||||
size--;
|
||||
}
|
||||
}
|
||||
|
||||
void GeoMapView::focus() {
|
||||
geopos.focus();
|
||||
|
||||
if (!map_opened)
|
||||
nav_.display_modal("No map", "No world_map.bin file in\n/ADSB/ directory", ABORT, nullptr);
|
||||
}
|
||||
|
||||
void GeoMapView::update_position(float lat, float lon) {
|
||||
lat_ = lat;
|
||||
lon_ = lon;
|
||||
geopos.set_lat(lat_);
|
||||
geopos.set_lon(lon_);
|
||||
geomap.move(lon_, lat_);
|
||||
geomap.set_dirty();
|
||||
}
|
||||
|
||||
void GeoMapView::setup() {
|
||||
add_child(&geomap);
|
||||
|
||||
geopos.set_altitude(altitude_);
|
||||
geopos.set_lat(lat_);
|
||||
geopos.set_lon(lon_);
|
||||
|
||||
geopos.on_change = [this](int32_t altitude, float lat, float lon) {
|
||||
altitude_ = altitude;
|
||||
lat_ = lat;
|
||||
lon_ = lon;
|
||||
geomap.move(lon_, lat_);
|
||||
geomap.set_dirty();
|
||||
};
|
||||
|
||||
geomap.on_move = [this](float move_x, float move_y) {
|
||||
lon_ += move_x;
|
||||
lat_ += move_y;
|
||||
|
||||
// Stupid hack to avoid an event loop
|
||||
geopos.set_report_change(false);
|
||||
geopos.set_lon(lon_);
|
||||
geopos.set_lat(lat_);
|
||||
geopos.set_report_change(true);
|
||||
|
||||
geomap.move(lon_, lat_);
|
||||
geomap.set_dirty();
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
GeoMapView::~GeoMapView() {
|
||||
if (on_close_)
|
||||
on_close_();
|
||||
}
|
||||
|
||||
// Display mode
|
||||
GeoMapView::GeoMapView(
|
||||
NavigationView& nav,
|
||||
const std::string& tag,
|
||||
int32_t altitude,
|
||||
GeoPos::alt_unit altitude_unit,
|
||||
float lat,
|
||||
float lon,
|
||||
float angle,
|
||||
const std::function<void(void)> on_close
|
||||
) : nav_ (nav),
|
||||
altitude_ (altitude),
|
||||
altitude_unit_ (altitude_unit),
|
||||
lat_ (lat),
|
||||
lon_ (lon),
|
||||
angle_ (angle),
|
||||
on_close_(on_close)
|
||||
{
|
||||
mode_ = DISPLAY;
|
||||
|
||||
add_child(&geopos);
|
||||
|
||||
map_opened = geomap.init();
|
||||
if (!map_opened) return;
|
||||
|
||||
setup();
|
||||
|
||||
geomap.set_mode(mode_);
|
||||
geomap.set_tag(tag);
|
||||
geomap.move(lon_, lat_);
|
||||
|
||||
geopos.set_read_only(true);
|
||||
}
|
||||
|
||||
// Prompt mode
|
||||
GeoMapView::GeoMapView(
|
||||
NavigationView& nav,
|
||||
int32_t altitude,
|
||||
GeoPos::alt_unit altitude_unit,
|
||||
float lat,
|
||||
float lon,
|
||||
const std::function<void(int32_t, float, float)> on_done
|
||||
) : nav_ (nav),
|
||||
altitude_ (altitude),
|
||||
altitude_unit_ (altitude_unit),
|
||||
lat_ (lat),
|
||||
lon_ (lon)
|
||||
{
|
||||
mode_ = PROMPT;
|
||||
|
||||
add_child(&geopos);
|
||||
|
||||
map_opened = geomap.init();
|
||||
if (!map_opened) return;
|
||||
|
||||
setup();
|
||||
add_child(&button_ok);
|
||||
|
||||
geomap.set_mode(mode_);
|
||||
geomap.move(lon_, lat_);
|
||||
|
||||
button_ok.on_select = [this, on_done, &nav](Button&) {
|
||||
if (on_done)
|
||||
on_done(altitude_, lat_, lon_);
|
||||
nav.pop();
|
||||
};
|
||||
}
|
||||
|
||||
} /* namespace ui */
|
215
firmware/application/ui/ui_geomap.hpp
Normal file
215
firmware/application/ui/ui_geomap.hpp
Normal file
@@ -0,0 +1,215 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
|
||||
* Copyright (C) 2017 Furrtek
|
||||
*
|
||||
* 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 __GEOMAP_H__
|
||||
#define __GEOMAP_H__
|
||||
|
||||
#include "ui.hpp"
|
||||
#include "file.hpp"
|
||||
#include "ui_navigation.hpp"
|
||||
#include "ui_font_fixed_8x16.hpp"
|
||||
|
||||
#include "portapack.hpp"
|
||||
|
||||
namespace ui {
|
||||
|
||||
enum GeoMapMode {
|
||||
DISPLAY,
|
||||
PROMPT
|
||||
};
|
||||
|
||||
class GeoPos : public View {
|
||||
public:
|
||||
enum alt_unit {
|
||||
FEET = 0,
|
||||
METERS
|
||||
};
|
||||
|
||||
std::function<void(int32_t, float, float)> on_change { };
|
||||
|
||||
GeoPos(const Point pos, const alt_unit altitude_unit);
|
||||
|
||||
void focus() override;
|
||||
|
||||
void set_read_only(bool v);
|
||||
void set_altitude(int32_t altitude);
|
||||
void set_lat(float lat);
|
||||
void set_lon(float lon);
|
||||
int32_t altitude();
|
||||
float lat();
|
||||
float lon();
|
||||
|
||||
void set_report_change(bool v);
|
||||
|
||||
private:
|
||||
bool read_only { false };
|
||||
bool report_change { true };
|
||||
alt_unit altitude_unit_ { };
|
||||
|
||||
Labels labels_position {
|
||||
{ { 1 * 8, 0 * 16 }, "Alt:", Color::light_grey() },
|
||||
{ { 1 * 8, 1 * 16 }, "Lat: * ' \"", Color::light_grey() }, // No ° symbol in 8x16 font
|
||||
{ { 1 * 8, 2 * 16 }, "Lon: * ' \"", Color::light_grey() },
|
||||
};
|
||||
|
||||
NumberField field_altitude {
|
||||
{ 6 * 8, 0 * 16 },
|
||||
5,
|
||||
{ -1000, 50000 },
|
||||
250,
|
||||
' '
|
||||
};
|
||||
Text text_alt_unit {
|
||||
{ 12 * 8, 0 * 16, 2 * 8, 16 },
|
||||
""
|
||||
};
|
||||
|
||||
NumberField field_lat_degrees {
|
||||
{ 5 * 8, 1 * 16 }, 4, { -90, 90 }, 1, ' '
|
||||
};
|
||||
NumberField field_lat_minutes {
|
||||
{ 10 * 8, 1 * 16 }, 2, { 0, 59 }, 1, ' '
|
||||
};
|
||||
NumberField field_lat_seconds {
|
||||
{ 13 * 8, 1 * 16 }, 2, { 0, 59 }, 1, ' '
|
||||
};
|
||||
Text text_lat_decimal {
|
||||
{ 17 * 8, 1 * 16, 13 * 8, 1 * 16 },
|
||||
""
|
||||
};
|
||||
|
||||
NumberField field_lon_degrees {
|
||||
{ 5 * 8, 2 * 16 }, 4, { -180, 180 }, 1, ' '
|
||||
};
|
||||
NumberField field_lon_minutes {
|
||||
{ 10 * 8, 2 * 16 }, 2, { 0, 59 }, 1, ' '
|
||||
};
|
||||
NumberField field_lon_seconds {
|
||||
{ 13 * 8, 2 * 16 }, 2, { 0, 59 }, 1, ' '
|
||||
};
|
||||
Text text_lon_decimal {
|
||||
{ 17 * 8, 2 * 16, 13 * 8, 1 * 16 },
|
||||
""
|
||||
};
|
||||
};
|
||||
|
||||
class GeoMap : public Widget {
|
||||
public:
|
||||
std::function<void(float, float)> on_move { };
|
||||
|
||||
GeoMap(Rect parent_rect);
|
||||
|
||||
void paint(Painter& painter) override;
|
||||
|
||||
bool on_touch(const TouchEvent event) override;
|
||||
|
||||
bool init();
|
||||
void set_mode(GeoMapMode mode);
|
||||
void move(const float lon, const float lat);
|
||||
void set_tag(std::string new_tag) {
|
||||
tag_ = new_tag;
|
||||
}
|
||||
|
||||
private:
|
||||
void draw_bearing(const Point origin, const uint32_t angle, uint32_t size, const Color color);
|
||||
|
||||
GeoMapMode mode_ { };
|
||||
File map_file { };
|
||||
uint16_t map_width { }, map_height { };
|
||||
int32_t map_center_x { }, map_center_y { };
|
||||
float lon_ratio { }, lat_ratio { };
|
||||
int32_t x_pos { }, y_pos { };
|
||||
int32_t prev_x_pos { 0xFFFF }, prev_y_pos { 0xFFFF };
|
||||
float lat_ { };
|
||||
float lon_ { };
|
||||
float angle_ { };
|
||||
std::string tag_ { };
|
||||
};
|
||||
|
||||
class GeoMapView : public View {
|
||||
public:
|
||||
GeoMapView(
|
||||
NavigationView& nav,
|
||||
const std::string& tag,
|
||||
int32_t altitude,
|
||||
GeoPos::alt_unit altitude_unit,
|
||||
float lat,
|
||||
float lon,
|
||||
float angle,
|
||||
const std::function<void(void)> on_close = nullptr
|
||||
);
|
||||
GeoMapView(NavigationView& nav,
|
||||
int32_t altitude,
|
||||
GeoPos::alt_unit altitude_unit,
|
||||
float lat,
|
||||
float lon,
|
||||
const std::function<void(int32_t, float, float)> on_done
|
||||
);
|
||||
~GeoMapView();
|
||||
|
||||
GeoMapView(const GeoMapView&) = delete;
|
||||
GeoMapView(GeoMapView&&) = delete;
|
||||
GeoMapView& operator=(const GeoMapView&) = delete;
|
||||
GeoMapView& operator=(GeoMapView&&) = delete;
|
||||
|
||||
void focus() override;
|
||||
|
||||
void update_position(float lat, float lon);
|
||||
|
||||
std::string title() const override { return "Map view"; };
|
||||
|
||||
private:
|
||||
NavigationView& nav_;
|
||||
|
||||
void setup();
|
||||
|
||||
const std::function<void(int32_t, float, float)> on_done { };
|
||||
|
||||
const Dim banner_height = 3 * 16;
|
||||
GeoMapMode mode_ { };
|
||||
int32_t altitude_ { };
|
||||
GeoPos::alt_unit altitude_unit_ { };
|
||||
float lat_ { };
|
||||
float lon_ { };
|
||||
float angle_ { };
|
||||
std::function<void(void)> on_close_ { nullptr };
|
||||
|
||||
bool map_opened { };
|
||||
|
||||
GeoPos geopos {
|
||||
{ 0, 0 },
|
||||
altitude_unit_
|
||||
};
|
||||
|
||||
GeoMap geomap {
|
||||
{ 0, banner_height, 240, 320 - 16 - banner_height }
|
||||
};
|
||||
|
||||
Button button_ok {
|
||||
{ 20 * 8, 8, 8 * 8, 2 * 16 },
|
||||
"OK"
|
||||
};
|
||||
};
|
||||
|
||||
} /* namespace ui */
|
||||
|
||||
#endif
|
299
firmware/application/ui/ui_menu.cpp
Normal file
299
firmware/application/ui/ui_menu.cpp
Normal file
@@ -0,0 +1,299 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
|
||||
* Copyright (C) 2016 Furrtek
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "ui_menu.hpp"
|
||||
#include "rtc_time.hpp"
|
||||
|
||||
namespace ui {
|
||||
|
||||
/* MenuItemView **********************************************************/
|
||||
|
||||
void MenuItemView::set_item(MenuItem* item_) {
|
||||
item = item_;
|
||||
}
|
||||
|
||||
void MenuItemView::highlight() {
|
||||
set_highlighted(true);
|
||||
set_dirty();
|
||||
}
|
||||
|
||||
void MenuItemView::unhighlight() {
|
||||
set_highlighted(false);
|
||||
set_dirty();
|
||||
}
|
||||
|
||||
void MenuItemView::paint(Painter& painter) {
|
||||
Coord offset_x;
|
||||
|
||||
if (!item) return;
|
||||
|
||||
const auto r = screen_rect();
|
||||
|
||||
const auto paint_style = (highlighted() && (parent()->has_focus() || keep_highlight)) ? style().invert() : style();
|
||||
|
||||
const auto font_height = paint_style.font.line_height();
|
||||
|
||||
ui::Color final_item_color = (highlighted() && (parent()->has_focus() || keep_highlight)) ? paint_style.foreground : item->color;
|
||||
ui::Color final_bg_color = (highlighted() && (parent()->has_focus() || keep_highlight)) ? item->color : paint_style.background;
|
||||
|
||||
if (final_item_color.v == final_bg_color.v) final_item_color = paint_style.foreground;
|
||||
|
||||
painter.fill_rectangle(
|
||||
r,
|
||||
final_bg_color
|
||||
);
|
||||
|
||||
if (item->bitmap) {
|
||||
painter.draw_bitmap(
|
||||
{ r.location().x() + 4, r.location().y() + 4 },
|
||||
*item->bitmap,
|
||||
final_item_color,
|
||||
final_bg_color
|
||||
);
|
||||
offset_x = 26;
|
||||
} else
|
||||
offset_x = 8;
|
||||
|
||||
Style text_style {
|
||||
.font = paint_style.font,
|
||||
.background = final_bg_color,
|
||||
.foreground = final_item_color
|
||||
};
|
||||
|
||||
painter.draw_string(
|
||||
{ r.location().x() + offset_x, r.location().y() + (r.size().height() - font_height) / 2 },
|
||||
text_style,
|
||||
item->text
|
||||
);
|
||||
}
|
||||
|
||||
/* MenuView **************************************************************/
|
||||
|
||||
MenuView::MenuView(
|
||||
Rect new_parent_rect,
|
||||
bool keep_highlight
|
||||
) : keep_highlight { keep_highlight }
|
||||
{
|
||||
set_parent_rect(new_parent_rect);
|
||||
|
||||
set_focusable(true);
|
||||
|
||||
signal_token_tick_second = rtc_time::signal_tick_second += [this]() {
|
||||
this->on_tick_second();
|
||||
};
|
||||
|
||||
add_child(&arrow_more);
|
||||
arrow_more.set_focusable(false);
|
||||
arrow_more.set_foreground(Color::black());
|
||||
}
|
||||
|
||||
MenuView::~MenuView() {
|
||||
rtc_time::signal_tick_second -= signal_token_tick_second;
|
||||
|
||||
for (auto item : menu_item_views) {
|
||||
delete item;
|
||||
}
|
||||
}
|
||||
|
||||
void MenuView::set_parent_rect(const Rect new_parent_rect) {
|
||||
View::set_parent_rect(new_parent_rect);
|
||||
|
||||
displayed_max = (parent_rect().size().height() / item_height);
|
||||
arrow_more.set_parent_rect( { 228, (Coord)(displayed_max * item_height), 8, 8 } );
|
||||
|
||||
// TODO: Clean this up :(
|
||||
if (menu_item_views.size()) {
|
||||
|
||||
for (auto item : menu_item_views) {
|
||||
remove_child(item);
|
||||
delete item;
|
||||
}
|
||||
|
||||
menu_item_views.clear();
|
||||
}
|
||||
|
||||
for (size_t c = 0; c < displayed_max; c++) {
|
||||
auto item = new MenuItemView { keep_highlight };
|
||||
menu_item_views.push_back(item);
|
||||
add_child(item);
|
||||
|
||||
Coord y_pos = c * item_height;
|
||||
item->set_parent_rect({
|
||||
{ 0, y_pos },
|
||||
{ size().width(), (Coord)item_height }
|
||||
});
|
||||
}
|
||||
|
||||
update_items();
|
||||
}
|
||||
|
||||
void MenuView::on_tick_second() {
|
||||
if (more && blink)
|
||||
arrow_more.set_foreground(Color::white());
|
||||
else
|
||||
arrow_more.set_foreground(Color::black());
|
||||
|
||||
blink = !blink;
|
||||
|
||||
arrow_more.set_dirty();
|
||||
}
|
||||
|
||||
void MenuView::clear() {
|
||||
for (auto item : menu_item_views) {
|
||||
item->set_item(nullptr);
|
||||
}
|
||||
|
||||
menu_items.clear();
|
||||
}
|
||||
|
||||
void MenuView::add_item(MenuItem new_item) {
|
||||
menu_items.push_back(new_item);
|
||||
|
||||
update_items();
|
||||
}
|
||||
|
||||
void MenuView::add_items(std::initializer_list<MenuItem> new_items) {
|
||||
for (auto item : new_items) {
|
||||
add_item(item);
|
||||
}
|
||||
}
|
||||
|
||||
void MenuView::update_items() {
|
||||
size_t i = 0;
|
||||
|
||||
if (menu_items.size() > displayed_max + offset) {
|
||||
more = true;
|
||||
blink = true;
|
||||
} else
|
||||
more = false;
|
||||
|
||||
for (auto item : menu_item_views) {
|
||||
if (i >= menu_items.size()) break;
|
||||
|
||||
// Assign item data to MenuItemViews according to offset
|
||||
item->set_item(&menu_items[i + offset]);
|
||||
item->set_dirty();
|
||||
|
||||
if (highlighted_item == (i + offset)) {
|
||||
item->highlight();
|
||||
} else
|
||||
item->unhighlight();
|
||||
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
MenuItemView* MenuView::item_view(size_t index) const {
|
||||
return menu_item_views[index];
|
||||
}
|
||||
|
||||
bool MenuView::set_highlighted(int32_t new_value) {
|
||||
int32_t item_count = (int32_t)menu_items.size();
|
||||
|
||||
if (new_value < 0)
|
||||
return false;
|
||||
|
||||
if (new_value >= item_count)
|
||||
new_value = item_count - 1;
|
||||
|
||||
if (((uint32_t)new_value > offset) && ((new_value - offset) >= displayed_max)) {
|
||||
// Shift MenuView up
|
||||
highlighted_item = new_value;
|
||||
offset = new_value - displayed_max + 1;
|
||||
update_items();
|
||||
} else if ((uint32_t)new_value < offset) {
|
||||
// Shift MenuView down
|
||||
highlighted_item = new_value;
|
||||
offset = new_value;
|
||||
update_items();
|
||||
} else {
|
||||
// Just update highlight
|
||||
item_view(highlighted_item - offset)->unhighlight();
|
||||
highlighted_item = new_value;
|
||||
item_view(highlighted_item - offset)->highlight();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
uint32_t MenuView::highlighted_index() {
|
||||
return highlighted_item;
|
||||
}
|
||||
|
||||
void MenuView::on_focus() {
|
||||
item_view(highlighted_item - offset)->highlight();
|
||||
}
|
||||
|
||||
void MenuView::on_blur() {
|
||||
if (!keep_highlight)
|
||||
item_view(highlighted_item - offset)->unhighlight();
|
||||
}
|
||||
|
||||
bool MenuView::on_key(const KeyEvent key) {
|
||||
switch(key) {
|
||||
case KeyEvent::Up:
|
||||
return set_highlighted(highlighted_item - 1);
|
||||
|
||||
case KeyEvent::Down:
|
||||
return set_highlighted(highlighted_item + 1);
|
||||
|
||||
case KeyEvent::Select:
|
||||
case KeyEvent::Right:
|
||||
if( menu_items[highlighted_item].on_select ) {
|
||||
menu_items[highlighted_item].on_select();
|
||||
}
|
||||
return true;
|
||||
|
||||
case KeyEvent::Left:
|
||||
if( on_left ) {
|
||||
on_left();
|
||||
}
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool MenuView::on_encoder(const EncoderEvent event) {
|
||||
set_highlighted(highlighted_item + event);
|
||||
return true;
|
||||
}
|
||||
|
||||
/* TODO: This could be handled by default behavior, if the UI stack were to
|
||||
* transmit consumable events from the top of the hit-stack down, and each
|
||||
* MenuItem could respond to a touch and update its parent MenuView.
|
||||
*/
|
||||
/*
|
||||
bool MenuView::on_touch(const TouchEvent event) {
|
||||
size_t i = 0;
|
||||
for(const auto child : children_) {
|
||||
if( child->screen_rect().contains(event.point) ) {
|
||||
return set_highlighted(i);
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
*/
|
||||
} /* namespace ui */
|
124
firmware/application/ui/ui_menu.hpp
Normal file
124
firmware/application/ui/ui_menu.hpp
Normal file
@@ -0,0 +1,124 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
|
||||
* Copyright (C) 2016 Furrtek
|
||||
*
|
||||
* 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 __UI_MENU_H__
|
||||
#define __UI_MENU_H__
|
||||
|
||||
#include "ui.hpp"
|
||||
#include "ui_widget.hpp"
|
||||
#include "ui_painter.hpp"
|
||||
#include "bitmap.hpp"
|
||||
#include "signal.hpp"
|
||||
|
||||
#include <cstddef>
|
||||
#include <string>
|
||||
#include <functional>
|
||||
|
||||
namespace ui {
|
||||
|
||||
struct MenuItem {
|
||||
std::string text;
|
||||
ui::Color color;
|
||||
const Bitmap* bitmap;
|
||||
std::function<void(void)> on_select;
|
||||
|
||||
// TODO: Prevent default-constructed MenuItems.
|
||||
// I managed to construct a menu with three extra, unspecified menu items
|
||||
// in the array that were default constructed...
|
||||
};
|
||||
|
||||
class MenuItemView : public Widget {
|
||||
public:
|
||||
MenuItemView(
|
||||
bool keep_highlight
|
||||
) : keep_highlight { keep_highlight }
|
||||
{
|
||||
}
|
||||
|
||||
MenuItemView(const MenuItemView&) = delete;
|
||||
MenuItemView(MenuItemView&&) = delete;
|
||||
MenuItemView& operator=(const MenuItemView&) = delete;
|
||||
MenuItemView& operator=(MenuItemView&&) = delete;
|
||||
|
||||
void paint(Painter& painter) override;
|
||||
|
||||
void set_item(MenuItem* item_);
|
||||
|
||||
void highlight();
|
||||
void unhighlight();
|
||||
|
||||
private:
|
||||
MenuItem* item { nullptr };
|
||||
bool keep_highlight = false;
|
||||
};
|
||||
|
||||
class MenuView : public View {
|
||||
public:
|
||||
std::function<void(void)> on_left { };
|
||||
|
||||
MenuView(Rect new_parent_rect = { 0, 0, 240, 304 }, bool keep_highlight = false);
|
||||
|
||||
~MenuView();
|
||||
|
||||
void add_item(MenuItem new_item);
|
||||
void add_items(std::initializer_list<MenuItem> new_items);
|
||||
void clear();
|
||||
|
||||
MenuItemView* item_view(size_t index) const;
|
||||
|
||||
bool set_highlighted(int32_t new_value);
|
||||
uint32_t highlighted_index();
|
||||
|
||||
void set_parent_rect(const Rect new_parent_rect) override;
|
||||
void on_focus() override;
|
||||
void on_blur() override;
|
||||
bool on_key(const KeyEvent event) override;
|
||||
bool on_encoder(const EncoderEvent event) override;
|
||||
|
||||
private:
|
||||
void update_items();
|
||||
void on_tick_second();
|
||||
|
||||
bool keep_highlight { false };
|
||||
|
||||
SignalToken signal_token_tick_second { };
|
||||
std::vector<MenuItem> menu_items { };
|
||||
std::vector<MenuItemView*> menu_item_views { };
|
||||
|
||||
Image arrow_more {
|
||||
{ 228, 320 - 8, 8, 8 },
|
||||
&bitmap_more,
|
||||
Color::white(),
|
||||
Color::black()
|
||||
};
|
||||
|
||||
const size_t item_height = 24;
|
||||
bool blink = false;
|
||||
bool more = false;
|
||||
size_t displayed_max { 0 };
|
||||
size_t highlighted_item { 0 };
|
||||
size_t offset { 0 };
|
||||
};
|
||||
|
||||
} /* namespace ui */
|
||||
|
||||
#endif/*__UI_MENU_H__*/
|
362
firmware/application/ui/ui_receiver.cpp
Normal file
362
firmware/application/ui/ui_receiver.cpp
Normal file
@@ -0,0 +1,362 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
|
||||
* Copyright (C) 2016 Furrtek
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "ui_receiver.hpp"
|
||||
#include "ui_freqman.hpp"
|
||||
|
||||
#include "portapack.hpp"
|
||||
using namespace portapack;
|
||||
|
||||
#include "string_format.hpp"
|
||||
|
||||
#include "max2837.hpp"
|
||||
|
||||
namespace ui {
|
||||
|
||||
/* FrequencyField ********************************************************/
|
||||
|
||||
FrequencyField::FrequencyField(
|
||||
const Point parent_pos
|
||||
) : Widget { { parent_pos, { 8 * 10, 16 } } },
|
||||
length_ { 11 },
|
||||
range(rf::tuning_range)
|
||||
{
|
||||
set_focusable(true);
|
||||
}
|
||||
|
||||
rf::Frequency FrequencyField::value() const {
|
||||
return value_;
|
||||
}
|
||||
|
||||
void FrequencyField::set_value(rf::Frequency new_value) {
|
||||
new_value = clamp_value(new_value);
|
||||
|
||||
if( new_value != value_ ) {
|
||||
value_ = new_value;
|
||||
if( on_change ) {
|
||||
on_change(value_);
|
||||
}
|
||||
set_dirty();
|
||||
}
|
||||
}
|
||||
|
||||
void FrequencyField::set_step(rf::Frequency new_value) {
|
||||
step = new_value;
|
||||
// TODO: Quantize current frequency to a step of the new size?
|
||||
}
|
||||
|
||||
void FrequencyField::paint(Painter& painter) {
|
||||
const std::string str_value = to_string_short_freq(value_);
|
||||
|
||||
const auto paint_style = has_focus() ? style().invert() : style();
|
||||
|
||||
painter.draw_string(
|
||||
screen_pos(),
|
||||
paint_style,
|
||||
str_value
|
||||
);
|
||||
}
|
||||
|
||||
bool FrequencyField::on_key(const ui::KeyEvent event) {
|
||||
if( event == ui::KeyEvent::Select ) {
|
||||
if( on_edit ) {
|
||||
on_edit();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool FrequencyField::on_encoder(const EncoderEvent delta) {
|
||||
set_value(value() + (delta * step));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FrequencyField::on_touch(const TouchEvent event) {
|
||||
if( event.type == TouchEvent::Type::Start ) {
|
||||
focus();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void FrequencyField::on_focus() {
|
||||
if( on_show_options ) {
|
||||
on_show_options();
|
||||
}
|
||||
}
|
||||
|
||||
rf::Frequency FrequencyField::clamp_value(rf::Frequency value) {
|
||||
return range.clip(value);
|
||||
}
|
||||
|
||||
/* FrequencyKeypadView ***************************************************/
|
||||
|
||||
FrequencyKeypadView::FrequencyKeypadView(
|
||||
NavigationView& nav,
|
||||
const rf::Frequency value
|
||||
) {
|
||||
add_child(&text_value);
|
||||
|
||||
const auto button_fn = [this](Button& button) {
|
||||
this->on_button(button);
|
||||
};
|
||||
|
||||
const char* const key_caps = "123456789<0.";
|
||||
|
||||
int n = 0;
|
||||
for(auto& button : buttons) {
|
||||
add_child(&button);
|
||||
const std::string label {
|
||||
key_caps[n]
|
||||
};
|
||||
button.on_select = button_fn;
|
||||
button.set_parent_rect({
|
||||
(n % 3) * button_w,
|
||||
(n / 3) * button_h + 24,
|
||||
button_w, button_h
|
||||
});
|
||||
button.set_text(label);
|
||||
n++;
|
||||
}
|
||||
|
||||
add_children({
|
||||
&button_save,
|
||||
&button_load,
|
||||
&button_close
|
||||
});
|
||||
|
||||
button_save.on_select = [this, &nav](Button&) {
|
||||
nav.push<FrequencySaveView>(this->value());
|
||||
};
|
||||
button_load.on_select = [this, &nav](Button&) {
|
||||
auto load_view = nav.push<FrequencyLoadView>();
|
||||
load_view->on_frequency_loaded = [this](rf::Frequency value) {
|
||||
set_value(value);
|
||||
};
|
||||
};
|
||||
|
||||
button_close.on_select = [this, &nav](Button&) {
|
||||
if( on_changed ) {
|
||||
on_changed(this->value());
|
||||
}
|
||||
nav.pop();
|
||||
};
|
||||
|
||||
set_value(value);
|
||||
}
|
||||
|
||||
void FrequencyKeypadView::focus() {
|
||||
button_close.focus();
|
||||
}
|
||||
|
||||
rf::Frequency FrequencyKeypadView::value() const {
|
||||
return mhz.as_int() * 1000000ULL + submhz.as_int() * submhz_base;
|
||||
}
|
||||
|
||||
void FrequencyKeypadView::set_value(const rf::Frequency new_value) {
|
||||
mhz.set(new_value / 1000000);
|
||||
mhz.remove_leading_zeros();
|
||||
|
||||
submhz.set((new_value % 1000000) / submhz_base);
|
||||
submhz.remove_trailing_zeros();
|
||||
|
||||
update_text();
|
||||
}
|
||||
|
||||
void FrequencyKeypadView::on_button(Button& button) {
|
||||
const auto s = button.text();
|
||||
if( s == "." ) {
|
||||
field_toggle();
|
||||
} else if( s == "<" ) {
|
||||
digit_delete();
|
||||
} else {
|
||||
digit_add(s[0]);
|
||||
}
|
||||
update_text();
|
||||
}
|
||||
|
||||
void FrequencyKeypadView::digit_add(const char c) {
|
||||
if( state == State::DigitMHz ) {
|
||||
if( clear_field_if_digits_entered ) {
|
||||
mhz.clear();
|
||||
}
|
||||
mhz.add_digit(c);
|
||||
} else {
|
||||
submhz.add_digit(c);
|
||||
}
|
||||
clear_field_if_digits_entered = false;
|
||||
}
|
||||
|
||||
void FrequencyKeypadView::digit_delete() {
|
||||
if( state == State::DigitMHz ) {
|
||||
mhz.delete_digit();
|
||||
} else {
|
||||
submhz.delete_digit();
|
||||
}
|
||||
}
|
||||
|
||||
void FrequencyKeypadView::field_toggle() {
|
||||
if( state == State::DigitMHz ) {
|
||||
state = State::DigitSubMHz;
|
||||
submhz.clear();
|
||||
} else {
|
||||
state = State::DigitMHz;
|
||||
clear_field_if_digits_entered = true;
|
||||
}
|
||||
}
|
||||
|
||||
void FrequencyKeypadView::update_text() {
|
||||
const auto s = mhz.as_string() + "." + submhz.as_string();
|
||||
text_value.set(s);
|
||||
}
|
||||
|
||||
/* FrequencyOptionsView **************************************************/
|
||||
|
||||
FrequencyOptionsView::FrequencyOptionsView(
|
||||
const Rect parent_rect,
|
||||
const Style* const style
|
||||
) : View { parent_rect }
|
||||
{
|
||||
set_style(style);
|
||||
|
||||
field_step.on_change = [this](size_t n, OptionsField::value_t v) {
|
||||
(void)n;
|
||||
this->on_step_changed(v);
|
||||
};
|
||||
|
||||
field_ppm.on_change = [this](int32_t v) {
|
||||
this->on_reference_ppm_correction_changed(v);
|
||||
};
|
||||
|
||||
add_children({
|
||||
&text_step,
|
||||
&field_step,
|
||||
&field_ppm,
|
||||
&text_ppm,
|
||||
});
|
||||
}
|
||||
|
||||
void FrequencyOptionsView::set_step(rf::Frequency f) {
|
||||
field_step.set_by_value(f);
|
||||
}
|
||||
|
||||
void FrequencyOptionsView::set_reference_ppm_correction(int32_t v) {
|
||||
field_ppm.set_value(v);
|
||||
}
|
||||
|
||||
void FrequencyOptionsView::on_step_changed(rf::Frequency v) {
|
||||
if( on_change_step ) {
|
||||
on_change_step(v);
|
||||
}
|
||||
}
|
||||
|
||||
void FrequencyOptionsView::on_reference_ppm_correction_changed(int32_t v) {
|
||||
if( on_change_reference_ppm_correction ) {
|
||||
on_change_reference_ppm_correction(v);
|
||||
}
|
||||
}
|
||||
|
||||
/* RFAmpField ************************************************************/
|
||||
|
||||
RFAmpField::RFAmpField(
|
||||
Point parent_pos
|
||||
) : NumberField {
|
||||
parent_pos,
|
||||
1,
|
||||
{ 0, 1 },
|
||||
1,
|
||||
' ',
|
||||
}
|
||||
{
|
||||
set_value(receiver_model.rf_amp());
|
||||
|
||||
on_change = [](int32_t v) {
|
||||
receiver_model.set_rf_amp(v);
|
||||
};
|
||||
}
|
||||
|
||||
/* RadioGainOptionsView **************************************************/
|
||||
|
||||
RadioGainOptionsView::RadioGainOptionsView(
|
||||
const Rect parent_rect,
|
||||
const Style* const style
|
||||
) : View { parent_rect }
|
||||
{
|
||||
set_style(style);
|
||||
|
||||
add_children({
|
||||
&label_rf_amp,
|
||||
&field_rf_amp,
|
||||
});
|
||||
}
|
||||
|
||||
/* LNAGainField **********************************************************/
|
||||
|
||||
LNAGainField::LNAGainField(
|
||||
Point parent_pos
|
||||
) : NumberField {
|
||||
parent_pos, 2,
|
||||
{ max2837::lna::gain_db_range.minimum, max2837::lna::gain_db_range.maximum },
|
||||
max2837::lna::gain_db_step,
|
||||
' ',
|
||||
}
|
||||
{
|
||||
set_value(receiver_model.lna());
|
||||
|
||||
on_change = [](int32_t v) {
|
||||
receiver_model.set_lna(v);
|
||||
};
|
||||
}
|
||||
|
||||
void LNAGainField::on_focus() {
|
||||
//Widget::on_focus();
|
||||
if( on_show_options ) {
|
||||
on_show_options();
|
||||
}
|
||||
}
|
||||
|
||||
/* VGAGainField **********************************************************/
|
||||
|
||||
VGAGainField::VGAGainField(
|
||||
Point parent_pos
|
||||
) : NumberField {
|
||||
parent_pos, 2,
|
||||
{ max2837::vga::gain_db_range.minimum, max2837::vga::gain_db_range.maximum },
|
||||
max2837::vga::gain_db_step,
|
||||
' ',
|
||||
}
|
||||
{
|
||||
set_value(receiver_model.vga());
|
||||
|
||||
on_change = [](int32_t v) {
|
||||
receiver_model.set_vga(v);
|
||||
};
|
||||
}
|
||||
|
||||
void VGAGainField::on_focus() {
|
||||
//Widget::on_focus();
|
||||
if( on_show_options ) {
|
||||
on_show_options();
|
||||
}
|
||||
}
|
||||
|
||||
} /* namespace ui */
|
343
firmware/application/ui/ui_receiver.hpp
Normal file
343
firmware/application/ui/ui_receiver.hpp
Normal file
@@ -0,0 +1,343 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
|
||||
*
|
||||
* 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 __UI_RECEIVER_H__
|
||||
#define __UI_RECEIVER_H__
|
||||
|
||||
#include "ui.hpp"
|
||||
#include "ui_navigation.hpp"
|
||||
#include "ui_painter.hpp"
|
||||
#include "ui_widget.hpp"
|
||||
|
||||
#include "rf_path.hpp"
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
|
||||
namespace ui {
|
||||
|
||||
class FrequencyField : public Widget {
|
||||
public:
|
||||
std::function<void(rf::Frequency)> on_change { };
|
||||
std::function<void(void)> on_edit { };
|
||||
std::function<void(void)> on_show_options { };
|
||||
|
||||
using range_t = rf::FrequencyRange;
|
||||
|
||||
FrequencyField(const Point parent_pos);
|
||||
|
||||
rf::Frequency value() const;
|
||||
|
||||
void set_value(rf::Frequency new_value);
|
||||
void set_step(rf::Frequency new_value);
|
||||
|
||||
void paint(Painter& painter) override;
|
||||
|
||||
bool on_key(const ui::KeyEvent event) override;
|
||||
bool on_encoder(const EncoderEvent delta) override;
|
||||
bool on_touch(const TouchEvent event) override;
|
||||
void on_focus() override;
|
||||
|
||||
private:
|
||||
const size_t length_;
|
||||
const range_t range;
|
||||
rf::Frequency value_ { 0 };
|
||||
rf::Frequency step { 25000 };
|
||||
|
||||
rf::Frequency clamp_value(rf::Frequency value);
|
||||
};
|
||||
|
||||
template<size_t N>
|
||||
class FieldString {
|
||||
public:
|
||||
enum Justify {
|
||||
Right,
|
||||
Left,
|
||||
};
|
||||
|
||||
constexpr FieldString(
|
||||
Justify justify
|
||||
) : justify { justify }
|
||||
{
|
||||
}
|
||||
|
||||
uint32_t as_int() const {
|
||||
uint32_t value = 0;
|
||||
for(const auto c : s) {
|
||||
const uint_fast8_t digit = (c == ' ') ? 0 : c - '0';
|
||||
value = (value * 10) + digit;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
void set(uint32_t value) {
|
||||
std::generate(s.rbegin(), s.rend(), [&value]() {
|
||||
const char digit = (value % 10) + '0';
|
||||
value /= 10;
|
||||
return digit;
|
||||
});
|
||||
}
|
||||
|
||||
void clear() {
|
||||
s.fill(' ');
|
||||
}
|
||||
|
||||
void add_digit(const char c) {
|
||||
insert_right(c);
|
||||
}
|
||||
|
||||
void delete_digit() {
|
||||
if( justify == Justify::Right ) {
|
||||
shift_right();
|
||||
s.front() = ' ';
|
||||
} else {
|
||||
auto first_digit = std::find_if(s.rbegin(), s.rend(), [](const char& a) {
|
||||
return a != ' ';
|
||||
});
|
||||
if( first_digit != s.rend() ) {
|
||||
*first_digit = ' ';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string as_string() const {
|
||||
return { s.data(), s.size() };
|
||||
}
|
||||
|
||||
void remove_leading_zeros() {
|
||||
remove_zeros(s.begin(), s.end());
|
||||
}
|
||||
|
||||
void remove_trailing_zeros() {
|
||||
remove_zeros(s.rbegin(), s.rend());
|
||||
}
|
||||
|
||||
private:
|
||||
using array_type = std::array<char, N>;
|
||||
|
||||
array_type s { };
|
||||
Justify justify { Justify::Left };
|
||||
|
||||
template<typename Iterator>
|
||||
void remove_zeros(Iterator begin, Iterator end) {
|
||||
const auto first_significant_digit =
|
||||
std::find_if(begin, end, [](const char& a) {
|
||||
return a != '0';
|
||||
});
|
||||
std::fill(begin, first_significant_digit, ' ');
|
||||
}
|
||||
|
||||
void insert_right(const char c) {
|
||||
auto insert_point = s.end() - 1;
|
||||
|
||||
if( justify == Justify::Left ) {
|
||||
insert_point = std::find_if(s.begin(), s.end(), [](const char& a) {
|
||||
return a == ' ';
|
||||
});
|
||||
}
|
||||
|
||||
if( *insert_point != ' ' ) {
|
||||
insert_point = shift_left();
|
||||
}
|
||||
|
||||
*insert_point = c;
|
||||
}
|
||||
|
||||
typename array_type::iterator shift_left() {
|
||||
return std::move(s.begin() + 1, s.end(), s.begin());
|
||||
}
|
||||
|
||||
typename array_type::iterator shift_right() {
|
||||
return std::move_backward(s.begin(), s.end() - 1, s.end());
|
||||
}
|
||||
};
|
||||
|
||||
class FrequencyKeypadView : public View {
|
||||
public:
|
||||
std::function<void(rf::Frequency)> on_changed { };
|
||||
|
||||
FrequencyKeypadView(
|
||||
NavigationView& nav,
|
||||
const rf::Frequency value
|
||||
);
|
||||
|
||||
void focus() override;
|
||||
|
||||
rf::Frequency value() const;
|
||||
void set_value(const rf::Frequency new_value);
|
||||
|
||||
private:
|
||||
static constexpr int button_w = 240 / 3;
|
||||
static constexpr int button_h = 48;
|
||||
|
||||
static constexpr int mhz_digits = 4;
|
||||
static constexpr int submhz_digits = 4;
|
||||
|
||||
static constexpr int mhz_mod = pow(10, mhz_digits);
|
||||
static constexpr int submhz_base = pow(10, 6 - submhz_digits);
|
||||
static constexpr int text_digits = mhz_digits + 1 + submhz_digits;
|
||||
|
||||
Text text_value {
|
||||
{ 0, 4, 240, 16 }
|
||||
};
|
||||
|
||||
std::array<Button, 12> buttons { };
|
||||
|
||||
Button button_save {
|
||||
{ 0, button_h * 5, 60, button_h },
|
||||
"Save"
|
||||
};
|
||||
Button button_load {
|
||||
{ 60, button_h * 5, 60, button_h },
|
||||
"Load"
|
||||
};
|
||||
Button button_close {
|
||||
{ 128, button_h * 5, 112, button_h },
|
||||
"Done"
|
||||
};
|
||||
|
||||
/* TODO: Template arg required in enum?! */
|
||||
FieldString<mhz_digits> mhz { FieldString<4>::Justify::Right };
|
||||
FieldString<submhz_digits> submhz { FieldString<4>::Justify::Left };
|
||||
|
||||
enum State {
|
||||
DigitMHz,
|
||||
DigitSubMHz
|
||||
};
|
||||
|
||||
State state { DigitMHz };
|
||||
bool clear_field_if_digits_entered { true };
|
||||
|
||||
void on_button(Button& button);
|
||||
|
||||
void digit_add(const char c);
|
||||
void digit_delete();
|
||||
|
||||
void field_toggle();
|
||||
void update_text();
|
||||
};
|
||||
|
||||
class FrequencyStepView : public OptionsField {
|
||||
public:
|
||||
FrequencyStepView(
|
||||
Point parent_pos
|
||||
) : OptionsField {
|
||||
parent_pos,
|
||||
5,
|
||||
{
|
||||
{ " 100", 100 },
|
||||
{ " 1k ", 1000 },
|
||||
{ " 3k ", 3000 }, /* Approximate SSB bandwidth */
|
||||
{ " 5k ", 5000 },
|
||||
{ " 6k3", 6250 },
|
||||
{ " 9k ", 9000 }, /* channel spacing for LF, MF in some regions */
|
||||
{ " 10k ", 10000 },
|
||||
{ " 12k5", 12500 },
|
||||
{ " 25k ", 25000 },
|
||||
{ "100k ", 100000 },
|
||||
{ " 1M ", 1000000 },
|
||||
{ " 10M ", 10000000 },
|
||||
}
|
||||
}
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
class FrequencyOptionsView : public View {
|
||||
public:
|
||||
std::function<void(rf::Frequency)> on_change_step { };
|
||||
std::function<void(int32_t)> on_change_reference_ppm_correction { };
|
||||
|
||||
FrequencyOptionsView(const Rect parent_rect, const Style* const style);
|
||||
|
||||
void set_step(rf::Frequency f);
|
||||
void set_reference_ppm_correction(int32_t v);
|
||||
|
||||
private:
|
||||
Text text_step {
|
||||
{ 0 * 8, 0 * 16, 4 * 8, 1 * 16 },
|
||||
"Step"
|
||||
};
|
||||
|
||||
FrequencyStepView field_step {
|
||||
{ 5 * 8, 0 * 16 },
|
||||
};
|
||||
|
||||
void on_step_changed(rf::Frequency v);
|
||||
void on_reference_ppm_correction_changed(int32_t v);
|
||||
|
||||
NumberField field_ppm {
|
||||
{ 23 * 8, 0 * 16 },
|
||||
3,
|
||||
{ -99, 99 },
|
||||
1,
|
||||
'0',
|
||||
};
|
||||
|
||||
Text text_ppm {
|
||||
{ 27 * 8, 0 * 16, 3 * 8, 16 },
|
||||
"PPM",
|
||||
};
|
||||
};
|
||||
|
||||
class RFAmpField : public NumberField {
|
||||
public:
|
||||
RFAmpField(Point parent_pos);
|
||||
};
|
||||
|
||||
class RadioGainOptionsView : public View {
|
||||
public:
|
||||
RadioGainOptionsView(const Rect parent_rect, const Style* const style);
|
||||
|
||||
private:
|
||||
Text label_rf_amp {
|
||||
{ 0 * 8, 0 * 16, 3 * 8, 1 * 16 },
|
||||
"Amp"
|
||||
};
|
||||
|
||||
RFAmpField field_rf_amp {
|
||||
{ 4 * 8, 0 * 16},
|
||||
};
|
||||
};
|
||||
|
||||
class LNAGainField : public NumberField {
|
||||
public:
|
||||
std::function<void(void)> on_show_options { };
|
||||
|
||||
LNAGainField(Point parent_pos);
|
||||
|
||||
void on_focus() override;
|
||||
};
|
||||
|
||||
class VGAGainField : public NumberField {
|
||||
public:
|
||||
std::function<void(void)> on_show_options { };
|
||||
|
||||
VGAGainField(Point parent_pos);
|
||||
|
||||
void on_focus() override;
|
||||
};
|
||||
|
||||
} /* namespace ui */
|
||||
|
||||
#endif/*__UI_RECEIVER_H__*/
|
94
firmware/application/ui/ui_rssi.cpp
Normal file
94
firmware/application/ui/ui_rssi.cpp
Normal file
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "ui_rssi.hpp"
|
||||
|
||||
#include "baseband_api.hpp"
|
||||
#include "utility.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
namespace ui {
|
||||
|
||||
void RSSI::paint(Painter& painter) {
|
||||
const auto r = screen_rect();
|
||||
|
||||
constexpr int rssi_sample_range = 256;
|
||||
constexpr float rssi_voltage_min = 0.4;
|
||||
constexpr float rssi_voltage_max = 2.2;
|
||||
constexpr float adc_voltage_max = 3.3;
|
||||
constexpr int raw_min = rssi_sample_range * rssi_voltage_min / adc_voltage_max;
|
||||
constexpr int raw_max = rssi_sample_range * rssi_voltage_max / adc_voltage_max;
|
||||
constexpr int raw_delta = raw_max - raw_min;
|
||||
const range_t<int> x_avg_range { 0, r.width() - 1 };
|
||||
const auto x_avg = x_avg_range.clip((avg_ - raw_min) * r.width() / raw_delta);
|
||||
const range_t<int> x_min_range { 0, x_avg };
|
||||
const auto x_min = x_min_range.clip((min_ - raw_min) * r.width() / raw_delta);
|
||||
const range_t<int> x_max_range { x_avg + 1, r.width() };
|
||||
const auto x_max = x_max_range.clip((max_ - raw_min) * r.width() / raw_delta);
|
||||
|
||||
const Rect r0 { r.left(), r.top(), x_min, r.height() };
|
||||
painter.fill_rectangle(
|
||||
r0,
|
||||
Color::blue()
|
||||
);
|
||||
|
||||
const Rect r1 { r.left() + x_min, r.top(), x_avg - x_min, r.height() };
|
||||
painter.fill_rectangle(
|
||||
r1,
|
||||
Color::red()
|
||||
);
|
||||
|
||||
const Rect r2 { r.left() + x_avg, r.top(), 1, r.height() };
|
||||
painter.fill_rectangle(
|
||||
r2,
|
||||
Color::white()
|
||||
);
|
||||
|
||||
const Rect r3 { r.left() + x_avg + 1, r.top(), x_max - (x_avg + 1), r.height() };
|
||||
painter.fill_rectangle(
|
||||
r3,
|
||||
Color::red()
|
||||
);
|
||||
|
||||
const Rect r4 { r.left() + x_max, r.top(), r.width() - x_max, r.height() };
|
||||
painter.fill_rectangle(
|
||||
r4,
|
||||
Color::black()
|
||||
);
|
||||
|
||||
if (pitch_rssi_enabled)
|
||||
baseband::set_pitch_rssi((avg_ - raw_min) * 2000 / raw_delta, true);
|
||||
}
|
||||
|
||||
void RSSI::set_pitch_rssi(bool enabled) {
|
||||
pitch_rssi_enabled = enabled;
|
||||
if (!enabled) baseband::set_pitch_rssi(0, false);
|
||||
}
|
||||
|
||||
void RSSI::on_statistics_update(const RSSIStatistics& statistics) {
|
||||
min_ = statistics.min;
|
||||
avg_ = statistics.accumulator / statistics.count;
|
||||
max_ = statistics.max;
|
||||
set_dirty();
|
||||
}
|
||||
|
||||
} /* namespace ui */
|
78
firmware/application/ui/ui_rssi.hpp
Normal file
78
firmware/application/ui/ui_rssi.hpp
Normal file
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
|
||||
*
|
||||
* 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 __UI_RSSI_H__
|
||||
#define __UI_RSSI_H__
|
||||
|
||||
#include "ui.hpp"
|
||||
#include "ui_widget.hpp"
|
||||
#include "ui_painter.hpp"
|
||||
|
||||
#include "event_m0.hpp"
|
||||
|
||||
#include "message.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace ui {
|
||||
|
||||
class RSSI : public Widget {
|
||||
public:
|
||||
RSSI(
|
||||
const Rect parent_rect
|
||||
) : Widget { parent_rect },
|
||||
min_ { 0 },
|
||||
avg_ { 0 },
|
||||
max_ { 0 }
|
||||
{
|
||||
}
|
||||
|
||||
void paint(Painter& painter) override;
|
||||
|
||||
private:
|
||||
int32_t min_;
|
||||
int32_t avg_;
|
||||
int32_t max_;
|
||||
|
||||
bool pitch_rssi_enabled = false;
|
||||
|
||||
MessageHandlerRegistration message_handler_stats {
|
||||
Message::ID::RSSIStatistics,
|
||||
[this](const Message* const p) {
|
||||
this->on_statistics_update(static_cast<const RSSIStatisticsMessage*>(p)->statistics);
|
||||
}
|
||||
};
|
||||
|
||||
MessageHandlerRegistration message_handler_pitch_rssi {
|
||||
Message::ID::PitchRSSIConfigure,
|
||||
[this](const Message* const p) {
|
||||
const auto message = *reinterpret_cast<const PitchRSSIConfigureMessage*>(p);
|
||||
this->set_pitch_rssi(message.enabled);
|
||||
}
|
||||
};
|
||||
|
||||
void on_statistics_update(const RSSIStatistics& statistics);
|
||||
void set_pitch_rssi(bool enabled);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif/*__UI_RSSI_H__*/
|
271
firmware/application/ui/ui_spectrum.cpp
Normal file
271
firmware/application/ui/ui_spectrum.cpp
Normal file
@@ -0,0 +1,271 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "ui_spectrum.hpp"
|
||||
|
||||
#include "spectrum_color_lut.hpp"
|
||||
|
||||
#include "portapack.hpp"
|
||||
using namespace portapack;
|
||||
|
||||
#include "baseband_api.hpp"
|
||||
|
||||
#include "string_format.hpp"
|
||||
|
||||
#include <cmath>
|
||||
#include <array>
|
||||
|
||||
namespace ui {
|
||||
namespace spectrum {
|
||||
|
||||
/* FrequencyScale ********************************************************/
|
||||
|
||||
void FrequencyScale::on_show() {
|
||||
clear();
|
||||
}
|
||||
|
||||
void FrequencyScale::set_spectrum_sampling_rate(const int new_sampling_rate) {
|
||||
if( (spectrum_sampling_rate != new_sampling_rate) ) {
|
||||
spectrum_sampling_rate = new_sampling_rate;
|
||||
set_dirty();
|
||||
}
|
||||
}
|
||||
|
||||
void FrequencyScale::set_channel_filter(
|
||||
const int pass_frequency,
|
||||
const int stop_frequency
|
||||
) {
|
||||
if( (channel_filter_pass_frequency != pass_frequency) ||
|
||||
(channel_filter_stop_frequency != stop_frequency) ) {
|
||||
channel_filter_pass_frequency = pass_frequency;
|
||||
channel_filter_stop_frequency = stop_frequency;
|
||||
set_dirty();
|
||||
}
|
||||
}
|
||||
|
||||
void FrequencyScale::paint(Painter& painter) {
|
||||
const auto r = screen_rect();
|
||||
|
||||
clear_background(painter, r);
|
||||
|
||||
if( !spectrum_sampling_rate ) {
|
||||
// Can't draw without non-zero scale.
|
||||
return;
|
||||
}
|
||||
|
||||
draw_filter_ranges(painter, r);
|
||||
draw_frequency_ticks(painter, r);
|
||||
}
|
||||
|
||||
void FrequencyScale::clear() {
|
||||
spectrum_sampling_rate = 0;
|
||||
set_dirty();
|
||||
}
|
||||
|
||||
void FrequencyScale::clear_background(Painter& painter, const Rect r) {
|
||||
painter.fill_rectangle(r, Color::black());
|
||||
}
|
||||
|
||||
void FrequencyScale::draw_frequency_ticks(Painter& painter, const Rect r) {
|
||||
const auto x_center = r.width() / 2;
|
||||
|
||||
const Rect tick { r.left() + x_center, r.top(), 1, r.height() };
|
||||
painter.fill_rectangle(tick, Color::white());
|
||||
|
||||
constexpr int tick_count_max = 4;
|
||||
float rough_tick_interval = float(spectrum_sampling_rate) / tick_count_max;
|
||||
int magnitude = 1;
|
||||
int magnitude_n = 0;
|
||||
while(rough_tick_interval >= 10.0f) {
|
||||
rough_tick_interval /= 10;
|
||||
magnitude *= 10;
|
||||
magnitude_n += 1;
|
||||
}
|
||||
const int tick_interval = std::ceil(rough_tick_interval);
|
||||
|
||||
auto tick_offset = tick_interval;
|
||||
while((tick_offset * magnitude) < spectrum_sampling_rate / 2) {
|
||||
const Dim pixel_offset = tick_offset * magnitude * spectrum_bins / spectrum_sampling_rate;
|
||||
|
||||
const std::string zero_pad =
|
||||
((magnitude_n % 3) == 0) ? "" :
|
||||
((magnitude_n % 3) == 1) ? "0" : "00";
|
||||
const std::string unit =
|
||||
(magnitude_n >= 6) ? "M" :
|
||||
(magnitude_n >= 3) ? "k" : "";
|
||||
const std::string label = to_string_dec_uint(tick_offset) + zero_pad + unit;
|
||||
const auto label_width = style().font.size_of(label).width();
|
||||
|
||||
const Coord offset_low = r.left() + x_center - pixel_offset;
|
||||
const Rect tick_low { offset_low, r.top(), 1, r.height() };
|
||||
painter.fill_rectangle(tick_low, Color::white());
|
||||
painter.draw_string({ offset_low + 2, r.top() }, style(), label );
|
||||
|
||||
const Coord offset_high = r.left() + x_center + pixel_offset;
|
||||
const Rect tick_high { offset_high, r.top(), 1, r.height() };
|
||||
painter.fill_rectangle(tick_high, Color::white());
|
||||
painter.draw_string({ offset_high - 2 - label_width, r.top() }, style(), label );
|
||||
|
||||
tick_offset += tick_interval;
|
||||
}
|
||||
}
|
||||
|
||||
void FrequencyScale::draw_filter_ranges(Painter& painter, const Rect r) {
|
||||
if( channel_filter_pass_frequency ) {
|
||||
const auto x_center = r.width() / 2;
|
||||
|
||||
const auto pass_offset = channel_filter_pass_frequency * spectrum_bins / spectrum_sampling_rate;
|
||||
const auto stop_offset = channel_filter_stop_frequency * spectrum_bins / spectrum_sampling_rate;
|
||||
|
||||
const auto pass_x_lo = x_center - pass_offset;
|
||||
const auto pass_x_hi = x_center + pass_offset;
|
||||
|
||||
if( channel_filter_stop_frequency ) {
|
||||
const auto stop_x_lo = x_center - stop_offset;
|
||||
const auto stop_x_hi = x_center + stop_offset;
|
||||
|
||||
const Rect r_stop_lo {
|
||||
r.left() + stop_x_lo, r.bottom() - filter_band_height,
|
||||
pass_x_lo - stop_x_lo, filter_band_height
|
||||
};
|
||||
painter.fill_rectangle(
|
||||
r_stop_lo,
|
||||
Color::yellow()
|
||||
);
|
||||
|
||||
const Rect r_stop_hi {
|
||||
r.left() + pass_x_hi, r.bottom() - filter_band_height,
|
||||
stop_x_hi - pass_x_hi, filter_band_height
|
||||
};
|
||||
painter.fill_rectangle(
|
||||
r_stop_hi,
|
||||
Color::yellow()
|
||||
);
|
||||
}
|
||||
|
||||
const Rect r_pass {
|
||||
r.left() + pass_x_lo, r.bottom() - filter_band_height,
|
||||
pass_x_hi - pass_x_lo, filter_band_height
|
||||
};
|
||||
painter.fill_rectangle(
|
||||
r_pass,
|
||||
Color::green()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/* WaterfallView *********************************************************/
|
||||
|
||||
void WaterfallView::on_show() {
|
||||
clear();
|
||||
|
||||
const auto screen_r = screen_rect();
|
||||
display.scroll_set_area(screen_r.top(), screen_r.bottom());
|
||||
}
|
||||
|
||||
void WaterfallView::on_hide() {
|
||||
/* TODO: Clear region to eliminate brief flash of content at un-shifted
|
||||
* position?
|
||||
*/
|
||||
display.scroll_disable();
|
||||
}
|
||||
|
||||
void WaterfallView::paint(Painter& painter) {
|
||||
// Do nothing.
|
||||
(void)painter;
|
||||
}
|
||||
|
||||
void WaterfallView::on_channel_spectrum(
|
||||
const ChannelSpectrum& spectrum
|
||||
) {
|
||||
/* TODO: static_assert that message.spectrum.db.size() >= pixel_row.size() */
|
||||
|
||||
std::array<Color, 240> pixel_row;
|
||||
for(size_t i=0; i<120; i++) {
|
||||
const auto pixel_color = spectrum_rgb3_lut[spectrum.db[256 - 120 + i]];
|
||||
pixel_row[i] = pixel_color;
|
||||
}
|
||||
|
||||
for(size_t i=120; i<240; i++) {
|
||||
const auto pixel_color = spectrum_rgb3_lut[spectrum.db[i - 120]];
|
||||
pixel_row[i] = pixel_color;
|
||||
}
|
||||
|
||||
const auto draw_y = display.scroll(1);
|
||||
|
||||
display.draw_pixels(
|
||||
{ { 0, draw_y }, { pixel_row.size(), 1 } },
|
||||
pixel_row
|
||||
);
|
||||
}
|
||||
|
||||
void WaterfallView::clear() {
|
||||
display.fill_rectangle(
|
||||
screen_rect(),
|
||||
Color::black()
|
||||
);
|
||||
}
|
||||
|
||||
/* WaterfallWidget *******************************************************/
|
||||
|
||||
WaterfallWidget::WaterfallWidget() {
|
||||
add_children({
|
||||
&waterfall_view,
|
||||
&frequency_scale,
|
||||
});
|
||||
}
|
||||
|
||||
void WaterfallWidget::on_show() {
|
||||
baseband::spectrum_streaming_start();
|
||||
}
|
||||
|
||||
void WaterfallWidget::on_hide() {
|
||||
baseband::spectrum_streaming_stop();
|
||||
}
|
||||
|
||||
void WaterfallWidget::set_parent_rect(const Rect new_parent_rect) {
|
||||
constexpr Dim scale_height = 20;
|
||||
|
||||
View::set_parent_rect(new_parent_rect);
|
||||
frequency_scale.set_parent_rect({ 0, 0, new_parent_rect.width(), scale_height });
|
||||
waterfall_view.set_parent_rect({
|
||||
0, scale_height,
|
||||
new_parent_rect.width(),
|
||||
new_parent_rect.height() - scale_height
|
||||
});
|
||||
}
|
||||
|
||||
void WaterfallWidget::paint(Painter& painter) {
|
||||
// TODO:
|
||||
(void)painter;
|
||||
}
|
||||
|
||||
void WaterfallWidget::on_channel_spectrum(const ChannelSpectrum& spectrum) {
|
||||
waterfall_view.on_channel_spectrum(spectrum);
|
||||
frequency_scale.set_spectrum_sampling_rate(spectrum.sampling_rate);
|
||||
frequency_scale.set_channel_filter(
|
||||
spectrum.channel_filter_pass_frequency,
|
||||
spectrum.channel_filter_stop_frequency
|
||||
);
|
||||
}
|
||||
|
||||
} /* namespace spectrum */
|
||||
} /* namespace ui */
|
121
firmware/application/ui/ui_spectrum.hpp
Normal file
121
firmware/application/ui/ui_spectrum.hpp
Normal file
@@ -0,0 +1,121 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
|
||||
*
|
||||
* 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 __UI_SPECTRUM_H__
|
||||
#define __UI_SPECTRUM_H__
|
||||
|
||||
#include "ui.hpp"
|
||||
#include "ui_widget.hpp"
|
||||
|
||||
#include "event_m0.hpp"
|
||||
|
||||
#include "message.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
|
||||
namespace ui {
|
||||
namespace spectrum {
|
||||
|
||||
class FrequencyScale : public Widget {
|
||||
public:
|
||||
void on_show() override;
|
||||
|
||||
void set_spectrum_sampling_rate(const int new_sampling_rate);
|
||||
void set_channel_filter(const int pass_frequency, const int stop_frequency);
|
||||
|
||||
void paint(Painter& painter) override;
|
||||
|
||||
private:
|
||||
static constexpr int filter_band_height = 4;
|
||||
|
||||
int spectrum_sampling_rate { 0 };
|
||||
const int spectrum_bins = std::tuple_size<decltype(ChannelSpectrum::db)>::value;
|
||||
int channel_filter_pass_frequency { 0 };
|
||||
int channel_filter_stop_frequency { 0 };
|
||||
|
||||
void clear();
|
||||
void clear_background(Painter& painter, const Rect r);
|
||||
|
||||
void draw_frequency_ticks(Painter& painter, const Rect r);
|
||||
void draw_filter_ranges(Painter& painter, const Rect r);
|
||||
};
|
||||
|
||||
class WaterfallView : public Widget {
|
||||
public:
|
||||
void on_show() override;
|
||||
void on_hide() override;
|
||||
|
||||
void paint(Painter& painter) override;
|
||||
|
||||
void on_channel_spectrum(const ChannelSpectrum& spectrum);
|
||||
|
||||
private:
|
||||
void clear();
|
||||
};
|
||||
|
||||
class WaterfallWidget : public View {
|
||||
public:
|
||||
WaterfallWidget();
|
||||
|
||||
WaterfallWidget(const WaterfallWidget&) = delete;
|
||||
WaterfallWidget(WaterfallWidget&&) = delete;
|
||||
WaterfallWidget& operator=(const WaterfallWidget&) = delete;
|
||||
WaterfallWidget& operator=(WaterfallWidget&&) = delete;
|
||||
|
||||
void on_show() override;
|
||||
void on_hide() override;
|
||||
|
||||
void set_parent_rect(const Rect new_parent_rect) override;
|
||||
|
||||
void paint(Painter& painter) override;
|
||||
|
||||
private:
|
||||
WaterfallView waterfall_view { };
|
||||
FrequencyScale frequency_scale { };
|
||||
ChannelSpectrumFIFO* fifo { nullptr };
|
||||
|
||||
MessageHandlerRegistration message_handler_spectrum_config {
|
||||
Message::ID::ChannelSpectrumConfig,
|
||||
[this](const Message* const p) {
|
||||
const auto message = *reinterpret_cast<const ChannelSpectrumConfigMessage*>(p);
|
||||
this->fifo = message.fifo;
|
||||
}
|
||||
};
|
||||
MessageHandlerRegistration message_handler_frame_sync {
|
||||
Message::ID::DisplayFrameSync,
|
||||
[this](const Message* const) {
|
||||
if( this->fifo ) {
|
||||
ChannelSpectrum channel_spectrum;
|
||||
while( fifo->out(channel_spectrum) ) {
|
||||
this->on_channel_spectrum(channel_spectrum);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
void on_channel_spectrum(const ChannelSpectrum& spectrum);
|
||||
};
|
||||
|
||||
} /* namespace spectrum */
|
||||
} /* namespace ui */
|
||||
|
||||
#endif/*__UI_SPECTRUM_H__*/
|
157
firmware/application/ui/ui_tabview.cpp
Normal file
157
firmware/application/ui/ui_tabview.cpp
Normal file
@@ -0,0 +1,157 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
|
||||
* Copyright (C) 2017 Furrtek
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "ui_tabview.hpp"
|
||||
|
||||
#include "portapack.hpp"
|
||||
using namespace portapack;
|
||||
|
||||
namespace ui {
|
||||
|
||||
Tab::Tab() {
|
||||
set_focusable(true);
|
||||
};
|
||||
|
||||
void Tab::set(
|
||||
uint32_t index,
|
||||
Dim width,
|
||||
std::string text,
|
||||
Color text_color
|
||||
) {
|
||||
set_parent_rect({ (Coord)(index * width), 0, width, 24 });
|
||||
|
||||
text_ = text.substr(0, (width - 8) / 8);
|
||||
text_color_ = text_color;
|
||||
|
||||
index_ = index;
|
||||
}
|
||||
|
||||
void Tab::paint(Painter& painter) {
|
||||
const auto rect = screen_rect();
|
||||
const Color color = highlighted() ? Color::black() : Color::grey();
|
||||
|
||||
painter.fill_rectangle({ rect.left(), rect.top(), rect.width() - 8, rect.height() }, color);
|
||||
|
||||
if (!highlighted())
|
||||
painter.draw_hline({ rect.left(), rect.top() }, rect.width() - 9, Color::light_grey());
|
||||
|
||||
painter.draw_bitmap(
|
||||
{ rect.right() - 8, rect.top() },
|
||||
bitmap_tab_edge,
|
||||
color,
|
||||
Color::dark_grey()
|
||||
);
|
||||
|
||||
auto text_point = rect.center() - Point(4, 0) - Point(text_.size() * 8 / 2, 16 / 2);
|
||||
|
||||
painter.draw_string(
|
||||
text_point,
|
||||
{ ui::font::fixed_8x16, color, text_color_ },
|
||||
text_
|
||||
);
|
||||
|
||||
if (has_focus())
|
||||
painter.draw_hline(text_point + Point(0, 16), text_.size() * 8, Color::white());
|
||||
}
|
||||
|
||||
bool Tab::on_key(const KeyEvent key) {
|
||||
if( key == KeyEvent::Select ) {
|
||||
static_cast<TabView*>(parent())->set_selected(index_);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool Tab::on_touch(const TouchEvent event) {
|
||||
switch(event.type) {
|
||||
case TouchEvent::Type::Start:
|
||||
focus();
|
||||
set_dirty();
|
||||
return true;
|
||||
|
||||
case TouchEvent::Type::End:
|
||||
static_cast<TabView*>(parent())->set_selected(index_);
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void TabView::set_selected(uint32_t index) {
|
||||
Tab * tab;
|
||||
|
||||
if (index >= n_tabs)
|
||||
return;
|
||||
|
||||
// Hide previous view
|
||||
views[current_tab]->hidden(true);
|
||||
|
||||
tab = &tabs[current_tab];
|
||||
tab->set_highlighted(false);
|
||||
tab->set_focusable(true);
|
||||
tab->set_dirty();
|
||||
|
||||
// Show new view
|
||||
views[index]->hidden(false);
|
||||
|
||||
tab = &tabs[index];
|
||||
current_tab = index;
|
||||
tab->set_highlighted(true);
|
||||
tab->set_focusable(false);
|
||||
tab->set_dirty();
|
||||
}
|
||||
|
||||
void TabView::on_show() {
|
||||
set_selected(current_tab);
|
||||
}
|
||||
|
||||
void TabView::focus() {
|
||||
views[current_tab]->focus();
|
||||
}
|
||||
|
||||
TabView::TabView(std::initializer_list<TabDef> tab_definitions) {
|
||||
size_t i = 0;
|
||||
|
||||
n_tabs = tab_definitions.size();
|
||||
if (n_tabs > MAX_TABS)
|
||||
n_tabs = MAX_TABS;
|
||||
|
||||
size_t tab_width = 240 / n_tabs;
|
||||
|
||||
set_parent_rect({ 0, 0, 30 * 8, 3 * 8 });
|
||||
|
||||
for (auto &tab_definition : tab_definitions) {
|
||||
tabs[i].set(i, tab_width, tab_definition.text, tab_definition.color);
|
||||
views[i] = tab_definition.view;
|
||||
add_child(&tabs[i]);
|
||||
i++;
|
||||
if (i == MAX_TABS) break;
|
||||
}
|
||||
}
|
||||
|
||||
TabView::~TabView() {
|
||||
}
|
||||
|
||||
} /* namespace ui */
|
80
firmware/application/ui/ui_tabview.hpp
Normal file
80
firmware/application/ui/ui_tabview.hpp
Normal file
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
|
||||
* Copyright (C) 2017 Furrtek
|
||||
*
|
||||
* 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 __UI_TABVIEW_H__
|
||||
#define __UI_TABVIEW_H__
|
||||
|
||||
#include "ui.hpp"
|
||||
#include "ui_widget.hpp"
|
||||
#include "ui_navigation.hpp"
|
||||
#include "ui_painter.hpp"
|
||||
|
||||
namespace ui {
|
||||
|
||||
#define MAX_TABS 5
|
||||
|
||||
class Tab : public Widget {
|
||||
public:
|
||||
Tab();
|
||||
|
||||
void paint(Painter& painter) override;
|
||||
|
||||
bool on_key(const KeyEvent key) override;
|
||||
bool on_touch(const TouchEvent event) override;
|
||||
|
||||
void set(uint32_t index, Dim width, std::string text, Color text_color);
|
||||
|
||||
private:
|
||||
std::string text_ { };
|
||||
Color text_color_ { };
|
||||
uint32_t index_ { };
|
||||
};
|
||||
|
||||
class TabView : public View {
|
||||
public:
|
||||
struct TabDef {
|
||||
std::string text;
|
||||
ui::Color color;
|
||||
View* view;
|
||||
};
|
||||
|
||||
TabView(std::initializer_list<TabDef> tab_definitions);
|
||||
~TabView();
|
||||
|
||||
void focus() override;
|
||||
void on_show() override;
|
||||
|
||||
void set_selected(uint32_t index);
|
||||
uint32_t selected() {
|
||||
return current_tab;
|
||||
};
|
||||
|
||||
private:
|
||||
size_t n_tabs { };
|
||||
std::array<Tab, MAX_TABS> tabs { };
|
||||
std::array<View*, MAX_TABS> views { };
|
||||
uint32_t current_tab { 0 };
|
||||
};
|
||||
|
||||
} /* namespace ui */
|
||||
|
||||
#endif/*__UI_TABVIEW_H__*/
|
118
firmware/application/ui/ui_textentry.cpp
Normal file
118
firmware/application/ui/ui_textentry.cpp
Normal file
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
|
||||
* Copyright (C) 2016 Furrtek
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "ui_textentry.hpp"
|
||||
//#include "portapack_persistent_memory.hpp"
|
||||
#include "ui_alphanum.hpp"
|
||||
//#include "ui_handwrite.hpp"
|
||||
|
||||
using namespace portapack;
|
||||
|
||||
namespace ui {
|
||||
|
||||
void text_prompt(NavigationView& nav, std::string * str, const size_t max_length, const std::function<void(std::string *)> on_done) {
|
||||
//if (persistent_memory::ui_config_textentry() == 0) {
|
||||
auto te_view = nav.push<AlphanumView>(str, max_length);
|
||||
te_view->on_changed = [on_done](std::string * value) {
|
||||
if (on_done)
|
||||
on_done(value);
|
||||
};
|
||||
/*} else {
|
||||
auto te_view = nav.push<HandWriteView>(str, max_length);
|
||||
te_view->on_changed = [on_done](std::string * value) {
|
||||
if (on_done)
|
||||
on_done(value);
|
||||
};
|
||||
}*/
|
||||
}
|
||||
|
||||
void TextEntryView::update_text() {
|
||||
if (_cursor_pos <= 28)
|
||||
text_input.set(' ' + *_str + std::string(_max_length - _str->length(), ' '));
|
||||
else
|
||||
text_input.set('<' + _str->substr(_cursor_pos - 28, 28));
|
||||
|
||||
draw_cursor();
|
||||
}
|
||||
|
||||
void TextEntryView::char_delete() {
|
||||
if (!_cursor_pos) return;
|
||||
|
||||
_cursor_pos--;
|
||||
_str->replace(_cursor_pos, 1, 1, 0);
|
||||
}
|
||||
|
||||
void TextEntryView::char_add(const char c) {
|
||||
if (_cursor_pos >= _max_length) return;
|
||||
|
||||
_str->replace(_cursor_pos, 1, 1, c);
|
||||
_cursor_pos++;
|
||||
}
|
||||
|
||||
void TextEntryView::draw_cursor() {
|
||||
Point draw_pos;
|
||||
|
||||
draw_pos = { text_input.screen_rect().location().x() + 8 + std::min((Coord)_cursor_pos, (Coord)28) * 8,
|
||||
text_input.screen_rect().location().y() + 16 };
|
||||
|
||||
display.fill_rectangle(
|
||||
{ { text_input.screen_rect().location().x(), draw_pos.y() }, { text_input.screen_rect().size().width(), 4 } },
|
||||
Color::black()
|
||||
);
|
||||
display.fill_rectangle(
|
||||
{ draw_pos, { 8, 4 } },
|
||||
Color::white()
|
||||
);
|
||||
}
|
||||
|
||||
void TextEntryView::focus() {
|
||||
button_ok.focus();
|
||||
}
|
||||
|
||||
TextEntryView::TextEntryView(
|
||||
NavigationView& nav,
|
||||
std::string * str,
|
||||
size_t max_length
|
||||
) : _str(str),
|
||||
_max_length(max_length)
|
||||
{
|
||||
|
||||
// Trim from right
|
||||
_str->erase(std::find_if(_str->rbegin(), _str->rend(), std::not1(std::ptr_fun<int, int>(std::isspace))).base(), _str->end());
|
||||
_cursor_pos = _str->length();
|
||||
|
||||
_str->reserve(_max_length);
|
||||
|
||||
add_children({
|
||||
&text_input,
|
||||
&button_ok
|
||||
});
|
||||
|
||||
button_ok.on_select = [this, &nav](Button&) {
|
||||
_str->substr(0, _cursor_pos);
|
||||
if (on_changed)
|
||||
on_changed(_str);
|
||||
nav.pop();
|
||||
};
|
||||
}
|
||||
|
||||
} /* namespace ui */
|
69
firmware/application/ui/ui_textentry.hpp
Normal file
69
firmware/application/ui/ui_textentry.hpp
Normal file
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
|
||||
* Copyright (C) 2016 Furrtek
|
||||
*
|
||||
* 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 __UI_TEXTENTRY_H__
|
||||
#define __UI_TEXTENTRY_H__
|
||||
|
||||
#include "ui.hpp"
|
||||
#include "ui_navigation.hpp"
|
||||
|
||||
namespace ui {
|
||||
|
||||
class TextEntryView : public View {
|
||||
public:
|
||||
std::function<void(std::string*)> on_changed { };
|
||||
|
||||
void focus() override;
|
||||
std::string title() const override { return "Text entry"; };
|
||||
|
||||
protected:
|
||||
TextEntryView(NavigationView& nav, std::string * str, size_t max_length);
|
||||
|
||||
TextEntryView(const TextEntryView&) = delete;
|
||||
TextEntryView(TextEntryView&&) = delete;
|
||||
TextEntryView& operator=(const TextEntryView&) = delete;
|
||||
TextEntryView& operator=(TextEntryView&&) = delete;
|
||||
|
||||
void char_add(const char c);
|
||||
void char_delete();
|
||||
void draw_cursor();
|
||||
void update_text();
|
||||
|
||||
std::string * _str;
|
||||
size_t _max_length;
|
||||
uint32_t _cursor_pos { 0 };
|
||||
|
||||
Text text_input {
|
||||
{ 0, 0, 240, 16 }
|
||||
};
|
||||
|
||||
Button button_ok {
|
||||
{ 10 * 8, 33 * 8, 9 * 8, 32 },
|
||||
"OK"
|
||||
};
|
||||
};
|
||||
|
||||
void text_prompt(NavigationView& nav, std::string * str, size_t max_length, const std::function<void(std::string*)> on_done = nullptr);
|
||||
|
||||
} /* namespace ui */
|
||||
|
||||
#endif/*__UI_TEXTENTRY_H__*/
|
164
firmware/application/ui/ui_transmitter.cpp
Normal file
164
firmware/application/ui/ui_transmitter.cpp
Normal file
@@ -0,0 +1,164 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
|
||||
* Copyright (C) 2016 Furrtek
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "ui_transmitter.hpp"
|
||||
|
||||
#include "audio.hpp"
|
||||
#include "baseband_api.hpp"
|
||||
#include "portapack.hpp"
|
||||
using namespace portapack;
|
||||
|
||||
#include "string_format.hpp"
|
||||
|
||||
#include "max2837.hpp"
|
||||
|
||||
namespace ui {
|
||||
|
||||
/* TXGainField **********************************************************/
|
||||
|
||||
TXGainField::TXGainField(
|
||||
Point parent_pos
|
||||
) : NumberField {
|
||||
parent_pos, 2,
|
||||
{ max2837::tx::gain_db_range.minimum, max2837::tx::gain_db_range.maximum },
|
||||
max2837::tx::gain_db_step,
|
||||
' ',
|
||||
}
|
||||
{
|
||||
set_value(transmitter_model.tx_gain());
|
||||
|
||||
on_change = [](int32_t v) {
|
||||
transmitter_model.set_tx_gain(v);
|
||||
};
|
||||
}
|
||||
|
||||
/* TransmitterView *******************************************************/
|
||||
|
||||
void TransmitterView::paint(Painter& painter) {
|
||||
size_t c;
|
||||
Point pos = { 0, screen_pos().y() };
|
||||
|
||||
for (c = 0; c < 20; c++) {
|
||||
painter.draw_bitmap(
|
||||
pos,
|
||||
bitmap_stripes,
|
||||
ui::Color(191, 191, 0),
|
||||
ui::Color::black()
|
||||
);
|
||||
if (c != 9)
|
||||
pos += { 24, 0 };
|
||||
else
|
||||
pos = { 0, screen_pos().y() + 32 + 8 };
|
||||
}
|
||||
}
|
||||
|
||||
void TransmitterView::on_tuning_frequency_changed(rf::Frequency f) {
|
||||
transmitter_model.set_tuning_frequency(f);
|
||||
}
|
||||
|
||||
void TransmitterView::on_channel_bandwidth_changed(uint32_t channel_bandwidth) {
|
||||
transmitter_model.set_channel_bandwidth(channel_bandwidth);
|
||||
}
|
||||
|
||||
void TransmitterView::set_transmitting(const bool transmitting) {
|
||||
if (transmitting) {
|
||||
button_start.set_text("STOP");
|
||||
button_start.set_style(&style_stop);
|
||||
} else {
|
||||
button_start.set_text("START");
|
||||
button_start.set_style(&style_start);
|
||||
}
|
||||
|
||||
transmitting_ = transmitting;
|
||||
}
|
||||
|
||||
void TransmitterView::on_show() {
|
||||
field_frequency.set_value(transmitter_model.tuning_frequency());
|
||||
}
|
||||
|
||||
void TransmitterView::focus() {
|
||||
button_start.focus();
|
||||
}
|
||||
|
||||
TransmitterView::TransmitterView(
|
||||
const Coord y, const uint64_t frequency_step, const uint32_t channel_bandwidth, const bool lock
|
||||
) : lock_ { lock }
|
||||
{
|
||||
set_parent_rect({ 0, y, 30 * 8, 6 * 8 });
|
||||
|
||||
add_children({
|
||||
&field_frequency,
|
||||
&text_gain,
|
||||
&field_gain,
|
||||
&button_start
|
||||
});
|
||||
|
||||
set_transmitting(false);
|
||||
|
||||
if (lock_) {
|
||||
field_frequency.set_focusable(false);
|
||||
field_frequency.set_style(&style_locked);
|
||||
} else {
|
||||
if (channel_bandwidth) {
|
||||
add_children({
|
||||
&text_bw,
|
||||
&field_bw
|
||||
});
|
||||
|
||||
field_bw.on_change = [this](int32_t v) {
|
||||
on_channel_bandwidth_changed(v * 1000);
|
||||
};
|
||||
field_bw.set_value(channel_bandwidth);
|
||||
}
|
||||
}
|
||||
|
||||
//field_frequency.set_value(transmitter_model.tuning_frequency());
|
||||
field_frequency.set_step(frequency_step);
|
||||
field_frequency.on_change = [this](rf::Frequency f) {
|
||||
on_tuning_frequency_changed(f);
|
||||
};
|
||||
field_frequency.on_edit = [this]() {
|
||||
if (on_edit_frequency)
|
||||
on_edit_frequency();
|
||||
};
|
||||
field_frequency.on_change = [this](rf::Frequency f) {
|
||||
transmitter_model.set_tuning_frequency(f);
|
||||
};
|
||||
|
||||
button_start.on_select = [this](Button&){
|
||||
if (transmitting_) {
|
||||
if (on_stop)
|
||||
on_stop();
|
||||
} else {
|
||||
if (on_start)
|
||||
on_start();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
TransmitterView::~TransmitterView() {
|
||||
audio::output::stop();
|
||||
transmitter_model.disable();
|
||||
baseband::shutdown();
|
||||
}
|
||||
|
||||
} /* namespace ui */
|
125
firmware/application/ui/ui_transmitter.hpp
Normal file
125
firmware/application/ui/ui_transmitter.hpp
Normal file
@@ -0,0 +1,125 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
|
||||
* Copyright (C) 2016 Furrtek
|
||||
*
|
||||
* 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 __UI_TRANSMITTER_H__
|
||||
#define __UI_TRANSMITTER_H__
|
||||
|
||||
#include "ui.hpp"
|
||||
#include "ui_navigation.hpp"
|
||||
#include "ui_painter.hpp"
|
||||
#include "ui_widget.hpp"
|
||||
#include "ui_receiver.hpp"
|
||||
#include "ui_font_fixed_8x16.hpp"
|
||||
|
||||
#include "rf_path.hpp"
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
|
||||
namespace ui {
|
||||
|
||||
class TXGainField : public NumberField {
|
||||
public:
|
||||
std::function<void(void)> on_show_options { };
|
||||
|
||||
TXGainField(Point parent_pos);
|
||||
};
|
||||
|
||||
class TransmitterView : public View {
|
||||
public:
|
||||
std::function<void(void)> on_edit_frequency { };
|
||||
std::function<void(void)> on_start { };
|
||||
std::function<void(void)> on_stop { };
|
||||
|
||||
TransmitterView(const Coord y, const uint64_t frequency_step, const uint32_t channel_bandwidth, const bool lock);
|
||||
TransmitterView(
|
||||
const Coord y, const uint32_t frequency_step, const uint32_t channel_bandwidth
|
||||
) : TransmitterView { y, frequency_step, channel_bandwidth, false }
|
||||
{
|
||||
}
|
||||
|
||||
~TransmitterView();
|
||||
|
||||
void on_show() override;
|
||||
void paint(Painter& painter) override;
|
||||
void focus() override;
|
||||
|
||||
void set_transmitting(const bool transmitting);
|
||||
|
||||
private:
|
||||
const Style style_start {
|
||||
.font = font::fixed_8x16,
|
||||
.background = Color::black(),
|
||||
.foreground = Color::green(),
|
||||
};
|
||||
const Style style_stop {
|
||||
.font = font::fixed_8x16,
|
||||
.background = Color::black(),
|
||||
.foreground = Color::red(),
|
||||
};
|
||||
const Style style_locked {
|
||||
.font = font::fixed_8x16,
|
||||
.background = Color::black(),
|
||||
.foreground = Color::dark_grey(),
|
||||
};
|
||||
|
||||
bool lock_ { false };
|
||||
bool transmitting_ { false };
|
||||
|
||||
FrequencyField field_frequency {
|
||||
{ 0, 1 * 8 }
|
||||
};
|
||||
|
||||
Text text_gain {
|
||||
{ 0, 3 * 8, 5 * 8, 1 * 16 },
|
||||
"Gain:"
|
||||
};
|
||||
TXGainField field_gain {
|
||||
{ 5 * 8, 3 * 8 }
|
||||
};
|
||||
|
||||
Text text_bw {
|
||||
{ 11 * 8, 1 * 8, 9 * 8, 1 * 16 },
|
||||
"BW: kHz"
|
||||
};
|
||||
NumberField field_bw {
|
||||
{ 14 * 8, 1 * 8 },
|
||||
3,
|
||||
{ 1, 150 },
|
||||
1,
|
||||
' '
|
||||
};
|
||||
|
||||
Button button_start {
|
||||
{ 21 * 8, 1 * 8, 9 * 8, 32 },
|
||||
"START"
|
||||
};
|
||||
|
||||
void on_tuning_frequency_changed(rf::Frequency f);
|
||||
void on_channel_bandwidth_changed(uint32_t channel_bandwidth);
|
||||
};
|
||||
|
||||
} /* namespace ui */
|
||||
|
||||
#endif/*__UI_TRANSMITTER_H__*/
|
Reference in New Issue
Block a user