diff --git a/firmware/application/CMakeLists.txt b/firmware/application/CMakeLists.txt index 6067e473..5e09f2f5 100644 --- a/firmware/application/CMakeLists.txt +++ b/firmware/application/CMakeLists.txt @@ -202,6 +202,7 @@ set(CPPSRC ui_soundboard.cpp ui_spectrum.cpp ui_sstvtx.cpp + ui_tabview.cpp ui_textentry.cpp ui_touch_calibration.cpp ui_transmitter.cpp diff --git a/firmware/application/bitmap.hpp b/firmware/application/bitmap.hpp index 01b48065..cb0c24c5 100644 --- a/firmware/application/bitmap.hpp +++ b/firmware/application/bitmap.hpp @@ -1025,6 +1025,36 @@ static constexpr Bitmap bitmap_target_calibrate { { 32, 32 }, bitmap_target_calibrate_data }; +static constexpr uint8_t bitmap_tab_edge_data[] = { + 0x00, + 0x01, + 0x01, + 0x03, + 0x03, + 0x03, + 0x07, + 0x07, + 0x07, + 0x0F, + 0x0F, + 0x0F, + 0x1F, + 0x1F, + 0x1F, + 0x1F, + 0x3F, + 0x3F, + 0x3F, + 0x7F, + 0x7F, + 0x7F, + 0xFF, + 0xFF, +}; +static constexpr Bitmap bitmap_tab_edge { + { 8, 24 }, bitmap_tab_edge_data +}; + static constexpr uint8_t bitmap_icon_tpms_data[] = { 0xE0, 0x07, 0xF8, 0x1A, diff --git a/firmware/application/ui_adsb_tx.cpp b/firmware/application/ui_adsb_tx.cpp index 4e63e5bb..a1f4ac92 100644 --- a/firmware/application/ui_adsb_tx.cpp +++ b/firmware/application/ui_adsb_tx.cpp @@ -40,7 +40,10 @@ Compass::Compass( const Point parent_pos ) : Widget { { parent_pos, { 64, 64 } } } { - set_focusable(false); // Useless ? +} + +Point Compass::polar_to_point(uint32_t angle, uint32_t distance) { + return Point(sin_f32(DEG_TO_RAD(angle) + (pi / 2)) * distance, -sin_f32(DEG_TO_RAD(angle)) * distance); } void Compass::set_value(uint32_t new_value) { @@ -50,13 +53,13 @@ void Compass::set_value(uint32_t new_value) { display.draw_line( center, - center + Point(sin_f32(DEG_TO_RAD(value_) + (pi / 2)) * 28, -sin_f32(DEG_TO_RAD(value_)) * 28), + center + polar_to_point(value_, 28), Color::dark_grey() ); display.draw_line( center, - center + Point(sin_f32(DEG_TO_RAD(new_value) + (pi / 2)) * 28, -sin_f32(DEG_TO_RAD(new_value)) * 28), + center + polar_to_point(new_value, 28), Color::green() ); @@ -65,6 +68,7 @@ void Compass::set_value(uint32_t new_value) { void Compass::paint(Painter&) { display.fill_circle(screen_pos() + Point(32, 32), 32, Color::dark_grey(), Color::black()); + display.fill_rectangle({ screen_pos() + Point(32 - 2, 0), { 4, 4 } }, Color::black()); // N display.fill_rectangle({ screen_pos() + Point(32 - 2, 64 - 4), { 4, 4 } }, Color::black()); // S display.fill_rectangle({ screen_pos() + Point(0, 32 - 2), { 4, 4 } }, Color::black()); // W @@ -78,7 +82,7 @@ uint32_t Compass::clamp_value(uint32_t value) { } void ADSBTxView::focus() { - tx_view.focus(); + button_callsign.focus(); } ADSBTxView::~ADSBTxView() { @@ -86,10 +90,6 @@ ADSBTxView::~ADSBTxView() { baseband::shutdown(); } -void ADSBTxView::paint(Painter&) { - button_callsign.set_text(callsign); -} - void ADSBTxView::generate_frames() { uint8_t * bin_ptr = shared_memory.bb_data.data; @@ -129,10 +129,8 @@ void ADSBTxView::generate_frames() { void ADSBTxView::start_tx() { generate_frames(); - transmitter_model.set_tuning_frequency(434000000); // DEBUG transmitter_model.set_sampling_rate(4000000U); transmitter_model.set_rf_amp(true); - transmitter_model.set_vga(40); transmitter_model.set_baseband_bandwidth(10000000); transmitter_model.enable(); @@ -140,10 +138,10 @@ void ADSBTxView::start_tx() { } void ADSBTxView::on_txdone(const bool v) { - if (v) { + /*if (v) { transmitter_model.disable(); tx_view.set_transmitting(false); - } + }*/ } /*void ADSBTxView::rotate_frames() { @@ -203,12 +201,11 @@ void ADSBTxView::on_txdone(const bool v) { }*/ ADSBTxView::ADSBTxView(NavigationView& nav) { - uint32_t c; - baseband::run_image(portapack::spi_flash::image_tag_adsb_tx); add_children({ &labels, + &tab_view, //&options_format, &sym_icao, &button_callsign, @@ -251,32 +248,27 @@ ADSBTxView::ADSBTxView(NavigationView& nav) { field_angle.set_value(0); field_speed.set_value(0); - field_altitude.on_change = [this](int32_t) { - generate_frames(); - }; - field_lat_degrees.on_change = [this](int32_t) { - generate_frames(); - }; - field_lon_degrees.on_change = [this](int32_t) { + const auto update_fn = [this](int32_t) { generate_frames(); }; + field_altitude.on_change = update_fn; + field_lat_degrees.on_change = update_fn; + field_lat_minutes.on_change = update_fn; + field_lat_seconds.on_change = update_fn; + field_lon_degrees.on_change = update_fn; + field_lon_minutes.on_change = update_fn; + field_lon_seconds.on_change = update_fn; + field_angle.on_change = [this](int32_t v) { compass.set_value(v); generate_frames(); }; - field_speed.on_change = [this](int32_t) { - generate_frames(); - }; - - for (c = 0; c < 4; c++) - field_squawk.set_sym(c, 0); + field_speed.on_change = update_fn; generate_frames(); - receiver_model.set_tuning_frequency(434000000); // DEBUG - tx_view.on_start = [this]() { start_tx(); tx_view.set_transmitting(true); diff --git a/firmware/application/ui_adsb_tx.hpp b/firmware/application/ui_adsb_tx.hpp index d741f737..7d15105e 100644 --- a/firmware/application/ui_adsb_tx.hpp +++ b/firmware/application/ui_adsb_tx.hpp @@ -26,6 +26,7 @@ #include "sine_table.hpp" #include "ui_textentry.hpp" #include "ui_widget.hpp" +#include "ui_tabview.hpp" #include "ui_navigation.hpp" #include "ui_transmitter.hpp" @@ -46,6 +47,8 @@ public: void paint(Painter&) override; private: + Point polar_to_point(uint32_t angle, uint32_t distance); + const range_t range { 0, 359 }; uint32_t value_ { 0 }; @@ -57,8 +60,6 @@ public: ADSBTxView(NavigationView& nav); ~ADSBTxView(); - void paint(Painter&) override; - void focus() override; std::string title() const override { return "ADS-B transmit"; }; @@ -111,7 +112,7 @@ private: void on_txdone(const bool v); Labels labels { - { { 2 * 8, 2 * 8 }, "Format: 17 (ADS-B)", Color::light_grey() }, + //{ { 2 * 8, 2 * 8 }, "Format: 17 (ADS-B)", Color::light_grey() }, { { 2 * 8, 4 * 8 }, "ICAO24:", Color::light_grey() }, { { 2 * 8, 7 * 8 }, "Callsign:", Color::light_grey() }, { { 1 * 8, 11 * 8 }, "Alt: feet", Color::light_grey() }, @@ -132,6 +133,39 @@ private: } };*/ + TabView tab_view { + { "Position#", Color::cyan(), + { + &labels, + &field_altitude, + &field_lat_degrees, + &field_lat_minutes, + &field_lat_seconds, + &field_lon_degrees, + &field_lon_minutes, + &field_lon_seconds + } + }, + { "Callsign", Color::green(), + { + &button_callsign, + } + }, + { "Speed", Color::yellow(), + { + &compass, + &field_angle, + &field_speed + } + }, + { "Squawk", Color::orange(), + { + &check_emergency, + &field_squawk + } + } + }; + SymField sym_icao { { 10 * 8, 2 * 16 }, 6, @@ -175,7 +209,7 @@ private: { 21 * 8, 5 * 16 } }; NumberField field_angle { - { 21 * 8 + 20, 9 * 16 }, 3, { 0, 359 }, 1, ' ' + { 21 * 8 + 20, 9 * 16 }, 3, { 0, 359 }, 1, ' ', true }; NumberField field_speed { @@ -201,9 +235,8 @@ private: TransmitterView tx_view { 16 * 16, - 0, - 0, - true + 1000000, + 0 }; MessageHandlerRegistration message_handler_tx_done { diff --git a/firmware/application/ui_debug.cpp b/firmware/application/ui_debug.cpp index 82cb1f66..08cb2588 100644 --- a/firmware/application/ui_debug.cpp +++ b/firmware/application/ui_debug.cpp @@ -261,7 +261,7 @@ DebugPeripheralsMenuView::DebugPeripheralsMenuView(NavigationView& nav) { [](const size_t register_number) { return portapack::clock_generator.read_register(register_number); } ); } }, { audio::debug::codec_name(), ui::Color::white(), nullptr, [&nav](){ nav.push( - audio::debug::codec_name(), RegistersWidgetConfig { audio::debug::reg_count(), audio::debug::reg_bits() }, + audio::debug::codec_name(), RegistersWidgetConfig { (int)audio::debug::reg_count(), (int)audio::debug::reg_bits() }, [](const size_t register_number) { return audio::debug::reg_read(register_number); } ); } }, }); diff --git a/firmware/application/ui_tabview.cpp b/firmware/application/ui_tabview.cpp new file mode 100644 index 00000000..6c714b19 --- /dev/null +++ b/firmware/application/ui_tabview.cpp @@ -0,0 +1,171 @@ +/* + * 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( + uint16_t index, + Dim width, + std::string text, + Color text_color +) { + set_parent_rect({ 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::light_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::white()); + + 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(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(parent())->set_selected(index_); + return true; + + default: + return false; + } +} + +void TabView::set_selected(uint16_t index) { + Tab * tab; + + if (index >= n_tabs) + return; + + //if (index == current_tab) + // return; + + // Hide all widgets + for (const auto widget : parent()->children()) { + widget->hidden(true); + } + + // Except this one :) + hidden(false); + + tab = &tabs[current_tab]; + tab->set_highlighted(false); + tab->set_focusable(true); + tab->set_dirty(); + + // Show new tab's widgets + for (auto widget : widget_lists[index]) { + widget->hidden(false); + } + + tab = &tabs[index]; + current_tab = index; + tab->set_highlighted(true); + tab->set_focusable(false); + tab->set_dirty(); + + parent()->set_dirty(); +} + +void TabView::focus() { + tabs[current_tab].focus(); +} + +void TabView::on_show() { + set_selected(current_tab); +} + +TabView::TabView(std::initializer_list tab_definitions) { + size_t i = 0; + + n_tabs = tab_definitions.size(); + if (n_tabs > 5) + n_tabs = 5; + + 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); + for (auto widget : tab_definition.widget_list) { + widget_lists[i].emplace_back(widget); + } + add_child(&tabs[i]); + i++; + if (i == 5) break; + } +} + +TabView::~TabView() { +} + +} /* namespace ui */ diff --git a/firmware/application/ui_tabview.hpp b/firmware/application/ui_tabview.hpp new file mode 100644 index 00000000..15843a84 --- /dev/null +++ b/firmware/application/ui_tabview.hpp @@ -0,0 +1,79 @@ +/* + * 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" +#include "ui_receiver.hpp" +#include "ui_font_fixed_8x16.hpp" + +namespace ui { + +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(uint16_t index, Dim width, std::string text, Color text_color); + +private: + std::string text_ { }; + Color text_color_ { }; + uint16_t index_ { }; +}; + +class TabView : public View { +public: + //std::function on_change { }; + + struct TabDef { + std::string text; + ui::Color color; + std::initializer_list widget_list; + }; + + TabView(std::initializer_list tab_definitions); + ~TabView(); + + void focus() override; + void on_show() override; + + void set_selected(uint16_t index); + +private: + size_t n_tabs { }; + std::array tabs { }; + std::array, 5> widget_lists { }; + uint32_t current_tab { 0 }; +}; + +} /* namespace ui */ + +#endif/*__UI_TABVIEW_H__*/ diff --git a/firmware/application/ui_transmitter.cpp b/firmware/application/ui_transmitter.cpp index a5cebcd4..543daad8 100644 --- a/firmware/application/ui_transmitter.cpp +++ b/firmware/application/ui_transmitter.cpp @@ -103,7 +103,7 @@ TransmitterView::TransmitterView( const Coord y, const uint64_t frequency_step, const uint32_t bandwidth, const bool lock ) : lock_ { lock } { - set_parent_rect({ 0 * 8, y, 30 * 8, 6 * 8 }); + set_parent_rect({ 0, y, 30 * 8, 6 * 8 }); add_children({ &field_frequency, @@ -118,15 +118,17 @@ TransmitterView::TransmitterView( field_frequency.set_focusable(false); field_frequency.set_style(&style_locked); } else { - add_children({ - &text_bw, - &field_bw - }); - - field_bw.on_change = [this](int32_t bandwidth) { - on_bandwidth_changed(bandwidth * 1000); - }; - field_bw.set_value(bandwidth); + if (bandwidth) { + add_children({ + &text_bw, + &field_bw + }); + + field_bw.on_change = [this](int32_t bandwidth) { + on_bandwidth_changed(bandwidth * 1000); + }; + field_bw.set_value(bandwidth); + } } field_frequency.set_value(receiver_model.tuning_frequency()); diff --git a/firmware/common/ui_widget.cpp b/firmware/common/ui_widget.cpp index dfaec804..122c0faa 100644 --- a/firmware/common/ui_widget.cpp +++ b/firmware/common/ui_widget.cpp @@ -114,7 +114,9 @@ void Widget::hidden(bool hide) { if( hide ) { // TODO: Instead of dirtying parent entirely, dirty only children // that overlap with this widget. - parent()->dirty_overlapping_children_in_rect(parent_rect()); + + //parent()->dirty_overlapping_children_in_rect(parent_rect()); + /* TODO: Notify self and all non-hidden children that they're * now effectively hidden? */ @@ -1122,12 +1124,14 @@ NumberField::NumberField( int length, range_t range, int32_t step, - char fill_char + char fill_char, + bool can_loop ) : Widget { { parent_pos, { 8 * length, 16 } } }, range { range }, step { step }, length_ { length }, - fill_char { fill_char } + fill_char { fill_char }, + can_loop { can_loop } { set_focusable(true); } @@ -1137,6 +1141,13 @@ int32_t NumberField::value() const { } void NumberField::set_value(int32_t new_value, bool trigger_change) { + if (can_loop) { + if (new_value >= 0) + new_value = new_value % (range.second + 1); + else + new_value = range.second + new_value + 1; + } + new_value = clip_value(new_value); if( new_value != value() ) { diff --git a/firmware/common/ui_widget.hpp b/firmware/common/ui_widget.hpp index 72326fa3..884242a0 100644 --- a/firmware/common/ui_widget.hpp +++ b/firmware/common/ui_widget.hpp @@ -485,9 +485,15 @@ public: using range_t = std::pair; - NumberField(Point parent_pos, int length, range_t range, int32_t step, char fill_char); + NumberField(Point parent_pos, int length, range_t range, int32_t step, char fill_char, bool can_loop); + + NumberField(Point parent_pos, int length, range_t range, int32_t step, char fill_char + ) : NumberField { parent_pos, length, range, step, fill_char, false } + { + } + NumberField( - ) : NumberField { { 0, 0 }, 1, { 0, 1 }, 1, ' ' } + ) : NumberField { { 0, 0 }, 1, { 0, 1 }, 1, ' ', false } { } @@ -510,6 +516,7 @@ private: const int length_; const char fill_char; int32_t value_ { 0 }; + bool can_loop { }; int32_t clip_value(int32_t value); }; diff --git a/firmware/graphics/tab_edge.png b/firmware/graphics/tab_edge.png new file mode 100644 index 00000000..94a7bdce Binary files /dev/null and b/firmware/graphics/tab_edge.png differ