diff --git a/firmware/application/CMakeLists.txt b/firmware/application/CMakeLists.txt index 7b8bfd15a..8a92d51f8 100644 --- a/firmware/application/CMakeLists.txt +++ b/firmware/application/CMakeLists.txt @@ -279,7 +279,6 @@ set(CPPSRC apps/ui_aprs_rx.cpp apps/ui_aprs_tx.cpp apps/ui_battinfo.cpp - apps/ui_bht_tx.cpp apps/ui_bmp_file_viewer.cpp apps/ui_btle_rx.cpp apps/ui_debug.cpp @@ -314,7 +313,6 @@ set(CPPSRC apps/ui_weatherstation.cpp protocols/aprs.cpp protocols/ax25.cpp - protocols/bht.cpp protocols/dcs.cpp protocols/encoders.cpp protocols/modems.cpp diff --git a/firmware/application/apps/ais_app.cpp b/firmware/application/apps/ais_app.cpp index dc4524f83..8eaa23bb0 100644 --- a/firmware/application/apps/ais_app.cpp +++ b/firmware/application/apps/ais_app.cpp @@ -272,14 +272,14 @@ void RecentEntriesTable::draw( const Entry& entry, const Rect& target_rect, Painter& painter, - const Style& style) { + const Style& style, + RecentEntriesColumns&) { std::string line = ais::format::mmsi(entry.mmsi) + " "; if (!entry.name.empty()) { line += ais::format::text(entry.name); } else { line += ais::format::text(entry.call_sign); } - line.resize(target_rect.width() / 8, ' '); painter.draw_string(target_rect.location(), style, line); } diff --git a/firmware/application/apps/ais_app.hpp b/firmware/application/apps/ais_app.hpp index 094d1b37a..59a8325db 100644 --- a/firmware/application/apps/ais_app.hpp +++ b/firmware/application/apps/ais_app.hpp @@ -137,10 +137,10 @@ class AISRecentEntryDetailView : public View { AISRecentEntry entry_{}; Button button_done{ - {125, 224, 96, 24}, + {UI_POS_X_CENTER(12) + UI_POS_WIDTH(6), UI_POS_Y(14), UI_POS_WIDTH(12), UI_POS_HEIGHT(2)}, "Done"}; Button button_see_map{ - {19, 224, 96, 24}, + {UI_POS_X_CENTER(12) - UI_POS_WIDTH(8), UI_POS_Y(14), UI_POS_WIDTH(12), UI_POS_HEIGHT(2)}, "See on map"}; GeoMapView* geomap_view{nullptr}; bool send_updates{false}; @@ -179,9 +179,9 @@ class AISAppView : public View { AISRecentEntries recent{}; std::unique_ptr logger{}; - const RecentEntriesColumns columns{{ + RecentEntriesColumns columns{{ {"MMSI", 9}, - {"Name/Call", 20}, + {"Name/Call", 0}, }}; AISRecentEntriesView recent_entries_view{columns, recent}; AISRecentEntryDetailView recent_entry_detail_view{nav_}; @@ -189,11 +189,11 @@ class AISAppView : public View { static constexpr auto header_height = 1 * 16; Text label_channel{ - {0 * 8, 0 * 16, 2 * 8, 1 * 16}, + {UI_POS_X(0), UI_POS_Y(0), 2 * 8, 1 * 16}, "Ch"}; OptionsField options_channel{ - {3 * 8, 0 * 16}, + {3 * 8, UI_POS_Y(0)}, 3, { {"87B", 161975000}, @@ -201,23 +201,23 @@ class AISAppView : public View { }}; RFAmpField field_rf_amp{ - {13 * 8, 0 * 16}}; + {13 * 8, UI_POS_Y(0)}}; LNAGainField field_lna{ - {15 * 8, 0 * 16}}; + {15 * 8, UI_POS_Y(0)}}; VGAGainField field_vga{ - {18 * 8, 0 * 16}}; + {18 * 8, UI_POS_Y(0)}}; RSSI rssi{ - {21 * 8, 0, 6 * 8, 4}, + {UI_POS_X(21), UI_POS_Y(0), UI_POS_WIDTH_REMAINING(23), 4}, }; AudioVolumeField field_volume{ - {screen_width - 2 * 8, 0 * 16}}; + {UI_POS_X_RIGHT(2), UI_POS_Y(0)}}; Channel channel{ - {21 * 8, 5, 6 * 8, 4}, + {UI_POS_X(21), 5, UI_POS_WIDTH_REMAINING(23), 4}, }; SignalToken signal_token_tick_second{}; uint8_t timer_seconds = 0; diff --git a/firmware/application/apps/analog_audio_app.hpp b/firmware/application/apps/analog_audio_app.hpp index 5979aecc0..33d7b41d0 100644 --- a/firmware/application/apps/analog_audio_app.hpp +++ b/firmware/application/apps/analog_audio_app.hpp @@ -43,19 +43,19 @@ class AMOptionsView : public View { private: Text label_config{ - {0 * 8, 0 * 16, 2 * 8, 1 * 16}, + {UI_POS_X(0), UI_POS_Y(0), UI_POS_WIDTH(2), UI_POS_HEIGHT(1)}, "BW", }; OptionsField options_config{ - {3 * 8, 0 * 16}, + {UI_POS_X(3), UI_POS_Y(0)}, 6, // Max option length { // Using common messages from freqman_ui.cpp }}; OptionsField zoom_config{ - {23 * 8, 0 * 16}, + {UI_POS_X_RIGHT(7), UI_POS_Y(0)}, 7, {{"ZOOM x1", 0}, {"ZOOM x2", 6}} // offset index AM modes array FIR filters. @@ -68,19 +68,19 @@ class AMFMAptOptionsView : public View { private: Text label_config{ - {0 * 8, 0 * 16, 2 * 8, 1 * 16}, + {UI_POS_X(0), UI_POS_Y(0), UI_POS_WIDTH(2), UI_POS_HEIGHT(1)}, "BW", }; OptionsField options_config{ - {3 * 8, 0 * 16}, + {UI_POS_X(3), UI_POS_Y(0)}, 17, // Max option length chars "USB+FM(Wefax Apt)" { // Using common messages from freqman_ui.cpp In HF USB , Here we only need USB Audio demod, + post-FM demod fsubcarrier FM tone to get APT signal. }}; OptionsField zoom_config{ - {23 * 8, 0 * 16}, + {UI_POS_X_RIGHT(7), UI_POS_Y(0)}, 7, {{"ZOOM x1", 0}, {"ZOOM x2", 6}} // offset index array filters. @@ -93,21 +93,21 @@ class NBFMOptionsView : public View { private: Text label_config{ - {0 * 8, 0 * 16, 2 * 8, 1 * 16}, + {UI_POS_X(0), UI_POS_Y(0), UI_POS_WIDTH(2), UI_POS_HEIGHT(1)}, "BW", }; OptionsField options_config{ - {3 * 8, 0 * 16}, + {UI_POS_X(3), UI_POS_Y(0)}, 3, // Max option length { // Using common messages from freqman_ui.cpp }}; Text text_squelch{ - {7 * 8, 0 * 16, 8 * 8, 1 * 16}, + {UI_POS_X(7), UI_POS_Y(0), UI_POS_WIDTH(8), UI_POS_HEIGHT(1)}, "SQ /99"}; NumberField field_squelch{ - {10 * 8, 0 * 16}, + {UI_POS_X(10), UI_POS_Y(0)}, 2, {0, 99}, 1, @@ -121,11 +121,11 @@ class WFMOptionsView : public View { private: Text label_config{ - {0 * 8, 0 * 16, 2 * 8, 1 * 16}, + {UI_POS_X(0), UI_POS_Y(0), UI_POS_WIDTH(2), UI_POS_HEIGHT(1)}, "BW", }; OptionsField options_config{ - {3 * 8, 0 * 16}, + {UI_POS_X(3), UI_POS_Y(0)}, 4, // Max option length { // Using common messages from freqman_ui.cpp @@ -138,11 +138,11 @@ class WFMAMAptOptionsView : public View { private: Text label_config{ - {0 * 8, 0 * 16, 2 * 8, 1 * 16}, + {UI_POS_X(0), UI_POS_Y(0), UI_POS_WIDTH(2), UI_POS_HEIGHT(1)}, "BW", }; OptionsField options_config{ - {3 * 8, 0 * 16}, + {UI_POS_X(3), UI_POS_Y(0)}, 16, // Max option char length "80k-NOAA Apt LPF" , example. { // Using common messages from freqman_ui.cpp @@ -155,11 +155,11 @@ class SPECOptionsView : public View { private: Text label_config{ - {0 * 8, 0 * 16, 2 * 8, 1 * 16}, + {UI_POS_X(0), UI_POS_Y(0), UI_POS_WIDTH(2), UI_POS_HEIGHT(1)}, "BW", }; OptionsField options_config{ - {3 * 8, 0 * 16}, + {UI_POS_X(3), UI_POS_Y(0)}, 4, { {"20m ", 20000000}, @@ -172,20 +172,20 @@ class SPECOptionsView : public View { }}; Text text_speed{ - {9 * 8, 0 * 16, 8 * 8, 1 * 16}, + {UI_POS_X(9), UI_POS_Y(0), UI_POS_WIDTH(8), UI_POS_HEIGHT(1)}, "SP /63"}; NumberField field_speed{ - {12 * 8, 0 * 16}, + {UI_POS_X(12), UI_POS_Y(0)}, 2, {0, 63}, 1, ' ', }; Text text_rx_cal{ - {19 * 8, 0 * 16, 11 * 8, 1 * 16}, // 18 (x col.) x char_size, 12 (length) x 8 blanking space to delete previous chars. + {UI_POS_X(19), UI_POS_Y(0), UI_POS_WIDTH(11), UI_POS_HEIGHT(1)}, // 18 (x col.) x char_size, 12 (length) x 8 blanking space to delete previous chars. "Rx_IQ_CAL "}; NumberField field_rx_iq_phase_cal{ - {screen_width - 2 * 8, 0 * 16}, + {screen_width - 2 * 8, UI_POS_Y(0)}, 2, {0, 63}, // 5 or 6 bits IQ CAL phase adjustment (range updated later) 1, @@ -244,34 +244,34 @@ class AnalogAudioView : public View { {"previous_zoom"sv, &previous_zoom}, // we are saving and restoring AMFM ZOOM factor from Settings. }}; - const Rect options_view_rect{0 * 8, 1 * 16, screen_width, 1 * 16}; - const Rect nbfm_view_rect{0 * 8, 1 * 16, 18 * 8, 1 * 16}; + const Rect options_view_rect{UI_POS_X(0), UI_POS_Y(1), UI_POS_MAXWIDTH, UI_POS_HEIGHT(1)}; + const Rect nbfm_view_rect{UI_POS_X(0), UI_POS_Y(1), UI_POS_WIDTH(18), UI_POS_HEIGHT(1)}; size_t spec_bw_index = 0; uint32_t spec_bw = 20000000; uint16_t spec_trigger = 63; RSSI rssi{ - {21 * 8, 0, 6 * 8, 4}}; + {UI_POS_X(21), 0, UI_POS_WIDTH_REMAINING(21) - UI_POS_WIDTH(2), 4}}; Channel channel{ - {21 * 8, 5, 6 * 8, 4}}; + {UI_POS_X(21), 5, UI_POS_WIDTH_REMAINING(21) - UI_POS_WIDTH(2), 4}}; Audio audio{ - {21 * 8, 10, 6 * 8, 4}}; + {UI_POS_X(21), 10, UI_POS_WIDTH_REMAINING(21) - UI_POS_WIDTH(2), 4}}; RxFrequencyField field_frequency{ - {5 * 8, 0 * 16}, + {UI_POS_X(5), UI_POS_Y(0)}, nav_}; LNAGainField field_lna{ - {15 * 8, 0 * 16}}; + {UI_POS_X(15), UI_POS_Y(0)}}; VGAGainField field_vga{ - {18 * 8, 0 * 16}}; + {UI_POS_X(18), UI_POS_Y(0)}}; OptionsField options_modulation{ - {0 * 8, 0 * 16}, + {UI_POS_X(0), UI_POS_Y(0)}, 4, { {" AM ", toUType(ReceiverModel::Mode::AMAudio)}, @@ -283,16 +283,16 @@ class AnalogAudioView : public View { }}; AudioVolumeField field_volume{ - {screen_width - 2 * 8, 0 * 16}}; + {screen_width - 2 * 8, UI_POS_Y(0)}}; Text text_ctcss{ - {16 * 8, 1 * 16, 14 * 8, 1 * 16}, + {UI_POS_X(16), UI_POS_Y(1), UI_POS_WIDTH(14), UI_POS_HEIGHT(1)}, ""}; std::unique_ptr options_widget{}; RecordView record_view{ - {0 * 8, 2 * 16, screen_width, 1 * 16}, + {UI_POS_X(0), UI_POS_Y(2), UI_POS_MAXWIDTH, UI_POS_HEIGHT(1)}, u"AUD", u"AUDIO", RecordView::FileType::WAV, diff --git a/firmware/application/apps/ble_comm_app.hpp b/firmware/application/apps/ble_comm_app.hpp index 1ff6311a1..76107dd77 100644 --- a/firmware/application/apps/ble_comm_app.hpp +++ b/firmware/application/apps/ble_comm_app.hpp @@ -131,7 +131,7 @@ class BLECommView : public View { static constexpr auto header_height = 5 * 16; OptionsField options_channel{ - {0 * 8, 0 * 8}, + {UI_POS_X(0), 0 * 8}, 5, {{"Ch.37 ", 37}, {"Ch.38", 38}, @@ -139,17 +139,17 @@ class BLECommView : public View { {"Auto", 40}}}; RxFrequencyField field_frequency{ - {6 * 8, 0 * 16}, + {6 * 8, UI_POS_Y(0)}, nav_}; RFAmpField field_rf_amp{ - {16 * 8, 0 * 16}}; + {16 * 8, UI_POS_Y(0)}}; LNAGainField field_lna{ - {18 * 8, 0 * 16}}; + {18 * 8, UI_POS_Y(0)}}; VGAGainField field_vga{ - {21 * 8, 0 * 16}}; + {21 * 8, UI_POS_Y(0)}}; RSSI rssi{ {24 * 8, 0, 6 * 8, 4}}; @@ -164,7 +164,7 @@ class BLECommView : public View { true}; Labels label_packets_sent{ - {{0 * 8, 4 * 8}, "Packets Left:", Theme::getInstance()->fg_light->foreground}}; + {{UI_POS_X(0), 4 * 8}, "Packets Left:", Theme::getInstance()->fg_light->foreground}}; Text text_packets_sent{ {13 * 8, 2 * 16, 12 * 8, 16}, diff --git a/firmware/application/apps/ble_rx_app.cpp b/firmware/application/apps/ble_rx_app.cpp index 6fd4e6363..b16cf913b 100644 --- a/firmware/application/apps/ble_rx_app.cpp +++ b/firmware/application/apps/ble_rx_app.cpp @@ -181,21 +181,16 @@ void RecentEntriesTable::draw( const Entry& entry, const Rect& target_rect, Painter& painter, - const Style& style) { + const Style& style, + RecentEntriesColumns& columns) { std::string line{}; - line.reserve(30); - + line.reserve(40); if (!entry.nameString.empty() && entry.include_name) { line = entry.nameString; - - if (line.length() < 17) { - line += pad_string_with_spaces(17 - line.length()); - } else { - line = truncate(line, 17); - } } else { line = to_string_mac_address(entry.packetData.macAddress, 6, false); } + line.resize(columns.at(0).second, ' '); std::string hitsStr; diff --git a/firmware/application/apps/ble_rx_app.hpp b/firmware/application/apps/ble_rx_app.hpp index cb0804f1b..99b4abd21 100644 --- a/firmware/application/apps/ble_rx_app.hpp +++ b/firmware/application/apps/ble_rx_app.hpp @@ -155,42 +155,42 @@ class BleRecentEntryDetailView : public View { static constexpr uint8_t total_data_lines{5}; Labels label_mac_address{ - {{0 * 8, 0 * 16}, "Mac Address:", Theme::getInstance()->fg_light->foreground}}; + {{UI_POS_X(0), UI_POS_Y(0)}, "Mac Address:", Theme::getInstance()->fg_light->foreground}}; Text text_mac_address{ - {12 * 8, 0 * 16, 17 * 8, 16}, + {UI_POS_X(12), UI_POS_Y(0), UI_POS_WIDTH(17), UI_POS_HEIGHT(1)}, "-"}; Labels label_pdu_type{ - {{0 * 8, 1 * 16}, "PDU Type:", Theme::getInstance()->fg_light->foreground}}; + {{UI_POS_X(0), UI_POS_Y(1)}, "PDU Type:", Theme::getInstance()->fg_light->foreground}}; Text text_pdu_type{ - {9 * 8, 1 * 16, 17 * 8, 16}, + {9 * 8, UI_POS_Y(1), 17 * 8, UI_POS_HEIGHT(1)}, "-"}; Labels label_vendor{ - {{0 * 8, 2 * 16}, "Vendor:", Theme::getInstance()->fg_light->foreground}}; + {{UI_POS_X(0), UI_POS_Y(2)}, "Vendor:", Theme::getInstance()->fg_light->foreground}}; Text text_vendor{ - {7 * 8, 2 * 16, 23 * 8, 16}, + {7 * 8, UI_POS_Y(2), 23 * 8, UI_POS_HEIGHT(1)}, "-"}; Labels labels{ - {{0 * 8, 3 * 16}, "Len", Theme::getInstance()->fg_light->foreground}, - {{5 * 8, 3 * 16}, "Type", Theme::getInstance()->fg_light->foreground}, - {{10 * 8, 3 * 16}, "Value", Theme::getInstance()->fg_light->foreground}, + {{UI_POS_X(0), UI_POS_Y(3)}, "Len", Theme::getInstance()->fg_light->foreground}, + {{5 * 8, UI_POS_Y(3)}, "Type", Theme::getInstance()->fg_light->foreground}, + {{10 * 8, UI_POS_Y(3)}, "Value", Theme::getInstance()->fg_light->foreground}, }; Button button_send{ - {19, 224, 96, 24}, + {UI_POS_X_CENTER(12) - UI_POS_WIDTH(8), 224, 96, 24}, "Send"}; Button button_done{ - {125, 224, 96, 24}, + {UI_POS_X_CENTER(12) + UI_POS_WIDTH(8), 224, 96, 24}, "Done"}; Button button_save{ - {72, 264, 96, 24}, + {UI_POS_X_CENTER(12), 264, 96, 24}, "Save"}; bool send_updates{false}; @@ -294,7 +294,7 @@ class BLERxView : public View { static constexpr auto switch_button_height = 3 * 16; OptionsField options_channel{ - {0 * 8, 0 * 8}, + {UI_POS_X(0), UI_POS_Y(0)}, 5, {{"Ch.37", 37}, {"Ch.38", 38}, @@ -302,29 +302,29 @@ class BLERxView : public View { {"Auto", 40}}}; RxFrequencyField field_frequency{ - {6 * 8, 0 * 16}, + {UI_POS_X(6), UI_POS_Y(0)}, nav_}; RFAmpField field_rf_amp{ - {16 * 8, 0 * 16}}; + {UI_POS_X(16), UI_POS_Y(0)}}; LNAGainField field_lna{ - {18 * 8, 0 * 16}}; + {UI_POS_X(18), UI_POS_Y(0)}}; VGAGainField field_vga{ - {21 * 8, 0 * 16}}; + {UI_POS_X(21), UI_POS_Y(0)}}; RSSI rssi{ - {24 * 8, 0, 6 * 8, 4}}; + {UI_POS_X(24), 0, UI_POS_WIDTH_REMAINING(6), 4}}; Channel channel{ - {24 * 8, 5, 6 * 8, 4}}; + {UI_POS_X(24), 5, UI_POS_WIDTH_REMAINING(6), 4}}; Labels label_sort{ - {{0 * 8, 2 * 8}, "Sort:", Theme::getInstance()->fg_light->foreground}}; + {{UI_POS_X(0), UI_POS_Y(1)}, "Sort:", Theme::getInstance()->fg_light->foreground}}; OptionsField options_sort{ - {5 * 8, 2 * 8}, + {5 * 8, UI_POS_Y(1)}, 4, {{"MAC", 0}, {"Hits", 1}, @@ -334,11 +334,11 @@ class BLERxView : public View { {"Info", 5}}}; Button button_filter{ - {11 * 8, 2 * 8, 7 * 8, 16}, + {11 * 8, UI_POS_Y(1), 7 * 8, 16}, "Filter:"}; OptionsField options_filter{ - {18 * 8 + 2, 2 * 8}, + {18 * 8 + 2, UI_POS_Y(1)}, 7, {{"Data", 0}, {"MAC", 1}, @@ -354,7 +354,7 @@ class BLERxView : public View { true}; Checkbox check_name{ - {0 * 8, 4 * 8 + 2}, + {UI_POS_X(0), 4 * 8 + 2}, 3, "Name", true}; @@ -378,7 +378,7 @@ class BLERxView : public View { true}; Button button_find{ - {0 * 8, 10 * 8 - 2, 4 * 8, 16}, + {UI_POS_X(0), 10 * 8 - 2, 4 * 8, 16}, "Find"}; Labels label_found{ @@ -389,15 +389,15 @@ class BLERxView : public View { "0/0"}; Button button_clear_list{ - {2 * 8, 320 - (16 + 32), 7 * 8, 32}, + {2 * 8, screen_height - (16 + 32), 7 * 8, 32}, "Clear"}; Button button_save_list{ - {11 * 8, 320 - (16 + 32), 11 * 8, 32}, + {UI_POS_X_CENTER(11), screen_height - (16 + 32), 11 * 8, 32}, "Export CSV"}; Button button_switch{ - {240 - 6 * 8, 320 - (16 + 32), 4 * 8, 32}, + {UI_POS_X_RIGHT(6), screen_height - (16 + 32), 4 * 8, 32}, "Tx"}; std::string str_log{""}; @@ -407,7 +407,7 @@ class BLERxView : public View { BleRecentEntries tempList{}; RecentEntriesColumns columns{{ - {"Name", 17}, + {"Name", 0}, {"Hits", 7}, {"dBm", 4}, }}; diff --git a/firmware/application/apps/ble_tx_app.hpp b/firmware/application/apps/ble_tx_app.hpp index 3f22281db..7ef4f3b5d 100644 --- a/firmware/application/apps/ble_tx_app.hpp +++ b/firmware/application/apps/ble_tx_app.hpp @@ -190,24 +190,24 @@ class BLETxView : public View { static constexpr auto switch_button_height = 6 * 16; Button button_open{ - {0 * 8, 0 * 16, 10 * 8, 2 * 16}, + {UI_POS_X(0), UI_POS_Y(0), 10 * 8, 2 * 16}, "Open file"}; Text text_filename{ - {11 * 8, 0 * 16, 12 * 8, 16}, + {11 * 8, UI_POS_Y(0), UI_POS_WIDTH_REMAINING(11), 16}, "-"}; ProgressBar progressbar{ - {11 * 8, 1 * 16, 9 * 8, 16}}; + {11 * 8, 1 * 16, UI_POS_WIDTH_REMAINING(11 + 10), 16}}; Checkbox check_rand_mac{ - {21 * 8, 1 * 16}, + {UI_POS_X_RIGHT(9), 1 * 16}, 6, "?? Mac", true}; TxFrequencyField field_frequency{ - {0 * 8, 2 * 16}, + {UI_POS_X(0), 2 * 16}, nav_}; TransmitterView2 tx_view{ @@ -215,19 +215,19 @@ class BLETxView : public View { /*short_ui*/ true}; Checkbox check_loop{ - {21 * 8, 2 * 16}, + {UI_POS_X_RIGHT(9), 2 * 16}, 4, "Loop", true}; ImageButton button_play{ - {28 * 8, 2 * 16, 2 * 8, 1 * 16}, + {UI_POS_X_RIGHT(2), 2 * 16, 2 * 8, 1 * 16}, &bitmap_play, Theme::getInstance()->fg_green->foreground, Theme::getInstance()->fg_green->background}; Labels label_speed{ - {{0 * 8, 6 * 8}, "Speed:", Theme::getInstance()->fg_light->foreground}}; + {{UI_POS_X(0), 6 * 8}, "Speed:", Theme::getInstance()->fg_light->foreground}}; OptionsField options_speed{ {7 * 8, 6 * 8}, @@ -259,7 +259,7 @@ class BLETxView : public View { {"CONNECT_REQ", PKT_TYPE_CONNECT_REQ}}}; Labels label_marked_data{ - {{0 * 8, 4 * 16}, "Marked Data:", Theme::getInstance()->fg_light->foreground}}; + {{UI_POS_X(0), 4 * 16}, "Marked Data:", Theme::getInstance()->fg_light->foreground}}; OptionsField marked_data_sequence{ {12 * 8, 8 * 8}, @@ -269,42 +269,42 @@ class BLETxView : public View { {"Random", 2}}}; Labels label_packet_index{ - {{0 * 8, 12 * 8}, "Packet Index:", Theme::getInstance()->fg_light->foreground}}; + {{UI_POS_X(0), 12 * 8}, "Packet Index:", Theme::getInstance()->fg_light->foreground}}; Text text_packet_index{ {13 * 8, 6 * 16, 12 * 8, 16}, "-"}; Labels label_packets_sent{ - {{0 * 8, 14 * 8}, "Repeat Count:", Theme::getInstance()->fg_light->foreground}}; + {{UI_POS_X(0), 14 * 8}, "Repeat Count:", Theme::getInstance()->fg_light->foreground}}; Text text_packets_sent{ {13 * 8, 7 * 16, 12 * 8, 16}, "-"}; Labels label_mac_address{ - {{0 * 8, 16 * 8}, "Mac Address:", Theme::getInstance()->fg_light->foreground}}; + {{UI_POS_X(0), 16 * 8}, "Mac Address:", Theme::getInstance()->fg_light->foreground}}; Text text_mac_address{ {12 * 8, 8 * 16, 20 * 8, 16}, "-"}; Labels label_data_packet{ - {{0 * 8, 9 * 16}, "Packet Data:", Theme::getInstance()->fg_light->foreground}}; + {{UI_POS_X(0), 9 * 16}, "Packet Data:", Theme::getInstance()->fg_light->foreground}}; TextViewer dataEditView{ {0, 9 * 18, 240, 240}}; Button button_clear_marked{ - {1 * 8, 14 * 16, 13 * 8, 3 * 8}, + {UI_POS_X(1), UI_POS_Y_BOTTOM(5), UI_POS_WIDTH(13), UI_POS_HEIGHT(1.5)}, "Clear Marked"}; Button button_save_packet{ - {1 * 8, 16 * 16, 13 * 8, 2 * 16}, + {UI_POS_X(1), UI_POS_Y_BOTTOM(3), UI_POS_WIDTH(13), UI_POS_HEIGHT(2)}, "Save Packet"}; Button button_switch{ - {16 * 8, 16 * 16, 13 * 8, 2 * 16}, + {UI_POS_X_RIGHT(14), UI_POS_Y_BOTTOM(3), UI_POS_WIDTH(13), UI_POS_HEIGHT(2)}, "Switch to Rx"}; std::string str_log{""}; diff --git a/firmware/application/apps/capture_app.hpp b/firmware/application/apps/capture_app.hpp index 12ae2854e..bdb1f84c7 100644 --- a/firmware/application/apps/capture_app.hpp +++ b/firmware/application/apps/capture_app.hpp @@ -65,31 +65,31 @@ class CaptureAppView : public View { }}; Labels labels{ - {{0 * 8, 1 * 16}, "Rate:", Theme::getInstance()->fg_light->foreground}, + {{UI_POS_X(0), 1 * 16}, "Rate:", Theme::getInstance()->fg_light->foreground}, {{11 * 8, 1 * 16}, "Format:", Theme::getInstance()->fg_light->foreground}, }; RSSI rssi{ - {24 * 8, 0, 6 * 8, 4}}; + {UI_POS_X(24), 0, UI_POS_WIDTH_REMAINING(24), 4}}; Channel channel{ - {24 * 8, 5, 6 * 8, 4}}; + {UI_POS_X(24), 5, UI_POS_WIDTH_REMAINING(24), 4}}; RxFrequencyField field_frequency{ - {0 * 8, 0 * 16}, + {UI_POS_X(0), UI_POS_Y(0)}, nav_}; FrequencyStepView field_frequency_step{ - {10 * 8, 0 * 16}}; + {10 * 8, UI_POS_Y(0)}}; RFAmpField field_rf_amp{ - {16 * 8, 0 * 16}}; + {16 * 8, UI_POS_Y(0)}}; LNAGainField field_lna{ - {18 * 8, 0 * 16}}; + {18 * 8, UI_POS_Y(0)}}; VGAGainField field_vga{ - {21 * 8, 0 * 16}}; + {21 * 8, UI_POS_Y(0)}}; OptionsField option_bandwidth{ {5 * 8, 1 * 16}, @@ -109,7 +109,7 @@ class CaptureAppView : public View { /*small*/ true}; RecordView record_view{ - {0 * 8, 2 * 16, screen_width, 1 * 16}, + {UI_POS_X(0), 2 * 16, screen_width, 1 * 16}, u"BBD_????.*", captures_dir, RecordView::FileType::RawS16, diff --git a/firmware/application/apps/pocsag_app.hpp b/firmware/application/apps/pocsag_app.hpp index 45a01f3aa..9b5dd4d92 100644 --- a/firmware/application/apps/pocsag_app.hpp +++ b/firmware/application/apps/pocsag_app.hpp @@ -183,7 +183,7 @@ class POCSAGSettingsView : public View { true /*explicit_edit*/}; Button button_save{ - {11 * 8, 16 * 16, 10 * 8, 2 * 16}, + {UI_POS_X_CENTER(10), UI_POS_Y(16), 10 * 8, 2 * 16}, "Save"}; }; @@ -236,30 +236,30 @@ class POCSAGAppView : public View { uint16_t packet_count = 0; RxFrequencyField field_frequency{ - {0 * 8, 0 * 8}, + {UI_POS_X(0), 0 * 8}, nav_}; RFAmpField field_rf_amp{ - {11 * 8, 0 * 16}}; + {11 * 8, UI_POS_Y(0)}}; LNAGainField field_lna{ - {13 * 8, 0 * 16}}; + {13 * 8, UI_POS_Y(0)}}; VGAGainField field_vga{ - {16 * 8, 0 * 16}}; + {16 * 8, UI_POS_Y(0)}}; RSSI rssi{ - {19 * 8 - 4, 3, 6 * 8, 4}}; + {19 * 8 - 4, 3, UI_POS_WIDTH_REMAINING(26), 4}}; Audio audio{ - {19 * 8 - 4, 8, 6 * 8, 4}}; + {19 * 8 - 4, 8, UI_POS_WIDTH_REMAINING(26), 4}}; NumberField field_squelch{ - {25 * 8, 0 * 16}, + {UI_POS_X_RIGHT(6), UI_POS_Y(0)}, 2, {0, 99}, 1, ' ', true /*wrap*/}; AudioVolumeField field_volume{ - {screen_width - 2 * 8, 0 * 16}}; + {UI_POS_X_RIGHT(2), UI_POS_Y(0)}}; Image image_status{ {0 * 8 + 4, 1 * 16 + 2, 16, 16}, diff --git a/firmware/application/apps/ui_adsb_rx.cpp b/firmware/application/apps/ui_adsb_rx.cpp index c0f9fd4c1..8a536bc40 100644 --- a/firmware/application/apps/ui_adsb_rx.cpp +++ b/firmware/application/apps/ui_adsb_rx.cpp @@ -51,7 +51,8 @@ void RecentEntriesTable::draw( const Entry& entry, const Rect& target_rect, Painter& painter, - const Style& style) { + const Style& style, + RecentEntriesColumns& columns) { Color target_color; std::string entry_string; @@ -70,9 +71,11 @@ void RecentEntriesTable::draw( target_color = Theme::getInstance()->fg_medium->foreground; }; - entry_string += - (entry.callsign.empty() ? entry.icao_str + " " : entry.callsign + " ") + - to_string_dec_uint((unsigned int)(entry.pos.altitude / 100), 4); + std::string ipc = (entry.callsign.empty() ? entry.icao_str + " " : entry.callsign + " "); + uint8_t firstcolwidth = columns.at(0).second; + ipc.resize(firstcolwidth, ' '); // Make sure this is always match the first column's width that is dynamic. + + entry_string += ipc + to_string_dec_uint((unsigned int)(entry.pos.altitude / 100), 4); if (entry.velo.type == SPD_IAS && entry.pos.alt_valid) { // IAS can be converted to TAS // It is generally accepted that for every thousand feet of altitude, @@ -100,7 +103,7 @@ void RecentEntriesTable::draw( entry_string); if (entry.pos.pos_valid) - painter.draw_bitmap(target_rect.location() + Point(8 * 8, 0), + painter.draw_bitmap(target_rect.location() + Point(firstcolwidth * 8 - 8, 0), bitmap_target, target_color, style.background); } @@ -409,7 +412,7 @@ ADSBRxView::ADSBRxView(NavigationView& nav) { &status_good_frame, &field_volume}); - recent_entries_view.set_parent_rect({0, 16, screen_width, 272}); + recent_entries_view.set_parent_rect({0, 16, screen_width, UI_POS_HEIGHT_REMAINING(2)}); recent_entries_view.on_select = [this, &nav](const AircraftRecentEntry& entry) { detail_key = entry.key(); details_view = nav.push(entry); diff --git a/firmware/application/apps/ui_adsb_rx.hpp b/firmware/application/apps/ui_adsb_rx.hpp index 66f2cfbbc..15280e051 100644 --- a/firmware/application/apps/ui_adsb_rx.hpp +++ b/firmware/application/apps/ui_adsb_rx.hpp @@ -208,54 +208,54 @@ class ADSBRxAircraftDetailsView : public View { private: Labels labels{ - {{0 * 8, 1 * 16}, "ICAO:", Theme::getInstance()->fg_light->foreground}, - {{0 * 8, 2 * 16}, "Registration:", Theme::getInstance()->fg_light->foreground}, - {{0 * 8, 3 * 16}, "Manufacturer:", Theme::getInstance()->fg_light->foreground}, - {{0 * 8, 5 * 16}, "Model:", Theme::getInstance()->fg_light->foreground}, - {{0 * 8, 7 * 16}, "Type:", Theme::getInstance()->fg_light->foreground}, - {{0 * 8, 8 * 16}, "Number of engines:", Theme::getInstance()->fg_light->foreground}, - {{0 * 8, 9 * 16}, "Engine type:", Theme::getInstance()->fg_light->foreground}, - {{0 * 8, 11 * 16}, "Owner:", Theme::getInstance()->fg_light->foreground}, - {{0 * 8, 13 * 16}, "Operator:", Theme::getInstance()->fg_light->foreground}}; + {{UI_POS_X(0), 1 * 16}, "ICAO:", Theme::getInstance()->fg_light->foreground}, + {{UI_POS_X(0), 2 * 16}, "Registration:", Theme::getInstance()->fg_light->foreground}, + {{UI_POS_X(0), 3 * 16}, "Manufacturer:", Theme::getInstance()->fg_light->foreground}, + {{UI_POS_X(0), 5 * 16}, "Model:", Theme::getInstance()->fg_light->foreground}, + {{UI_POS_X(0), 7 * 16}, "Type:", Theme::getInstance()->fg_light->foreground}, + {{UI_POS_X(0), 8 * 16}, "Number of engines:", Theme::getInstance()->fg_light->foreground}, + {{UI_POS_X(0), 9 * 16}, "Engine type:", Theme::getInstance()->fg_light->foreground}, + {{UI_POS_X(0), 11 * 16}, "Owner:", Theme::getInstance()->fg_light->foreground}, + {{UI_POS_X(0), 13 * 16}, "Operator:", Theme::getInstance()->fg_light->foreground}}; Text text_icao_address{ - {5 * 8, 1 * 16, 6 * 8, 16}, + {5 * 8, 1 * 16, 6 * 8, UI_POS_HEIGHT(1)}, "-"}; Text text_registration{ - {13 * 8, 2 * 16, 8 * 8, 16}, + {13 * 8, 2 * 16, 8 * 8, UI_POS_HEIGHT(1)}, "-"}; Text text_manufacturer{ - {0 * 8, 4 * 16, 19 * 8, 16}, + {UI_POS_X(0), 4 * 16, 19 * 8, UI_POS_HEIGHT(1)}, "-"}; Text text_model{ - {0 * 8, 6 * 16, screen_width, 16}, + {UI_POS_X(0), 6 * 16, screen_width, UI_POS_HEIGHT(1)}, "-"}; Text text_type{ - {5 * 8, 7 * 16, 22 * 8, 16}, + {5 * 8, 7 * 16, 22 * 8, UI_POS_HEIGHT(1)}, "-"}; Text text_number_of_engines{ - {18 * 8, 8 * 16, screen_width, 16}, + {18 * 8, 8 * 16, screen_width, UI_POS_HEIGHT(1)}, "-"}; Text text_engine_type{ - {0 * 8, 10 * 16, screen_width, 16}, + {UI_POS_X(0), 10 * 16, screen_width, UI_POS_HEIGHT(1)}, "-"}; Text text_owner{ - {0 * 8, 12 * 16, screen_width, 16}, + {UI_POS_X(0), 12 * 16, screen_width, UI_POS_HEIGHT(1)}, "-"}; Text text_operator{ - {0 * 8, 14 * 16, screen_width, 16}, + {UI_POS_X(0), 14 * 16, screen_width, UI_POS_HEIGHT(1)}, "-"}; Button button_close{ - {9 * 8, 16 * 16, 12 * 8, 3 * 16}, + {UI_POS_X_CENTER(12), UI_POS_Y(16), UI_POS_WIDTH(12), UI_POS_HEIGHT(3)}, "Back"}; }; @@ -299,55 +299,55 @@ class ADSBRxDetailsView : public View { bool airline_checked{false}; Labels labels{ - {{0 * 8, 1 * 16}, "ICAO:", Theme::getInstance()->fg_light->foreground}, + {{UI_POS_X(0), 1 * 16}, "ICAO:", Theme::getInstance()->fg_light->foreground}, {{13 * 8, 1 * 16}, "Callsign:", Theme::getInstance()->fg_light->foreground}, - {{0 * 8, 2 * 16}, "Last seen:", Theme::getInstance()->fg_light->foreground}, - {{0 * 8, 3 * 16}, "Airline:", Theme::getInstance()->fg_light->foreground}, - {{0 * 8, 5 * 16}, "Country:", Theme::getInstance()->fg_light->foreground}, - {{0 * 8, 13 * 16}, "Even position frame:", Theme::getInstance()->fg_light->foreground}, - {{0 * 8, 15 * 16}, "Odd position frame:", Theme::getInstance()->fg_light->foreground}}; + {{UI_POS_X(0), 2 * 16}, "Last seen:", Theme::getInstance()->fg_light->foreground}, + {{UI_POS_X(0), 3 * 16}, "Airline:", Theme::getInstance()->fg_light->foreground}, + {{UI_POS_X(0), 5 * 16}, "Country:", Theme::getInstance()->fg_light->foreground}, + {{UI_POS_X(0), 13 * 16}, "Even position frame:", Theme::getInstance()->fg_light->foreground}, + {{UI_POS_X(0), 15 * 16}, "Odd position frame:", Theme::getInstance()->fg_light->foreground}}; Text text_icao_address{ - {5 * 8, 1 * 16, 6 * 8, 16}, + {5 * 8, 1 * 16, 6 * 8, UI_POS_HEIGHT(1)}, "-"}; Text text_callsign{ - {22 * 8, 1 * 16, 8 * 8, 16}, + {22 * 8, 1 * 16, 8 * 8, UI_POS_HEIGHT(1)}, "-"}; Text text_last_seen{ - {11 * 8, 2 * 16, 19 * 8, 16}, + {11 * 8, 2 * 16, 19 * 8, UI_POS_HEIGHT(1)}, "-"}; Text text_airline{ - {0 * 8, 4 * 16, screen_width, 16}, + {UI_POS_X(0), 4 * 16, screen_width, UI_POS_HEIGHT(1)}, "-"}; Text text_country{ - {8 * 8, 5 * 16, 22 * 8, 16}, + {8 * 8, 5 * 16, 22 * 8, UI_POS_HEIGHT(1)}, "-"}; Text text_infos{ - {0 * 8, 6 * 16, screen_width, 16}, + {UI_POS_X(0), 6 * 16, screen_width, UI_POS_HEIGHT(1)}, "-"}; Text text_info2{ - {0 * 8, 7 * 16, screen_width, 16}, + {UI_POS_X(0), 7 * 16, screen_width, UI_POS_HEIGHT(1)}, "-"}; Text text_frame_pos_even{ - {0 * 8, 14 * 16, screen_width, 16}, + {UI_POS_X(0), 14 * 16, screen_width, UI_POS_HEIGHT(1)}, "-"}; Text text_frame_pos_odd{ - {0 * 8, 16 * 16, screen_width, 16}, + {UI_POS_X(0), 16 * 16, screen_width, UI_POS_HEIGHT(1)}, "-"}; Button button_aircraft_details{ - {2 * 8, 9 * 16, 12 * 8, 3 * 16}, + {UI_POS_X_CENTER(12) - UI_POS_X(8), UI_POS_Y(9), UI_POS_WIDTH(12), UI_POS_HEIGHT(3)}, "A/C details"}; Button button_see_map{ - {16 * 8, 9 * 16, 12 * 8, 3 * 16}, + {UI_POS_X_CENTER(12) + UI_POS_X(8), UI_POS_Y(9), UI_POS_WIDTH(12), UI_POS_HEIGHT(3)}, "See on map"}; MessageHandlerRegistration message_handler_gps{ @@ -403,8 +403,8 @@ class ADSBRxView : public View { static constexpr uint8_t max_update_entries = 16; /* Recent Entries */ - const RecentEntriesColumns columns{ - {{"ICAO/Call", 9}, + RecentEntriesColumns columns{ + {{"ICAO/Call", 0}, {"Lvl", 3}, {"Spd", 3}, {"Amp", 3}, @@ -424,33 +424,33 @@ class ADSBRxView : public View { ADSBRxDetailsView* details_view{nullptr}; Labels labels{ - {{0 * 8, 0 * 8}, "LNA: VGA: AMP:", Theme::getInstance()->fg_light->foreground}}; + {{UI_POS_X(0), UI_POS_Y(0)}, "LNA: VGA: AMP:", Theme::getInstance()->fg_light->foreground}}; LNAGainField field_lna{ - {4 * 8, 0 * 16}}; + {UI_POS_X(4), UI_POS_Y(0)}}; VGAGainField field_vga{ - {11 * 8, 0 * 16}}; + {UI_POS_X(11), UI_POS_Y(0)}}; RFAmpField field_rf_amp{ - {18 * 8, 0 * 16}}; + {UI_POS_X(18), UI_POS_Y(0)}}; RSSI rssi{ - {20 * 8, 4, 7 * 8, 8}, + {UI_POS_X(20), 4, UI_POS_WIDTH_REMAINING(23), 8}, }; ActivityDot status_frame{ - {27 * 8 + 2, 5, 2, 2}, + {UI_POS_X_RIGHT(3) + 2, 5, 2, 2}, Theme::getInstance()->bg_darkest->foreground, }; ActivityDot status_good_frame{ - {27 * 8 + 2, 9, 2, 2}, + {UI_POS_X_RIGHT(3) + 2, 9, 2, 2}, Theme::getInstance()->fg_green->foreground, }; AudioVolumeField field_volume{ - {screen_width - 2 * 8, 0 * 16}}; + {UI_POS_X_RIGHT(2), UI_POS_Y(0)}}; MessageHandlerRegistration message_handler_frame{ Message::ID::ADSBFrame, diff --git a/firmware/application/apps/ui_aprs_rx.cpp b/firmware/application/apps/ui_aprs_rx.cpp index a853bac05..bada71afa 100644 --- a/firmware/application/apps/ui_aprs_rx.cpp +++ b/firmware/application/apps/ui_aprs_rx.cpp @@ -42,7 +42,8 @@ void RecentEntriesTable::draw( const Entry& entry, const Rect& target_rect, Painter& painter, - const Style& style) { + const Style& style, + RecentEntriesColumns& columns) { Color target_color; // auto entry_age = entry.age; @@ -51,8 +52,8 @@ void RecentEntriesTable::draw( std::string entry_string = ""; entry_string += entry.source_formatted; - entry_string.append(10 - entry.source_formatted.size(), ' '); - entry_string += " "; + entry_string.resize(columns.at(0).second, ' '); + entry_string += " "; entry_string += (entry.hits <= 999 ? to_string_dec_uint(entry.hits, 4) : "999+"); entry_string += " "; entry_string += entry.time_string; diff --git a/firmware/application/apps/ui_aprs_rx.hpp b/firmware/application/apps/ui_aprs_rx.hpp index 41a55f2cf..bcb10b07c 100644 --- a/firmware/application/apps/ui_aprs_rx.hpp +++ b/firmware/application/apps/ui_aprs_rx.hpp @@ -134,14 +134,14 @@ class APRSDetailsView : public View { bool send_updates{false}; Console console{ - {0, 0 * 16, screen_width, 224}}; + {UI_POS_X(0), UI_POS_Y(0), screen_width, screen_height - 80}}; Button button_done{ - {160, 14 * 16, 8 * 8, 3 * 16}, + {UI_POS_X_CENTER(4) - UI_POS_WIDTH(8), UI_POS_Y(14), UI_POS_WIDTH(8), UI_POS_HEIGHT(3)}, "Close"}; Button button_see_map{ - {80, 14 * 16, 8 * 8, 3 * 16}, + {UI_POS_X_CENTER(4) + UI_POS_WIDTH(8), UI_POS_Y(14), UI_POS_WIDTH(8), UI_POS_HEIGHT(3)}, "Map"}; }; @@ -161,10 +161,10 @@ class APRSTableView : public View { private: NavigationView& nav_; - const RecentEntriesColumns columns{{{"Source", 9}, - {"Loc", 6}, - {"Hits", 4}, - {"Time", 8}}}; + RecentEntriesColumns columns{{{"Source", 0}, + {"Loc", 6}, + {"Hits", 4}, + {"Time", 8}}}; APRSRecentEntries recent{}; RecentEntriesView> recent_entries_view{columns, recent}; APRSDetailsView details_view{nav_}; @@ -209,21 +209,21 @@ class APRSRxView : public View { std::string str_log{""}; RFAmpField field_rf_amp{ - {13 * 8, 0 * 16}}; + {UI_POS_X(13), UI_POS_Y(0)}}; LNAGainField field_lna{ - {15 * 8, 0 * 16}}; + {UI_POS_X(15), UI_POS_Y(0)}}; VGAGainField field_vga{ - {18 * 8, 0 * 16}}; + {UI_POS_X(18), UI_POS_Y(0)}}; RSSI rssi{ - {21 * 8, 0, 6 * 8, 4}}; + {UI_POS_X(21), 0, UI_POS_WIDTH_REMAINING(21) - UI_POS_WIDTH(2), 4}}; Channel channel{ - {21 * 8, 5, 6 * 8, 4}}; + {UI_POS_X(21), 5, UI_POS_WIDTH_REMAINING(21) - UI_POS_WIDTH(2), 4}}; AudioVolumeField field_volume{ - {screen_width - 2 * 8, 0 * 16}}; + {UI_POS_X_RIGHT(2), UI_POS_Y(0)}}; OptionsField options_region{ - {0 * 8, 0 * 8}, + {UI_POS_X(0), UI_POS_Y(0)}, 3, {{"MAN", 0}, {"NA ", 1}, @@ -237,11 +237,11 @@ class APRSRxView : public View { {"ISS", 9}}}; FrequencyField field_frequency{ - {3 * 8, 0 * 16}}; + {UI_POS_X(3), UI_POS_Y(0)}}; // DEBUG RecordView record_view{ - {0 * 8, 1 * 16, screen_width, 1 * 16}, + {UI_POS_X(0), UI_POS_Y(1), UI_POS_MAXWIDTH, UI_POS_HEIGHT(1)}, u"AFS_????.WAV", aprs_dir, RecordView::FileType::WAV, @@ -249,7 +249,7 @@ class APRSRxView : public View { 4}; Console console{ - {0, 2 * 16, screen_width, screen_height - 80}}; + {UI_POS_X(0), UI_POS_Y(2), UI_POS_MAXWIDTH, screen_height - 80}}; std::unique_ptr logger{}; }; @@ -265,7 +265,7 @@ class APRSRXView : public View { private: NavigationView& nav_; - Rect view_rect = {0, 3 * 8, screen_width, screen_height - 40}; + Rect view_rect = {UI_POS_X(0), 3 * 8, UI_POS_MAXWIDTH, screen_height - 40}; APRSRxView view_stream{nav_, view_rect}; APRSTableView view_table{nav_, view_rect}; diff --git a/firmware/application/apps/ui_aprs_tx.hpp b/firmware/application/apps/ui_aprs_tx.hpp index 014c3b9cd..18f499441 100644 --- a/firmware/application/apps/ui_aprs_tx.hpp +++ b/firmware/application/apps/ui_aprs_tx.hpp @@ -61,9 +61,9 @@ class APRSTXView : public View { void on_tx_progress(const uint32_t progress, const bool done); Labels labels{ - {{0 * 8, 1 * 16}, "Source: SSID:", Theme::getInstance()->fg_light->foreground}, // 6 alphanum + SSID - {{0 * 8, 2 * 16}, " Dest.: SSID:", Theme::getInstance()->fg_light->foreground}, - {{0 * 8, 4 * 16}, "Info field:", Theme::getInstance()->fg_light->foreground}, + {{UI_POS_X(0), 1 * 16}, "Source: SSID:", Theme::getInstance()->fg_light->foreground}, // 6 alphanum + SSID + {{UI_POS_X(0), 2 * 16}, " Dest.: SSID:", Theme::getInstance()->fg_light->foreground}, + {{UI_POS_X(0), 4 * 16}, "Info field:", Theme::getInstance()->fg_light->foreground}, }; SymField sym_source{ @@ -91,14 +91,14 @@ class APRSTXView : public View { ' '}; Text text_payload{ - {0 * 8, 5 * 16, screen_width, 16}, + {UI_POS_X(0), 5 * 16, screen_width, 16}, "-"}; Button button_set{ - {0 * 8, 6 * 16, 80, 32}, + {UI_POS_X(0), 6 * 16, 80, 32}, "Set"}; TransmitterView tx_view{ - 16 * 16, + (int16_t)UI_POS_Y_BOTTOM(4), 5000, 0 // disable setting bandwith, since APRS used fixed 10k bandwidth }; diff --git a/firmware/application/apps/ui_battinfo.hpp b/firmware/application/apps/ui_battinfo.hpp index 4628f6b3b..062f163f0 100644 --- a/firmware/application/apps/ui_battinfo.hpp +++ b/firmware/application/apps/ui_battinfo.hpp @@ -98,7 +98,7 @@ class BattinfoView : public View { "Volt"}; Button button_exit{ - {72, 17 * 16, 96, 32}, + {UI_POS_X_CENTER(12), 17 * 16, UI_POS_WIDTH(12), UI_POS_HEIGHT(3)}, "Back"}; static msg_t static_fn(void* arg); Thread* thread{nullptr}; diff --git a/firmware/application/apps/ui_btle_rx.hpp b/firmware/application/apps/ui_btle_rx.hpp index 70f4edc93..43125509b 100644 --- a/firmware/application/apps/ui_btle_rx.hpp +++ b/firmware/application/apps/ui_btle_rx.hpp @@ -63,22 +63,22 @@ class BTLERxView : public View { static constexpr uint8_t channel_number = 37; RFAmpField field_rf_amp{ - {13 * 8, 0 * 16}}; + {UI_POS_X(13), UI_POS_Y(0)}}; LNAGainField field_lna{ - {15 * 8, 0 * 16}}; + {UI_POS_X(15), UI_POS_Y(0)}}; VGAGainField field_vga{ - {18 * 8, 0 * 16}}; + {UI_POS_X(18), UI_POS_Y(0)}}; RSSI rssi{ - {21 * 8, 0, 6 * 8, 4}}; + {UI_POS_X(21), UI_POS_Y(0), UI_POS_WIDTH_REMAINING(21), 4}}; Channel channel{ - {21 * 8, 5, 6 * 8, 4}}; + {UI_POS_X(21), 5, UI_POS_WIDTH_REMAINING(21), 4}}; RxFrequencyField field_frequency{ - {0 * 8, 0 * 16}, + {UI_POS_X(0), UI_POS_Y(0)}, nav_}; Button button_modem_setup{ - {screen_width - 12 * 8, 1 * 16, 96, 24}, + {screen_width - 12 * 8, UI_POS_Y(1), 96, 24}, "Modem setup"}; Console console{ diff --git a/firmware/application/apps/ui_debug.hpp b/firmware/application/apps/ui_debug.hpp index 519d94f2f..e6c0bf56d 100644 --- a/firmware/application/apps/ui_debug.hpp +++ b/firmware/application/apps/ui_debug.hpp @@ -242,9 +242,9 @@ class DebugControlsView : public View { private: Labels labels{ - {{8 * 8, 1 * 16}, "Controls State", Theme::getInstance()->bg_darkest->foreground}, - {{0 * 8, 11 * 16}, "Dial:", Theme::getInstance()->fg_medium->foreground}, - {{0 * 8, 14 * 16}, "Long-Press Mode:", Theme::getInstance()->fg_medium->foreground}}; + {{UI_POS_X_CENTER(14), 1 * 16}, "Controls State", Theme::getInstance()->bg_darkest->foreground}, + {{UI_POS_X(0), 11 * 16}, "Dial:", Theme::getInstance()->fg_medium->foreground}, + {{UI_POS_X(0), 14 * 16}, "Long-Press Mode:", Theme::getInstance()->fg_medium->foreground}}; ControlsSwitchesWidget switches_widget{ {80, 80, 80, 112}, @@ -259,7 +259,7 @@ class DebugControlsView : public View { }}; Button button_done{ - {72, 264, 96, 24}, + {UI_POS_X_CENTER(12), 264, 96, 24}, "Done"}; }; @@ -271,28 +271,28 @@ class DebugMemoryDumpView : public View { private: Button button_dump{ - {72, 4 * 16, 96, 24}, + {UI_POS_X_CENTER(12), 4 * 16, 96, 24}, "Dump"}; Button button_read{ - {16, 11 * 16, 96, 24}, + {UI_POS_X_CENTER(12) - UI_POS_WIDTH(10), 11 * 16, 96, 24}, "Read"}; Button button_write{ - {128, 11 * 16, 96, 24}, + {UI_POS_X_CENTER(12) + UI_POS_WIDTH(10), 11 * 16, 96, 24}, "Write"}; Button button_done{ - {128, screen_height - 80, 96, 24}, + {UI_POS_X_RIGHT(12), UI_POS_Y_BOTTOM(3), 96, 24}, "Done"}; Labels labels{ {{5 * 8, 1 * 16}, "Dump Range to File", Theme::getInstance()->fg_yellow->foreground}, - {{0 * 8, 2 * 16}, "Starting Address: 0x", Theme::getInstance()->fg_light->foreground}, - {{0 * 8, 3 * 16}, "Byte Count: 0x", Theme::getInstance()->fg_light->foreground}, + {{UI_POS_X(0), 2 * 16}, "Starting Address: 0x", Theme::getInstance()->fg_light->foreground}, + {{UI_POS_X(0), 3 * 16}, "Byte Count: 0x", Theme::getInstance()->fg_light->foreground}, {{3 * 8, 8 * 16}, "Read/Write Single Word", Theme::getInstance()->fg_yellow->foreground}, - {{0 * 8, 9 * 16}, "Memory Address: 0x", Theme::getInstance()->fg_light->foreground}, - {{0 * 8, 10 * 16}, "Data Value: 0x", Theme::getInstance()->fg_light->foreground}}; + {{UI_POS_X(0), 9 * 16}, "Memory Address: 0x", Theme::getInstance()->fg_light->foreground}, + {{UI_POS_X(0), 10 * 16}, "Data Value: 0x", Theme::getInstance()->fg_light->foreground}}; SymField field_starting_address{ {20 * 8, 2 * 16}, diff --git a/firmware/application/apps/ui_debug_max17055.cpp b/firmware/application/apps/ui_debug_max17055.cpp index 79cd45d5b..afa320197 100644 --- a/firmware/application/apps/ui_debug_max17055.cpp +++ b/firmware/application/apps/ui_debug_max17055.cpp @@ -12,7 +12,7 @@ BatteryCapacityView::RegisterEntry BatteryCapacityView::get_entry(size_t index) BatteryCapacityView::BatteryCapacityView(NavigationView& nav) { for (size_t i = 0; i < ENTRIES_PER_PAGE; ++i) { - name_texts[i].set_parent_rect({0 * 8, static_cast((i + 1) * 16), 8 * 8, 16}); + name_texts[i].set_parent_rect({UI_POS_X(0), static_cast((i + 1) * 16), 8 * 8, 16}); addr_texts[i].set_parent_rect({9 * 8, static_cast((i + 1) * 16), 4 * 8, 16}); hex_texts[i].set_parent_rect({14 * 8, static_cast((i + 1) * 16), 6 * 8, 16}); value_texts[i].set_parent_rect({21 * 8, static_cast((i + 1) * 16), 10 * 8, 16}); diff --git a/firmware/application/apps/ui_debug_max17055.hpp b/firmware/application/apps/ui_debug_max17055.hpp index 10791026b..7b7fd5ca6 100644 --- a/firmware/application/apps/ui_debug_max17055.hpp +++ b/firmware/application/apps/ui_debug_max17055.hpp @@ -24,18 +24,18 @@ class BatteryCapacityView : public View { static RegisterEntry get_entry(size_t index); Labels labels{ - {{0 * 8, 0 * 16}, "Reg", Theme::getInstance()->fg_yellow->foreground}, - {{9 * 8, 0 * 16}, "Addr", Theme::getInstance()->fg_yellow->foreground}, - {{14 * 8, 0 * 16}, "Hex", Theme::getInstance()->fg_yellow->foreground}, - {{21 * 8, 0 * 16}, "Value", Theme::getInstance()->fg_yellow->foreground}, + {{UI_POS_X(0), UI_POS_Y(0)}, "Reg", Theme::getInstance()->fg_yellow->foreground}, + {{9 * 8, UI_POS_Y(0)}, "Addr", Theme::getInstance()->fg_yellow->foreground}, + {{14 * 8, UI_POS_Y(0)}, "Hex", Theme::getInstance()->fg_yellow->foreground}, + {{21 * 8, UI_POS_Y(0)}, "Value", Theme::getInstance()->fg_yellow->foreground}, }; std::array name_texts = {}; std::array addr_texts = {}; std::array hex_texts = {}; std::array value_texts = {}; - Text page_text{{144, 284, 80, 16}, "Page 1/1"}; - Button button_done{{16, 280, 96, 24}, "Done"}; + Text page_text{{144, UI_POS_Y_BOTTOM(3), 80, 16}, "Page 1/1"}; + Button button_done{{16, UI_POS_Y_BOTTOM(3), 96, 24}, "Done"}; void update_values(); void populate_page(int start_index); diff --git a/firmware/application/apps/ui_encoders.hpp b/firmware/application/apps/ui_encoders.hpp index 7c32584aa..95e8e0ee0 100644 --- a/firmware/application/apps/ui_encoders.hpp +++ b/firmware/application/apps/ui_encoders.hpp @@ -216,10 +216,10 @@ class EncodersView : public View { "Ready"}; ProgressBar progressbar{ - {2 * 8, 13 * 16 + 20, 208, 16}}; + {2 * 8, 13 * 16 + 20, UI_POS_WIDTH_REMAINING(4), 16}}; TransmitterView tx_view{ - 16 * 16, + (int16_t)UI_POS_Y_BOTTOM(4), 50000, 9}; diff --git a/firmware/application/apps/ui_fileman.cpp b/firmware/application/apps/ui_fileman.cpp index af156f8d6..20869fe7e 100644 --- a/firmware/application/apps/ui_fileman.cpp +++ b/firmware/application/apps/ui_fileman.cpp @@ -294,6 +294,7 @@ FileManBaseView::FileManBaseView( std::string filter) : nav_{nav}, extension_filter{filter} { + max_filename_length = screen_width / 8 - 10; add_children({&labels, &text_current, &button_exit}); @@ -403,7 +404,7 @@ void FileManBaseView::refresh_list() { menu_view.clear(); for (const auto& entry : entry_list) { - auto entry_name = std::string{entry.path.length() <= 20 ? entry.path : entry.path.substr(0, 20)}; + auto entry_name = std::string{entry.path.length() <= max_filename_length ? entry.path : entry.path.substr(0, max_filename_length)}; if (entry.is_directory) { std::string size_str{}; @@ -414,7 +415,7 @@ void FileManBaseView::refresh_list() { } menu_view.add_item( - {entry_name.substr(0, max_filename_length) + std::string(21 - entry_name.length(), ' ') + size_str, + {entry_name.substr(0, max_filename_length) + std::string((max_filename_length + 1) - entry_name.length(), ' ') + size_str, Theme::getInstance()->fg_yellow->foreground, &bitmap_icon_dir, [this](KeyEvent key) { @@ -427,7 +428,7 @@ void FileManBaseView::refresh_list() { auto size_str = to_string_file_size(entry.size); menu_view.add_item( - {entry_name.substr(0, max_filename_length) + std::string(21 - entry_name.length(), ' ') + size_str, + {entry_name.substr(0, max_filename_length) + std::string((max_filename_length + 1) - entry_name.length(), ' ') + size_str, assoc.color, assoc.icon, [this](KeyEvent key) { @@ -486,7 +487,7 @@ FileLoadView::FileLoadView( add_children({&menu_view}); // Resize menu view to fill screen - menu_view.set_parent_rect({0, 3 * 8, screen_width, 29 * 8}); + menu_view.set_parent_rect({0, 3 * 8, screen_width, UI_POS_HEIGHT_REMAINING(4.5)}); refresh_list(); diff --git a/firmware/application/apps/ui_fileman.hpp b/firmware/application/apps/ui_fileman.hpp index 892fe0914..05a10cce9 100644 --- a/firmware/application/apps/ui_fileman.hpp +++ b/firmware/application/apps/ui_fileman.hpp @@ -65,7 +65,7 @@ class FileManBaseView : public View { uint8_t pagination = 0; uint8_t nb_pages = 1; bool restoring_navigation = false; - static constexpr size_t max_filename_length = 20; + size_t max_filename_length = 20; static constexpr size_t max_items_loaded = 75; // too memory hungry, so won't sort it static constexpr size_t items_per_page = 20; @@ -122,16 +122,16 @@ class FileManBaseView : public View { {{0, 0}, "Path:", Theme::getInstance()->fg_light->foreground}}; Text text_current{ - {6 * 8, 0 * 8, 24 * 8, 16}, + {UI_POS_X(6), UI_POS_Y(0), UI_POS_WIDTH_REMAINING(6), UI_POS_HEIGHT(1)}, "", }; MenuView menu_view{ - {0, 2 * 8, screen_width, 26 * 8}, + {0, UI_POS_Y(1), UI_POS_MAXWIDTH, UI_POS_HEIGHT_REMAINING(7)}, true}; Button button_exit{ - {22 * 8, 34 * 8, 8 * 8, 32}, + {UI_POS_X_RIGHT(8), UI_POS_Y_BOTTOM(3), 8 * 8, UI_POS_HEIGHT(2)}, "Exit"}; }; @@ -230,66 +230,66 @@ class FileManagerView : public FileManBaseView { bool selected_is_valid() const; Text text_date{ - {0 * 8, 26 * 8, 28 * 8, 16}, + {UI_POS_X(0), UI_POS_Y_BOTTOM(6), 28 * 8, UI_POS_HEIGHT(1)}, ""}; NewButton button_rename{ - {0 * 8, 29 * 8, 4 * 8, 32}, + {UI_POS_X(0), UI_POS_Y_BOTTOM(5), UI_POS_WIDTH(4), UI_POS_HEIGHT(2)}, {}, &bitmap_icon_rename, Theme::getInstance()->fg_blue->foreground}; NewButton button_delete{ - {9 * 8, 34 * 8, 4 * 8, 32}, + {UI_POS_X(9), UI_POS_Y_BOTTOM(3), UI_POS_WIDTH(4), UI_POS_HEIGHT(2)}, {}, &bitmap_icon_trash, Theme::getInstance()->fg_red->foreground}; NewButton button_clean{ - {13 * 8, 34 * 8, 4 * 8, 32}, + {UI_POS_X(13), UI_POS_Y_BOTTOM(3), UI_POS_WIDTH(4), UI_POS_HEIGHT(2)}, {}, &bitmap_icon_clean, Theme::getInstance()->fg_red->foreground}; NewButton button_cut{ - {9 * 8, 29 * 8, 4 * 8, 32}, + {UI_POS_X(9), UI_POS_Y_BOTTOM(5), UI_POS_WIDTH(4), UI_POS_HEIGHT(2)}, {}, &bitmap_icon_cut, Theme::getInstance()->fg_dark->foreground}; NewButton button_copy{ - {13 * 8, 29 * 8, 4 * 8, 32}, + {UI_POS_X(13), UI_POS_Y_BOTTOM(5), UI_POS_WIDTH(4), UI_POS_HEIGHT(2)}, {}, &bitmap_icon_copy, Theme::getInstance()->fg_dark->foreground}; NewButton button_paste{ - {17 * 8, 29 * 8, 4 * 8, 32}, + {UI_POS_X(17), UI_POS_Y_BOTTOM(5), UI_POS_WIDTH(4), UI_POS_HEIGHT(2)}, {}, &bitmap_icon_paste, Theme::getInstance()->fg_dark->foreground}; NewButton button_new_dir{ - {22 * 8, 29 * 8, 4 * 8, 32}, + {UI_POS_X(22), UI_POS_Y_BOTTOM(5), UI_POS_WIDTH(4), UI_POS_HEIGHT(2)}, {}, &bitmap_icon_new_dir, Theme::getInstance()->fg_green->foreground}; NewButton button_new_file{ - {26 * 8, 29 * 8, 4 * 8, 32}, + {UI_POS_X(26), UI_POS_Y_BOTTOM(5), UI_POS_WIDTH(4), UI_POS_HEIGHT(2)}, {}, &bitmap_icon_new_file, Theme::getInstance()->fg_green->foreground}; NewButton button_open_notepad{ - {0 * 8, 34 * 8, 4 * 8, 32}, + {UI_POS_X(0), UI_POS_Y_BOTTOM(3), UI_POS_WIDTH(4), UI_POS_HEIGHT(2)}, {}, &bitmap_icon_notepad, Theme::getInstance()->fg_orange->foreground}; NewButton button_rename_timestamp{ - {4 * 8, 29 * 8, 4 * 8, 32}, + {UI_POS_X(4), UI_POS_Y_BOTTOM(5), UI_POS_WIDTH(4), UI_POS_HEIGHT(2)}, {}, &bitmap_icon_options_datetime, Theme::getInstance()->fg_blue->foreground, @@ -297,13 +297,13 @@ class FileManagerView : public FileManBaseView { NewButton button_open_iq_trim{ - {4 * 8, 34 * 8, 4 * 8, 32}, + {UI_POS_X(4), UI_POS_Y_BOTTOM(3), UI_POS_WIDTH(4), UI_POS_HEIGHT(2)}, {}, &bitmap_icon_trim, Theme::getInstance()->fg_orange->foreground}; NewButton button_show_hidden_files{ - {17 * 8, 34 * 8, 4 * 8, 32}, + {UI_POS_X(17), UI_POS_Y_BOTTOM(3), UI_POS_WIDTH(4), UI_POS_HEIGHT(2)}, {}, &bitmap_icon_hide, Theme::getInstance()->fg_dark->foreground}; diff --git a/firmware/application/apps/ui_flash_utility.cpp b/firmware/application/apps/ui_flash_utility.cpp index 64c10f618..b15bd7bdc 100644 --- a/firmware/application/apps/ui_flash_utility.cpp +++ b/firmware/application/apps/ui_flash_utility.cpp @@ -130,6 +130,12 @@ bool FlashUtilityView::endsWith(const std::u16string& str, const std::u16string& } } +void FlashUtilityView::wait_till_loaded() { + while (!isLoaded) { + chThdSleepMilliseconds(50); + } +} + std::filesystem::path FlashUtilityView::extract_tar(std::filesystem::path::string_type path, ui::Painter& painter) { // painter.fill_rectangle( diff --git a/firmware/application/apps/ui_flash_utility.hpp b/firmware/application/apps/ui_flash_utility.hpp index 6b623bc34..0d9f8d9fa 100644 --- a/firmware/application/apps/ui_flash_utility.hpp +++ b/firmware/application/apps/ui_flash_utility.hpp @@ -49,6 +49,7 @@ class FlashUtilityView : public View { std::string title() const override { return "Flash Utility"; }; bool flash_firmware(std::filesystem::path::string_type path); + void wait_till_loaded(); private: NavigationView& nav_; @@ -60,13 +61,21 @@ class FlashUtilityView : public View { {{4, 4}, "Select firmware to flash:", Theme::getInstance()->bg_darkest->foreground}}; MenuView menu_view{ - {0, 2 * 8, screen_width, 26 * 8}, + {0, UI_POS_Y(1), screen_width, UI_POS_HEIGHT_REMAINING(2)}, true}; std::filesystem::path extract_tar(std::filesystem::path::string_type path, ui::Painter& painter); // extracts the tar file, and returns the firmware.bin path from it. empty string if no fw void firmware_selected(std::filesystem::path::string_type path); bool endsWith(const std::u16string& str, const std::u16string& suffix); + bool isLoaded = false; + uint8_t refreshcnt = 0; + MessageHandlerRegistration message_handler_frame_sync{ + Message::ID::DisplayFrameSync, + [this](const Message* const) { + refreshcnt++; + if (refreshcnt > 5) isLoaded = true; + }}; }; } /* namespace ui */ diff --git a/firmware/application/apps/ui_freqman.hpp b/firmware/application/apps/ui_freqman.hpp index 8877a30a3..a1f32f45d 100644 --- a/firmware/application/apps/ui_freqman.hpp +++ b/firmware/application/apps/ui_freqman.hpp @@ -72,7 +72,7 @@ class FreqManBaseView : public View { {0, 3 * 8, screen_width, 12 * 16 + 2 /* 2 Keeps text out of border. */}}; Button button_exit{ - {15 * 8, 17 * 16, 15 * 8, 2 * 16}, + {UI_POS_X_RIGHT(14), UI_POS_Y_BOTTOM(3), UI_POS_WIDTH(14), UI_POS_HEIGHT(2)}, "Exit"}; protected: @@ -97,14 +97,14 @@ class FrequencySaveView : public FreqManBaseView { 0}; Labels labels{ - {{0 * 8, 6 * 16}, "Description:", Theme::getInstance()->bg_darkest->foreground}}; + {{UI_POS_X(0), 6 * 16}, "Description:", Theme::getInstance()->bg_darkest->foreground}}; TextField field_description{ - {0 * 8, 7 * 16, screen_width, 1 * 16}, + {UI_POS_X(0), 7 * 16, screen_width, 1 * 16}, ""}; Button button_save{ - {0 * 8, 17 * 16, 15 * 8, 2 * 16}, + {UI_POS_X(0), UI_POS_Y_BOTTOM(3), UI_POS_WIDTH(14), UI_POS_HEIGHT(2)}, "Save"}; }; @@ -134,44 +134,44 @@ class FrequencyManagerView : public FreqManBaseView { void on_del_entry(); NewButton button_add_category{ - {23 * 8, 0 * 16, 7 * 4, 20}, + {UI_POS_X_RIGHT(8), UI_POS_Y(0), 7 * 4, 20}, {}, &bitmap_icon_new_file, Theme::getInstance()->bg_darkest->foreground, true}; NewButton button_del_category{ - {26 * 8 + 4, 0 * 16, 7 * 4, 20}, + {UI_POS_X_RIGHT(4), UI_POS_Y(0), 7 * 4, 20}, {}, &bitmap_icon_trash, Theme::getInstance()->fg_red->foreground, true}; Button button_edit_entry{ - {0 * 8, 14 * 16 - 4, 15 * 8, 1 * 16 + 4}, + {UI_POS_X(0), UI_POS_Y_BOTTOM(6.5) + 4, 15 * 8, 1 * 16 + 4}, "Edit"}; Rectangle rect_padding{ - {15 * 8, 14 * 16 - 4, 15 * 8, 1 * 16 + 4}, + {15 * 8, UI_POS_Y_BOTTOM(6.5), 15 * 8, 1 * 16 + 4}, Theme::getInstance()->fg_medium->background}; Button button_edit_freq{ - {0 * 8, 15 * 16, 15 * 8, 2 * 16}, + {UI_POS_X(0), UI_POS_Y_BOTTOM(5), 15 * 8, 2 * 16}, "Frequency"}; Button button_edit_desc{ - {0 * 8, 17 * 16, 15 * 8, 2 * 16}, + {UI_POS_X(0), UI_POS_Y_BOTTOM(3), 15 * 8, 2 * 16}, "Description"}; NewButton button_add_entry{ - {15 * 8, 15 * 16, 7 * 8 + 4, 2 * 16}, + {15 * 8, UI_POS_Y_BOTTOM(5), 7 * 8 + 4, 2 * 16}, {}, &bitmap_icon_add, Theme::getInstance()->bg_darkest->foreground, true}; NewButton button_del_entry{ - {22 * 8 + 4, 15 * 16, 7 * 8 + 4, 2 * 16}, + {22 * 8 + 4, UI_POS_Y_BOTTOM(5), 7 * 8 + 4, 2 * 16}, {}, &bitmap_icon_delete, Theme::getInstance()->fg_red->foreground, @@ -201,14 +201,14 @@ class FrequencyEditView : public View { Labels labels{ {{5 * 8, 1 * 16}, "Edit Frequency Entry", Theme::getInstance()->bg_darkest->foreground}, - {{0 * 8, 3 * 16}, "Entry Type :", Theme::getInstance()->fg_light->foreground}, - {{0 * 8, 4 * 16}, "Frequency A:", Theme::getInstance()->fg_light->foreground}, - {{0 * 8, 5 * 16}, "Frequency B:", Theme::getInstance()->fg_light->foreground}, - {{0 * 8, 6 * 16}, "Modulation :", Theme::getInstance()->fg_light->foreground}, - {{0 * 8, 7 * 16}, "Bandwidth :", Theme::getInstance()->fg_light->foreground}, - {{0 * 8, 8 * 16}, "Step :", Theme::getInstance()->fg_light->foreground}, - {{0 * 8, 9 * 16}, "Tone Freq :", Theme::getInstance()->fg_light->foreground}, - {{0 * 8, 10 * 16}, "Description:", Theme::getInstance()->fg_light->foreground}, + {{UI_POS_X(0), 3 * 16}, "Entry Type :", Theme::getInstance()->fg_light->foreground}, + {{UI_POS_X(0), 4 * 16}, "Frequency A:", Theme::getInstance()->fg_light->foreground}, + {{UI_POS_X(0), 5 * 16}, "Frequency B:", Theme::getInstance()->fg_light->foreground}, + {{UI_POS_X(0), 6 * 16}, "Modulation :", Theme::getInstance()->fg_light->foreground}, + {{UI_POS_X(0), 7 * 16}, "Bandwidth :", Theme::getInstance()->fg_light->foreground}, + {{UI_POS_X(0), 8 * 16}, "Step :", Theme::getInstance()->fg_light->foreground}, + {{UI_POS_X(0), 9 * 16}, "Tone Freq :", Theme::getInstance()->fg_light->foreground}, + {{UI_POS_X(0), 10 * 16}, "Description:", Theme::getInstance()->fg_light->foreground}, }; OptionsField field_type{ @@ -243,11 +243,11 @@ class FrequencyEditView : public View { {}}; Button button_save{ - {0 * 8, 17 * 16, 15 * 8, 2 * 16}, + {UI_POS_X(0), UI_POS_Y_BOTTOM(3), 15 * 8, 2 * 16}, "Save"}; Button button_cancel{ - {15 * 8, 17 * 16, 15 * 8, 2 * 16}, + {UI_POS_X_RIGHT(15), UI_POS_Y_BOTTOM(3), 15 * 8, 2 * 16}, "Cancel"}; }; diff --git a/firmware/application/apps/ui_iq_trim.hpp b/firmware/application/apps/ui_iq_trim.hpp index 03d33a5e9..070eee54b 100644 --- a/firmware/application/apps/ui_iq_trim.hpp +++ b/firmware/application/apps/ui_iq_trim.hpp @@ -54,7 +54,7 @@ class TrimProgressUI { } void clear() { - p.fill_rectangle({0 * 8, 4 * 16, screen_width, 3 * 16}, Theme::getInstance()->bg_darkest->background); + p.fill_rectangle({UI_POS_X(0), 4 * 16, screen_width, 3 * 16}, Theme::getInstance()->bg_darkest->background); } auto get_callback() { @@ -100,22 +100,22 @@ class IQTrimView : public View { TrimProgressUI progress_ui{}; Labels labels{ - {{0 * 8, 0 * 16}, "Capture File:", Theme::getInstance()->fg_light->foreground}, - {{0 * 8, 6 * 16}, "Start :", Theme::getInstance()->fg_light->foreground}, - {{0 * 8, 7 * 16}, "End :", Theme::getInstance()->fg_light->foreground}, - {{0 * 8, 8 * 16}, "Samples:", Theme::getInstance()->fg_light->foreground}, - {{0 * 8, 9 * 16}, "Max Pwr:", Theme::getInstance()->fg_light->foreground}, - {{0 * 8, 10 * 16}, "Cutoff :", Theme::getInstance()->fg_light->foreground}, + {{UI_POS_X(0), UI_POS_Y(0)}, "Capture File:", Theme::getInstance()->fg_light->foreground}, + {{UI_POS_X(0), 6 * 16}, "Start :", Theme::getInstance()->fg_light->foreground}, + {{UI_POS_X(0), 7 * 16}, "End :", Theme::getInstance()->fg_light->foreground}, + {{UI_POS_X(0), 8 * 16}, "Samples:", Theme::getInstance()->fg_light->foreground}, + {{UI_POS_X(0), 9 * 16}, "Max Pwr:", Theme::getInstance()->fg_light->foreground}, + {{UI_POS_X(0), 10 * 16}, "Cutoff :", Theme::getInstance()->fg_light->foreground}, {{12 * 8, 10 * 16}, "%", Theme::getInstance()->fg_light->foreground}, - {{0 * 8, 12 * 16}, "Amplify:", Theme::getInstance()->fg_light->foreground}, + {{UI_POS_X(0), 12 * 16}, "Amplify:", Theme::getInstance()->fg_light->foreground}, {{10 * 8, 12 * 16}, "x", Theme::getInstance()->fg_light->foreground}, }; TextField field_path{ - {0 * 8, 1 * 16, screen_width, 1 * 16}, + {UI_POS_X(0), 1 * 16, screen_width, 1 * 16}, "Open File..."}; - Point pos_lines{0 * 8, 4 * 16}; + Point pos_lines{UI_POS_X(0), 4 * 16}; Dim height_lines{2 * 16}; NumberField field_start{ @@ -155,7 +155,7 @@ class IQTrimView : public View { ' '}; Button button_trim{ - {20 * 8, 16 * 16, 8 * 8, 2 * 16}, + {UI_POS_X_CENTER(8), UI_POS_Y_BOTTOM(3), 8 * 8, 2 * 16}, "Trim"}; }; diff --git a/firmware/application/apps/ui_looking_glass_app.hpp b/firmware/application/apps/ui_looking_glass_app.hpp index 9ddfd18fc..5d4a99af0 100644 --- a/firmware/application/apps/ui_looking_glass_app.hpp +++ b/firmware/application/apps/ui_looking_glass_app.hpp @@ -170,7 +170,7 @@ class GlassView : public View { uint8_t ignore_dc = 0; Labels labels{ - {{0, 0 * 16}, "MIN: MAX: LNA VGA ", Theme::getInstance()->fg_light->foreground}, + {{0, UI_POS_Y(0)}, "MIN: MAX: LNA VGA ", Theme::getInstance()->fg_light->foreground}, {{0, 1 * 16}, "RANGE: FILTER: AMP:", Theme::getInstance()->fg_light->foreground}, {{0, 2 * 16}, "P:", Theme::getInstance()->fg_light->foreground}, {{0, 3 * 16}, "MARKER: MHz RXIQCAL", Theme::getInstance()->fg_light->foreground}, @@ -178,24 +178,24 @@ class GlassView : public View { {{0, 4 * 16}, "RES: VOL:", Theme::getInstance()->fg_light->foreground}}; NumberField field_frequency_min{ - {4 * 8, 0 * 16}, + {4 * 8, UI_POS_Y(0)}, 4, {0, 7199}, 1, // number of steps by encoder delta ' '}; NumberField field_frequency_max{ - {13 * 8, 0 * 16}, + {13 * 8, UI_POS_Y(0)}, 4, {1, 7200}, 1, // number of steps by encoder delta ' '}; LNAGainField field_lna{ - {21 * 8, 0 * 16}}; + {21 * 8, UI_POS_Y(0)}}; VGAGainField field_vga{ - {27 * 8, 0 * 16}}; + {27 * 8, UI_POS_Y(0)}}; TextField field_range{ {6 * 8, 1 * 16, 6 * 8, 16}, @@ -298,7 +298,7 @@ class GlassView : public View { "RST"}; Text freq_stats{ - {0 * 8, 5 * 16, screen_width - 10 * 8, 8}, + {UI_POS_X(0), 5 * 16, screen_width - 10 * 8, 8}, ""}; MessageHandlerRegistration message_handler_spectrum_config{ diff --git a/firmware/application/apps/ui_mictx.hpp b/firmware/application/apps/ui_mictx.hpp index 87c8610f4..cf375e4cf 100644 --- a/firmware/application/apps/ui_mictx.hpp +++ b/firmware/application/apps/ui_mictx.hpp @@ -170,7 +170,7 @@ class MicTXView : public View { {{17 * 8, 1 * 8}, "ALC", Theme::getInstance()->fg_light->foreground}}; VuMeter vumeter{ - {0 * 8, 1 * 8, 2 * 8, 33 * 8}, + {UI_POS_X(0), 1 * 8, 2 * 8, 33 * 8}, 12, true}; diff --git a/firmware/application/apps/ui_modemsetup.hpp b/firmware/application/apps/ui_modemsetup.hpp index d5719b6bd..c0f496b36 100644 --- a/firmware/application/apps/ui_modemsetup.hpp +++ b/firmware/application/apps/ui_modemsetup.hpp @@ -104,7 +104,7 @@ class ModemSetupView : public View { "SET"}; Button button_save{ - {9 * 8, 250, 96, 40}, + {UI_POS_X_CENTER(12), 250, UI_POS_WIDTH(12), 40}, "Save"}; }; diff --git a/firmware/application/apps/ui_playlist.cpp b/firmware/application/apps/ui_playlist.cpp index e08654b80..da8c62e66 100644 --- a/firmware/application/apps/ui_playlist.cpp +++ b/firmware/application/apps/ui_playlist.cpp @@ -387,7 +387,6 @@ PlaylistView::PlaylistView( &button_next, &waterfall, }); - ensure_directory(playlist_dir); waterfall.show_audio_spectrum_view(false); diff --git a/firmware/application/apps/ui_playlist.hpp b/firmware/application/apps/ui_playlist.hpp index cf98e6c24..8662cafa4 100644 --- a/firmware/application/apps/ui_playlist.hpp +++ b/firmware/application/apps/ui_playlist.hpp @@ -107,24 +107,24 @@ class PlaylistView : public View { void handle_replay_thread_done(uint32_t return_code); Text text_filename{ - {0 * 8, 0 * 16, screen_width, 16}}; + {UI_POS_X(0), UI_POS_Y(0), screen_width, 16}}; FrequencyField field_frequency{ - {0 * 8, 1 * 16}}; + {UI_POS_X(0), 1 * 16}}; Text text_sample_rate{ {10 * 8, 1 * 16, 7 * 8, 16}}; ProgressBar progressbar_track{ - {18 * 8, 1 * 16, 12 * 8, 8 + 1}}; + {18 * 8, 1 * 16, UI_POS_WIDTH_REMAINING(19), 8 + 1}}; // (-1) to overlap with progressbar_track so there's // only 1 pixel between them instead of 2. ProgressBar progressbar_transmit{ - {18 * 8, 3 * 8 - 1, 12 * 8, 8}}; + {18 * 8, 3 * 8 - 1, UI_POS_WIDTH_REMAINING(19), 8}}; Text text_duration{ - {0 * 8, 2 * 16, 5 * 8, 16}}; + {UI_POS_X(0), 2 * 16, 5 * 8, 16}}; // TODO: delay duration field. @@ -133,7 +133,7 @@ class PlaylistView : public View { /*short_ui*/ true}; Checkbox check_loop{ - {21 * 8, 2 * 16}, + {UI_POS_X_RIGHT(9), 2 * 16}, 4, "Loop", true}; @@ -145,7 +145,7 @@ class PlaylistView : public View { Theme::getInstance()->fg_green->background}; Text text_track{ - {0 * 8, 3 * 16, screen_width, 16}}; + {UI_POS_X(0), 3 * 16, screen_width, 16}}; NewButton button_prev{ {2 * 8, 4 * 16, 4 * 8, 2 * 16}, diff --git a/firmware/application/apps/ui_pocsag_tx.hpp b/firmware/application/apps/ui_pocsag_tx.hpp index b23e27dd8..daafb574e 100644 --- a/firmware/application/apps/ui_pocsag_tx.hpp +++ b/firmware/application/apps/ui_pocsag_tx.hpp @@ -85,7 +85,7 @@ class POCSAGTXView : public View { {{6 * 8, 8 * 8}, "Type:", Theme::getInstance()->fg_light->foreground}, {{2 * 8, 10 * 8}, "Function:", Theme::getInstance()->fg_light->foreground}, {{5 * 8, 12 * 8}, "Phase:", Theme::getInstance()->fg_light->foreground}, - {{0 * 8, 14 * 8}, "Message:", Theme::getInstance()->fg_light->foreground}}; + {{UI_POS_X(0), 14 * 8}, "Message:", Theme::getInstance()->fg_light->foreground}}; OptionsField options_bitrate{ {11 * 8, 4 * 8}, @@ -122,21 +122,21 @@ class POCSAGTXView : public View { }}; Text text_message{ - {0 * 8, 16 * 8, screen_width, 16}, + {UI_POS_X(0), 16 * 8, screen_width, 16}, ""}; Text text_message_l2{ - {0 * 8, 18 * 8, screen_width, 16}, + {UI_POS_X(0), 18 * 8, screen_width, 16}, ""}; Button button_message{ - {0 * 8, 20 * 8, 14 * 8, 32}, + {UI_POS_X(0), 20 * 8, 14 * 8, 32}, "Set message"}; ProgressBar progressbar{ - {16, 210, 208, 16}}; + {16, 210, UI_POS_WIDTH_REMAINING(4), 16}}; TransmitterView tx_view{ - 16 * 16, + (int16_t)UI_POS_Y_BOTTOM(4), 10000, 9}; diff --git a/firmware/application/apps/ui_rds.hpp b/firmware/application/apps/ui_rds.hpp index 40e9d285f..c7f450c5c 100644 --- a/firmware/application/apps/ui_rds.hpp +++ b/firmware/application/apps/ui_rds.hpp @@ -82,7 +82,7 @@ class RDSRadioTextView : public OptionTabView { {1 * 8, 4 * 16, 28 * 8, 16}, "-"}; Button button_set{ - {88, 6 * 16, 64, 32}, + {UI_POS_X_CENTER(8), 6 * 16, UI_POS_WIDTH(8), 32}, "Set"}; }; @@ -92,7 +92,7 @@ class RDSDateTimeView : public OptionTabView { private: Labels labels{ - {{44, 5 * 16}, "Not yet implemented", Theme::getInstance()->error_dark->foreground}}; + {{UI_POS_X_CENTER(19), 5 * 16}, "Not yet implemented", Theme::getInstance()->error_dark->foreground}}; }; class RDSAudioView : public OptionTabView { @@ -101,7 +101,7 @@ class RDSAudioView : public OptionTabView { private: Labels labels{ - {{44, 5 * 16}, "Not yet implemented", Theme::getInstance()->error_dark->foreground}}; + {{UI_POS_X_CENTER(19), 5 * 16}, "Not yet implemented", Theme::getInstance()->error_dark->foreground}}; }; class RDSThread { @@ -174,7 +174,7 @@ class RDSView : public View { {"Audio", Theme::getInstance()->fg_orange->foreground, &view_audio}}; Labels labels{ - {{0 * 8, 28}, "Program type:", Theme::getInstance()->fg_light->foreground}, + {{UI_POS_X(0), 28}, "Program type:", Theme::getInstance()->fg_light->foreground}, //{ { 14 * 8, 16 + 8 }, "CC:", Theme::getInstance()->fg_light->foreground }, {{2 * 8, 28 + 16}, "Program ID:", Theme::getInstance()->fg_light->foreground}, //{ { 13 * 8, 32 + 8 }, "Cov:",Theme::getInstance()->fg_light->foreground }, @@ -319,7 +319,7 @@ class RDSView : public View { "TP"}; TransmitterView tx_view{ - 16 * 16, + (int16_t)UI_POS_Y_BOTTOM(4), 50000, 9}; diff --git a/firmware/application/apps/ui_recon.hpp b/firmware/application/apps/ui_recon.hpp index e610b2438..3448ee97c 100644 --- a/firmware/application/apps/ui_recon.hpp +++ b/firmware/application/apps/ui_recon.hpp @@ -217,23 +217,24 @@ class ReconView : public View { std::unique_ptr record_view{}; Labels labels{ - {{0 * 8, 0 * 16}, "LNA: VGA: AMP: VOL: ", Theme::getInstance()->fg_light->foreground}, + {{UI_POS_X(0), UI_POS_Y(0)}, "LNA: VGA: AMP: ", Theme::getInstance()->fg_light->foreground}, + {{UI_POS_X_RIGHT(6), UI_POS_Y(0)}, "VOL: ", Theme::getInstance()->fg_light->foreground}, {{3 * 8, 8 * 16}, "START END", Theme::getInstance()->fg_light->foreground}, - {{0 * 8, (22 * 8)}, " S: ", Theme::getInstance()->fg_light->foreground}, - {{0 * 8, (24 * 8) + 4}, "NBLCKS:x W,L: , ", Theme::getInstance()->fg_light->foreground}, - {{0 * 8, (26 * 8) + 4}, "MODE: , SQUELCH: ", Theme::getInstance()->fg_light->foreground}}; + {{UI_POS_X(0), (22 * 8)}, " S: ", Theme::getInstance()->fg_light->foreground}, + {{UI_POS_X(0), (24 * 8) + 4}, "NBLCKS:x W,L: , ", Theme::getInstance()->fg_light->foreground}, + {{UI_POS_X(0), (26 * 8) + 4}, "MODE: , SQUELCH: ", Theme::getInstance()->fg_light->foreground}}; LNAGainField field_lna{ - {4 * 8, 0 * 16}}; + {4 * 8, UI_POS_Y(0)}}; VGAGainField field_vga{ - {11 * 8, 0 * 16}}; + {11 * 8, UI_POS_Y(0)}}; RFAmpField field_rf_amp{ - {18 * 8, 0 * 16}}; + {18 * 8, UI_POS_Y(0)}}; AudioVolumeField field_volume{ - {24 * 8, 0 * 16}}; + {UI_POS_X_RIGHT(2), UI_POS_Y(0)}}; Text file_name{ // show file used @@ -297,7 +298,7 @@ class ReconView : public View { "CONFIG"}; ButtonWithEncoder button_manual_start{ - {0 * 8, 9 * 16, 11 * 8, 28}, + {UI_POS_X(0), 9 * 16, 11 * 8, 28}, ""}; ButtonWithEncoder button_manual_end{ @@ -305,7 +306,7 @@ class ReconView : public View { ""}; OptionsField field_recon_match_mode{ - {0 * 8, 11 * 16}, + {UI_POS_X(0), 11 * 16}, 16, // CONTINUOUS MATCH MODE / SPARSE TIMED MATCH MODE { {"MATCH:CONTINOUS", 0}, @@ -363,15 +364,15 @@ class ReconView : public View { }; ButtonWithEncoder button_pause{ - {0, (15 * 16) - 4, 72, 28}, + {0, (15 * 16) - 4, UI_POS_WIDTH(9), 28}, "PAUSE"}; Button button_audio_app{ - {84, (15 * 16) - 4, 72, 28}, + {UI_POS_X_CENTER(9), (15 * 16) - 4, UI_POS_WIDTH(9), 28}, "AUDIO"}; ButtonWithEncoder button_add{ - {168, (15 * 16) - 4, 72, 28}, + {UI_POS_X_RIGHT(9), (15 * 16) - 4, UI_POS_WIDTH(9), 28}, ""}; Button button_dir{ @@ -383,15 +384,15 @@ class ReconView : public View { "RST"}; Button button_mic_app{ - {84, (35 * 8) - 4, 72, 28}, + {UI_POS_X_CENTER(9), (35 * 8) - 4, UI_POS_WIDTH(9), 28}, "MIC TX"}; ButtonWithEncoder button_remove{ - {168, (35 * 8) - 4, 72, 28}, + {UI_POS_X_RIGHT(9), (35 * 8) - 4, UI_POS_WIDTH(9), 28}, ""}; ProgressBar progressbar{ - {0 * 8, screen_height / 2 - 16, screen_width, 32}}; + {UI_POS_X(0), screen_height / 2 - 16, screen_width, 32}}; TransmitterView2 tx_view{ {11 * 8, 2 * 16}, diff --git a/firmware/application/apps/ui_recon_settings.hpp b/firmware/application/apps/ui_recon_settings.hpp index fe4393369..b5b59b40e 100644 --- a/firmware/application/apps/ui_recon_settings.hpp +++ b/firmware/application/apps/ui_recon_settings.hpp @@ -225,7 +225,7 @@ class ReconSetupView : public View { {"More", Theme::getInstance()->fg_green->foreground, &viewMore}}; Button button_save{ - {9 * 8, 255, 14 * 8, 40}, + {UI_POS_X_CENTER(18), 255, UI_POS_WIDTH(18), 40}, "SAVE"}; }; diff --git a/firmware/application/apps/ui_sd_over_usb.hpp b/firmware/application/apps/ui_sd_over_usb.hpp index b9707693e..1c39c7e89 100644 --- a/firmware/application/apps/ui_sd_over_usb.hpp +++ b/firmware/application/apps/ui_sd_over_usb.hpp @@ -46,15 +46,15 @@ class SdOverUsbView : public View { NavigationView& nav_; Labels labels{ - {{3 * 8, 2 * 16}, "Click Run to start the", Theme::getInstance()->bg_darkest->foreground}, - {{3 * 8, 3 * 16}, "USB Mass Storage Mode.", Theme::getInstance()->bg_darkest->foreground}, - {{3 * 8, 5 * 16}, "It can take up to 20s", Theme::getInstance()->bg_darkest->foreground}, - {{3 * 8, 6 * 16}, "for the drive to be", Theme::getInstance()->bg_darkest->foreground}, - {{3 * 8, 7 * 16}, "available.", Theme::getInstance()->bg_darkest->foreground}, + {{UI_POS_X_CENTER(22), 2 * 16}, "Click Run to start the", Theme::getInstance()->bg_darkest->foreground}, + {{UI_POS_X_CENTER(22), 3 * 16}, "USB Mass Storage Mode.", Theme::getInstance()->bg_darkest->foreground}, + {{UI_POS_X_CENTER(21), 5 * 16}, "It can take up to 20s", Theme::getInstance()->bg_darkest->foreground}, + {{UI_POS_X_CENTER(19), 6 * 16}, "for the drive to be", Theme::getInstance()->bg_darkest->foreground}, + {{UI_POS_X_CENTER(10), 7 * 16}, "available.", Theme::getInstance()->bg_darkest->foreground}, }; Button button_run{ - {9 * 8, 15 * 16, 12 * 8, 3 * 16}, + {UI_POS_X_CENTER(12), 15 * 16, 12 * 8, 3 * 16}, "Run"}; }; diff --git a/firmware/application/apps/ui_search.cpp b/firmware/application/apps/ui_search.cpp index 1735a9a37..b3b4ee084 100644 --- a/firmware/application/apps/ui_search.cpp +++ b/firmware/application/apps/ui_search.cpp @@ -42,7 +42,8 @@ void RecentEntriesTable::draw( const Entry& entry, const Rect& target_rect, Painter& painter, - const Style& style) { + const Style& style, + RecentEntriesColumns& columns) { std::string str_duration = ""; if (entry.duration < 600) @@ -50,9 +51,10 @@ void RecentEntriesTable::draw( else str_duration = to_string_dec_uint(entry.duration / 600) + "m" + to_string_dec_uint((entry.duration / 10) % 60) + "s"; - str_duration.resize(target_rect.width() / 8, ' '); - - painter.draw_string(target_rect.location(), style, to_string_short_freq(entry.frequency) + " " + entry.time + " " + str_duration); + str_duration.resize(11, ' '); + std::string freq = to_string_short_freq(entry.frequency); + freq.resize(columns.at(0).second, ' '); + painter.draw_string(target_rect.location(), style, freq + " " + entry.time + " " + str_duration); } /* SearchView ********************************************/ @@ -60,6 +62,7 @@ void RecentEntriesTable::draw( SearchView::SearchView( NavigationView& nav) : nav_(nav) { + spectrum_row.resize(240); baseband::run_image(portapack::spi_flash::image_tag_wideband_spectrum); if (!gradient.load_file(default_gradient_file)) { @@ -86,7 +89,7 @@ SearchView::SearchView( baseband::set_spectrum(SEARCH_SLICE_WIDTH, 31); - recent_entries_view.set_parent_rect({0, 28 * 8, screen_width, 12 * 8}); + recent_entries_view.set_parent_rect({0, 28 * 8, screen_width, screen_height - 28 * 8}); recent_entries_view.on_select = [this, &nav](const SearchRecentEntry& entry) { nav.push(entry.frequency); }; @@ -160,9 +163,8 @@ void SearchView::do_detection() { // Display spectrum bin_skip_acc = 0; pixel_index = 0; - display.draw_pixels( - {{0, 88}, {(Dim)spectrum_row.size(), 1}}, - spectrum_row); + uint16_t center_align_start = (screen_width - spectrum_row.size()) / 2; + display.draw_pixels({{center_align_start, 88}, {(Dim)spectrum_row.size(), 1}}, spectrum_row); mean_power = mean_acc / (SEARCH_BIN_NB_NO_DC * slices_nb); mean_acc = 0; @@ -249,7 +251,7 @@ void SearchView::do_detection() { // Refresh red tick portapack::display.fill_rectangle({last_tick_pos, 90, 1, 6}, Theme::getInstance()->fg_red->background); if (bin_max > -1) { - last_tick_pos = (Coord)(bin_max / slices_nb); + last_tick_pos = (Coord)(bin_max / slices_nb) + center_align_start; portapack::display.fill_rectangle({last_tick_pos, 90, 1, 6}, Theme::getInstance()->fg_red->foreground); } } @@ -398,7 +400,7 @@ void SearchView::add_spectrum_pixel(Color color) { bin_skip_acc -= 0x10000; - if (pixel_index < screen_width) + if (pixel_index < spectrum_row.size()) spectrum_row[pixel_index++] = color; } diff --git a/firmware/application/apps/ui_search.hpp b/firmware/application/apps/ui_search.hpp index cbcb46e71..72558e413 100644 --- a/firmware/application/apps/ui_search.hpp +++ b/firmware/application/apps/ui_search.hpp @@ -143,7 +143,7 @@ class SearchView : public View { uint32_t bin_skip_acc = 0; uint32_t bin_skip_frac = 0; uint32_t pixel_index = 0; - std::array spectrum_row{}; + std::vector spectrum_row{}; ChannelSpectrumFIFO* fifo = nullptr; uint8_t detect_timer = 0; @@ -174,81 +174,82 @@ class SearchView : public View { void on_range_changed(); void add_spectrum_pixel(Color color); - const RecentEntriesColumns columns{{{"Frequency", 9}, - {"Time", 8}, - {"Duration", 11}}}; + RecentEntriesColumns columns{{{"Frequency", 0}, + {"Time", 8}, + {"Duration", 11}}}; SearchRecentEntries recent{}; RecentEntriesView> recent_entries_view{columns, recent}; Labels labels{ - {{1 * 8, 0}, "Min: Max: LNA VGA", Theme::getInstance()->fg_light->foreground}, - {{1 * 8, 4 * 8}, "Trig: /255 Mean: /255", Theme::getInstance()->fg_light->foreground}, - {{1 * 8, 6 * 8}, "Slices: /32 Rate: Hz", Theme::getInstance()->fg_light->foreground}, - {{6 * 8, 10 * 8}, "Timer Status", Theme::getInstance()->fg_light->foreground}, - {{1 * 8, 25 * 8}, "Accuracy +/-4.9kHz", Theme::getInstance()->fg_light->foreground}, - {{26 * 8, 25 * 8}, "MHz", Theme::getInstance()->fg_light->foreground}}; + {{UI_POS_X(1), UI_POS_Y(0)}, "Min: Max: ", Theme::getInstance()->fg_light->foreground}, + {{UI_POS_X_RIGHT(7), UI_POS_Y(0)}, "LNA VGA", Theme::getInstance()->fg_light->foreground}, + {{UI_POS_X(1), UI_POS_Y(2)}, "Trig: /255", Theme::getInstance()->fg_light->foreground}, + {{UI_POS_X_RIGHT(12), UI_POS_Y(2)}, "Mean: /255", Theme::getInstance()->fg_light->foreground}, + {{UI_POS_X(1), UI_POS_Y(3)}, "Slices: /32", Theme::getInstance()->fg_light->foreground}, + {{UI_POS_X_RIGHT(10), UI_POS_Y(3)}, "Rate: Hz", Theme::getInstance()->fg_light->foreground}, + {{UI_POS_X(6), UI_POS_Y(5)}, "Timer Status", Theme::getInstance()->fg_light->foreground}, + {{UI_POS_X(1), 25 * 8}, "Accuracy +/-4.9kHz", Theme::getInstance()->fg_light->foreground}, + {{UI_POS_X_RIGHT(4), 25 * 8}, "MHz", Theme::getInstance()->fg_light->foreground}}; Checkbox check_log{ - {24 * 8, 10 * 8}, + {UI_POS_X_RIGHT(6), UI_POS_Y(5)}, 3, "LOG", true}; FrequencyField field_frequency_min{ - {1 * 8, 1 * 16}}; + {UI_POS_X(1), UI_POS_Y(1)}}; FrequencyField field_frequency_max{ - {11 * 8, 1 * 16}}; + {UI_POS_X(11), UI_POS_Y(1)}}; LNAGainField field_lna{ - {22 * 8, 1 * 16}}; + {UI_POS_X_RIGHT(7), UI_POS_Y(1)}}; VGAGainField field_vga{ - {26 * 8, 1 * 16}}; + {UI_POS_X_RIGHT(3), UI_POS_Y(1)}}; NumberField field_threshold{ - {6 * 8, 2 * 16}, + {UI_POS_X(6), UI_POS_Y(2)}, 3, {5, 255}, 5, ' '}; Text text_mean{ - {22 * 8, 2 * 16, 3 * 8, 16}, + {UI_POS_X_RIGHT(7), UI_POS_Y(2), UI_POS_WIDTH(3), UI_POS_HEIGHT(1)}, "---"}; Text text_slices{ - {8 * 8, 3 * 16, 2 * 8, 16}, + {UI_POS_X(8), UI_POS_Y(3), UI_POS_WIDTH(2), UI_POS_HEIGHT(1)}, "--"}; Text text_rate{ - {24 * 8, 3 * 16, 3 * 8, 16}, + {UI_POS_X_RIGHT(5), UI_POS_Y(3), UI_POS_WIDTH(3), UI_POS_HEIGHT(1)}, "---"}; VuMeter vu_max{ - {1 * 8, 11 * 8 - 4, 3 * 8, 48}, + {UI_POS_X(1), 11 * 8 - 4, 3 * 8, 48}, 18, false}; ProgressBar progress_timers{ - {6 * 8, 12 * 8, 6 * 8, 16}}; + {UI_POS_X(6), UI_POS_Y(6), UI_POS_WIDTH(6), UI_POS_HEIGHT(1)}}; Text text_infos{ - {13 * 8, 12 * 8, 15 * 8, 16}, + {UI_POS_X(13), UI_POS_Y(6), UI_POS_WIDTH(15), UI_POS_HEIGHT(1)}, "Listening"}; Checkbox check_snap{ - {6 * 8, 15 * 8}, + {UI_POS_X(6), 15 * 8}, 7, "Snap to:", true}; OptionsField options_snap{ - {17 * 8, 15 * 8}, // Position - 7, // Length - { // Options + {UI_POS_X(17), 15 * 8}, // Position + 7, // Length + { // Options {"25kHz ", 25'000}, {"12.5kHz", 12'500}, {"8.33kHz", 8'333}, {"2.5kHz", 2'500}, {"500Hz", 500}}}; - BigFrequency big_display{ - {4, 9 * 16, 28 * 8, 52}, - 0}; + BigFrequency big_display{{UI_POS_X_CENTER(28), UI_POS_Y(9), UI_POS_WIDTH(28), 52}, 0}; MessageHandlerRegistration message_handler_spectrum_config{ Message::ID::ChannelSpectrumConfig, diff --git a/firmware/application/apps/ui_settings.cpp b/firmware/application/apps/ui_settings.cpp index eb981692e..7d77a0716 100644 --- a/firmware/application/apps/ui_settings.cpp +++ b/firmware/application/apps/ui_settings.cpp @@ -40,6 +40,7 @@ using namespace lpc43xx; #include "audio.hpp" #include "portapack.hpp" +#include "portapack_io.hpp" using namespace portapack; #include "file.hpp" @@ -759,7 +760,7 @@ AppSettingsView::AppSettingsView( add_children({&labels, &menu_view}); - menu_view.set_parent_rect({0, 3 * 8, screen_width, 33 * 8}); + menu_view.set_parent_rect({0, 3 * 8, screen_width, UI_POS_HEIGHT_REMAINING(3)}); ensure_directory(settings_dir); @@ -810,9 +811,12 @@ SetDisplayView::SetDisplayView(NavigationView& nav) { &field_fake_brightness, &button_save, &button_cancel, - &checkbox_ips_screen_switch, &checkbox_brightness_switch}); + if (portapack::device_type == portapack::DeviceType::DEV_PORTAPACK) { + add_child(&checkbox_ips_screen_switch); + } + field_fake_brightness.set_by_value(pmem::fake_brightness_level()); checkbox_brightness_switch.set_value(pmem::apply_fake_brightness()); checkbox_ips_screen_switch.set_value(pmem::config_lcd_normally_black()); diff --git a/firmware/application/apps/ui_settings.hpp b/firmware/application/apps/ui_settings.hpp index 1ac95c933..c1b4e1b52 100644 --- a/firmware/application/apps/ui_settings.hpp +++ b/firmware/application/apps/ui_settings.hpp @@ -72,8 +72,8 @@ class SetDateTimeView : public View { {{1 * 8, 5 * 16 - 2}, "YYYY-MM-DD HH:MM:SS DoW DoY", Theme::getInstance()->fg_medium->foreground}, {{5 * 8, 6 * 16}, "- - : :", Theme::getInstance()->fg_light->foreground}, {{1 * 8, 11 * 16}, "DST adds 1 hour to RTC time.", Theme::getInstance()->fg_light->foreground}, - {{0 * 8, 12 * 16}, "Start: 0:00 on Nth DDD in", Theme::getInstance()->fg_light->foreground}, - {{0 * 8, 13 * 16}, "End: 1:00 on Nth DDD in", Theme::getInstance()->fg_light->foreground}}; + {{UI_POS_X(0), 12 * 16}, "Start: 0:00 on Nth DDD in", Theme::getInstance()->fg_light->foreground}, + {{UI_POS_X(0), 13 * 16}, "End: 1:00 on Nth DDD in", Theme::getInstance()->fg_light->foreground}}; NumberField field_year{ {1 * 8, 6 * 16}, @@ -170,10 +170,10 @@ class SetDateTimeView : public View { {}}; Button button_save{ - {2 * 8, 16 * 16, 12 * 8, 32}, + {UI_POS_X_CENTER(12) - UI_POS_WIDTH(8), UI_POS_Y_BOTTOM(4), 12 * 8, 32}, "Save"}; Button button_cancel{ - {16 * 8, 16 * 16, 12 * 8, 32}, + {UI_POS_X_CENTER(12) + UI_POS_WIDTH(8), UI_POS_Y_BOTTOM(4), 12 * 8, 32}, "Cancel"}; void form_init(const SetDateTimeModel& model); @@ -253,10 +253,10 @@ class SetRadioView : public View { "Disable external TCXO"}; Button button_save{ - {2 * 8, 16 * 16, 12 * 8, 32}, + {UI_POS_X_CENTER(12) - UI_POS_WIDTH(8), UI_POS_Y_BOTTOM(4), 12 * 8, 32}, "Save"}; Button button_cancel{ - {16 * 8, 16 * 16, 12 * 8, 32}, + {UI_POS_X_CENTER(12) + UI_POS_WIDTH(8), UI_POS_Y_BOTTOM(4), 12 * 8, 32}, "Cancel", }; @@ -372,11 +372,11 @@ class SetUIView : public View { &bitmap_sd_card_ok}; Button button_save{ - {2 * 8, 16 * 16, 12 * 8, 32}, + {UI_POS_X_CENTER(12) - UI_POS_WIDTH(8), UI_POS_Y_BOTTOM(4), UI_POS_WIDTH(12), UI_POS_HEIGHT(2)}, "Save"}; Button button_cancel{ - {16 * 8, 16 * 16, 12 * 8, 32}, + {UI_POS_X_CENTER(16) + UI_POS_WIDTH(8), UI_POS_Y_BOTTOM(4), UI_POS_WIDTH(12), UI_POS_HEIGHT(2)}, "Cancel"}; }; @@ -391,28 +391,28 @@ class SetSDCardView : public View { private: Labels labels{ // 01234567890123456789012345678 - {{1 * 8, 120 - 48}, " HIGH SPEED SDCARD IO ", Theme::getInstance()->fg_light->foreground}, - {{1 * 8, 120 - 32}, " May or may not work !! ", Theme::getInstance()->fg_light->foreground}}; + {{UI_POS_X_CENTER(26), 120 - 48}, " HIGH SPEED SDCARD IO ", Theme::getInstance()->fg_light->foreground}, + {{UI_POS_X_CENTER(26), 120 - 32}, " May or may not work !! ", Theme::getInstance()->fg_light->foreground}}; Checkbox checkbox_sdcard_speed{ - {2 * 8, 120}, + {UI_POS_X_CENTER(26), 120}, 20, "enable high speed IO"}; Button button_test_sdcard_high_speed{ - {2 * 8, 152, 27 * 8, 32}, + {UI_POS_X_CENTER(27), 152, UI_POS_WIDTH(27), UI_POS_HEIGHT(2)}, "TEST BUTTON (NO PMEM SAVE)"}; Text text_sdcard_test_status{ - {2 * 8, 198, 28 * 8, 16}, + {UI_POS_X_CENTER(28), 198, UI_POS_WIDTH(28), UI_POS_HEIGHT(1)}, ""}; Button button_save{ - {2 * 8, 16 * 16, 12 * 8, 32}, + {UI_POS_X_CENTER(12) - UI_POS_WIDTH(8), UI_POS_Y_BOTTOM(4), UI_POS_WIDTH(12), UI_POS_HEIGHT(2)}, "Save"}; Button button_cancel{ - {16 * 8, 16 * 16, 12 * 8, 32}, + {UI_POS_X_CENTER(16) + UI_POS_WIDTH(8), UI_POS_Y_BOTTOM(4), UI_POS_WIDTH(12), UI_POS_HEIGHT(2)}, "Cancel"}; }; @@ -455,7 +455,7 @@ class SetConverterSettingsView : public View { {8 * 8, 10 * 16}}; Button button_return{ - {16 * 8, 16 * 16, 12 * 8, 32}, + {UI_POS_X_CENTER(12), UI_POS_Y_BOTTOM(4), 12 * 8, 32}, "Return", }; }; @@ -498,7 +498,7 @@ class SetFrequencyCorrectionView : public View { {8 * 8, 10 * 16}}; Button button_return{ - {16 * 8, 16 * 16, 12 * 8, 32}, + {UI_POS_X_CENTER(12), UI_POS_Y_BOTTOM(4), 12 * 8, UI_POS_HEIGHT(2)}, "Return", }; }; @@ -536,11 +536,11 @@ class SetAudioView : public View { "Beep on RX packets"}; Button button_save{ - {2 * 8, 16 * 16, 12 * 8, 32}, + {UI_POS_X_CENTER(12) - UI_POS_WIDTH(8), UI_POS_Y_BOTTOM(4), 12 * 8, 32}, "Save"}; Button button_cancel{ - {16 * 8, 16 * 16, 12 * 8, 32}, + {UI_POS_X_CENTER(12) + UI_POS_WIDTH(8), UI_POS_Y_BOTTOM(4), 12 * 8, 32}, "Cancel", }; }; @@ -559,11 +559,11 @@ class SetEncoderDialView : public View { private: Labels labels{ - {{0 * 8, 0 * 16}, "Sensitivity to dial rotation", Theme::getInstance()->fg_light->foreground}, - {{0 * 8, 1 * 16}, "position (x steps per 360):", Theme::getInstance()->fg_light->foreground}, + {{UI_POS_X(0), UI_POS_Y(0)}, "Sensitivity to dial rotation", Theme::getInstance()->fg_light->foreground}, + {{UI_POS_X(0), 1 * 16}, "position (x steps per 360):", Theme::getInstance()->fg_light->foreground}, {{1 * 8, 3 * 16}, "Sensitivity:", Theme::getInstance()->fg_light->foreground}, - {{0 * 8, 7 * 16}, "Rotation rate (default 1", Theme::getInstance()->fg_light->foreground}, - {{0 * 8, 8 * 16}, "means no rate dependency):", Theme::getInstance()->fg_light->foreground}, + {{UI_POS_X(0), 7 * 16}, "Rotation rate (default 1", Theme::getInstance()->fg_light->foreground}, + {{UI_POS_X(0), 8 * 16}, "means no rate dependency):", Theme::getInstance()->fg_light->foreground}, {{2 * 8, 10 * 16}, "Rate multiplier:", Theme::getInstance()->fg_light->foreground}, {{4 * 8, 14 * 16}, "Direction:", Theme::getInstance()->fg_light->foreground}, @@ -606,11 +606,11 @@ class SetEncoderDialView : public View { "-"}; Button button_save{ - {2 * 8, 16 * 16, 12 * 8, 32}, + {UI_POS_X_CENTER(12) - UI_POS_WIDTH(8), UI_POS_Y_BOTTOM(4), 12 * 8, 32}, "Save"}; Button button_cancel{ - {16 * 8, 16 * 16, 12 * 8, 32}, + {UI_POS_X_CENTER(12) + UI_POS_WIDTH(8), UI_POS_Y_BOTTOM(4), 12 * 8, 32}, "Cancel", }; }; @@ -649,11 +649,11 @@ class SetButtonsView : public View { {"FAST", true}}}; Button button_save{ - {2 * 8, 16 * 16, 12 * 8, 32}, + {UI_POS_X_CENTER(12) - UI_POS_WIDTH(8), UI_POS_Y_BOTTOM(4), 12 * 8, 32}, "Save"}; Button button_cancel{ - {16 * 8, 16 * 16, 12 * 8, 32}, + {UI_POS_X_CENTER(12) + UI_POS_WIDTH(8), UI_POS_Y_BOTTOM(4), 12 * 8, 32}, "Cancel", }; }; @@ -674,7 +674,7 @@ class SetPersistentMemoryView : public View { }; Text text_pmem_status{ - {1 * 8, 4 * 16 + 8, 28 * 8, 16}, + {UI_POS_X_CENTER(28), 4 * 16 + 8, 28 * 8, 16}, ""}; Checkbox check_use_sdcard_for_pmem{ @@ -683,19 +683,19 @@ class SetPersistentMemoryView : public View { "Use SD card for P.Mem"}; Button button_save_mem_to_file{ - {1 * 8, 8 * 16, 28 * 8, 2 * 16}, + {UI_POS_X_CENTER(28), 8 * 16, 28 * 8, 2 * 16}, "Save P.Mem to SD card"}; Button button_load_mem_from_file{ - {1 * 8, 10 * 16 + 2, 28 * 8, 2 * 16}, + {UI_POS_X_CENTER(28), 10 * 16 + 2, 28 * 8, 2 * 16}, "Load P.Mem from SD Card"}; Button button_load_mem_defaults{ - {1 * 8, 12 * 16 + 4, 28 * 8, 2 * 16}, + {UI_POS_X_CENTER(28), 12 * 16 + 4, 28 * 8, 2 * 16}, "Reset P.Mem to defaults"}; Button button_return{ - {16 * 8, 16 * 16, 12 * 8, 32}, + {UI_POS_X_CENTER(12), UI_POS_Y_BOTTOM(4), 12 * 8, 32}, "Return", }; }; @@ -713,7 +713,7 @@ class AppSettingsView : public View { {{0, 4}, "Select file to edit:", Theme::getInstance()->bg_darkest->foreground}}; MenuView menu_view{ - {0, 2 * 8, screen_width, 26 * 8}, + {0, 2 * 8, screen_width, UI_POS_HEIGHT_REMAINING(3)}, true}; }; @@ -738,11 +738,11 @@ class SetConfigModeView : public View { "Config Mode enable"}; Button button_save{ - {2 * 8, 16 * 16, 12 * 8, 32}, + {UI_POS_X_CENTER(12) - UI_POS_WIDTH(8), UI_POS_Y_BOTTOM(4), 12 * 8, 32}, "Save"}; Button button_cancel{ - {16 * 8, 16 * 16, 12 * 8, 32}, + {UI_POS_X_CENTER(12) + UI_POS_WIDTH(8), UI_POS_Y_BOTTOM(4), 12 * 8, 32}, "Cancel", }; }; @@ -784,11 +784,11 @@ class SetDisplayView : public View { "IPS Screen"}; Button button_save{ - {2 * 8, 16 * 16, 12 * 8, 32}, + {UI_POS_X_CENTER(12) - UI_POS_WIDTH(8), UI_POS_Y_BOTTOM(4), UI_POS_WIDTH(12), UI_POS_HEIGHT(2)}, "Save"}; Button button_cancel{ - {16 * 8, 16 * 16, 12 * 8, 32}, + {UI_POS_X_CENTER(16) + UI_POS_WIDTH(8), UI_POS_Y_BOTTOM(4), UI_POS_WIDTH(12), UI_POS_HEIGHT(2)}, "Cancel", }; }; @@ -841,19 +841,19 @@ class SetTouchscreenThresholdView : public View { }; Button button_autodetect{ - {2 * 8, 13 * 16, 12 * 8, 32}, + {UI_POS_X_CENTER(12) - UI_POS_WIDTH(8), 13 * 16, 12 * 8, 32}, "Auto Detect"}; Button button_reset{ - {16 * 8, 13 * 16, 12 * 8, 32}, + {UI_POS_X_CENTER(12) + UI_POS_WIDTH(8), 13 * 16, 12 * 8, 32}, "Reset", }; Button button_save{ - {2 * 8, 16 * 16, 12 * 8, 32}, + {UI_POS_X_CENTER(12) - UI_POS_WIDTH(8), 16 * 16, 12 * 8, 32}, "Save"}; Button button_cancel{ - {16 * 8, 16 * 16, 12 * 8, 32}, + {UI_POS_X_CENTER(12) + UI_POS_WIDTH(8), 16 * 16, 12 * 8, 32}, "Cancel", }; @@ -876,14 +876,14 @@ class SetMenuColorView : public View { void paint_sample(); Labels labels{ - {{3 * 8, 1 * 16}, "Menu Button Color Scheme", Theme::getInstance()->fg_light->foreground}, + {{UI_POS_X_CENTER(25), 1 * 16}, "Menu Button Color Scheme", Theme::getInstance()->fg_light->foreground}, {{2 * 8, 8 * 16}, "Red Level:", Theme::getInstance()->fg_light->foreground}, {{2 * 8, 9 * 16}, "Green Level:", Theme::getInstance()->fg_light->foreground}, {{2 * 8, 10 * 16}, "Blue Level:", Theme::getInstance()->fg_light->foreground}, }; NewButton button_sample{ - {8 * 8, 4 * 16, 14 * 8, 3 * 16}, + {UI_POS_X_CENTER(14), 4 * 16, 14 * 8, 3 * 16}, "New Color", &bitmap_icon_brightness, }; @@ -913,16 +913,16 @@ class SetMenuColorView : public View { }; Button button_reset{ - {2 * 8, 13 * 16, 12 * 8, 32}, + {UI_POS_X_CENTER(12) - UI_POS_WIDTH(8), UI_POS_Y_BOTTOM(7), UI_POS_WIDTH(12), UI_POS_HEIGHT(2)}, "Reset", }; Button button_save{ - {2 * 8, 16 * 16, 12 * 8, 32}, + {UI_POS_X_CENTER(12) - UI_POS_WIDTH(8), UI_POS_Y_BOTTOM(4), UI_POS_WIDTH(12), UI_POS_HEIGHT(2)}, "Save"}; Button button_cancel{ - {16 * 8, 16 * 16, 12 * 8, 32}, + {UI_POS_X_CENTER(12) + UI_POS_WIDTH(8), UI_POS_Y_BOTTOM(4), UI_POS_WIDTH(12), UI_POS_HEIGHT(2)}, "Cancel", }; }; @@ -941,12 +941,8 @@ class SetThemeView : public View { {{1 * 8, 1 * 16}, "Select a theme.", Theme::getInstance()->fg_light->foreground}, {{1 * 8, 2 * 16}, "Restart PP to fully apply!", Theme::getInstance()->fg_light->foreground}}; - Button button_save{ - {2 * 8, 16 * 16, 12 * 8, 32}, - "Save"}; - OptionsField options{ - {0 * 8, 4 * 16}, + {UI_POS_X(0), 4 * 16}, (size_t)(screen_width / 8), { {"Default - Grey", 0}, @@ -963,8 +959,12 @@ class SetThemeView : public View { 23, "Set Menu color too"}; + Button button_save{ + {UI_POS_X_CENTER(12) - UI_POS_WIDTH(8), UI_POS_Y_BOTTOM(4), UI_POS_WIDTH(12), UI_POS_HEIGHT(2)}, + "Save"}; + Button button_cancel{ - {16 * 8, 16 * 16, 12 * 8, 32}, + {UI_POS_X_CENTER(12) + UI_POS_WIDTH(8), UI_POS_Y_BOTTOM(4), UI_POS_WIDTH(12), UI_POS_HEIGHT(2)}, "Cancel", }; }; @@ -988,10 +988,6 @@ class SetBatteryView : public View { Labels labels2{{{1 * 8, 11 * 16}, "Reset IC's learned params.", Theme::getInstance()->fg_light->foreground}}; - Button button_save{ - {2 * 8, 16 * 16, 12 * 8, 32}, - "Save"}; - Checkbox checkbox_overridebatt{ {2 * 8, 4 * 16}, 23, @@ -1003,12 +999,16 @@ class SetBatteryView : public View { "Charge hint"}; Button button_cancel{ - {16 * 8, 16 * 16, 12 * 8, 32}, + {UI_POS_X_CENTER(12) + UI_POS_WIDTH(8), UI_POS_Y_BOTTOM(4), UI_POS_WIDTH(12), UI_POS_HEIGHT(2)}, "Cancel", }; + Button button_save{ + {UI_POS_X_CENTER(12) - UI_POS_WIDTH(8), UI_POS_Y_BOTTOM(4), UI_POS_WIDTH(12), UI_POS_HEIGHT(2)}, + "Save"}; + Button button_reset{ - {2 * 8, 13 * 16, 12 * 8, 32}, + {UI_POS_X_CENTER(12) - UI_POS_WIDTH(8), UI_POS_Y_BOTTOM(7), UI_POS_WIDTH(12), UI_POS_HEIGHT(2)}, "Reset", }; }; diff --git a/firmware/application/apps/ui_siggen.hpp b/firmware/application/apps/ui_siggen.hpp index bbeeba28b..ce771fff0 100644 --- a/firmware/application/apps/ui_siggen.hpp +++ b/firmware/application/apps/ui_siggen.hpp @@ -127,7 +127,7 @@ class SigGenView : public View { {"Pulse CW 25%", 7}}}; TransmitterView tx_view{ - 16 * 16, + (int16_t)UI_POS_Y_BOTTOM(4), 10000, 12}; diff --git a/firmware/application/apps/ui_sonde.hpp b/firmware/application/apps/ui_sonde.hpp index 9215372fe..11f4b942d 100644 --- a/firmware/application/apps/ui_sonde.hpp +++ b/firmware/application/apps/ui_sonde.hpp @@ -96,42 +96,42 @@ class SondeView : public View { Labels labels{ {{4 * 8, 2 * 16}, "Type:", Theme::getInstance()->fg_light->foreground}, {{6 * 8, 3 * 16}, "ID:", Theme::getInstance()->fg_light->foreground}, - {{0 * 8, 4 * 16}, "DateTime:", Theme::getInstance()->fg_light->foreground}, + {{UI_POS_X(0), 4 * 16}, "DateTime:", Theme::getInstance()->fg_light->foreground}, {{3 * 8, 5 * 16}, "Vbatt:", Theme::getInstance()->fg_light->foreground}, {{3 * 8, 6 * 16}, "Frame:", Theme::getInstance()->fg_light->foreground}, {{4 * 8, 7 * 16}, "Temp:", Theme::getInstance()->fg_light->foreground}, - {{0 * 8, 8 * 16}, "Humidity:", Theme::getInstance()->fg_light->foreground}}; + {{UI_POS_X(0), 8 * 16}, "Humidity:", Theme::getInstance()->fg_light->foreground}}; RxFrequencyField field_frequency{ - {0 * 8, 0 * 8}, + {UI_POS_X(0), 0 * 8}, nav_}; RFAmpField field_rf_amp{ - {13 * 8, 0 * 16}}; + {13 * 8, UI_POS_Y(0)}}; LNAGainField field_lna{ - {15 * 8, 0 * 16}}; + {15 * 8, UI_POS_Y(0)}}; VGAGainField field_vga{ - {18 * 8, 0 * 16}}; + {18 * 8, UI_POS_Y(0)}}; RSSI rssi{ - {21 * 8, 0, 6 * 8, 4}}; + {21 * 8, 0, UI_POS_WIDTH_REMAINING(24), 4}}; Channel channel{ - {21 * 8, 5, 6 * 8, 4}, + {21 * 8, 5, UI_POS_WIDTH_REMAINING(24), 4}, }; AudioVolumeField field_volume{ - {screen_width - 2 * 8, 0 * 16}}; + {UI_POS_X_RIGHT(2), UI_POS_Y(0)}}; Checkbox check_log{ - {22 * 8, 8 * 16}, + {UI_POS_X_RIGHT(8), UI_POS_Y(8)}, 3, "Log"}; Checkbox check_crc{ - {22 * 8, 10 * 16}, + {UI_POS_X_RIGHT(8), UI_POS_Y(10)}, 3, "CRC"}; @@ -169,11 +169,11 @@ class SondeView : public View { GeoPos::spd_unit::HIDDEN}; Button button_see_qr{ - {2 * 8, 15 * 16, 12 * 8, 3 * 16}, + {UI_POS_X_CENTER(12) - UI_POS_WIDTH(8), UI_POS_Y_BOTTOM(4), 12 * 8, 3 * 16}, "See QR"}; Button button_see_map{ - {16 * 8, 15 * 16, 12 * 8, 3 * 16}, + {UI_POS_X_CENTER(12) + UI_POS_WIDTH(8), UI_POS_Y_BOTTOM(4), 12 * 8, 3 * 16}, "See on map"}; GeoMapView* geomap_view_{nullptr}; diff --git a/firmware/application/apps/ui_subghzd.cpp b/firmware/application/apps/ui_subghzd.cpp index 6a36075cb..06ddf997c 100644 --- a/firmware/application/apps/ui_subghzd.cpp +++ b/firmware/application/apps/ui_subghzd.cpp @@ -262,17 +262,14 @@ void RecentEntriesTable::draw( const Entry& entry, const Rect& target_rect, Painter& painter, - const Style& style) { + const Style& style, + RecentEntriesColumns& columns) { std::string line{}; line.reserve(30); line = SubGhzDView::getSensorTypeName((FPROTO_SUBGHZD_SENSOR)entry.sensorType); line = line + " " + to_string_hex(entry.data << 32); - if (line.length() < 19) { - line += SubGhzDView::pad_string_with_spaces(19 - line.length()); - } else { - line = truncate(line, 19); - } + line.resize(columns.at(0).second, ' '); std::string ageStr = to_string_dec_uint(entry.age); std::string bitsStr = to_string_dec_uint(entry.bits); line += SubGhzDView::pad_string_with_spaces(5 - bitsStr.length()) + bitsStr; diff --git a/firmware/application/apps/ui_subghzd.hpp b/firmware/application/apps/ui_subghzd.hpp index 232357db6..0c0ed8a3f 100644 --- a/firmware/application/apps/ui_subghzd.hpp +++ b/firmware/application/apps/ui_subghzd.hpp @@ -122,18 +122,18 @@ class SubGhzDView : public View { SubGhzDRecentEntries recent{}; RFAmpField field_rf_amp{ - {13 * 8, 0 * 16}}; + {13 * 8, UI_POS_Y(0)}}; LNAGainField field_lna{ - {15 * 8, 0 * 16}}; + {15 * 8, UI_POS_Y(0)}}; VGAGainField field_vga{ - {18 * 8, 0 * 16}}; + {18 * 8, UI_POS_Y(0)}}; RSSI rssi{ - {21 * 8, 0, 6 * 8, 4}}; + {21 * 8, 0, UI_POS_WIDTH_REMAINING(24), 4}}; Channel channel{ - {21 * 8, 5, 6 * 8, 4}, + {21 * 8, 5, UI_POS_WIDTH_REMAINING(24), 4}, }; RxFrequencyField field_frequency{ - {0 * 8, 0 * 16}, + {UI_POS_X(0), UI_POS_Y(0)}, nav_}; SignalToken signal_token_tick_second{}; @@ -152,8 +152,8 @@ class SubGhzDView : public View { std::unique_ptr logger{}; - const RecentEntriesColumns columns{{ - {"Type", 19}, + RecentEntriesColumns columns{{ + {"Type", 0}, {"Bits", 4}, {"Age", 3}, }}; @@ -191,16 +191,16 @@ class SubGhzDRecentEntryDetailView : public View { uint32_t cnt = SD_NO_CNT; uint32_t seed = 0; - Text text_type{{0 * 8, 1 * 16, 15 * 8, 16}, "?"}; + Text text_type{{UI_POS_X(0), 1 * 16, 15 * 8, 16}, "?"}; Text text_id{{6 * 8, 2 * 16, 10 * 8, 16}, "?"}; Console console{ {0, 4 * 16, screen_width, screen_height - (4 * 16) - 36}}; Labels labels{ - {{0 * 8, 0 * 16}, "Type:", Theme::getInstance()->fg_light->foreground}, - {{0 * 8, 2 * 16}, "Serial: ", Theme::getInstance()->fg_light->foreground}, - {{0 * 8, 3 * 16}, "Data:", Theme::getInstance()->fg_light->foreground}, + {{UI_POS_X(0), UI_POS_Y(0)}, "Type:", Theme::getInstance()->fg_light->foreground}, + {{UI_POS_X(0), 2 * 16}, "Serial: ", Theme::getInstance()->fg_light->foreground}, + {{UI_POS_X(0), 3 * 16}, "Data:", Theme::getInstance()->fg_light->foreground}, }; Button button_done{ diff --git a/firmware/application/apps/ui_test.hpp b/firmware/application/apps/ui_test.hpp index 48180ab25..57e56d53b 100644 --- a/firmware/application/apps/ui_test.hpp +++ b/firmware/application/apps/ui_test.hpp @@ -76,29 +76,29 @@ class TestView : public View { bool logging{false}; Labels labels{ - {{0 * 8, 1 * 16}, "Data:", Theme::getInstance()->fg_light->foreground}}; + {{UI_POS_X(0), 1 * 16}, "Data:", Theme::getInstance()->fg_light->foreground}}; RxFrequencyField field_frequency{ - {0 * 8, 0 * 8}, + {UI_POS_X(0), 0 * 8}, nav_}; RFAmpField field_rf_amp{ - {13 * 8, 0 * 16}}; + {13 * 8, UI_POS_Y(0)}}; LNAGainField field_lna{ - {15 * 8, 0 * 16}}; + {15 * 8, UI_POS_Y(0)}}; VGAGainField field_vga{ - {18 * 8, 0 * 16}}; + {18 * 8, UI_POS_Y(0)}}; RSSI rssi{ {21 * 8, 0, 6 * 8, 4}, }; Text text_debug_a{ - {0 * 8, 4 * 16, screen_width, 16}, + {UI_POS_X(0), 4 * 16, screen_width, 16}, "..."}; Text text_debug_b{ - {0 * 8, 5 * 16, screen_width, 16}, + {UI_POS_X(0), 5 * 16, screen_width, 16}, "..."}; Button button_cal{ diff --git a/firmware/application/apps/ui_text_editor.cpp b/firmware/application/apps/ui_text_editor.cpp index ac05f5bf5..c96274354 100644 --- a/firmware/application/apps/ui_text_editor.cpp +++ b/firmware/application/apps/ui_text_editor.cpp @@ -427,7 +427,7 @@ void TextViewer::set_font_zoom(bool zoom) { /* TextEditorMenu ***************************************************/ TextEditorMenu::TextEditorMenu() - : View{{7 * 4, 9 * 4, 25 * 8, 25 * 8}} { + : View{{UI_POS_X_CENTER(25), 9 * 4, 25 * 8, 25 * 8}} { add_children( { &rect_frame, diff --git a/firmware/application/apps/ui_text_editor.hpp b/firmware/application/apps/ui_text_editor.hpp index d40fcaeef..afc886bc8 100644 --- a/firmware/application/apps/ui_text_editor.hpp +++ b/firmware/application/apps/ui_text_editor.hpp @@ -164,7 +164,7 @@ class TextEditorMenu : public View { void hide_children(bool hidden); Rectangle rect_frame{ - {0 * 8, 0 * 8, 23 * 8, 23 * 8}, + {UI_POS_X(0), 0 * 8, 23 * 8, 23 * 8}, Theme::getInstance()->fg_dark->foreground}; NewButton button_home{ @@ -267,23 +267,23 @@ class TextEditorView : public View { TextViewer viewer{ /* 272 = screen_height - 16 (top bar) - 32 (bottom controls) */ - {0, 0, screen_width, 272}}; + {0, 0, screen_width, UI_POS_HEIGHT_REMAINING(4)}}; TextEditorMenu menu{}; NewButton button_menu{ - {26 * 8, 34 * 8, 4 * 8, 4 * 8}, + {UI_POS_X_RIGHT(4), UI_POS_Y_BOTTOM(3), UI_POS_WIDTH(4), UI_POS_HEIGHT(2)}, {}, &bitmap_icon_controls, Theme::getInstance()->bg_dark->background, /*vcenter*/ true}; Text text_position{ - {0 * 8, 34 * 8, 26 * 8, 2 * 8}, + {UI_POS_X(0), UI_POS_Y_BOTTOM(3), UI_POS_WIDTH_REMAINING(5), UI_POS_HEIGHT(1)}, ""}; Text text_size{ - {0 * 8, 36 * 8, 26 * 8, 2 * 8}, + {UI_POS_X(0), UI_POS_Y_BOTTOM(2), UI_POS_WIDTH_REMAINING(5), UI_POS_HEIGHT(1)}, ""}; }; diff --git a/firmware/application/apps/ui_touchtunes.hpp b/firmware/application/apps/ui_touchtunes.hpp index 5a0f71458..c950991bb 100644 --- a/firmware/application/apps/ui_touchtunes.hpp +++ b/firmware/application/apps/ui_touchtunes.hpp @@ -162,16 +162,16 @@ class TouchTunesView : public View { {{18 * 8 + 4, 18 * 8}, "V"}, {{22 * 8, 18 * 8}, "F4"}, - {{0 * 8, 5 * 8}, "1"}, + {{UI_POS_X(0), 5 * 8}, "1"}, {{4 * 8, 5 * 8}, "2"}, {{8 * 8, 5 * 8}, "3"}, - {{0 * 8, 10 * 8}, "4"}, + {{UI_POS_X(0), 10 * 8}, "4"}, {{4 * 8, 10 * 8}, "5"}, {{8 * 8, 10 * 8}, "6"}, - {{0 * 8, 15 * 8}, "7"}, + {{UI_POS_X(0), 15 * 8}, "7"}, {{4 * 8, 15 * 8}, "8"}, {{8 * 8, 15 * 8}, "9"}, - {{0 * 8, 20 * 8}, "*"}, + {{UI_POS_X(0), 20 * 8}, "*"}, {{4 * 8, 20 * 8}, "0"}, {{8 * 8, 20 * 8}, "#"}, diff --git a/firmware/application/apps/ui_weatherstation.cpp b/firmware/application/apps/ui_weatherstation.cpp index 0c334731e..c030c358d 100644 --- a/firmware/application/apps/ui_weatherstation.cpp +++ b/firmware/application/apps/ui_weatherstation.cpp @@ -270,16 +270,13 @@ void RecentEntriesTable::draw( const Entry& entry, const Rect& target_rect, Painter& painter, - const Style& style) { + const Style& style, + RecentEntriesColumns& columns) { std::string line{}; line.reserve(30); line = WeatherView::getWeatherSensorTypeName((FPROTO_WEATHER_SENSOR)entry.sensorType); - if (line.length() < 10) { - line += WeatherView::pad_string_with_spaces(10 - line.length()); - } else { - line = truncate(line, 10); - } + line.resize(columns.at(0).second, ' '); std::string temp = (weather_units_fahr ? to_string_decimal((entry.temp * 9 / 5) + 32, 1) : to_string_decimal(entry.temp, 1)); std::string humStr = (entry.humidity != WS_NO_HUMIDITY) ? to_string_dec_uint(entry.humidity) + "%" : "-"; diff --git a/firmware/application/apps/ui_weatherstation.hpp b/firmware/application/apps/ui_weatherstation.hpp index d12f91029..2c992bbbd 100644 --- a/firmware/application/apps/ui_weatherstation.hpp +++ b/firmware/application/apps/ui_weatherstation.hpp @@ -134,28 +134,28 @@ class WeatherView : public View { WeatherRecentEntries recent{}; OptionsField options_temperature{ - {10 * 8, 0 * 16}, + {10 * 8, UI_POS_Y(0)}, 2, {{STR_DEGREES_C, 0}, {STR_DEGREES_F, 1}}}; RFAmpField field_rf_amp{ - {13 * 8, 0 * 16}}; + {13 * 8, UI_POS_Y(0)}}; LNAGainField field_lna{ - {15 * 8, 0 * 16}}; + {15 * 8, UI_POS_Y(0)}}; VGAGainField field_vga{ - {18 * 8, 0 * 16}}; + {18 * 8, UI_POS_Y(0)}}; RSSI rssi{ - {21 * 8, 0, 6 * 8, 4}}; + {21 * 8, 0, UI_POS_WIDTH_REMAINING(24), 4}}; Channel channel{ - {21 * 8, 5, 6 * 8, 4}, + {21 * 8, 5, UI_POS_WIDTH_REMAINING(24), 4}, }; AudioVolumeField field_volume{ - {screen_width - 2 * 8, 0 * 16}}; + {UI_POS_X_RIGHT(2), UI_POS_Y(0)}}; RxFrequencyField field_frequency{ - {0 * 8, 0 * 16}, + {UI_POS_X(0), UI_POS_Y(0)}, nav_}; SignalToken signal_token_tick_second{}; @@ -174,8 +174,8 @@ class WeatherView : public View { std::unique_ptr logger{}; - const RecentEntriesColumns columns{{ - {"Type", 10}, + RecentEntriesColumns columns{{ + {"Type", 0}, {"Temp", 5}, {"Hum", 4}, {"Ch", 3}, @@ -218,14 +218,14 @@ class WeatherRecentEntryDetailView : public View { Text text_age{{10 * 8, 7 * 16, 10 * 8, 16}, "?"}; Labels labels{ - {{0 * 8, 0 * 16}, "Weather Station", Theme::getInstance()->fg_light->foreground}, - {{0 * 8, 1 * 16}, "Type:", Theme::getInstance()->fg_light->foreground}, - {{0 * 8, 2 * 16}, "Id: ", Theme::getInstance()->fg_light->foreground}, - {{0 * 8, 3 * 16}, "Temp:", Theme::getInstance()->fg_light->foreground}, - {{0 * 8, 4 * 16}, "Humidity:", Theme::getInstance()->fg_light->foreground}, - {{0 * 8, 5 * 16}, "Channel:", Theme::getInstance()->fg_light->foreground}, - {{0 * 8, 6 * 16}, "Battery:", Theme::getInstance()->fg_light->foreground}, - {{0 * 8, 7 * 16}, "Age:", Theme::getInstance()->fg_light->foreground}, + {{UI_POS_X(0), UI_POS_Y(0)}, "Weather Station", Theme::getInstance()->fg_light->foreground}, + {{UI_POS_X(0), 1 * 16}, "Type:", Theme::getInstance()->fg_light->foreground}, + {{UI_POS_X(0), 2 * 16}, "Id: ", Theme::getInstance()->fg_light->foreground}, + {{UI_POS_X(0), 3 * 16}, "Temp:", Theme::getInstance()->fg_light->foreground}, + {{UI_POS_X(0), 4 * 16}, "Humidity:", Theme::getInstance()->fg_light->foreground}, + {{UI_POS_X(0), 5 * 16}, "Channel:", Theme::getInstance()->fg_light->foreground}, + {{UI_POS_X(0), 6 * 16}, "Battery:", Theme::getInstance()->fg_light->foreground}, + {{UI_POS_X(0), 7 * 16}, "Age:", Theme::getInstance()->fg_light->foreground}, }; Button button_done{ diff --git a/firmware/application/external/acars_rx/acars_app.hpp b/firmware/application/external/acars_rx/acars_app.hpp index abefedce0..8e5175ce3 100644 --- a/firmware/application/external/acars_rx/acars_app.hpp +++ b/firmware/application/external/acars_rx/acars_app.hpp @@ -67,18 +67,18 @@ class ACARSAppView : public View { uint32_t packet_counter{0}; RFAmpField field_rf_amp{ - {13 * 8, 0 * 16}}; + {13 * 8, UI_POS_Y(0)}}; LNAGainField field_lna{ - {15 * 8, 0 * 16}}; + {15 * 8, UI_POS_Y(0)}}; VGAGainField field_vga{ - {18 * 8, 0 * 16}}; + {18 * 8, UI_POS_Y(0)}}; RSSI rssi{ - {21 * 8, 0, 6 * 8, 4}}; + {UI_POS_X(21), 0, UI_POS_WIDTH_REMAINING(24), 4}}; Channel channel{ - {21 * 8, 5, 6 * 8, 4}}; + {UI_POS_X(21), 5, UI_POS_WIDTH_REMAINING(24), 4}}; RxFrequencyField field_frequency{ - {0 * 8, 0 * 8}, + {UI_POS_X(0), 0 * 8}, nav_}; Checkbox check_log{ {16 * 8, 1 * 16}, diff --git a/firmware/application/external/adsbtx/ui_adsb_tx.hpp b/firmware/application/external/adsbtx/ui_adsb_tx.hpp index 245ffd43e..3009e9474 100644 --- a/firmware/application/external/adsbtx/ui_adsb_tx.hpp +++ b/firmware/application/external/adsbtx/ui_adsb_tx.hpp @@ -62,7 +62,7 @@ class ADSBPositionView : public OptionTabView { GeoPos::HIDDEN}; Button button_set_map{ - {8 * 8, 6 * 16, 14 * 8, 2 * 16}, + {UI_POS_X_CENTER(14), 6 * 16, 14 * 8, 2 * 16}, "Set from map"}; }; @@ -244,7 +244,7 @@ class ADSBTxView : public View { "-"}; TransmitterView tx_view{ - 16 * 16, + (int16_t)UI_POS_Y_BOTTOM(4), 1000000, 0}; diff --git a/firmware/application/external/afsk_rx/ui_afsk_rx.hpp b/firmware/application/external/afsk_rx/ui_afsk_rx.hpp index 388a966c7..dbca67df0 100644 --- a/firmware/application/external/afsk_rx/ui_afsk_rx.hpp +++ b/firmware/application/external/afsk_rx/ui_afsk_rx.hpp @@ -73,31 +73,31 @@ class AFSKRxView : public View { bool logging{false}; RFAmpField field_rf_amp{ - {13 * 8, 0 * 16}}; + {13 * 8, UI_POS_Y(0)}}; LNAGainField field_lna{ - {15 * 8, 0 * 16}}; + {15 * 8, UI_POS_Y(0)}}; VGAGainField field_vga{ - {18 * 8, 0 * 16}}; + {18 * 8, UI_POS_Y(0)}}; RSSI rssi{ - {21 * 8, 0, 6 * 8, 4}}; + {UI_POS_X(21), 0, UI_POS_WIDTH_REMAINING(24), 4}}; Channel channel{ - {21 * 8, 5, 6 * 8, 4}}; + {UI_POS_X(21), 5, UI_POS_WIDTH_REMAINING(24), 4}}; AudioVolumeField field_volume{ - {screen_width - 2 * 8, 0 * 16}}; + {screen_width - 2 * 8, UI_POS_Y(0)}}; RxFrequencyField field_frequency{ - {0 * 8, 0 * 16}, + {UI_POS_X(0), UI_POS_Y(0)}, nav_}; Checkbox check_log{ - {0 * 8, 1 * 16}, + {UI_POS_X(0), 1 * 16}, 3, LanguageHelper::currentMessages[LANG_LOG], false}; Text text_debug{ - {0 * 8, 12 + 2 * 16, screen_width, 16}, + {UI_POS_X(0), 12 + 2 * 16, screen_width, 16}, LanguageHelper::currentMessages[LANG_DEBUG]}; Button button_modem_setup{ diff --git a/firmware/application/external/analogtv/analog_tv_app.hpp b/firmware/application/external/analogtv/analog_tv_app.hpp index 04aadd52a..eb5bf11b2 100644 --- a/firmware/application/external/analogtv/analog_tv_app.hpp +++ b/firmware/application/external/analogtv/analog_tv_app.hpp @@ -58,8 +58,8 @@ class AnalogTvView : public View { app_settings::SettingsManager settings_{ "rx_tv", app_settings::Mode::RX}; - const Rect options_view_rect{0 * 8, 1 * 16, screen_width, 1 * 16}; - const Rect nbfm_view_rect{0 * 8, 1 * 16, 18 * 8, 1 * 16}; + const Rect options_view_rect{UI_POS_X(0), 1 * 16, screen_width, 1 * 16}; + const Rect nbfm_view_rect{UI_POS_X(0), 1 * 16, 18 * 8, 1 * 16}; RSSI rssi{ {21 * 8, 0, 6 * 8, 4}}; @@ -71,17 +71,17 @@ class AnalogTvView : public View { {21 * 8, 10, 6 * 8, 4}}; RxFrequencyField field_frequency{ - {5 * 8, 0 * 16}, + {5 * 8, UI_POS_Y(0)}, nav_}; LNAGainField field_lna{ - {15 * 8, 0 * 16}}; + {15 * 8, UI_POS_Y(0)}}; VGAGainField field_vga{ - {18 * 8, 0 * 16}}; + {18 * 8, UI_POS_Y(0)}}; OptionsField options_modulation{ - {0 * 8, 0 * 16}, + {UI_POS_X(0), UI_POS_Y(0)}, 4, { {"TV ", toUType(ReceiverModel::Mode::WidebandFMAudio)}, @@ -90,7 +90,7 @@ class AnalogTvView : public View { }}; AudioVolumeField field_volume{ - {27 * 8, 0 * 16}}; + {27 * 8, UI_POS_Y(0)}}; std::unique_ptr options_widget{}; diff --git a/firmware/application/external/antenna_length/ui_whipcalc.hpp b/firmware/application/external/antenna_length/ui_whipcalc.hpp index 78fa3f56a..a30ae185c 100644 --- a/firmware/application/external/antenna_length/ui_whipcalc.hpp +++ b/firmware/application/external/antenna_length/ui_whipcalc.hpp @@ -89,7 +89,7 @@ class WhipCalcView : public View { {0, 6 * 16, screen_width, 160}}; Button button_exit{ - {72, 17 * 16, 96, 32}, + {UI_POS_X_RIGHT(12), UI_POS_Y_BOTTOM(3), 96, 32}, "Back"}; }; diff --git a/firmware/application/external/app_manager/ui_app_manager.cpp b/firmware/application/external/app_manager/ui_app_manager.cpp index 35abee7b9..f8ccf70fd 100644 --- a/firmware/application/external/app_manager/ui_app_manager.cpp +++ b/firmware/application/external/app_manager/ui_app_manager.cpp @@ -50,7 +50,7 @@ AppManagerView::AppManagerView(NavigationView& nav) &button_set_cancel_autostart, &button_clean_autostart}); - menu_view.set_parent_rect({0, 2 * 8, screen_width, 24 * 8}); + menu_view.set_parent_rect({0, 2 * 8, screen_width, UI_POS_HEIGHT_REMAINING(7)}); menu_view.on_highlight = [this]() { if (menu_view.highlighted_index() >= app_list_index) { diff --git a/firmware/application/external/app_manager/ui_app_manager.hpp b/firmware/application/external/app_manager/ui_app_manager.hpp index e8460a9df..83fdd8434 100644 --- a/firmware/application/external/app_manager/ui_app_manager.hpp +++ b/firmware/application/external/app_manager/ui_app_manager.hpp @@ -43,28 +43,28 @@ class AppManagerView : public View { uint16_t app_list_index{0}; Labels labels{ - {{0 * 8, 0 * 16}, "App list:", Theme::getInstance()->fg_light->foreground}}; + {{UI_POS_X(0), UI_POS_Y(0)}, "App list:", Theme::getInstance()->fg_light->foreground}}; MenuView menu_view{}; Text text_app_info{ - {0, 27 * 8, screen_width, 16}, + {0, UI_POS_Y_BOTTOM(6), screen_width, UI_POS_HEIGHT(1)}, "Highlight an app"}; Button button_hide_unhide{ - {0, 29 * 8, screen_width / 2 - 1, 32}, + {0, UI_POS_Y_BOTTOM(5), screen_width / 2 - 1, UI_POS_HEIGHT(2)}, "Hide/Show"}; Button button_clean_hide{ - {screen_width / 2 + 2, 29 * 8, screen_width / 2 - 2, 32}, + {screen_width / 2 + 2, UI_POS_Y_BOTTOM(5), screen_width / 2 - 2, UI_POS_HEIGHT(2)}, "Clean Hidden"}; Button button_set_cancel_autostart{ - {0, screen_height - 32 - 16, screen_width / 2 - 1, 32}, + {0, screen_height - 32 - 16, screen_width / 2 - 1, UI_POS_HEIGHT(2)}, "Set Autostart"}; Button button_clean_autostart{ - {screen_width / 2 + 2, screen_height - 32 - 16, screen_width / 2 - 2, 32}, + {screen_width / 2 + 2, screen_height - 32 - 16, screen_width / 2 - 2, UI_POS_HEIGHT(2)}, "Del Autostart"}; std::string get_app_info(uint16_t index, bool is_display_name); diff --git a/firmware/application/external/audio_test/ui_audio_test.hpp b/firmware/application/external/audio_test/ui_audio_test.hpp index 650033030..d0d853b2e 100644 --- a/firmware/application/external/audio_test/ui_audio_test.hpp +++ b/firmware/application/external/audio_test/ui_audio_test.hpp @@ -44,13 +44,13 @@ class AudioTestView : public View { bool beep{false}; Labels labels{ - {{7 * 8, 3 * 16}, "Audio Beep Test", Theme::getInstance()->fg_light->foreground}, - {{0 * 8, 6 * 16}, "Sample Rate (Hz):", Theme::getInstance()->fg_light->foreground}, + {{UI_POS_X_CENTER(16), 3 * 16}, "Audio Beep Test", Theme::getInstance()->fg_light->foreground}, + {{UI_POS_X(0), 6 * 16}, "Sample Rate (Hz):", Theme::getInstance()->fg_light->foreground}, {{25 * 8, 7 * 16}, "Step:", Theme::getInstance()->fg_light->foreground}, - {{0 * 8, 8 * 16}, "Frequency (Hz):", Theme::getInstance()->fg_light->foreground}, - {{0 * 8, 10 * 16}, "Duration (ms):", Theme::getInstance()->fg_light->foreground}, + {{UI_POS_X(0), 8 * 16}, "Frequency (Hz):", Theme::getInstance()->fg_light->foreground}, + {{UI_POS_X(0), 10 * 16}, "Duration (ms):", Theme::getInstance()->fg_light->foreground}, {{25 * 8, 10 * 16}, "0=con", Theme::getInstance()->fg_light->foreground}, - {{0 * 8, 12 * 16}, "Volume:", Theme::getInstance()->fg_light->foreground}}; + {{UI_POS_X(0), 12 * 16}, "Volume:", Theme::getInstance()->fg_light->foreground}}; OptionsField options_sample_rate{ {18 * 8, 6 * 16}, diff --git a/firmware/application/protocols/bht.cpp b/firmware/application/external/bht_tx/bht.cpp similarity index 98% rename from firmware/application/protocols/bht.cpp rename to firmware/application/external/bht_tx/bht.cpp index 1abe90e0e..6ac6f0f53 100644 --- a/firmware/application/protocols/bht.cpp +++ b/firmware/application/external/bht_tx/bht.cpp @@ -23,6 +23,8 @@ #include "bht.hpp" #include "portapack_persistent_memory.hpp" +namespace ui::external_app::bht_tx { + size_t gen_message_ep(uint8_t city_code, size_t family_code_ep, uint32_t relay_number, uint32_t relay_state) { size_t c; const encoder_def_t* um3750_def; @@ -152,3 +154,5 @@ std::string ccir_to_ascii(uint8_t* ccir) { return ascii; } + +} // namespace ui::external_app::bht_tx \ No newline at end of file diff --git a/firmware/application/protocols/bht.hpp b/firmware/application/external/bht_tx/bht.hpp similarity index 95% rename from firmware/application/protocols/bht.hpp rename to firmware/application/external/bht_tx/bht.hpp index 7f4f6fae5..d41214879 100644 --- a/firmware/application/protocols/bht.hpp +++ b/firmware/application/external/bht_tx/bht.hpp @@ -28,6 +28,8 @@ using namespace encoders; +namespace ui::external_app::bht_tx { + #define XY_TONE_DURATION ((TONES_SAMPLERATE * 0.1) - 1) // 100ms #define XY_SILENCE (TONES_SAMPLERATE * 0.4) // 400ms #define XY_TONE_COUNT 20 @@ -47,3 +49,5 @@ size_t gen_message_ep(uint8_t city_code, size_t family_code_ep, uint32_t relay_s std::string gen_message_xy(const std::string& code); std::string gen_message_xy(size_t header_code_a, size_t header_code_b, size_t city_code, size_t family_code, bool subfamily_wc, size_t subfamily_code, bool id_wc, size_t receiver_code, size_t relay_state_A, size_t relay_state_B, size_t relay_state_C, size_t relay_state_D); std::string ccir_to_ascii(uint8_t* ccir); + +} // namespace ui::external_app::bht_tx \ No newline at end of file diff --git a/firmware/application/external/bht_tx/main.cpp b/firmware/application/external/bht_tx/main.cpp new file mode 100644 index 000000000..59af754bb --- /dev/null +++ b/firmware/application/external/bht_tx/main.cpp @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2024 EPIRB Decoder Implementation + * + * 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.hpp" +#include "ui_bht_tx.hpp" +#include "ui_navigation.hpp" +#include "external_app.hpp" + +namespace ui::external_app::bht_tx { +void initialize_app(ui::NavigationView& nav) { + nav.push(); +} +} // namespace ui::external_app::bht_tx + +extern "C" { + +__attribute__((section(".external_app.app_bht_tx.application_information"), used)) application_information_t _application_information_bht_tx = { + /*.memory_location = */ (uint8_t*)0x00000000, + /*.externalAppEntry = */ ui::external_app::bht_tx::initialize_app, + /*.header_version = */ CURRENT_HEADER_VERSION, + /*.app_version = */ VERSION_MD5, + + /*.app_name = */ "BHT TX", + /*.bitmap_data = */ { + 0x00, + 0x00, + 0xE0, + 0x07, + 0xF8, + 0x08, + 0x9C, + 0x07, + 0x0C, + 0x00, + 0x8E, + 0x0A, + 0x46, + 0x12, + 0x26, + 0x22, + 0x06, + 0x02, + 0x06, + 0x00, + 0x06, + 0x00, + 0x06, + 0x00, + 0x06, + 0x00, + 0x06, + 0x00, + 0x06, + 0x00, + 0x00, + 0x00, + }, + /*.icon_color = */ ui::Color::green().v, + /*.menu_location = */ app_location_t::TX, + /*.desired_menu_position = */ -1, + + /*.m4_app_tag = portapack::spi_flash::image_tag_tones */ {'P', 'T', 'O', 'N'}, + /*.m4_app_offset = */ 0x00000000, // will be filled at compile time +}; +} \ No newline at end of file diff --git a/firmware/application/apps/ui_bht_tx.cpp b/firmware/application/external/bht_tx/ui_bht_tx.cpp similarity index 98% rename from firmware/application/apps/ui_bht_tx.cpp rename to firmware/application/external/bht_tx/ui_bht_tx.cpp index f830f767a..18738f4b4 100644 --- a/firmware/application/apps/ui_bht_tx.cpp +++ b/firmware/application/external/bht_tx/ui_bht_tx.cpp @@ -28,7 +28,7 @@ using namespace portapack; -namespace ui { +namespace ui::external_app::bht_tx { void BHTView::focus() { tx_view.focus(); @@ -40,8 +40,7 @@ void BHTView::start_tx() { transmitter_model.set_baseband_bandwidth(1750000); if (target_system == XYLOS) { - baseband::run_image(portapack::spi_flash::image_tag_tones); - + baseband::run_prepared_image(portapack::memory::map::m4_code.base()); view_xylos.generate_message(); // if (tx_mode == SINGLE) { @@ -339,4 +338,4 @@ void XylosView::focus() { field_city.focus(); } -} /* namespace ui */ +} // namespace ui::external_app::bht_tx diff --git a/firmware/application/apps/ui_bht_tx.hpp b/firmware/application/external/bht_tx/ui_bht_tx.hpp similarity index 97% rename from firmware/application/apps/ui_bht_tx.hpp rename to firmware/application/external/bht_tx/ui_bht_tx.hpp index 71008bf31..564ae5ee2 100644 --- a/firmware/application/apps/ui_bht_tx.hpp +++ b/firmware/application/external/bht_tx/ui_bht_tx.hpp @@ -35,7 +35,7 @@ #include "radio_state.hpp" #include "portapack.hpp" -namespace ui { +namespace ui::external_app::bht_tx { class XylosView : public View { public: @@ -218,11 +218,11 @@ class BHTView : public View { ' '}; ProgressBar progressbar{ - {0 * 8, 29 * 8, screen_width, 16}, + {UI_POS_X(0), UI_POS_Y_BOTTOM(5), screen_width, 16}, }; TransmitterView tx_view{ - 16 * 16, + (int16_t)UI_POS_Y_BOTTOM(4), 10000, 12}; @@ -234,4 +234,4 @@ class BHTView : public View { }}; }; -} /* namespace ui */ +} // namespace ui::external_app::bht_tx diff --git a/firmware/application/external/blackjack/ui_blackjack.cpp b/firmware/application/external/blackjack/ui_blackjack.cpp index 4d93b98ef..d298e2d5e 100644 --- a/firmware/application/external/blackjack/ui_blackjack.cpp +++ b/firmware/application/external/blackjack/ui_blackjack.cpp @@ -70,7 +70,7 @@ void BlackjackView::frame_sync() { } void BlackjackView::clear_screen() { - painter.fill_rectangle({0, 0, 240, 320}, Color::black()); + painter.fill_rectangle({0, 0, screen_width, screen_height}, Color::black()); } void BlackjackView::init_deck() { @@ -265,24 +265,24 @@ void BlackjackView::draw_menu_static() { auto style_text = *ui::Theme::getInstance()->fg_light; auto style_rules = *ui::Theme::getInstance()->fg_cyan; - painter.draw_string({84, 20}, style_title, "BLACKJACK"); + painter.draw_string({UI_POS_X_CENTER(10), 20}, style_title, "BLACKJACK"); // Draw rules - painter.draw_string({70, 55}, style_rules, "-- RULES --"); - painter.draw_string({61, 75}, style_text, "Get close to 21"); - painter.draw_string({61, 90}, style_text, "without going over"); - painter.draw_string({61, 110}, style_text, "Dealer hits on 16"); - painter.draw_string({61, 125}, style_text, "Dealer stays on 17"); - painter.draw_string({61, 145}, style_text, "Blackjack pays 1:1"); + painter.draw_string({UI_POS_X_CENTER(12), 55}, style_rules, "-- RULES --"); + painter.draw_string({UI_POS_X_CENTER(16), 75}, style_text, "Get close to 21"); + painter.draw_string({UI_POS_X_CENTER(19), 90}, style_text, "without going over"); + painter.draw_string({UI_POS_X_CENTER(18), 110}, style_text, "Dealer hits on 16"); + painter.draw_string({UI_POS_X_CENTER(19), 125}, style_text, "Dealer stays on 17"); + painter.draw_string({UI_POS_X_CENTER(19), 145}, style_text, "Blackjack pays 1:1"); // Controls - painter.draw_string({65, 175}, style_rules, "-- CONTROLS --"); - painter.draw_string({61, 195}, style_text, "SELECT: Start/Hit"); - painter.draw_string({61, 210}, style_text, "LEFT: Stats"); - painter.draw_string({61, 225}, style_text, "RIGHT: Exit/Stay"); + painter.draw_string({UI_POS_X_CENTER(15), 175}, style_rules, "-- CONTROLS --"); + painter.draw_string({UI_POS_X_CENTER(18), 195}, style_text, "SELECT: Start/Hit"); + painter.draw_string({UI_POS_X_CENTER(12), 210}, style_text, "LEFT: Stats"); + painter.draw_string({UI_POS_X_CENTER(17), 225}, style_text, "RIGHT: Exit/Stay"); // Draw high score - painter.draw_string({61, 250}, style_text, "High Score: $" + std::to_string(high_score)); + painter.draw_string({UI_POS_X_CENTER(17), 250}, style_text, "High Score: $" + std::to_string(high_score)); } void BlackjackView::draw_menu() { @@ -293,10 +293,10 @@ void BlackjackView::draw_menu() { if (blink_state) { auto style = *ui::Theme::getInstance()->fg_yellow; - painter.draw_string({55, 280}, style, "* PRESS SELECT *"); + painter.draw_string({UI_POS_X_CENTER(17), 280}, style, "* PRESS SELECT *"); } else { // Clear just the text area - painter.fill_rectangle({55, 278, 130, 20}, Color::black()); + painter.fill_rectangle({UI_POS_X_CENTER(17), 278, 130, 20}, Color::black()); } } } @@ -308,27 +308,27 @@ void BlackjackView::draw_stats() { auto style_text = *ui::Theme::getInstance()->fg_light; auto style_value = *ui::Theme::getInstance()->fg_yellow; - painter.draw_string({75, 30}, style_title, "STATISTICS"); + painter.draw_string({UI_POS_X_CENTER(11), 30}, style_title, "STATISTICS"); painter.draw_string({30, 80}, style_text, "Wins:"); - painter.draw_string({150, 80}, style_value, std::to_string(wins)); + painter.draw_string({UI_POS_X_CENTER(5), 80}, style_value, std::to_string(wins)); painter.draw_string({30, 100}, style_text, "Losses:"); - painter.draw_string({150, 100}, style_value, std::to_string(losses)); + painter.draw_string({UI_POS_X_CENTER(5), 100}, style_value, std::to_string(losses)); // Win percentage uint32_t total = wins + losses; if (total > 0) { uint32_t win_pct = (wins * 100) / total; painter.draw_string({30, 120}, style_text, "Win %:"); - painter.draw_string({150, 120}, style_value, std::to_string(win_pct) + "%"); + painter.draw_string({UI_POS_X_CENTER(5), 120}, style_value, std::to_string(win_pct) + "%"); } painter.draw_string({30, 160}, style_text, "High Score:"); - painter.draw_string({150, 160}, style_value, "$" + std::to_string(high_score)); + painter.draw_string({UI_POS_X_CENTER(5), 160}, style_value, "$" + std::to_string(high_score)); painter.draw_string({30, 180}, style_text, "Cash:"); - painter.draw_string({150, 180}, style_value, "$" + std::to_string(cash)); + painter.draw_string({UI_POS_X_CENTER(5), 180}, style_value, "$" + std::to_string(cash)); painter.draw_string({40, 250}, style_text, "SELECT: Back"); } @@ -416,7 +416,7 @@ void BlackjackView::draw_game() { } // Draw result - painter.draw_string({60, 270}, *style_result, result); + painter.draw_string({UI_POS_X_CENTER(result.length()), 270}, *style_result, result); // Draw compact bet selector in top right area auto style_bet = *ui::Theme::getInstance()->fg_cyan; @@ -620,8 +620,8 @@ void BlackjackView::draw_card(int x, int y, uint8_t card, bool hidden) { void BlackjackView::draw_hand(int x, int y, uint8_t* cards, uint8_t count, bool is_dealer) { // Calculate total width needed const int card_width = 60; - const int overlap = 40; // Amount of overlap when cards need to fit - const int max_width = 230 - x; // Available width on screen + const int overlap = 40; // Amount of overlap when cards need to fit + const int max_width = screen_width - 10 - x; // Available width on screen int spacing; if (count == 1) { diff --git a/firmware/application/external/blackjack/ui_blackjack.hpp b/firmware/application/external/blackjack/ui_blackjack.hpp index 90d8d8cb6..ec8206a64 100644 --- a/firmware/application/external/blackjack/ui_blackjack.hpp +++ b/firmware/application/external/blackjack/ui_blackjack.hpp @@ -141,7 +141,7 @@ class BlackjackView : public View { {"highscore"sv, &high_score}}}; Button dummy{ - {240, 0, 0, 0}, + {screen_width, 0, 0, 0}, ""}; MessageHandlerRegistration message_handler_frame_sync{ diff --git a/firmware/application/external/blespam/ui_blespam.hpp b/firmware/application/external/blespam/ui_blespam.hpp index 73640328b..c01c0ffe3 100644 --- a/firmware/application/external/blespam/ui_blespam.hpp +++ b/firmware/application/external/blespam/ui_blespam.hpp @@ -104,10 +104,10 @@ class BLESpamView : public View { 4'000'000 /* sampling rate */ }; TxFrequencyField field_frequency{ - {0 * 8, 0 * 16}, + {UI_POS_X(0), UI_POS_Y(0)}, nav_}; TransmitterView2 tx_view{ - {11 * 8, 0 * 16}, + {11 * 8, UI_POS_Y(0)}, /*short_ui*/ true}; app_settings::SettingsManager settings_{ "tx_blespam", app_settings::Mode::TX}; @@ -122,7 +122,7 @@ class BLESpamView : public View { {0, 70, screen_width, 220}}; #endif OptionsField options_atkmode{ - {0 * 8, 2 * 8}, + {UI_POS_X(0), 2 * 8}, 10, {{"Android", 0}, {"iOs", 1}, diff --git a/firmware/application/external/breakout/ui_breakout.cpp b/firmware/application/external/breakout/ui_breakout.cpp index b71ec57d6..3e872e505 100644 --- a/firmware/application/external/breakout/ui_breakout.cpp +++ b/firmware/application/external/breakout/ui_breakout.cpp @@ -333,11 +333,11 @@ void BreakoutView::show_menu() { auto style_blue = *ui::Theme::getInstance()->fg_blue; auto style_cyan = *ui::Theme::getInstance()->fg_cyan; - painter.draw_string({50, 40}, style_yellow, "*** BREAKOUT ***"); - painter.draw_string({20, 100}, style_blue, "========================"); - painter.draw_string({30, 130}, style_cyan, "ROTARY: MOVE PADDLE"); - painter.draw_string({30, 160}, style_cyan, "SELECT: START/LAUNCH"); - painter.draw_string({20, 190}, style_blue, "========================"); + painter.draw_string({UI_POS_X_CENTER(17), 40}, style_yellow, "*** BREAKOUT ***"); + painter.draw_string({UI_POS_X_CENTER(25), 100}, style_blue, "========================"); + painter.draw_string({UI_POS_X_CENTER(20), 130}, style_cyan, "ROTARY: MOVE PADDLE"); + painter.draw_string({UI_POS_X_CENTER(21), 160}, style_cyan, "SELECT: START/LAUNCH"); + painter.draw_string({UI_POS_X_CENTER(25), 190}, style_blue, "========================"); } auto style_red = *ui::Theme::getInstance()->fg_red; @@ -346,10 +346,10 @@ void BreakoutView::show_menu() { menu_blink_counter = 0; menu_blink_state = !menu_blink_state; - painter.fill_rectangle({56, 228, 128, 20}, Color::black()); + painter.fill_rectangle({UI_POS_X_CENTER(17), 228, 128, 20}, Color::black()); if (menu_blink_state) { - painter.draw_string({56, 230}, style_red, "* PRESS SELECT *"); + painter.draw_string({UI_POS_X_CENTER(17), 230}, style_red, "* PRESS SELECT *"); } } } @@ -362,8 +362,8 @@ void BreakoutView::show_game_over() { auto style_red = *ui::Theme::getInstance()->fg_red; auto style_yellow = *ui::Theme::getInstance()->fg_yellow; - painter.draw_string({90, 100}, style_red, "GAME OVER"); - painter.draw_string({50, 150}, style_yellow, "SCORE: " + std::to_string(score)); + painter.draw_string({UI_POS_X_CENTER(10), 100}, style_red, "GAME OVER"); + painter.draw_string({UI_POS_X_CENTER(11), 150}, style_yellow, "SCORE: " + std::to_string(score)); } auto style_green = *ui::Theme::getInstance()->fg_green; @@ -372,10 +372,10 @@ void BreakoutView::show_game_over() { gameover_blink_counter = 0; gameover_blink_state = !gameover_blink_state; - painter.fill_rectangle({72, 198, 96, 20}, Color::black()); + painter.fill_rectangle({UI_POS_X_CENTER(13), 198, 96, 20}, Color::black()); if (gameover_blink_state) { - painter.draw_string({72, 200}, style_green, "PRESS SELECT"); + painter.draw_string({UI_POS_X_CENTER(13), 200}, style_green, "PRESS SELECT"); } } } diff --git a/firmware/application/external/calculator/ui_calculator.hpp b/firmware/application/external/calculator/ui_calculator.hpp index a4cdaba9d..c31bffb51 100644 --- a/firmware/application/external/calculator/ui_calculator.hpp +++ b/firmware/application/external/calculator/ui_calculator.hpp @@ -48,25 +48,25 @@ class CalculatorView : public View { private: NavigationView& nav_; - Button button_F{{0 * 8, 11 * 16, 6 * 8, 16 + 8}, "F"}; - Button button_7{{8 * 8, 11 * 16, 6 * 8, 16 + 8}, "7"}; - Button button_8{{16 * 8, 11 * 16, 6 * 8, 16 + 8}, "8"}; - Button button_9{{24 * 8, 11 * 16, 6 * 8, 16 + 8}, "9"}; - Button button_E{{0 * 8, 13 * 16, 6 * 8, 16 + 8}, "E"}; - Button button_4{{8 * 8, 13 * 16, 6 * 8, 16 + 8}, "4"}; - Button button_5{{16 * 8, 13 * 16, 6 * 8, 16 + 8}, "5"}; - Button button_6{{24 * 8, 13 * 16, 6 * 8, 16 + 8}, "6"}; - Button button_N{{0 * 8, 15 * 16, 6 * 8, 16 + 8}, "N"}; - Button button_1{{8 * 8, 15 * 16, 6 * 8, 16 + 8}, "1"}; - Button button_2{{16 * 8, 15 * 16, 6 * 8, 16 + 8}, "2"}; - Button button_3{{24 * 8, 15 * 16, 6 * 8, 16 + 8}, "3"}; - Button button_C{{0 * 8, 17 * 16, 6 * 8, 16 + 8}, "C"}; - Button button_0{{8 * 8, 17 * 16, 6 * 8, 16 + 8}, "0"}; - Button button_P{{16 * 8, 17 * 16, 6 * 8, 16 + 8}, "."}; - Button button_D{{24 * 8, 17 * 16, 6 * 8, 16 + 8}, "D"}; + Button button_F{{UI_POS_X_TABLE(4, 0), UI_POS_Y_BOTTOM(9), UI_POS_WIDTH(6), UI_POS_HEIGHT(1.5)}, "F"}; + Button button_7{{UI_POS_X_TABLE(4, 1), UI_POS_Y_BOTTOM(9), UI_POS_WIDTH(6), UI_POS_HEIGHT(1.5)}, "7"}; + Button button_8{{UI_POS_X_TABLE(4, 2), UI_POS_Y_BOTTOM(9), UI_POS_WIDTH(6), UI_POS_HEIGHT(1.5)}, "8"}; + Button button_9{{UI_POS_X_TABLE(4, 3), UI_POS_Y_BOTTOM(9), UI_POS_WIDTH(6), UI_POS_HEIGHT(1.5)}, "9"}; + Button button_E{{UI_POS_X_TABLE(4, 0), UI_POS_Y_BOTTOM(7), UI_POS_WIDTH(6), UI_POS_HEIGHT(1.5)}, "E"}; + Button button_4{{UI_POS_X_TABLE(4, 1), UI_POS_Y_BOTTOM(7), UI_POS_WIDTH(6), UI_POS_HEIGHT(1.5)}, "4"}; + Button button_5{{UI_POS_X_TABLE(4, 2), UI_POS_Y_BOTTOM(7), UI_POS_WIDTH(6), UI_POS_HEIGHT(1.5)}, "5"}; + Button button_6{{UI_POS_X_TABLE(4, 3), UI_POS_Y_BOTTOM(7), UI_POS_WIDTH(6), UI_POS_HEIGHT(1.5)}, "6"}; + Button button_N{{UI_POS_X_TABLE(4, 0), UI_POS_Y_BOTTOM(5), UI_POS_WIDTH(6), UI_POS_HEIGHT(1.5)}, "N"}; + Button button_1{{UI_POS_X_TABLE(4, 1), UI_POS_Y_BOTTOM(5), UI_POS_WIDTH(6), UI_POS_HEIGHT(1.5)}, "1"}; + Button button_2{{UI_POS_X_TABLE(4, 2), UI_POS_Y_BOTTOM(5), UI_POS_WIDTH(6), UI_POS_HEIGHT(1.5)}, "2"}; + Button button_3{{UI_POS_X_TABLE(4, 3), UI_POS_Y_BOTTOM(5), UI_POS_WIDTH(6), UI_POS_HEIGHT(1.5)}, "3"}; + Button button_C{{UI_POS_X_TABLE(4, 0), UI_POS_Y_BOTTOM(3), UI_POS_WIDTH(6), UI_POS_HEIGHT(1.5)}, "C"}; + Button button_0{{UI_POS_X_TABLE(4, 1), UI_POS_Y_BOTTOM(3), UI_POS_WIDTH(6), UI_POS_HEIGHT(1.5)}, "0"}; + Button button_P{{UI_POS_X_TABLE(4, 2), UI_POS_Y_BOTTOM(3), UI_POS_WIDTH(6), UI_POS_HEIGHT(1.5)}, "."}; + Button button_D{{UI_POS_X_TABLE(4, 3), UI_POS_Y_BOTTOM(3), UI_POS_WIDTH(6), UI_POS_HEIGHT(1.5)}, "D"}; Console console{ - {0 * 8, 0 * 16, screen_width, 10 * 16}}; + {UI_POS_X(0), UI_POS_Y(0), screen_width, UI_POS_HEIGHT_REMAINING(10)}}; void on_button_press(uint8_t button); void update_button_labels(); diff --git a/firmware/application/external/coasterp/ui_coasterp.hpp b/firmware/application/external/coasterp/ui_coasterp.hpp index 5856f92c2..d2b724510 100644 --- a/firmware/application/external/coasterp/ui_coasterp.hpp +++ b/firmware/application/external/coasterp/ui_coasterp.hpp @@ -88,7 +88,7 @@ class CoasterPagerView : public View { ""}; TransmitterView tx_view{ - 16 * 16, + (int16_t)UI_POS_Y_BOTTOM(4), 10000, 12}; diff --git a/firmware/application/external/cvs_spam/cvs_spam.hpp b/firmware/application/external/cvs_spam/cvs_spam.hpp index 07396df9b..6b573bb30 100644 --- a/firmware/application/external/cvs_spam/cvs_spam.hpp +++ b/firmware/application/external/cvs_spam/cvs_spam.hpp @@ -53,38 +53,38 @@ class CVSSpamView : public View { std::vector file_list{}; MenuView menu_view{ - {0, 0, screen_width, 180}, + {0, 0, screen_width, UI_POS_HEIGHT_REMAINING(12)}, true}; Text text_empty{ - {7 * 8, 12 * 8, 16 * 8, 16}, + {UI_POS_X_CENTER(16), 12 * 8, UI_POS_WIDTH(16), UI_POS_HEIGHT(1)}, "Empty directory!"}; Button button_prev_page{ - {0, 180, 50, 32}, + {0, UI_POS_Y_BOTTOM(10), UI_POS_WIDTH(4), UI_POS_HEIGHT(2)}, "<"}; Button button_next_page{ - {190, 180, 50, 32}, + {UI_POS_X_RIGHT(3), UI_POS_Y_BOTTOM(10), UI_POS_WIDTH(4), UI_POS_HEIGHT(2)}, ">"}; Text page_info{ - {95, 188, 50, 16}}; + {UI_POS_X_CENTER(7), UI_POS_Y_BOTTOM(9.5), UI_POS_WIDTH(7), UI_POS_HEIGHT(1)}}; Button button_send{ - {0, 212, 75, 36}, + {0, UI_POS_Y_BOTTOM(8), UI_POS_WIDTH(9), UI_POS_HEIGHT(2)}, "CALL"}; Button button_chaos{ - {82, 212, 75, 36}, + {UI_POS_X_CENTER(9), UI_POS_Y_BOTTOM(8), UI_POS_WIDTH(9), UI_POS_HEIGHT(2)}, "CHAOS"}; Button button_stop{ - {165, 212, 75, 36}, + {UI_POS_X_RIGHT(9), UI_POS_Y_BOTTOM(8), UI_POS_WIDTH(9), UI_POS_HEIGHT(2)}, LanguageHelper::currentMessages[LANG_STOP]}; ProgressBar progressbar{ - {0, 256, screen_width, 44}}; + {0, UI_POS_Y_BOTTOM(6), screen_width, UI_POS_HEIGHT(5)}}; MessageHandlerRegistration message_handler_fifo_signal{ Message::ID::RequestSignal, diff --git a/firmware/application/external/debug_pmem/ui_debug_pmem.hpp b/firmware/application/external/debug_pmem/ui_debug_pmem.hpp index f5ebedd97..933b2b0ae 100644 --- a/firmware/application/external/debug_pmem/ui_debug_pmem.hpp +++ b/firmware/application/external/debug_pmem/ui_debug_pmem.hpp @@ -44,11 +44,11 @@ class DebugDumpView : public View { NavigationView& nav_; Text dump_output{ - {0 * 8, 19 * 8, screen_width, 16}, + {UI_POS_X(0), 19 * 8, screen_width, 16}, ""}; Button button_exit{ - {22 * 8, 34 * 8, 8 * 8, 32}, + {UI_POS_X_CENTER(8), UI_POS_Y_BOTTOM(4), 8 * 8, 32}, "Exit"}; }; diff --git a/firmware/application/external/detector_rx/ui_detector_rx.hpp b/firmware/application/external/detector_rx/ui_detector_rx.hpp index 4814f297f..ee579e6c5 100644 --- a/firmware/application/external/detector_rx/ui_detector_rx.hpp +++ b/firmware/application/external/detector_rx/ui_detector_rx.hpp @@ -77,7 +77,9 @@ class DetectorRxView : public View { }}; Labels labels{ - {{UI_POS_X(0), UI_POS_Y(0)}, "LNA: VGA: AMP: VOL: ", Theme::getInstance()->fg_light->foreground}}; + {{UI_POS_X(0), UI_POS_Y(0)}, "LNA: VGA: AMP: ", Theme::getInstance()->fg_light->foreground}, + {{UI_POS_X_RIGHT(6), UI_POS_Y(0)}, "VOL: ", Theme::getInstance()->fg_light->foreground}, + }; LNAGainField field_lna{ {UI_POS_X(4), UI_POS_Y(0)}}; @@ -89,7 +91,7 @@ class DetectorRxView : public View { {UI_POS_X(18), UI_POS_Y(0)}}; AudioVolumeField field_volume{ - {UI_POS_X(24), UI_POS_Y(0)}}; + {UI_POS_X_RIGHT(2), UI_POS_Y(0)}}; OptionsField field_mode{ {UI_POS_X(0), UI_POS_Y(1)}, diff --git a/firmware/application/external/dinogame/ui_dinogame.cpp b/firmware/application/external/dinogame/ui_dinogame.cpp index f9c456637..0734ed735 100644 --- a/firmware/application/external/dinogame/ui_dinogame.cpp +++ b/firmware/application/external/dinogame/ui_dinogame.cpp @@ -178,12 +178,12 @@ void DinoGameView::show_menu() { auto style_title = *ui::Theme::getInstance()->fg_light; // Draw title - painter.draw_string({80, 60}, style_title, "DINO GAME"); + painter.draw_string({UI_POS_X_CENTER(10), 60}, style_title, "DINO GAME"); // Draw instructions - painter.draw_string({45, 130}, style, "SELECT: Jump/Start"); - painter.draw_string({65, 150}, style, "DOWN: Duck"); - painter.draw_string({50, 170}, style, "Avoid obstacles!"); + painter.draw_string({UI_POS_X_CENTER(19), 130}, style, "SELECT: Jump/Start"); + painter.draw_string({UI_POS_X_CENTER(11), 150}, style, "DOWN: Duck"); + painter.draw_string({UI_POS_X_CENTER(17), 170}, style, "Avoid obstacles!"); // Draw high score draw_high_score(); @@ -209,9 +209,9 @@ void DinoGameView::show_menu() { blink_counter = 0; blink_state = !blink_state; - painter.fill_rectangle({55, 258, 130, 20}, Color::black()); + painter.fill_rectangle({UI_POS_X_CENTER(17), 258, 130, 20}, Color::black()); if (blink_state) { - painter.draw_string({55, 260}, style_prompt, "* PRESS SELECT *"); + painter.draw_string({UI_POS_X_CENTER(17), 260}, style_prompt, "* PRESS SELECT *"); } } } @@ -234,19 +234,19 @@ void DinoGameView::show_game_over() { auto style_score = *ui::Theme::getInstance()->fg_medium; // Game over text - painter.draw_string({85, 70}, style, "GAME OVER"); + painter.draw_string({UI_POS_X_CENTER(10), 70}, style, "GAME OVER"); // Show final score std::string score_text = "SCORE: " + score_to_string(score); - int score_x = (240 - score_text.length() * 8) / 2; + int score_x = (screen_width - score_text.length() * 8) / 2; painter.draw_string({score_x, 90}, style_score, score_text); - painter.draw_string({65, 110}, style, "SELECT TO RETRY"); + painter.draw_string({UI_POS_X_CENTER(16), 110}, style, "SELECT TO RETRY"); // Update high score if (score > highScore) { highScore = score; - painter.draw_string({55, 130}, style, "NEW HIGH SCORE!"); + painter.draw_string({UI_POS_X_CENTER(16), 130}, style, "NEW HIGH SCORE!"); } } } @@ -743,8 +743,8 @@ void DinoGameView::draw_current_score() { void DinoGameView::draw_high_score() { auto style = *ui::Theme::getInstance()->fg_light; - painter.fill_rectangle({152, 28, 100, 14}, Color::black()); - painter.draw_string({152, 30}, style, "HI " + score_to_string(highScore)); + painter.fill_rectangle({UI_POS_X_RIGHT(9), 28, UI_POS_WIDTH(9), 18}, Color::black()); + painter.draw_string({UI_POS_X_RIGHT(9), 30}, style, "HI " + score_to_string(highScore)); } void DinoGameView::jump() { diff --git a/firmware/application/external/dinogame/ui_dinogame.hpp b/firmware/application/external/dinogame/ui_dinogame.hpp index a8d51e574..076336534 100644 --- a/firmware/application/external/dinogame/ui_dinogame.hpp +++ b/firmware/application/external/dinogame/ui_dinogame.hpp @@ -229,7 +229,7 @@ class DinoGameView : public View { bool easy_mode = false; Button button_difficulty{ - {70, 195, 100, 20}, + {UI_POS_X_CENTER(13), 195, 13 * 8, 20}, "Mode: HARD"}; app_settings::SettingsManager settings_{ diff --git a/firmware/application/external/epirb_rx/ui_epirb_rx.hpp b/firmware/application/external/epirb_rx/ui_epirb_rx.hpp index 366193490..7b58874fa 100644 --- a/firmware/application/external/epirb_rx/ui_epirb_rx.hpp +++ b/firmware/application/external/epirb_rx/ui_epirb_rx.hpp @@ -197,11 +197,11 @@ class EPIRBAppView : public ui::View { static constexpr auto header_height = 4 * 16; ui::Text label_frequency{ - {0 * 8, 0 * 16, 4 * 8, 1 * 16}, + {UI_POS_X(0), UI_POS_Y(0), 4 * 8, 1 * 16}, "Freq"}; ui::OptionsField options_frequency{ - {5 * 8, 0 * 16}, + {5 * 8, UI_POS_Y(0)}, 7, { {"406.028", 406028000}, @@ -212,26 +212,26 @@ class EPIRBAppView : public ui::View { }}; ui::RFAmpField field_rf_amp{ - {13 * 8, 0 * 16}}; + {13 * 8, UI_POS_Y(0)}}; ui::LNAGainField field_lna{ - {15 * 8, 0 * 16}}; + {15 * 8, UI_POS_Y(0)}}; ui::VGAGainField field_vga{ - {18 * 8, 0 * 16}}; + {18 * 8, UI_POS_Y(0)}}; ui::RSSI rssi{ - {21 * 8, 0, 6 * 8, 4}}; - - ui::AudioVolumeField field_volume{ - {screen_width - 2 * 8, 0 * 16}}; + {UI_POS_X(21), 0, UI_POS_WIDTH_REMAINING(24), 4}}; ui::Channel channel{ - {21 * 8, 5, 6 * 8, 4}}; + {UI_POS_X(21), 5, UI_POS_WIDTH_REMAINING(24), 4}}; + + ui::AudioVolumeField field_volume{ + {screen_width - 2 * 8, UI_POS_Y(0)}}; // Status display ui::Text label_status{ - {0 * 8, 1 * 16, 15 * 8, 1 * 16}, + {UI_POS_X(0), 1 * 16, 15 * 8, 1 * 16}, "Listening..."}; ui::Text label_beacons_count{ @@ -239,12 +239,12 @@ class EPIRBAppView : public ui::View { "Beacons: 0"}; ui::Text label_packet_stats{ - {0 * 8, 3 * 16, 29 * 8, 1 * 16}, + {UI_POS_X(0), 3 * 16, 29 * 8, 1 * 16}, ""}; // Latest beacon info display ui::Text label_latest{ - {0 * 8, 2 * 16, 8 * 8, 1 * 16}, + {UI_POS_X(0), 2 * 16, 8 * 8, 1 * 16}, "Latest:"}; ui::Text text_latest_info{ diff --git a/firmware/application/external/ert/ert_app.cpp b/firmware/application/external/ert/ert_app.cpp index 567db3d06..b18df97cb 100644 --- a/firmware/application/external/ert/ert_app.cpp +++ b/firmware/application/external/ert/ert_app.cpp @@ -175,8 +175,11 @@ void RecentEntriesTable::draw( const Entry& entry, const Rect& target_rect, Painter& painter, - const Style& style) { - std::string line = ert::format::id(entry.id) + " " + ert::format::commodity_type(entry.commodity_type) + " " + ert::format::consumption(entry.last_consumption) + " "; + const Style& style, + RecentEntriesColumns& columns) { + std::string lid = ert::format::id(entry.id); + lid.resize(columns.at(0).second, ' '); + std::string line = lid + " " + ert::format::commodity_type(entry.commodity_type) + " " + ert::format::consumption(entry.last_consumption) + " "; line += (entry.packet_type == ert::Packet::Type::SCM) ? ert::format::tamper_flags_scm(entry.last_tamper_flags) : ert::format::tamper_flags(entry.last_tamper_flags); line += (entry.received_count > 99) ? " ++" : to_string_dec_uint(entry.received_count, 3); diff --git a/firmware/application/external/ert/ert_app.hpp b/firmware/application/external/ert/ert_app.hpp index 80883b5e4..95d8183fa 100644 --- a/firmware/application/external/ert/ert_app.hpp +++ b/firmware/application/external/ert/ert_app.hpp @@ -136,8 +136,8 @@ class ERTAppView : public View { app_settings::SettingsManager settings_{ "rx_ert", app_settings::Mode::RX}; - const RecentEntriesColumns columns{{ - {"ID", 10}, + RecentEntriesColumns columns{{ + {"ID", 0}, {"Ty", 2}, {"Consumpt", 8}, {"Tamp", 4}, @@ -148,24 +148,24 @@ class ERTAppView : public View { static constexpr auto header_height = 1 * 16; RxFrequencyField field_frequency{ - {0 * 8, 0 * 16}, + {UI_POS_X(0), UI_POS_Y(0)}, nav_}; RFAmpField field_rf_amp{ - {13 * 8, 0 * 16}}; + {13 * 8, UI_POS_Y(0)}}; LNAGainField field_lna{ - {15 * 8, 0 * 16}}; + {15 * 8, UI_POS_Y(0)}}; VGAGainField field_vga{ - {18 * 8, 0 * 16}}; + {18 * 8, UI_POS_Y(0)}}; RSSI rssi{ - {21 * 8, 0, 6 * 8, 4}, + {UI_POS_X(21), 0, UI_POS_WIDTH_REMAINING(24), 4}, }; AudioVolumeField field_volume{ - {screen_width - 2 * 8, 0 * 16}}; + {screen_width - 2 * 8, UI_POS_Y(0)}}; MessageHandlerRegistration message_handler_packet{ Message::ID::ERTPacket, diff --git a/firmware/application/external/external.cmake b/firmware/application/external/external.cmake index cd3a53630..73d93b0e5 100644 --- a/firmware/application/external/external.cmake +++ b/firmware/application/external/external.cmake @@ -249,6 +249,11 @@ set(EXTCPPSRC #game2048 external/game2048/main.cpp external/game2048/ui_game2048.cpp + + #bht_tx + external/bht_tx/main.cpp + external/bht_tx/ui_bht_tx.cpp + external/bht_tx/bht.cpp ) set(EXTAPPLIST @@ -312,4 +317,5 @@ set(EXTAPPLIST epirb_rx soundboard game2048 + bht_tx ) diff --git a/firmware/application/external/external.ld b/firmware/application/external/external.ld index c1197082d..e228830df 100644 --- a/firmware/application/external/external.ld +++ b/firmware/application/external/external.ld @@ -83,6 +83,7 @@ MEMORY ram_external_app_epirb_rx (rwx) : org = 0xADEA0000, len = 32k ram_external_app_soundboard (rwx) : org = 0xADEB0000, len = 32k ram_external_app_game2048 (rwx) : org = 0xADEC0000, len = 32k + ram_external_app_bht_tx (rwx) : org = 0xADED0000, len = 32k } @@ -449,5 +450,13 @@ SECTIONS *(*ui*external_app*game2048*); } > ram_external_app_game2048 + + .external_app_bht_tx : ALIGN(4) SUBALIGN(4) + { + KEEP(*(.external_app.app_bht_tx.application_information)); + *(*ui*external_app*bht_tx*); + } > ram_external_app_bht_tx + + } diff --git a/firmware/application/external/extsensors/ui_extsensors.hpp b/firmware/application/external/extsensors/ui_extsensors.hpp index f222f8a8e..5f3d86c16 100644 --- a/firmware/application/external/extsensors/ui_extsensors.hpp +++ b/firmware/application/external/extsensors/ui_extsensors.hpp @@ -56,16 +56,16 @@ class ExtSensorsView : public View { uint16_t prev_scan_int = 0; Labels labels{ - {{0 * 8, 3 * 16}, "GPS:", Theme::getInstance()->fg_light->foreground}, - {{0 * 8, 5 * 16}, "ORI:", Theme::getInstance()->fg_light->foreground}, - {{0 * 8, 7 * 16}, "ENV:", Theme::getInstance()->fg_light->foreground}}; + {{UI_POS_X(0), 3 * 16}, "GPS:", Theme::getInstance()->fg_light->foreground}, + {{UI_POS_X(0), 5 * 16}, "ORI:", Theme::getInstance()->fg_light->foreground}, + {{UI_POS_X(0), 7 * 16}, "ENV:", Theme::getInstance()->fg_light->foreground}}; - Text text_info{{0 * 8, 0 * 8, screen_width, 16 * 1}, "Connect a compatible module..."}; - Text text_gps{{5 * 8, 3 * 16, 24 * 8, 16}, "-"}; - Text text_orientation{{5 * 8, 5 * 16, 24 * 8, 16}, "-"}; - Text text_envl1{{5 * 8, 7 * 16, 24 * 8, 16}, "-"}; - Text text_envl2{{1 * 8, 9 * 16, 24 * 8, 16}, "-"}; - Text text_envl3{{1 * 8, 11 * 16, 24 * 8, 16}, "-"}; + Text text_info{{UI_POS_X(0), 0 * 8, screen_width, 16 * 1}, "Connect a compatible module..."}; + Text text_gps{{5 * 8, 3 * 16, UI_POS_WIDTH_REMAINING(6), UI_POS_HEIGHT(1)}, "-"}; + Text text_orientation{{5 * 8, 5 * 16, UI_POS_WIDTH_REMAINING(6), UI_POS_HEIGHT(1)}, "-"}; + Text text_envl1{{5 * 8, 7 * 16, UI_POS_WIDTH_REMAINING(6), UI_POS_HEIGHT(1)}, "-"}; + Text text_envl2{{1 * 8, 9 * 16, UI_POS_WIDTH_REMAINING(6), UI_POS_HEIGHT(1)}, "-"}; + Text text_envl3{{1 * 8, 11 * 16, UI_POS_WIDTH_REMAINING(6), UI_POS_HEIGHT(1)}, "-"}; Console console{ {1, 13 * 16, screen_width - 1, screen_height - 13 * 16}}; diff --git a/firmware/application/external/flippertx/ui_flippertx.hpp b/firmware/application/external/flippertx/ui_flippertx.hpp index b28eca3cc..27d974e30 100644 --- a/firmware/application/external/flippertx/ui_flippertx.hpp +++ b/firmware/application/external/flippertx/ui_flippertx.hpp @@ -49,10 +49,10 @@ class FlipperTxView : public View { }; TxFrequencyField field_frequency{ - {0 * 8, 0 * 16}, + {UI_POS_X(0), UI_POS_Y(0)}, nav_}; TransmitterView2 tx_view{ - {11 * 8, 0 * 16}, + {11 * 8, UI_POS_Y(0)}, /*short_ui*/ true}; app_settings::SettingsManager settings_{ diff --git a/firmware/application/external/fmradio/ui_fmradio.hpp b/firmware/application/external/fmradio/ui_fmradio.hpp index 4cad10c2a..2706caeb7 100644 --- a/firmware/application/external/fmradio/ui_fmradio.hpp +++ b/firmware/application/external/fmradio/ui_fmradio.hpp @@ -121,18 +121,18 @@ class FmRadioView : public View { {"theme"sv, ¤t_theme}}}; RFAmpField field_rf_amp{ - {13 * 8, 0 * 16}}; + {13 * 8, UI_POS_Y(0)}}; LNAGainField field_lna{ - {15 * 8, 0 * 16}}; + {15 * 8, UI_POS_Y(0)}}; VGAGainField field_vga{ - {18 * 8, 0 * 16}}; + {18 * 8, UI_POS_Y(0)}}; RSSI rssi{ - {21 * 8, 0, 6 * 8, 4}}; + {UI_POS_X(21), 0, UI_POS_WIDTH_REMAINING(24), 4}}; AudioVolumeField field_volume{ - {screen_width - 2 * 8, 0 * 16}}; + {screen_width - 2 * 8, UI_POS_Y(0)}}; RxFrequencyField field_frequency{ - {0 * 8, 0 * 16}, + {UI_POS_X(0), UI_POS_Y(0)}, nav_}; OptionsField field_bw{ @@ -171,16 +171,16 @@ class FmRadioView : public View { GraphEq gr{{2, FMR_BTNGRID_TOP, UI_POS_MAXWIDTH - 4, UI_POS_MAXHEIGHT - FMR_BTNGRID_TOP}, true}; - Button btn_fav_0{{2, FMR_BTNGRID_TOP + 0 * 34, 10 * 8, 28}, "---"}; - Button btn_fav_1{{2 + 15 * 8, FMR_BTNGRID_TOP + 0 * 34, 10 * 8, 28}, "---"}; - Button btn_fav_2{{2, FMR_BTNGRID_TOP + 1 * 34, 10 * 8, 28}, "---"}; - Button btn_fav_3{{2 + 15 * 8, FMR_BTNGRID_TOP + 1 * 34, 10 * 8, 28}, "---"}; - Button btn_fav_4{{2, FMR_BTNGRID_TOP + 2 * 34, 10 * 8, 28}, "---"}; - Button btn_fav_5{{2 + 15 * 8, FMR_BTNGRID_TOP + 2 * 34, 10 * 8, 28}, "---"}; - Button btn_fav_6{{2, FMR_BTNGRID_TOP + 3 * 34, 10 * 8, 28}, "---"}; - Button btn_fav_7{{2 + 15 * 8, FMR_BTNGRID_TOP + 3 * 34, 10 * 8, 28}, "---"}; - Button btn_fav_8{{2, FMR_BTNGRID_TOP + 4 * 34, 10 * 8, 28}, "---"}; - Button btn_fav_9{{2 + 15 * 8, FMR_BTNGRID_TOP + 4 * 34, 10 * 8, 28}, "---"}; + Button btn_fav_0{{UI_POS_X_CENTER(10) - UI_POS_WIDTH(8), FMR_BTNGRID_TOP + 0 * 34, 10 * 8, 28}, "---"}; + Button btn_fav_1{{UI_POS_X_CENTER(10) + UI_POS_WIDTH(8), FMR_BTNGRID_TOP + 0 * 34, 10 * 8, 28}, "---"}; + Button btn_fav_2{{UI_POS_X_CENTER(10) - UI_POS_WIDTH(8), FMR_BTNGRID_TOP + 1 * 34, 10 * 8, 28}, "---"}; + Button btn_fav_3{{UI_POS_X_CENTER(10) + UI_POS_WIDTH(8), FMR_BTNGRID_TOP + 1 * 34, 10 * 8, 28}, "---"}; + Button btn_fav_4{{UI_POS_X_CENTER(10) - UI_POS_WIDTH(8), FMR_BTNGRID_TOP + 2 * 34, 10 * 8, 28}, "---"}; + Button btn_fav_5{{UI_POS_X_CENTER(10) + UI_POS_WIDTH(8), FMR_BTNGRID_TOP + 2 * 34, 10 * 8, 28}, "---"}; + Button btn_fav_6{{UI_POS_X_CENTER(10) - UI_POS_WIDTH(8), FMR_BTNGRID_TOP + 3 * 34, 10 * 8, 28}, "---"}; + Button btn_fav_7{{UI_POS_X_CENTER(10) + UI_POS_WIDTH(8), FMR_BTNGRID_TOP + 3 * 34, 10 * 8, 28}, "---"}; + Button btn_fav_8{{UI_POS_X_CENTER(10) - UI_POS_WIDTH(8), FMR_BTNGRID_TOP + 4 * 34, 10 * 8, 28}, "---"}; + Button btn_fav_9{{UI_POS_X_CENTER(10) + UI_POS_WIDTH(8), FMR_BTNGRID_TOP + 4 * 34, 10 * 8, 28}, "---"}; Button btn_fav_save{{2, FMR_BTNGRID_TOP + 6 * 34, 7 * 8, 1 * 28}, "Save"}; bool save_fav = false; diff --git a/firmware/application/external/font_viewer/ui_font_viewer.hpp b/firmware/application/external/font_viewer/ui_font_viewer.hpp index 97e7ef9a7..45f851767 100644 --- a/firmware/application/external/font_viewer/ui_font_viewer.hpp +++ b/firmware/application/external/font_viewer/ui_font_viewer.hpp @@ -49,7 +49,7 @@ class DebugFontsView : public View { void paint_zoomed_text(Painter& painter); NumberField field_cursor{ - {0 * 8, 0 * 8}, + {UI_POS_X(0), 0 * 8}, 4, {0, 1000}, 1, @@ -61,7 +61,7 @@ class DebugFontsView : public View { 1, ' '}; Text text_address{ - {screen_width / 2, 0 * 16, screen_width / 2, 16}, + {screen_width / 2, UI_POS_Y(0), screen_width / 2, 16}, "0x20", }; NavigationView& nav_; diff --git a/firmware/application/external/foxhunt/ui_foxhunt_rx.hpp b/firmware/application/external/foxhunt/ui_foxhunt_rx.hpp index 25d48c0c3..f36e366e7 100644 --- a/firmware/application/external/foxhunt/ui_foxhunt_rx.hpp +++ b/firmware/application/external/foxhunt/ui_foxhunt_rx.hpp @@ -54,23 +54,23 @@ class FoxhuntRxView : public View { "rx_foxhunt", app_settings::Mode::RX}; RFAmpField field_rf_amp{ - {13 * 8, 0 * 16}}; + {13 * 8, UI_POS_Y(0)}}; LNAGainField field_lna{ - {15 * 8, 0 * 16}}; + {15 * 8, UI_POS_Y(0)}}; VGAGainField field_vga{ - {18 * 8, 0 * 16}}; + {18 * 8, UI_POS_Y(0)}}; RSSI rssi{ - {21 * 8, 0, 6 * 8, 4}}; + {UI_POS_X(21), 0, UI_POS_WIDTH_REMAINING(24), 4}}; AudioVolumeField field_volume{ - {screen_width - 2 * 8, 0 * 16}}; + {screen_width - 2 * 8, UI_POS_Y(0)}}; RxFrequencyField field_frequency{ - {0 * 8, 0 * 16}, + {UI_POS_X(0), UI_POS_Y(0)}, nav_}; // Power: -XXX db Text freq_stats_db{ - {0 * 8, 2 * 16 + 4, 14 * 8, 14}, + {UI_POS_X(0), 2 * 16 + 4, 14 * 8, 14}, }; RSSIGraph rssi_graph{ {0, 50, screen_width, 30}, diff --git a/firmware/application/external/game2048/ui_game2048.cpp b/firmware/application/external/game2048/ui_game2048.cpp index 44c03eec9..694d951da 100644 --- a/firmware/application/external/game2048/ui_game2048.cpp +++ b/firmware/application/external/game2048/ui_game2048.cpp @@ -27,6 +27,7 @@ namespace ui::external_app::game2048 { Game2048View::Game2048View(NavigationView& nav) : nav_(nav), score(0), best_score(0), game_state(STATE_PLAYING) { add_children({&dummy}); + TILE_SIZE = (screen_width - 2 * BOARD_START_X - (GRID_SIZE + 1) * TILE_MARGIN) / GRID_SIZE; init_game(); } @@ -301,11 +302,11 @@ void Game2048View::draw_game() { void Game2048View::show_game_over() { if (show_you_won_you_lost_slow_down_cunter++ % 1000 != 0) return; // slow down otherwies it repaint even faster than it finished paint full stuff - painter.fill_rectangle({40, 100, 160, 100}, Color::black()); - painter.draw_rectangle({40, 100, 160, 100}, Color::white()); - painter.draw_string({80, 115}, *Theme::getInstance()->bg_darkest, "GAME OVER"); - painter.draw_string({70, 135}, *Theme::getInstance()->bg_darkest, "Press SELECT"); - painter.draw_string({75, 155}, *Theme::getInstance()->bg_darkest, "to restart"); + painter.fill_rectangle({UI_POS_X_CENTER(20), 100, 160, 100}, Color::black()); + painter.draw_rectangle({UI_POS_X_CENTER(20), 100, 160, 100}, Color::white()); + painter.draw_string({UI_POS_X_CENTER(10), 115}, *Theme::getInstance()->bg_darkest, "GAME OVER"); + painter.draw_string({UI_POS_X_CENTER(13), 135}, *Theme::getInstance()->bg_darkest, "Press SELECT"); + painter.draw_string({UI_POS_X_CENTER(11), 155}, *Theme::getInstance()->bg_darkest, "to restart"); need_repaint = false; need_repaint_bg_frame = true; } @@ -313,11 +314,11 @@ void Game2048View::show_game_over() { void Game2048View::show_you_won() { if (show_you_won_you_lost_slow_down_cunter++ % 1000 != 0) return; // slow down otherwies it repaint even faster than it finished paint full stuff - painter.fill_rectangle({40, 100, 160, 100}, Color::black()); - painter.draw_rectangle({40, 100, 160, 100}, Color::white()); - painter.draw_string({80, 115}, *Theme::getInstance()->bg_darkest, "YOU WON!"); - painter.draw_string({70, 135}, *Theme::getInstance()->bg_darkest, "Press SELECT"); - painter.draw_string({75, 155}, *Theme::getInstance()->bg_darkest, "to restart"); + painter.fill_rectangle({UI_POS_X_CENTER(20), 100, 160, 100}, Color::black()); + painter.draw_rectangle({UI_POS_X_CENTER(20), 100, 160, 100}, Color::white()); + painter.draw_string({UI_POS_X_CENTER(9), 115}, *Theme::getInstance()->bg_darkest, "YOU WON!"); + painter.draw_string({UI_POS_X_CENTER(13), 135}, *Theme::getInstance()->bg_darkest, "Press SELECT"); + painter.draw_string({UI_POS_X_CENTER(11), 155}, *Theme::getInstance()->bg_darkest, "to restart"); need_repaint = false; need_repaint_bg_frame = true; } diff --git a/firmware/application/external/game2048/ui_game2048.hpp b/firmware/application/external/game2048/ui_game2048.hpp index 7050c9f9c..6d0013e81 100644 --- a/firmware/application/external/game2048/ui_game2048.hpp +++ b/firmware/application/external/game2048/ui_game2048.hpp @@ -33,7 +33,6 @@ namespace ui::external_app::game2048 { #define GRID_SIZE 4 -#define TILE_SIZE 50 #define TILE_MARGIN 5 #define BOARD_START_X 15 #define BOARD_START_Y 35 @@ -63,6 +62,7 @@ class Game2048View : public View { Painter painter{}; int grid[GRID_SIZE][GRID_SIZE]; + int TILE_SIZE = 50; int score; int best_score; GameState game_state; diff --git a/firmware/application/external/gfxeq/ui_gfxeq.hpp b/firmware/application/external/gfxeq/ui_gfxeq.hpp index a94ec7e20..7b01ab477 100644 --- a/firmware/application/external/gfxeq/ui_gfxeq.hpp +++ b/firmware/application/external/gfxeq/ui_gfxeq.hpp @@ -68,12 +68,12 @@ class gfxEQView : public View { ColorTheme{Color(64, 64, 64), Color(255, 0, 0)}, ColorTheme{Color(255, 192, 0), Color(0, 64, 128)}}; - ButtonWithEncoder button_frequency{{0 * 8, 0 * 16 + 4, 11 * 8, 1 * 8}, ""}; - RFAmpField field_rf_amp{{13 * 8, 0 * 16}}; - LNAGainField field_lna{{15 * 8, 0 * 16}}; - VGAGainField field_vga{{18 * 8, 0 * 16}}; + ButtonWithEncoder button_frequency{{UI_POS_X(0), UI_POS_Y(0) + 4, 11 * 8, 1 * 8}, ""}; + RFAmpField field_rf_amp{{13 * 8, UI_POS_Y(0)}}; + LNAGainField field_lna{{15 * 8, UI_POS_Y(0)}}; + VGAGainField field_vga{{18 * 8, UI_POS_Y(0)}}; Button button_mood{{21 * 8, 0, 6 * 8, 16}, "MOOD"}; - AudioVolumeField field_volume{{screen_width - 2 * 8, 0 * 16}}; + AudioVolumeField field_volume{{screen_width - 2 * 8, UI_POS_Y(0)}}; GraphEq gr{{2, UI_POS_DEFAULT_HEIGHT, UI_POS_MAXWIDTH - 4, UI_POS_HEIGHT_REMAINING(2)}, false}; rf::Frequency frequency_value{93100000}; diff --git a/firmware/application/external/gpssim/gps_sim_app.hpp b/firmware/application/external/gpssim/gps_sim_app.hpp index 42b4906ea..30511971f 100644 --- a/firmware/application/external/gpssim/gps_sim_app.hpp +++ b/firmware/application/external/gpssim/gps_sim_app.hpp @@ -82,24 +82,24 @@ class GpsSimAppView : public View { bool ready_signal{false}; Button button_open{ - {0 * 8, 0 * 16, 10 * 8, 2 * 16}, + {UI_POS_X(0), UI_POS_Y(0), 10 * 8, 2 * 16}, "Open file"}; Text text_filename{ - {11 * 8, 0 * 16, 12 * 8, 16}, + {11 * 8, UI_POS_Y(0), 12 * 8, 16}, "-"}; Text text_sample_rate{ - {24 * 8, 0 * 16, 6 * 8, 16}, + {24 * 8, UI_POS_Y(0), 6 * 8, 16}, "-"}; Text text_duration{ {11 * 8, 1 * 16, 6 * 8, 16}, "-"}; ProgressBar progressbar{ - {18 * 8, 1 * 16, 12 * 8, 16}}; + {18 * 8, 1 * 16, UI_POS_WIDTH_REMAINING(18), 16}}; TxFrequencyField field_frequency{ - {0 * 8, 2 * 16}, + {UI_POS_X(0), 2 * 16}, nav_}; TransmitterView2 tx_view{ diff --git a/firmware/application/external/hopper/ui_hopper.hpp b/firmware/application/external/hopper/ui_hopper.hpp index 1bb0c4644..384f9b0b0 100644 --- a/firmware/application/external/hopper/ui_hopper.hpp +++ b/firmware/application/external/hopper/ui_hopper.hpp @@ -88,55 +88,55 @@ class HopperView : public View { false}; NewButton button_load_list{ - {0 * 8, 9 * 16 + 4, 4 * 8, 32}, + {UI_POS_X(0), UI_POS_Y_BOTTOM(10), 4 * 8, 32}, {}, &bitmap_icon_load, Color::dark_blue(), /*vcenter*/ true}; NewButton button_save_list{ - {4 * 8, 9 * 16 + 4, 4 * 8, 32}, + {4 * 8, UI_POS_Y_BOTTOM(10), 4 * 8, 32}, {}, &bitmap_icon_save, Color::dark_blue(), /*vcenter*/ true}; NewButton button_add_freq{ - {8 * 8 + 4, 9 * 16 + 4, 4 * 8, 32}, + {8 * 8 + 4, UI_POS_Y_BOTTOM(10), 4 * 8, 32}, {}, &bitmap_icon_add, Color::dark_green(), /*vcenter*/ true}; NewButton button_delete_freq{ - {12 * 8 + 4, 9 * 16 + 4, 4 * 8, 32}, + {12 * 8 + 4, UI_POS_Y_BOTTOM(10), 4 * 8, 32}, {}, &bitmap_icon_trash, Color::dark_red(), /*vcenter*/ true}; NewButton button_clear{ - {screen_width - 4 * 8, 9 * 16 + 4, 4 * 8, 32}, + {screen_width - 4 * 8, UI_POS_Y_BOTTOM(10), 4 * 8, 32}, {}, &bitmap_icon_tools_wipesd, Color::red(), /*vcenter*/ true}; Labels labels{ - {{2 * 8, 23 * 8}, "Type:", Theme::getInstance()->fg_light->foreground}, - {{1 * 8, 25 * 8}, "Speed:", Theme::getInstance()->fg_light->foreground}, - {{3 * 8, 27 * 8}, "Hop:", Theme::getInstance()->fg_light->foreground}, - {{4 * 8, 29 * 8}, "TX:", Theme::getInstance()->fg_light->foreground}, - {{1 * 8, 31 * 8}, "Sle3p:", Theme::getInstance()->fg_light->foreground}, // euquiq: Token of appreciation to TheSle3p, which made this ehnancement a reality with his bounty. - {{0 * 8, 33 * 8}, "Jitter:", Theme::getInstance()->fg_light->foreground}, // Maybe the repository curator can keep the "mystype" for some versions. - {{11 * 8, 29 * 8}, "Secs.", Theme::getInstance()->fg_light->foreground}, - {{11 * 8, 31 * 8}, "Secs.", Theme::getInstance()->fg_light->foreground}, - {{11 * 8, 33 * 8}, "/60", Theme::getInstance()->fg_light->foreground}, - {{2 * 8, 35 * 8}, "Gain:", Theme::getInstance()->fg_light->foreground}, - {{11 * 8, 35 * 8}, "A:", Theme::getInstance()->fg_light->foreground}}; + {{2 * 8, UI_POS_Y_BOTTOM(8)}, "Type:", Theme::getInstance()->fg_light->foreground}, + {{1 * 8, UI_POS_Y_BOTTOM(7)}, "Speed:", Theme::getInstance()->fg_light->foreground}, + {{3 * 8, UI_POS_Y_BOTTOM(6)}, "Hop:", Theme::getInstance()->fg_light->foreground}, + {{4 * 8, UI_POS_Y_BOTTOM(5)}, "TX:", Theme::getInstance()->fg_light->foreground}, + {{1 * 8, UI_POS_Y_BOTTOM(4)}, "Sle3p:", Theme::getInstance()->fg_light->foreground}, // euquiq: Token of appreciation to TheSle3p, which made this ehnancement a reality with his bounty. + {{UI_POS_X(0), UI_POS_Y_BOTTOM(3)}, "Jitter:", Theme::getInstance()->fg_light->foreground}, // Maybe the repository curator can keep the "mystype" for some versions. + {{11 * 8, UI_POS_Y_BOTTOM(5)}, "Secs.", Theme::getInstance()->fg_light->foreground}, + {{11 * 8, UI_POS_Y_BOTTOM(4)}, "Secs.", Theme::getInstance()->fg_light->foreground}, + {{11 * 8, UI_POS_Y_BOTTOM(3)}, "/60", Theme::getInstance()->fg_light->foreground}, + {{2 * 8, UI_POS_Y_BOTTOM(2)}, "Gain:", Theme::getInstance()->fg_light->foreground}, + {{11 * 8, UI_POS_Y_BOTTOM(2)}, "A:", Theme::getInstance()->fg_light->foreground}}; OptionsField options_type{ - {7 * 8, 23 * 8}, + {7 * 8, UI_POS_Y_BOTTOM(8)}, 8, { {"Rand FSK", 0}, @@ -153,14 +153,14 @@ class HopperView : public View { }}; Text text_range_number{ - {16 * 8, 23 * 8, 2 * 8, 16}, + {16 * 8, UI_POS_Y_BOTTOM(8), 2 * 8, 16}, "--"}; Text text_range_total{ - {18 * 8, 23 * 8, 3 * 8, 16}, + {18 * 8, UI_POS_Y_BOTTOM(8), 3 * 8, 16}, "/--"}; OptionsField options_speed{ - {7 * 8, 25 * 8}, + {7 * 8, UI_POS_Y_BOTTOM(7)}, 6, {{"10Hz ", 10}, {"100Hz ", 100}, @@ -169,7 +169,7 @@ class HopperView : public View { {"100kHz", 100000}}}; OptionsField options_hop{ - {7 * 8, 27 * 8}, + {7 * 8, UI_POS_Y_BOTTOM(6)}, 6, {{"0ms !!", 0}, {"1ms ", 1}, @@ -182,7 +182,7 @@ class HopperView : public View { {"10s ", 10000}}}; NumberField field_timetx{ - {7 * 8, 29 * 8}, + {7 * 8, UI_POS_Y_BOTTOM(5)}, 3, {1, 180}, 1, @@ -190,7 +190,7 @@ class HopperView : public View { }; NumberField field_timepause{ - {8 * 8, 31 * 8}, + {8 * 8, UI_POS_Y_BOTTOM(4)}, 2, {1, 60}, 1, @@ -198,7 +198,7 @@ class HopperView : public View { }; NumberField field_jitter{ - {8 * 8, 33 * 8}, + {8 * 8, UI_POS_Y_BOTTOM(3)}, 2, {1, 60}, 1, @@ -206,7 +206,7 @@ class HopperView : public View { }; NumberField field_gain{ - {8 * 8, 35 * 8}, + {8 * 8, UI_POS_Y_BOTTOM(2)}, 2, {0, 47}, 1, @@ -214,7 +214,7 @@ class HopperView : public View { }; NumberField field_amp{ - {13 * 8, 35 * 8}, + {13 * 8, UI_POS_Y_BOTTOM(2)}, 1, {0, 1}, 1, @@ -222,7 +222,7 @@ class HopperView : public View { }; Button button_transmit{ - {148, 216, 80, 80}, + {148, UI_POS_Y_BOTTOM(6), 80, 80}, LanguageHelper::currentMessages[LANG_START]}; MessageHandlerRegistration message_handler_retune{ diff --git a/firmware/application/external/jammer/ui_jammer.hpp b/firmware/application/external/jammer/ui_jammer.hpp index b338cdd47..ab99b3c8e 100644 --- a/firmware/application/external/jammer/ui_jammer.hpp +++ b/firmware/application/external/jammer/ui_jammer.hpp @@ -57,7 +57,7 @@ class RangeView : public View { "Load range"}; Button button_start{ - {0 * 8, 11 * 8, 11 * 8, 28}, + {UI_POS_X(0), 11 * 8, 11 * 8, 28}, ""}; Button button_stop{ @@ -144,7 +144,7 @@ class JammerView : public View { {{3 * 8, 27 * 8}, "Hop:", Theme::getInstance()->fg_light->foreground}, {{4 * 8, 29 * 8}, "TX:", Theme::getInstance()->fg_light->foreground}, {{1 * 8, 31 * 8}, "Sleep:", Theme::getInstance()->fg_light->foreground}, - {{0 * 8, 33 * 8}, "Jitter:", Theme::getInstance()->fg_light->foreground}, + {{UI_POS_X(0), 33 * 8}, "Jitter:", Theme::getInstance()->fg_light->foreground}, {{11 * 8, 29 * 8}, "Secs.", Theme::getInstance()->fg_light->foreground}, {{11 * 8, 31 * 8}, "Secs.", Theme::getInstance()->fg_light->foreground}, {{11 * 8, 33 * 8}, "/60", Theme::getInstance()->fg_light->foreground}, diff --git a/firmware/application/external/keyfob/ui_keyfob.hpp b/firmware/application/external/keyfob/ui_keyfob.hpp index 970598b5c..36e767afd 100644 --- a/firmware/application/external/keyfob/ui_keyfob.hpp +++ b/firmware/application/external/keyfob/ui_keyfob.hpp @@ -106,14 +106,14 @@ class KeyfobView : public View { SymField::Type::Hex}; Text text_status{ - {2 * 8, 13 * 16, 128, 16}, + {2 * 8, UI_POS_Y_BOTTOM(7), 128, 16}, "Ready"}; ProgressBar progressbar{ - {2 * 8, 13 * 16 + 20, 208, 16}}; + {2 * 8, UI_POS_Y_BOTTOM(6), UI_POS_WIDTH_REMAINING(4), 16}}; TransmitterView tx_view{ - 16 * 16, + (int16_t)UI_POS_Y_BOTTOM(4), 0, 15, true}; diff --git a/firmware/application/external/lcr/ui_lcr.hpp b/firmware/application/external/lcr/ui_lcr.hpp index d0767fc8e..1c8801aa0 100644 --- a/firmware/application/external/lcr/ui_lcr.hpp +++ b/firmware/application/external/lcr/ui_lcr.hpp @@ -145,10 +145,10 @@ class LCRView : public View { {2 * 8, 27 * 8 + 4, 26 * 8, 16}, LanguageHelper::currentMessages[LANG_READY]}; ProgressBar progress{ - {2 * 8, 29 * 8 + 4, 26 * 8, 16}}; + {2 * 8, 29 * 8 + 4, UI_POS_WIDTH_REMAINING(4), 16}}; TransmitterView tx_view{ - 16 * 16, + (int16_t)UI_POS_Y_BOTTOM(4), 10000, 12}; diff --git a/firmware/application/external/level/ui_level.hpp b/firmware/application/external/level/ui_level.hpp index e08a46087..b88973198 100644 --- a/firmware/application/external/level/ui_level.hpp +++ b/firmware/application/external/level/ui_level.hpp @@ -80,21 +80,21 @@ class LevelView : public View { }}; Labels labels{ - {{0 * 8, 0 * 16}, "LNA: VGA: AMP: VOL: ", Theme::getInstance()->fg_light->foreground}, - {{0 * 8, 1 * 16}, "BW: MODE: S: ", Theme::getInstance()->fg_light->foreground}, + {{UI_POS_X(0), UI_POS_Y(0)}, "LNA: VGA: AMP: VOL: ", Theme::getInstance()->fg_light->foreground}, + {{UI_POS_X(0), 1 * 16}, "BW: MODE: S: ", Theme::getInstance()->fg_light->foreground}, }; LNAGainField field_lna{ - {4 * 8, 0 * 16}}; + {4 * 8, UI_POS_Y(0)}}; VGAGainField field_vga{ - {11 * 8, 0 * 16}}; + {11 * 8, UI_POS_Y(0)}}; RFAmpField field_rf_amp{ - {18 * 8, 0 * 16}}; + {18 * 8, UI_POS_Y(0)}}; AudioVolumeField field_volume{ - {24 * 8, 0 * 16}}; + {24 * 8, UI_POS_Y(0)}}; OptionsField field_bw{ {3 * 8, 1 * 16}, @@ -112,7 +112,7 @@ class LevelView : public View { {}}; ButtonWithEncoder button_frequency{ - {0 * 8, 2 * 16 + 8, 15 * 8, 1 * 8}, + {UI_POS_X(0), 2 * 16 + 8, 15 * 8, 1 * 8}, ""}; OptionsField field_audio_mode{ @@ -135,12 +135,12 @@ class LevelView : public View { // RSSI: XX/XX/XXX Text freq_stats_rssi{ - {0 * 8, 3 * 16 + 4, 15 * 8, 1 * 16}, + {UI_POS_X(0), 3 * 16 + 4, 15 * 8, 1 * 16}, }; // Power: -XXX db Text freq_stats_db{ - {0 * 8, 4 * 16 + 4, 15 * 8, 1 * 16}, + {UI_POS_X(0), 4 * 16 + 4, 15 * 8, 1 * 16}, }; OptionsField peak_mode{ @@ -169,7 +169,7 @@ class LevelView : public View { // RxSat: XX% Text freq_stats_rx{ - {0 * 8, 5 * 16 + 4, 10 * 8, 1 * 16}, + {UI_POS_X(0), 5 * 16 + 4, 10 * 8, 1 * 16}, }; Text text_ctcss{ diff --git a/firmware/application/external/lge/lge_app.hpp b/firmware/application/external/lge/lge_app.hpp index 2e612a7ec..42b698a66 100644 --- a/firmware/application/external/lge/lge_app.hpp +++ b/firmware/application/external/lge/lge_app.hpp @@ -93,8 +93,8 @@ class LGEView : public View { {{2 * 8, 3 * 8}, "Room:", Theme::getInstance()->fg_light->foreground}, {{14 * 8, 3 * 8}, "Text:", Theme::getInstance()->fg_light->foreground}, {{2 * 8, 5 * 8}, "Team:", Theme::getInstance()->fg_light->foreground}, - {{0 * 8, 7 * 8}, "Player:", Theme::getInstance()->fg_light->foreground}, - {{0 * 8, 10 * 8}, "Vest:", Theme::getInstance()->fg_light->foreground}, + {{UI_POS_X(0), 7 * 8}, "Player:", Theme::getInstance()->fg_light->foreground}, + {{UI_POS_X(0), 10 * 8}, "Vest:", Theme::getInstance()->fg_light->foreground}, {{4 * 8, 12 * 8}, "ID:", Theme::getInstance()->fg_light->foreground}, {{3 * 8, 14 * 8}, "Pow: /10", Theme::getInstance()->fg_light->foreground}, {{2 * 8, 16 * 8}, "Time: x100ms", Theme::getInstance()->fg_light->foreground}}; @@ -171,10 +171,10 @@ class LGEView : public View { '0'}; Console console{ - {0, 18 * 8, screen_width, 7 * 16}}; + {0, 18 * 8, screen_width, UI_POS_Y_BOTTOM(4) - 18 * 8}}; TransmitterView tx_view{ - 16 * 16, + (int16_t)UI_POS_Y_BOTTOM(4), 10000, 12}; diff --git a/firmware/application/external/mcu_temperature/mcu_temperature.hpp b/firmware/application/external/mcu_temperature/mcu_temperature.hpp index 217ab9a25..d004681d3 100644 --- a/firmware/application/external/mcu_temperature/mcu_temperature.hpp +++ b/firmware/application/external/mcu_temperature/mcu_temperature.hpp @@ -72,7 +72,7 @@ class McuTemperatureView : public View { private: Text text_title{ - {76, 16, screen_width, 16}, + {UI_POS_X_CENTER(11), 16, UI_POS_WIDTH(11), 16}, "Temperature", }; @@ -81,7 +81,7 @@ class McuTemperatureView : public View { }; Button button_done{ - {72, screen_height - 56, 96, 24}, + {UI_POS_X_CENTER(12), UI_POS_Y_BOTTOM(3), UI_POS_WIDTH(12), 24}, "Done"}; }; diff --git a/firmware/application/external/metronome/ui_metronome.hpp b/firmware/application/external/metronome/ui_metronome.hpp index 3763ae226..3fdb3d265 100644 --- a/firmware/application/external/metronome/ui_metronome.hpp +++ b/firmware/application/external/metronome/ui_metronome.hpp @@ -60,13 +60,13 @@ class MetronomeView : public View { uint32_t current_beat_{0}; Labels labels{ - {{0 * 8, 1 * 16}, "BPM:", Theme::getInstance()->fg_light->foreground}, - {{0 * 8, 2 * 16}, "Accent Beep Tune:", Theme::getInstance()->fg_light->foreground}, - {{0 * 8, 3 * 16}, "Unaccent Beep Tune:", Theme::getInstance()->fg_light->foreground}, - {{0 * 8, 4 * 16}, "Rhythm:", Theme::getInstance()->fg_light->foreground}, + {{UI_POS_X(0), 1 * 16}, "BPM:", Theme::getInstance()->fg_light->foreground}, + {{UI_POS_X(0), 2 * 16}, "Accent Beep Tune:", Theme::getInstance()->fg_light->foreground}, + {{UI_POS_X(0), 3 * 16}, "Unaccent Beep Tune:", Theme::getInstance()->fg_light->foreground}, + {{UI_POS_X(0), 4 * 16}, "Rhythm:", Theme::getInstance()->fg_light->foreground}, {{(sizeof("Rhythm:") + 1) * 8 + 4 * 8, 4 * 16}, "/", Theme::getInstance()->fg_light->foreground}, - {{0 * 8, 5 * 16}, "Beep Flash Duration:", Theme::getInstance()->fg_light->foreground}, - {{0 * 8, 6 * 16}, "Volume:", Theme::getInstance()->fg_light->foreground}}; + {{UI_POS_X(0), 5 * 16}, "Beep Flash Duration:", Theme::getInstance()->fg_light->foreground}, + {{UI_POS_X(0), 6 * 16}, "Volume:", Theme::getInstance()->fg_light->foreground}}; NumberField field_bpm{ {(sizeof("BPM:") + 1) * 8, 1 * 16}, diff --git a/firmware/application/external/morse_tx/ui_morse.hpp b/firmware/application/external/morse_tx/ui_morse.hpp index fb54fcd7a..d398b28c2 100644 --- a/firmware/application/external/morse_tx/ui_morse.hpp +++ b/firmware/application/external/morse_tx/ui_morse.hpp @@ -171,10 +171,10 @@ class MorseView : public View { "Set message"}; ProgressBar progressbar{ - {2 * 8, 28 * 8, 208, 16}}; + {2 * 8, 28 * 8, UI_POS_WIDTH_REMAINING(4), 16}}; TransmitterView tx_view{ - 16 * 16, + (int16_t)UI_POS_Y_BOTTOM(4), 10000, 12}; diff --git a/firmware/application/external/noaaapt_rx/ui_noaaapt_rx.hpp b/firmware/application/external/noaaapt_rx/ui_noaaapt_rx.hpp index a2db6b9bb..16293638b 100644 --- a/firmware/application/external/noaaapt_rx/ui_noaaapt_rx.hpp +++ b/firmware/application/external/noaaapt_rx/ui_noaaapt_rx.hpp @@ -87,7 +87,7 @@ class NoaaAptRxView : public View { VGAGainField field_vga{ {UI_POS_X(18), UI_POS_Y(0)}}; RSSI rssi{ - {UI_POS_X(21), UI_POS_Y(0), UI_POS_WIDTH(6), 4}}; + {UI_POS_X(21), UI_POS_Y(0), UI_POS_WIDTH_REMAINING(24), 4}}; AudioVolumeField field_volume{ {UI_POS_X_RIGHT(2), UI_POS_Y(0)}}; @@ -114,7 +114,7 @@ class NoaaAptRxView : public View { }; Button button_ss{ - {UI_POS_X_RIGHT(6), UI_POS_Y(1), UI_POS_WIDTH(5), UI_POS_DEFAULT_HEIGHT}, + {UI_POS_X_RIGHT(6), UI_POS_Y(1), UI_POS_WIDTH(6), UI_POS_HEIGHT(2)}, LanguageHelper::currentMessages[LANG_START]}; MessageHandlerRegistration message_handler_stats{ diff --git a/firmware/application/external/nrf_rx/ui_nrf_rx.hpp b/firmware/application/external/nrf_rx/ui_nrf_rx.hpp index 0401604bd..ba1a8dead 100644 --- a/firmware/application/external/nrf_rx/ui_nrf_rx.hpp +++ b/firmware/application/external/nrf_rx/ui_nrf_rx.hpp @@ -62,18 +62,18 @@ class NRFRxView : public View { uint32_t prev_value{0}; RFAmpField field_rf_amp{ - {13 * 8, 0 * 16}}; + {13 * 8, UI_POS_Y(0)}}; LNAGainField field_lna{ - {15 * 8, 0 * 16}}; + {15 * 8, UI_POS_Y(0)}}; VGAGainField field_vga{ - {18 * 8, 0 * 16}}; + {18 * 8, UI_POS_Y(0)}}; RSSI rssi{ - {21 * 8, 0, 6 * 8, 4}}; + {21 * 8, 0, UI_POS_WIDTH_REMAINING(22), 4}}; Channel channel{ - {21 * 8, 5, 6 * 8, 4}}; + {21 * 8, 5, UI_POS_WIDTH_REMAINING(22), 4}}; RxFrequencyField field_frequency{ - {0 * 8, 0 * 16}, + {UI_POS_X(0), UI_POS_Y(0)}, nav_}; Button button_modem_setup{ diff --git a/firmware/application/external/ook_editor/ui_ook_editor.hpp b/firmware/application/external/ook_editor/ui_ook_editor.hpp index 20134271f..ac4718ed6 100644 --- a/firmware/application/external/ook_editor/ui_ook_editor.hpp +++ b/firmware/application/external/ook_editor/ui_ook_editor.hpp @@ -93,8 +93,8 @@ class OOKEditorAppView : public View { {{"ook_editor_tx_freq"sv, &ook_editor_tx_freq}}}; // UI components for frequency and transmitter view. - FrequencyField field_frequency{{0 * 8, 0 * 16}}; - TransmitterView2 tx_view{{20 * 7, 0 * 16}, true}; + FrequencyField field_frequency{{UI_POS_X(0), UI_POS_Y(0)}}; + TransmitterView2 tx_view{{UI_POS_X_RIGHT(9), UI_POS_Y(0)}, true}; // Labels for various fields such as sample rate and repeat count. Labels label_step{{{170, 20}, "Step:", Theme::getInstance()->fg_light->foreground}}; @@ -120,17 +120,17 @@ class OOKEditorAppView : public View { NumberField field_repeat{{210, 40}, 3, {1, 999}, 1, '0', false}; // Text field to display the payload data. - Text text_payload{{0 * 8, 100, screen_width, 16}, ""}; + Text text_payload{{UI_POS_X(0), 100, screen_width, 16}, ""}; // Buttons for setting configurations, opening files, and starting transmission. Button button_set{{0, 125, 60, 28}, LanguageHelper::currentMessages[LANG_SET]}; Button button_open{{68, 125, 80, 28}, LanguageHelper::currentMessages[LANG_OPEN_FILE]}; Button button_save{{154, 125, 80, 28}, LanguageHelper::currentMessages[LANG_SAVE_FILE]}; Button button_bug_key{{0, 125 + 28 + 3, screen_width, 28}, "Bug Key"}; - Button button_send_stop{{80, 273, 80, 32}, LanguageHelper::currentMessages[LANG_SEND]}; + Button button_send_stop{{UI_POS_X_CENTER(10), 273, UI_POS_WIDTH(10), 32}, LanguageHelper::currentMessages[LANG_SEND]}; // Progress bar to display transmission progress. - ProgressBar progressbar{{2 * 8, 250, 208, 16}}; + ProgressBar progressbar{{2 * 8, 250, UI_POS_WIDTH_REMAINING(4), 16}}; // Waveform display using waveform buffer and yellow theme color. Waveform waveform{{0, 208, screen_width, 32}, waveform_buffer, 0, 0, true, Theme::getInstance()->fg_yellow->foreground}; @@ -167,11 +167,11 @@ class OOKEditorBugKeyView : public View { std::string build_payload(); Labels labels{ - {{0 * 8, 0 * 16}, "Primary Step", Theme::getInstance()->fg_light->foreground}, - {{(screen_width / 2), 0 * 16}, "Secondary Step", Theme::getInstance()->fg_light->foreground}}; + {{UI_POS_X(0), UI_POS_Y(0)}, "Primary Step", Theme::getInstance()->fg_light->foreground}, + {{(screen_width / 2), UI_POS_Y(0)}, "Secondary Step", Theme::getInstance()->fg_light->foreground}}; NumberField field_primary_step{ - {0 * 8, 1 * 16}, + {UI_POS_X(0), 1 * 16}, 3, {0, 550}, 1, @@ -185,30 +185,30 @@ class OOKEditorBugKeyView : public View { ' '}; Console console{ - {0, 3 * 16, screen_width, screen_height - 10 * 16}}; + {0, UI_POS_Y(3), screen_width, UI_POS_HEIGHT_REMAINING(10)}}; Button button_insert_low_level_long{ - {0 * 8, 13 * 16, screen_width / 2, 2 * 16}, + {UI_POS_X(0), UI_POS_Y_BOTTOM(7), screen_width / 2, UI_POS_HEIGHT(2)}, "00"}; Button button_insert_low_level_short{ - {0 * 8, 15 * 16, screen_width / 2, 2 * 16}, + {UI_POS_X(0), UI_POS_Y_BOTTOM(5), screen_width / 2, UI_POS_HEIGHT(2)}, "0"}; Button button_insert_high_level_long{ - {(screen_width / 2), 13 * 16, screen_width / 2, 2 * 16}, + {(screen_width / 2), UI_POS_Y_BOTTOM(7), screen_width / 2, UI_POS_HEIGHT(2)}, "11"}; Button button_insert_high_level_short{ - {(screen_width / 2), 15 * 16, screen_width / 2, 2 * 16}, + {(screen_width / 2), UI_POS_Y_BOTTOM(5), screen_width / 2, UI_POS_HEIGHT(2)}, "1"}; Button button_delete{ - {1, 17 * 16, screen_width / 2 - 4, 2 * 16}, + {1, UI_POS_Y_BOTTOM(3), screen_width / 2 - 4, UI_POS_HEIGHT(2)}, "fg_light->foreground}, - {{0 * 8, 7 * 16}, "Stop Position:", Theme::getInstance()->fg_light->foreground}, - {{0 * 8, 13 * 16}, "Encoder Type:", Theme::getInstance()->fg_light->foreground}}; + {{UI_POS_X(0), 2 * 16}, "Start Position:", Theme::getInstance()->fg_light->foreground}, + {{UI_POS_X(0), 7 * 16}, "Stop Position:", Theme::getInstance()->fg_light->foreground}, + {{UI_POS_X(0), 13 * 16}, "Encoder Type:", Theme::getInstance()->fg_light->foreground}}; Button button_startstop{ {8, screen_height - 48 - 16, screen_width - 2 * 8, 48}, @@ -71,7 +71,7 @@ class OOKBruteView : public View { "Input Stop Pos"}; NumberField field_start{ - {0 * 8, 3 * 16}, + {UI_POS_X(0), 3 * 16}, 8, {0, 2500}, 1, diff --git a/firmware/application/external/playlist_editor/ui_playlist_editor.cpp b/firmware/application/external/playlist_editor/ui_playlist_editor.cpp index a764acfb5..0f1e3032b 100644 --- a/firmware/application/external/playlist_editor/ui_playlist_editor.cpp +++ b/firmware/application/external/playlist_editor/ui_playlist_editor.cpp @@ -52,7 +52,7 @@ PlaylistEditorView::PlaylistEditorView(NavigationView& nav) &button_insert, &button_save_playlist}); - menu_view.set_parent_rect({0, 2 * 8, screen_width, 24 * 8}); + menu_view.set_parent_rect({0, 2 * 8, screen_width, UI_POS_HEIGHT_REMAINING(9)}); menu_view.on_highlight = [this]() { text_hint.set("Edit:" + diff --git a/firmware/application/external/playlist_editor/ui_playlist_editor.hpp b/firmware/application/external/playlist_editor/ui_playlist_editor.hpp index 8f1ca9de5..36666c361 100644 --- a/firmware/application/external/playlist_editor/ui_playlist_editor.hpp +++ b/firmware/application/external/playlist_editor/ui_playlist_editor.hpp @@ -48,40 +48,40 @@ class PlaylistEditorView : public View { bool ever_opened = false; Labels labels{ - {{0 * 8, 0 * 16}, "PPL file:", Theme::getInstance()->fg_light->foreground}}; + {{UI_POS_X(0), UI_POS_Y(0)}, "PPL file:", Theme::getInstance()->fg_light->foreground}}; Button button_new{ - {(sizeof("PPL file:") + 1) * 8, 0 * 16, 8 * 5, 16}, + {(sizeof("PPL file:") + 1) * 8, UI_POS_Y(0), 8 * 5, 16}, "New"}; Text text_current_ppl_file{ - {sizeof("PPL file:") * 8, 0 * 16, screen_width - (int)sizeof("PPL file:") * 8, 16}, + {sizeof("PPL file:") * 8, UI_POS_Y(0), screen_width - (int)sizeof("PPL file:") * 8, 16}, ""}; MenuView menu_view{}; Text text_hint{ - {0, 27 * 8, screen_width, 16}, + {0, UI_POS_Y_BOTTOM(6), screen_width, 16}, "Open a PPL file"}; Text text_ppl_name{ - {0, 27 * 8, screen_width, 16}, + {0, UI_POS_Y_BOTTOM(6), screen_width, 16}, "Highlight an app"}; Button button_open_playlist{ - {0, 29 * 8, screen_width / 2 - 1, 32}, + {0, UI_POS_Y_BOTTOM(5), screen_width / 2 - 1, 32}, "Open PPL"}; Button button_edit{ - {screen_width / 2 + 2, 29 * 8, screen_width / 2 - 2, 32}, + {screen_width / 2 + 2, UI_POS_Y_BOTTOM(5), screen_width / 2 - 2, 32}, "Edit Item"}; Button button_insert{ - {0, screen_height - 32 - 16, screen_width / 2 - 1, 32}, + {0, UI_POS_Y_BOTTOM(3), screen_width / 2 - 1, 32}, "Ins. After"}; Button button_save_playlist{ - {screen_width / 2 + 2, screen_height - 32 - 16, screen_width / 2 - 2, 32}, + {screen_width / 2 + 2, UI_POS_Y_BOTTOM(3), screen_width / 2 - 2, 32}, "Save PPL"}; void open_file(); @@ -122,7 +122,7 @@ class PlaylistItemEditView : public View { std::string build_item() const; Labels labels{ - {{0 * 8, 1 * 16}, "Path:", Theme::getInstance()->fg_light->foreground}, + {{UI_POS_X(0), 1 * 16}, "Path:", Theme::getInstance()->fg_light->foreground}, {{2 * 8, 5 * 16}, "Delay(ms):", Theme::getInstance()->fg_light->foreground}}; TextField field_path{ diff --git a/firmware/application/external/random_password/ui_random_password.hpp b/firmware/application/external/random_password/ui_random_password.hpp index 8586a9733..d565309cf 100644 --- a/firmware/application/external/random_password/ui_random_password.hpp +++ b/firmware/application/external/random_password/ui_random_password.hpp @@ -98,9 +98,9 @@ class RandomPasswordView : public View { "rx_passgen", app_settings::Mode::RX}; Labels labels{ - {{0 * 8, 0 * 16}, "------------seeds-------------", Theme::getInstance()->fg_light->foreground}, - {{0 * 8, 3 * 16}, "-----------password-----------", Theme::getInstance()->fg_light->foreground}, - {{0 * 8, 7 * 16 - 2}, "digits:", Theme::getInstance()->fg_light->foreground}, + {{UI_POS_X_CENTER(30), UI_POS_Y(0)}, "------------seeds-------------", Theme::getInstance()->fg_light->foreground}, + {{UI_POS_X_CENTER(30), 3 * 16}, "-----------password-----------", Theme::getInstance()->fg_light->foreground}, + {{UI_POS_X(0), 7 * 16 - 2}, "digits:", Theme::getInstance()->fg_light->foreground}, {{screen_width / 2, 7 * 16 - 2}, "method:", Theme::getInstance()->fg_light->foreground}, }; @@ -112,12 +112,12 @@ class RandomPasswordView : public View { {18 * 8, 1 * 16}}; RSSI rssi{ - {21 * 8, 1 * 16 + 0, 6 * 8, 4}}; + {UI_POS_X(21), 1 * 16 + 0, UI_POS_WIDTH_REMAINING(21), 4}}; Channel channel{ - {21 * 8, 1 * 16 + 5, 6 * 8, 4}}; + {UI_POS_X(21), 1 * 16 + 5, UI_POS_WIDTH_REMAINING(21), 4}}; RxFrequencyField field_frequency{ - {0 * 8, 1 * 16}, + {UI_POS_X(0), 1 * 16}, nav_}; Button button_modem_setup{ @@ -180,7 +180,7 @@ class RandomPasswordView : public View { LanguageHelper::currentMessages[LANG_SAVE]}; Button button_flood{ - {0 * 8, 15 * 16 + 18, screen_width / 2, 22}, + {UI_POS_X(0), 15 * 16 + 18, screen_width / 2, 22}, LanguageHelper::currentMessages[LANG_FLOOD]}; Button button_send{ @@ -188,7 +188,7 @@ class RandomPasswordView : public View { "Send pwd"}; Button button_refresh{ - {0 * 8, 17 * 16 + 10, screen_width / 2, 22}, + {UI_POS_X(0), 17 * 16 + 10, screen_width / 2, 22}, "Generate"}; Button button_show_qr{ diff --git a/firmware/application/external/remote/ui_remote.hpp b/firmware/application/external/remote/ui_remote.hpp index b8a7ec251..af10f7a5b 100644 --- a/firmware/application/external/remote/ui_remote.hpp +++ b/firmware/application/external/remote/ui_remote.hpp @@ -208,7 +208,7 @@ class RemoteEntryEditView : public View { {{2 * 8, 5 * 16}, "Icon:", Theme::getInstance()->fg_light->foreground}, {{2 * 8, 6 * 16}, "FG Color:", Theme::getInstance()->fg_light->foreground}, {{2 * 8, 7 * 16}, "BG Color:", Theme::getInstance()->fg_light->foreground}, - {{8 * 8, 9 * 16}, "Button preview", Theme::getInstance()->fg_light->foreground}, + {{UI_POS_X_CENTER(14), 9 * 16}, "Button preview", Theme::getInstance()->fg_light->foreground}, }; TextField field_name{{8 * 8, 1 * 16, 20 * 8, 1 * 16}, {}}; @@ -244,7 +244,7 @@ class RemoteEntryEditView : public View { /*loop*/ true}; RemoteButton button_preview{ - {10 * 8, 11 * 16 - 8, 10 * 8, 50}, + {UI_POS_X_CENTER(10), 11 * 16 - 8, 10 * 8, 50}, &entry_}; NewButton button_delete{ @@ -253,7 +253,7 @@ class RemoteEntryEditView : public View { &bitmap_icon_trash, Color::red()}; - Button button_done{{11 * 8, 16 * 16, 8 * 8, 2 * 16}, "Done"}; + Button button_done{{UI_POS_X_CENTER(10), 16 * 16, 10 * 8, 2 * 16}, "Done"}; }; /* App that allows for buttons to be bound to captures for playback. */ @@ -328,39 +328,39 @@ class RemoteAppView : public View { bool ready_signal_{}; // Used to signal ReplayThread ready. TextField field_title{ - {0 * 8, 0 * 16 + 2, screen_width, 1 * 16}, + {UI_POS_X(0), UI_POS_Y(0) + 2, screen_width, 1 * 16}, {}}; TransmitterView2 tx_view{ - {0 * 8, 17 * 16}, + {UI_POS_X(0), UI_POS_Y_BOTTOM(3)}, /*short_ui*/ true}; Checkbox check_loop{ - {10 * 8, 17 * 16}, + {10 * 8, UI_POS_Y_BOTTOM(3)}, 4, "Loop", /*small*/ true}; TextField field_filename{ - {0 * 8, 18 * 16, 17 * 8, 1 * 16}, + {UI_POS_X(0), UI_POS_Y_BOTTOM(2), 17 * 8, 1 * 16}, {}}; NewButton button_add{ - {17 * 8 + 4, 17 * 16, 4 * 8, 2 * 16}, + {17 * 8 + 4, UI_POS_Y_BOTTOM(3), 4 * 8, 2 * 16}, "", &bitmap_icon_add, Color::orange(), /*vcenter*/ true}; NewButton button_new{ - {22 * 8, 17 * 16, 4 * 8, 2 * 16}, + {22 * 8, UI_POS_Y_BOTTOM(3), 4 * 8, 2 * 16}, "", &bitmap_icon_new_file, Color::dark_blue(), /*vcenter*/ true}; NewButton button_open{ - {26 * 8, 17 * 16, 4 * 8, 2 * 16}, + {26 * 8, UI_POS_Y_BOTTOM(3), 4 * 8, 2 * 16}, "", &bitmap_icon_load, Color::dark_blue(), diff --git a/firmware/application/external/scanner/ui_scanner.hpp b/firmware/application/external/scanner/ui_scanner.hpp index bb5821907..c39204fbd 100644 --- a/firmware/application/external/scanner/ui_scanner.hpp +++ b/firmware/application/external/scanner/ui_scanner.hpp @@ -173,33 +173,37 @@ class ScannerView : public View { }; Labels labels{ - {{0 * 8, 0 * 16}, "LNA: VGA: AMP: VOL:", Theme::getInstance()->fg_light->foreground}, - {{0 * 8, 1 * 16}, "BW: SQ: Wsa: Wsl:", Theme::getInstance()->fg_light->foreground}, - {{0 * 8, 10 * 16}, "SRCH START SEARCH END SWITCH", Theme::getInstance()->fg_light->foreground}, + {{UI_POS_X(0), UI_POS_Y(0)}, "LNA: VGA: AMP:", Theme::getInstance()->fg_light->foreground}, + {{UI_POS_X_RIGHT(6), UI_POS_Y(0)}, "VOL:", Theme::getInstance()->fg_light->foreground}, + {{UI_POS_X(0), UI_POS_Y(1)}, "BW: SQ: Wsa: Wsl:", Theme::getInstance()->fg_light->foreground}, + {{UI_POS_X_RIGHT(13), UI_POS_Y(1)}, "Wsa: Wsl:", Theme::getInstance()->fg_light->foreground}, + {{UI_POS_X(0), UI_POS_Y(10)}, "SRCH START", Theme::getInstance()->fg_light->foreground}, + {{UI_POS_X_CENTER(10) + 8, UI_POS_Y(10)}, "SEARCH END", Theme::getInstance()->fg_light->foreground}, + {{UI_POS_X_RIGHT(6), UI_POS_Y(10)}, "SWITCH", Theme::getInstance()->fg_light->foreground}, - {{0 * 8, (26 * 8) + 4}, "MODE:", Theme::getInstance()->fg_light->foreground}, + {{UI_POS_X(0), (26 * 8) + 4}, "MODE:", Theme::getInstance()->fg_light->foreground}, {{11 * 8, (26 * 8) + 4}, "STEP:", Theme::getInstance()->fg_light->foreground}, }; LNAGainField field_lna{ - {4 * 8, 0 * 16}}; + {UI_POS_X(4), UI_POS_Y(0)}}; VGAGainField field_vga{ - {11 * 8, 0 * 16}}; + {UI_POS_X(11), UI_POS_Y(0)}}; RFAmpField field_rf_amp{ - {18 * 8, 0 * 16}}; + {UI_POS_X(18), UI_POS_Y(0)}}; AudioVolumeField field_volume{ - {24 * 8, 0 * 16}}; + {UI_POS_X_RIGHT(2), UI_POS_Y(0)}}; OptionsField field_bw{ - {3 * 8, 1 * 16}, + {UI_POS_X(3), UI_POS_Y(1)}, 6, {}}; NumberField field_squelch{ - {13 * 8, 1 * 16}, + {UI_POS_X(13), UI_POS_Y(1)}, 3, {-90, 20}, 1, @@ -208,7 +212,7 @@ class ScannerView : public View { NumberField field_browse_wait{ // Signal-Active wait timer - time to wait before moving on even when signal locked - {21 * 8, 1 * 16}, + {UI_POS_X_RIGHT(9), UI_POS_Y(1)}, 2, {0, 99}, 1, @@ -217,7 +221,7 @@ class ScannerView : public View { NumberField field_lock_wait{ // Signal-Lost wait timer - time to wait before moving on after losing signal lock - {screen_width - 2 * 8, 1 * 16}, + {UI_POS_X_RIGHT(2), UI_POS_Y(1)}, 2, {0, 99}, 1, @@ -225,20 +229,20 @@ class ScannerView : public View { }; RSSI rssi{ - {0 * 16, 2 * 16, 15 * 16, 8}, + {UI_POS_X(0), UI_POS_Y(2), UI_POS_WIDTH_REMAINING(6), 8}, }; TextField field_current_index{ - {0, 3 * 16, 3 * 8, 16}, + {UI_POS_X(0), UI_POS_Y(3), 3 * 8, 16}, {}, }; Text text_max_index{ - {4 * 8, 3 * 16, 18 * 8, 16}, + {UI_POS_X(4), UI_POS_Y(3), 18 * 8, 16}, }; Text text_current_desc{ - {0, 4 * 16, screen_width - 6 * 8, 16}, + {UI_POS_X(0), UI_POS_Y(4), UI_POS_WIDTH_REMAINING(6), UI_POS_HEIGHT(1)}, }; BigFrequency big_display{ @@ -246,15 +250,15 @@ class ScannerView : public View { 0}; Button button_manual_start{ - {0 * 8, 11 * 16, 11 * 8, 28}, + {UI_POS_X(0), UI_POS_Y(11), UI_POS_WIDTH(10), 28}, ""}; Button button_manual_end{ - {12 * 8, 11 * 16, 11 * 8, 28}, + {UI_POS_X_CENTER(10) + 8, UI_POS_Y(11), UI_POS_WIDTH(10), 28}, ""}; Button button_manual_search{ - {24 * 8, 11 * 16, 6 * 8, 28}, + {UI_POS_X_RIGHT(6), UI_POS_Y(11), UI_POS_WIDTH(6), 28}, ""}; OptionsField field_mode{ @@ -273,34 +277,34 @@ class ScannerView : public View { {0, (15 * 16) - 4, 72, 28}, ""}; + Button button_audio_app{ + {UI_POS_X_CENTER(9), (15 * 16) - 4, UI_POS_WIDTH(9), 28}, + "AUDIO"}; + + Button button_add{ + {UI_POS_X_RIGHT(9), (15 * 16) - 4, UI_POS_WIDTH(9), 28}, + "ADD FQ"}; + Button button_dir{ {0, (35 * 8) - 4, 72, 28}, "REVERSE"}; - Button button_audio_app{ - {84, (15 * 16) - 4, 72, 28}, - "AUDIO"}; - Button button_mic_app{ - {84, (35 * 8) - 4, 72, 28}, + {UI_POS_X_CENTER(9), (35 * 8) - 4, UI_POS_WIDTH(9), 28}, "MIC TX"}; - Button button_add{ - {168, (15 * 16) - 4, 72, 28}, - "ADD FQ"}; + Button button_remove{ + {UI_POS_X_RIGHT(9), (35 * 8) - 4, UI_POS_WIDTH(9), 28}, + "DEL FQ"}; Button button_load{ - {24 * 8, 3 * 16 - 10, 6 * 8, 22}, + {UI_POS_X_RIGHT(6), UI_POS_Y(3) - 10, UI_POS_WIDTH(6), 22}, "LOAD"}; Button button_clear{ - {24 * 8, 4 * 16, 6 * 8, 22}, + {UI_POS_X_RIGHT(6), UI_POS_Y(4), UI_POS_WIDTH(6), 22}, "MCLR"}; - Button button_remove{ - {168, (35 * 8) - 4, 72, 28}, - "DEL FQ"}; - std::unique_ptr scan_thread{}; MessageHandlerRegistration message_handler_retune{ diff --git a/firmware/application/external/shoppingcart_lock/shoppingcart_lock.hpp b/firmware/application/external/shoppingcart_lock/shoppingcart_lock.hpp index 8bbb54e5e..cea91d435 100644 --- a/firmware/application/external/shoppingcart_lock/shoppingcart_lock.hpp +++ b/firmware/application/external/shoppingcart_lock/shoppingcart_lock.hpp @@ -62,19 +62,19 @@ class ShoppingCartLock : public View { true}; Text text_empty{ - {40, 70, 160, 16}, + {UI_POS_X_CENTER(20), 70, 160, 16}, "RocketGod was here"}; Button button_lock{ - {40, 165, 160, 35}, + {UI_POS_X_CENTER(20), 165, 160, 35}, LanguageHelper::currentMessages[LANG_LOCK]}; Button button_unlock{ - {40, 205, 160, 35}, + {UI_POS_X_CENTER(20), 205, 160, 35}, LanguageHelper::currentMessages[LANG_UNLOCK]}; Button button_stop{ - {40, 245, 160, 35}, + {UI_POS_X_CENTER(20), 245, 160, 35}, LanguageHelper::currentMessages[LANG_STOP]}; MessageHandlerRegistration message_handler_fifo_signal{ diff --git a/firmware/application/external/snake/ui_snake.cpp b/firmware/application/external/snake/ui_snake.cpp index 5e9f3ca4a..2b62e9f77 100644 --- a/firmware/application/external/snake/ui_snake.cpp +++ b/firmware/application/external/snake/ui_snake.cpp @@ -175,7 +175,7 @@ void SnakeView::erase_food() { void SnakeView::draw_score() { auto style = *ui::Theme::getInstance()->fg_blue; - painter.draw_string({5, 5}, style, "Score: " + std::to_string(score)); + painter.draw_string({UI_POS_X_CENTER(11), 5}, style, "Score: " + std::to_string(score)); } void SnakeView::draw_borders() { @@ -189,11 +189,11 @@ void SnakeView::show_menu() { auto style_yellow = *ui::Theme::getInstance()->fg_yellow; auto style_green = *ui::Theme::getInstance()->fg_green; auto style_blue = *ui::Theme::getInstance()->fg_blue; - painter.draw_string({50, 40}, style_yellow, "* * * SNAKE * * *"); - painter.draw_string({0, 120}, style_blue, "USE THE D-PAD TO MOVE"); - painter.draw_string({0, 150}, style_blue, "EAT THE RED SQUARES TO GROW"); - painter.draw_string({0, 180}, style_blue, "DON'T HIT THE WALLS OR SELF"); - painter.draw_string({15, 240}, style_green, "** PRESS SELECT TO START **"); + painter.draw_string({UI_POS_X_CENTER(18), 40}, style_yellow, "* * * SNAKE * * *"); + painter.draw_string({UI_POS_X_CENTER(22), 120}, style_blue, "USE THE D-PAD TO MOVE"); + painter.draw_string({UI_POS_X_CENTER(28), 150}, style_blue, "EAT THE RED SQUARES TO GROW"); + painter.draw_string({UI_POS_X_CENTER(28), 180}, style_blue, "DON'T HIT THE WALLS OR SELF"); + painter.draw_string({UI_POS_X_CENTER(28), 240}, style_green, "** PRESS SELECT TO START **"); } void SnakeView::show_game_over() { @@ -202,9 +202,9 @@ void SnakeView::show_game_over() { auto style_red = *ui::Theme::getInstance()->fg_red; auto style_yellow = *ui::Theme::getInstance()->fg_yellow; auto style_green = *ui::Theme::getInstance()->fg_green; - painter.draw_string({75, 90}, style_red, "GAME OVER"); - painter.draw_string({74, 150}, style_yellow, "SCORE: " + std::to_string(score)); - painter.draw_string({20, 220}, style_green, "PRESS SELECT TO RESTART"); + painter.draw_string({UI_POS_X_CENTER(10), 90}, style_red, "GAME OVER"); + painter.draw_string({UI_POS_X_CENTER(11), 150}, style_yellow, "SCORE: " + std::to_string(score)); + painter.draw_string({UI_POS_X_CENTER(24), 220}, style_green, "PRESS SELECT TO RESTART"); wait(1); } diff --git a/firmware/application/external/soundboard/soundboard_app.hpp b/firmware/application/external/soundboard/soundboard_app.hpp index fd69c9f04..57e8c2c57 100644 --- a/firmware/application/external/soundboard/soundboard_app.hpp +++ b/firmware/application/external/soundboard/soundboard_app.hpp @@ -95,25 +95,25 @@ class SoundBoardView : public View { void update_config(); Labels labels{ - {{24 * 8, 180}, "Vol:", Theme::getInstance()->fg_light->foreground}, - {{0, 180}, "Key:", Theme::getInstance()->fg_light->foreground}}; + {{24 * 8, UI_POS_Y_BOTTOM(9)}, "Vol:", Theme::getInstance()->fg_light->foreground}, + {{0, UI_POS_Y_BOTTOM(9)}, "Key:", Theme::getInstance()->fg_light->foreground}}; Button button_next_page{ - {30 * 7, 25 * 8, 10 * 3, 2 * 14}, + {UI_POS_X_RIGHT(4), UI_POS_Y_BOTTOM(8), 10 * 3, 2 * 14}, "=>"}; Button button_prev_page{ - {17 * 10, 25 * 8, 10 * 3, 2 * 14}, + {UI_POS_X_RIGHT(8), UI_POS_Y_BOTTOM(8), 10 * 3, 2 * 14}, "<="}; Text page_info{ - {0, 29 * 8, screen_width, 16}}; + {0, UI_POS_Y_BOTTOM(6), screen_width, 16}}; MenuView menu_view{ - {0, 0, screen_width, 175}, + {0, 0, screen_width, UI_POS_Y_BOTTOM(9)}, true}; Text text_empty{ - {7 * 8, 12 * 8, 16 * 8, 16}, + {UI_POS_X_CENTER(16), 12 * 8, 16 * 8, 16}, "Empty directory !", }; @@ -126,31 +126,31 @@ class SoundBoardView : public View { };*/ OptionsField options_tone_key{ - {4 * 8, 180}, + {4 * 8, UI_POS_Y_BOTTOM(9)}, 18, {}}; AudioVolumeField field_volume{ - {screen_width - 2 * 8, 180}}; + {screen_width - 2 * 8, UI_POS_Y_BOTTOM(9)}}; Text text_volume_disabled{ - {screen_width - 2 * 8, 180, 3 * 8, 16}, + {screen_width - 2 * 8, UI_POS_Y_BOTTOM(9), 3 * 8, 16}, "--"}; Checkbox check_loop{ - {0, 25 * 8 + 4}, + {0, UI_POS_Y_BOTTOM(8)}, 4, "Loop"}; Checkbox check_random{ - {10 * 7, 25 * 8 + 4}, + {10 * 7, UI_POS_Y_BOTTOM(8)}, 6, "Random"}; ProgressBar progressbar{ - {0 * 8, 31 * 8 + 2, screen_width, 4}}; + {UI_POS_X(0), UI_POS_Y_BOTTOM(4.5) + 2, screen_width, 4}}; TransmitterView tx_view{ - 16 * 16, + (int16_t)UI_POS_Y_BOTTOM(4), 5000, 12}; diff --git a/firmware/application/external/spainter/ui_spectrum_painter.cpp b/firmware/application/external/spainter/ui_spectrum_painter.cpp index 90b13c7da..09423a268 100644 --- a/firmware/application/external/spainter/ui_spectrum_painter.cpp +++ b/firmware/application/external/spainter/ui_spectrum_painter.cpp @@ -183,20 +183,23 @@ void SpectrumPainterView::focus() { void SpectrumPainterView::paint(Painter& painter) { View::paint(painter); - size_t c; - Point pos = {0, screen_pos().y() + 8 + footer_location}; - - for (c = 0; c < 20; c++) { + uint16_t c; + Point pos = {0, UI_POS_Y_BOTTOM(1)}; + Point pos2 = {0, UI_POS_Y_BOTTOM(4)}; + // 24*8 pxstripes + for (c = 0; c < screen_width / 24 + 1; c++) { painter.draw_bitmap( pos, bitmap_stripes, Theme::getInstance()->fg_yellow->foreground, Theme::getInstance()->fg_yellow->background); - if (c != 9) - pos += {24, 0}; - else - pos = {0, screen_pos().y() + 8 + footer_location + 32 + 8}; + painter.draw_bitmap( + pos2, + bitmap_stripes, + Theme::getInstance()->fg_yellow->foreground, + Theme::getInstance()->fg_yellow->background); + pos += {24, 0}; + pos2 += {24, 0}; } } - } // namespace ui::external_app::spainter diff --git a/firmware/application/external/spainter/ui_spectrum_painter.hpp b/firmware/application/external/spainter/ui_spectrum_painter.hpp index 4efde91ed..d6c98f89a 100644 --- a/firmware/application/external/spainter/ui_spectrum_painter.hpp +++ b/firmware/application/external/spainter/ui_spectrum_painter.hpp @@ -89,57 +89,57 @@ class SpectrumPainterView : public View { static constexpr int32_t footer_location = 15 * 16 + 8; ProgressBar progressbar{ - {4, footer_location - 16, screen_width - 8, 16}}; + {4, UI_POS_Y_BOTTOM(6) - 3, screen_width - 8, 16}}; Labels labels{ - {{10 * 8, footer_location + 1 * 16}, "GAIN A:", Theme::getInstance()->fg_light->foreground}, - {{1 * 8, footer_location + 2 * 16}, "BW: Du: P:", Theme::getInstance()->fg_light->foreground}, + {{10 * 8, UI_POS_Y_BOTTOM(4)}, "GAIN A:", Theme::getInstance()->fg_light->foreground}, + {{1 * 8, UI_POS_Y_BOTTOM(3)}, "BW: Du: P:", Theme::getInstance()->fg_light->foreground}, }; TxFrequencyField field_frequency{ - {0 * 8, footer_location + 1 * 16}, + {UI_POS_X(0), UI_POS_Y_BOTTOM(4)}, nav_}; NumberField field_rfgain{ - {14 * 8, footer_location + 1 * 16}, + {14 * 8, UI_POS_Y_BOTTOM(4)}, 2, {0, 47}, 1, ' '}; NumberField field_rfamp{ - {19 * 8, footer_location + 1 * 16}, + {19 * 8, UI_POS_Y_BOTTOM(4)}, 2, {0, 14}, 14, ' '}; Checkbox check_loop{ - {21 * 8, footer_location + 1 * 16}, + {21 * 8, UI_POS_Y_BOTTOM(4)}, 4, LanguageHelper::currentMessages[LANG_LOOP], true}; ImageButton button_play{ - {screen_width - 2 * 8, footer_location + 1 * 16, 2 * 8, 1 * 16}, + {screen_width - 2 * 8, UI_POS_Y_BOTTOM(4), 2 * 8, 1 * 16}, &bitmap_play, Theme::getInstance()->fg_green->foreground, Theme::getInstance()->fg_green->background}; OptionsField option_bandwidth{ - {4 * 8, footer_location + 2 * 16}, + {4 * 8, UI_POS_Y_BOTTOM(3)}, 5, {}}; NumberField field_duration{ - {13 * 8, footer_location + 2 * 16}, + {13 * 8, UI_POS_Y_BOTTOM(3)}, 3, {1, 999}, 1, ' '}; NumberField field_pause{ - {19 * 8, footer_location + 2 * 16}, + {19 * 8, UI_POS_Y_BOTTOM(3)}, 2, {0, 99}, 1, diff --git a/firmware/application/external/spainter/ui_spectrum_painter_image.hpp b/firmware/application/external/spainter/ui_spectrum_painter_image.hpp index 2b0e2ee64..ae1a6d8ca 100644 --- a/firmware/application/external/spainter/ui_spectrum_painter_image.hpp +++ b/firmware/application/external/spainter/ui_spectrum_painter_image.hpp @@ -58,7 +58,7 @@ class SpectrumInputImageView : public View { uint32_t data_start{0}; Button button_load_image{ - {0 * 8, 11 * 16 - 4, screen_width, 28}, + {UI_POS_X(0), UI_POS_Y_BOTTOM(10), screen_width, 28}, "Load Image ..."}; bool drawBMP_scaled(const ui::Rect r, const std::string file); diff --git a/firmware/application/external/spainter/ui_spectrum_painter_text.hpp b/firmware/application/external/spainter/ui_spectrum_painter_text.hpp index f4c91a2f3..d3c270d4f 100644 --- a/firmware/application/external/spainter/ui_spectrum_painter_text.hpp +++ b/firmware/application/external/spainter/ui_spectrum_painter_text.hpp @@ -53,43 +53,43 @@ class SpectrumInputTextView : public View { void on_set_text(NavigationView& nav); Text text_message_0{ - {0 * 8, 0 * 16, screen_width, 16}, + {UI_POS_X(0), UI_POS_Y(0), screen_width, 16}, ""}; Text text_message_1{ - {0 * 8, 1 * 16, screen_width, 16}, + {UI_POS_X(0), 1 * 16, screen_width, 16}, ""}; Text text_message_2{ - {0 * 8, 2 * 16, screen_width, 16}, + {UI_POS_X(0), 2 * 16, screen_width, 16}, ""}; Text text_message_3{ - {0 * 8, 3 * 16, screen_width, 16}, + {UI_POS_X(0), 3 * 16, screen_width, 16}, ""}; Text text_message_4{ - {0 * 8, 4 * 16, screen_width, 16}, + {UI_POS_X(0), 4 * 16, screen_width, 16}, ""}; Text text_message_5{ - {0 * 8, 5 * 16, screen_width, 16}, + {UI_POS_X(0), 5 * 16, screen_width, 16}, ""}; Text text_message_6{ - {0 * 8, 6 * 16, screen_width, 16}, + {UI_POS_X(0), 6 * 16, screen_width, 16}, ""}; Text text_message_7{ - {0 * 8, 7 * 16, screen_width, 16}, + {UI_POS_X(0), 7 * 16, screen_width, 16}, ""}; Text text_message_8{ - {0 * 8, 8 * 16, screen_width, 16}, + {UI_POS_X(0), 8 * 16, screen_width, 16}, ""}; Text text_message_9{ - {0 * 8, 9 * 16, screen_width, 16}, + {UI_POS_X(0), 9 * 16, screen_width, 16}, ""}; std::array text_message{{ @@ -106,7 +106,7 @@ class SpectrumInputTextView : public View { }}; Button button_message{ - {0 * 8, 11 * 16 - 4, screen_width, 28}, + {UI_POS_X(0), UI_POS_Y_BOTTOM(10), screen_width, 28}, "Set message"}; }; diff --git a/firmware/application/external/sstvtx/ui_sstvtx.hpp b/firmware/application/external/sstvtx/ui_sstvtx.hpp index f28c49617..7206817df 100644 --- a/firmware/application/external/sstvtx/ui_sstvtx.hpp +++ b/firmware/application/external/sstvtx/ui_sstvtx.hpp @@ -93,10 +93,10 @@ class SSTVTXView : public View { {}}; ProgressBar progressbar{ - {16, 25 * 8, 208, 16}}; + {16, 25 * 8, UI_POS_WIDTH_REMAINING(4), 16}}; TransmitterView tx_view{ - 16 * 16, + (int16_t)UI_POS_Y_BOTTOM(4), 10000, 12}; diff --git a/firmware/application/external/stopwatch/ui_stopwatch.hpp b/firmware/application/external/stopwatch/ui_stopwatch.hpp index 2005fba94..7257e2e79 100644 --- a/firmware/application/external/stopwatch/ui_stopwatch.hpp +++ b/firmware/application/external/stopwatch/ui_stopwatch.hpp @@ -57,8 +57,8 @@ class StopwatchView : public View { /* m m s s ms ms ms*/ Labels labels{ - {{0 * 8, 1 * 16}, "TOTAL:", Theme::getInstance()->fg_light->foreground}, - {{0 * 8, 7 * 16}, "LAP:", Theme::getInstance()->fg_light->foreground}, + {{UI_POS_X(0), 1 * 16}, "TOTAL:", Theme::getInstance()->fg_light->foreground}, + {{UI_POS_X(0), 7 * 16}, "LAP:", Theme::getInstance()->fg_light->foreground}, }; OptionsField options_ms_display_level{ @@ -71,15 +71,15 @@ class StopwatchView : public View { Painter painter; Button button_run_stop{ - {72, 210, 96, 24}, + {UI_POS_X_CENTER(12), 210, 96, 24}, "START"}; Button button_reset_lap{ - {72, screen_height - 80, 96, 24}, + {UI_POS_X_CENTER(12), screen_height - 80, 96, 24}, "RESET"}; Button button_done{ - {72, screen_height - 50, 96, 24}, + {UI_POS_X_CENTER(12), screen_height - 50, 96, 24}, "EXIT"}; MessageHandlerRegistration message_handler_frame_sync{ diff --git a/firmware/application/external/tpmsrx/tpms_app.cpp b/firmware/application/external/tpmsrx/tpms_app.cpp index ac849e3c3..ef31ca8fa 100644 --- a/firmware/application/external/tpmsrx/tpms_app.cpp +++ b/firmware/application/external/tpmsrx/tpms_app.cpp @@ -198,8 +198,12 @@ void RecentEntriesTable::draw( const Entry& entry, const Rect& target_rect, Painter& painter, - const Style& style) { - std::string line = ui::external_app::tpmsrx::format::type(entry.type) + " " + ui::external_app::tpmsrx::format::id(entry.id); + const Style& style, + RecentEntriesColumns& columns) { + std::string line = ui::external_app::tpmsrx::format::type(entry.type) + " "; + std::string lid = ui::external_app::tpmsrx::format::id(entry.id); + lid.resize(columns.at(1).second, ' '); + line += lid; if (entry.last_pressure.is_valid()) { line += " " + ui::external_app::tpmsrx::format::pressure(entry.last_pressure.value()); diff --git a/firmware/application/external/tpmsrx/tpms_app.hpp b/firmware/application/external/tpmsrx/tpms_app.hpp index 05766441b..9a6a4bd2b 100644 --- a/firmware/application/external/tpmsrx/tpms_app.hpp +++ b/firmware/application/external/tpmsrx/tpms_app.hpp @@ -134,19 +134,17 @@ class TPMSAppView : public View { ui::Rect view_normal_rect{}; RSSI rssi{ - {21 * 8, 0, 6 * 8, 4}, + {UI_POS_X(21), 0, UI_POS_WIDTH_REMAINING(24), 4}, }; - - AudioVolumeField field_volume{ - {screen_width - 2 * 8, 0 * 16}}; - Channel channel{ - {21 * 8, 5, 6 * 8, 4}, + {UI_POS_X(21), 5, UI_POS_WIDTH_REMAINING(24), 4}, }; + AudioVolumeField field_volume{ + {UI_POS_X_RIGHT(2), UI_POS_Y(0)}}; // "315 MHz" TPMS sensors transmit at either 314.9 or 315 MHz but we should pick up either OptionsField options_band{ - {0 * 8, 0 * 16}, + {UI_POS_X(0), UI_POS_Y(0)}, 5, { {"314.9", 314900000}, @@ -155,32 +153,32 @@ class TPMSAppView : public View { }}; OptionsField options_pressure{ - {6 * 8, 0 * 16}, + {6 * 8, UI_POS_Y(0)}, 3, {{"kPa", 0}, {"PSI", 1}}}; OptionsField options_temperature{ - {10 * 8, 0 * 16}, + {10 * 8, UI_POS_Y(0)}, 2, {{STR_DEGREES_C, 0}, {STR_DEGREES_F, 1}}}; RFAmpField field_rf_amp{ - {13 * 8, 0 * 16}}; + {13 * 8, UI_POS_Y(0)}}; LNAGainField field_lna{ - {15 * 8, 0 * 16}}; + {15 * 8, UI_POS_Y(0)}}; VGAGainField field_vga{ - {18 * 8, 0 * 16}}; + {18 * 8, UI_POS_Y(0)}}; TPMSRecentEntries recent{}; std::unique_ptr logger{}; - const RecentEntriesColumns columns{{ + RecentEntriesColumns columns{{ {"Tp", 2}, - {"ID", 8}, + {"ID", 0}, {"Pres", 4}, {"Temp", 4}, {"Cnt", 3}, diff --git a/firmware/application/external/tuner/ui_tuner.hpp b/firmware/application/external/tuner/ui_tuner.hpp index 54f6e81cf..3a31840b3 100644 --- a/firmware/application/external/tuner/ui_tuner.hpp +++ b/firmware/application/external/tuner/ui_tuner.hpp @@ -69,11 +69,11 @@ class TunerView : public View { bool current_note_playing{false}; Labels labels{ - {{0 * 8, 1 * 16}, "Instrument:", Theme::getInstance()->fg_light->foreground}, - {{0 * 8, 2 * 16}, "Note:", Theme::getInstance()->fg_light->foreground}, - {{0 * 8, 3 * 16}, "Note Frequency:", Theme::getInstance()->fg_light->foreground}, - {{0 * 8, 4 * 16}, "Note Octave Shift:", Theme::getInstance()->fg_light->foreground}, - {{0 * 8, 5 * 16}, "Volume:", Theme::getInstance()->fg_light->foreground}}; + {{UI_POS_X(0), 1 * 16}, "Instrument:", Theme::getInstance()->fg_light->foreground}, + {{UI_POS_X(0), 2 * 16}, "Note:", Theme::getInstance()->fg_light->foreground}, + {{UI_POS_X(0), 3 * 16}, "Note Frequency:", Theme::getInstance()->fg_light->foreground}, + {{UI_POS_X(0), 4 * 16}, "Note Octave Shift:", Theme::getInstance()->fg_light->foreground}, + {{UI_POS_X(0), 5 * 16}, "Volume:", Theme::getInstance()->fg_light->foreground}}; Text text_note_frequency{ {(sizeof("Note Frequency:") + 1) * 8, 3 * 16, (int)screen_width - (int)(sizeof("Note Frequency:") + 1) * 8, 16}, @@ -102,7 +102,7 @@ class TunerView : public View { {}}; NewButton button_play_stop{ - {0 * 8, 16 * 16, screen_width, screen_height - 16 * 16}, + {UI_POS_X(0), 16 * 16, screen_width, screen_height - 16 * 16}, {}, &bitmap_icon_replay, Theme::getInstance()->fg_red->foreground}; diff --git a/firmware/application/external/wardrivemap/ui_wardrivemap.hpp b/firmware/application/external/wardrivemap/ui_wardrivemap.hpp index 6d7608583..f4270d1a6 100644 --- a/firmware/application/external/wardrivemap/ui_wardrivemap.hpp +++ b/firmware/application/external/wardrivemap/ui_wardrivemap.hpp @@ -48,16 +48,16 @@ class WardriveMapView : public View { const std::filesystem::path flippersub_dir = u"subghz"; NavigationView& nav_; - Text text_info{{0 * 8, 0 * 8, 20 * 8, 16 * 1}, "0 / 30"}; - Text text_notfound{{0 * 8, 0 * 8, screen_width, 16 * 1}, "No GeoTagged captures found"}; + Text text_info{{UI_POS_X(0), 0 * 8, 20 * 8, 16 * 1}, "0 / 30"}; + Text text_notfound{{UI_POS_X(0), 0 * 8, screen_width, 16 * 1}, "No GeoTagged captures found"}; GeoPos geopos{ {0, 20}, GeoPos::alt_unit::METERS, GeoPos::spd_unit::HIDDEN}; GeoMap geomap{{0, 75, screen_width, screen_height - 75}}; - Button btn_back{{22 * 8, 0 * 8, 3 * 8, 16}, "<-"}; - Button btn_next{{26 * 8, 0 * 8, 3 * 8, 16}, "->"}; + Button btn_back{{UI_POS_X_RIGHT(8), 0 * 8, 3 * 8, 16}, "<-"}; + Button btn_next{{UI_POS_X_RIGHT(4), 0 * 8, 3 * 8, 16}, "->"}; void on_gps(const GPSPosDataMessage* msg); void on_orientation(const OrientationDataMessage* msg); diff --git a/firmware/application/external/wav_view/ui_view_wav.cpp b/firmware/application/external/wav_view/ui_view_wav.cpp index 30a8b34f5..bbf9bf2e6 100644 --- a/firmware/application/external/wav_view/ui_view_wav.cpp +++ b/firmware/application/external/wav_view/ui_view_wav.cpp @@ -49,26 +49,26 @@ void ViewWavView::refresh_waveform() { uint8_t bits_per_sample = wav_reader->bits_per_sample(); - for (size_t i = 0; i < 240; i++) { + for (size_t i = 0; i < screen_width; i++) { wav_reader->data_seek(position + (i * scale)); if (bits_per_sample == 8) { uint8_t sample; wav_reader->read(&sample, 1); waveform_buffer[i] = (sample - 0x80) * 256; } else { - wav_reader->read(&waveform_buffer[i], 2); + wav_reader->read(&waveform_buffer.data()[i], 2); } } waveform.set_dirty(); // Window - uint64_t w_start = (position * 240) / wav_reader->sample_count(); - uint64_t w_width = (scale * 240) / (wav_reader->sample_count() / 240); - display.fill_rectangle({0, 10 * 16 + 1, 240, 16}, Theme::getInstance()->bg_darkest->background); + uint64_t w_start = (position * screen_width) / wav_reader->sample_count(); + uint64_t w_width = (scale * screen_width) / (wav_reader->sample_count() / screen_width); + display.fill_rectangle({0, 10 * 16 + 1, screen_width, 16}, Theme::getInstance()->bg_darkest->background); display.fill_rectangle({(Coord)w_start, 21 * 8, (Dim)w_width + 1, 8}, Theme::getInstance()->bg_darkest->foreground); display.draw_line({0, 10 * 16 + 1}, {(Coord)w_start, 21 * 8}, Theme::getInstance()->bg_darkest->foreground); - display.draw_line({239, 10 * 16 + 1}, {(Coord)(w_start + w_width), 21 * 8}, Theme::getInstance()->bg_darkest->foreground); + display.draw_line({screen_width - 1, 10 * 16 + 1}, {(Coord)(w_start + w_width), 21 * 8}, Theme::getInstance()->bg_darkest->foreground); } void ViewWavView::refresh_measurements() { @@ -82,11 +82,11 @@ void ViewWavView::refresh_measurements() { void ViewWavView::paint(Painter& painter) { // Waveform limits - painter.draw_hline({0, 6 * 16 - 1}, 240, Theme::getInstance()->bg_medium->background); - painter.draw_hline({0, 10 * 16}, 240, Theme::getInstance()->bg_medium->background); + painter.draw_hline({0, 6 * 16 - 1}, screen_width, Theme::getInstance()->bg_medium->background); + painter.draw_hline({0, 10 * 16}, screen_width, Theme::getInstance()->bg_medium->background); // Overall amplitude view, 0~127 to 0~255 color index - for (size_t i = 0; i < 240; i++) + for (size_t i = 0; i < screen_width; i++) painter.draw_vline({(Coord)i, 11 * 16}, 8, spectrum_rgb2_lut[amplitude_buffer[i] << 1]); } @@ -128,10 +128,10 @@ void ViewWavView::load_wav(std::filesystem::path file_path) { text_title.set(wav_reader->title()); // Fill amplitude buffer, world's worst downsampling - uint64_t skip = wav_reader->sample_count() / (240 * subsampling_factor); + uint64_t skip = wav_reader->sample_count() / (screen_width * subsampling_factor); uint8_t bits_per_sample = wav_reader->bits_per_sample(); - for (size_t i = 0; i < 240; i++) { + for (size_t i = 0; i < screen_width; i++) { average = 0; for (size_t s = 0; s < subsampling_factor; s++) { @@ -163,7 +163,7 @@ void ViewWavView::reset_controls() { field_pos_seconds.set_range(0, wav_reader->ms_duration() / 1000); field_pos_milliseconds.set_range(0, (wav_reader->ms_duration() < 1000) ? wav_reader->ms_duration() % 1000 : 999); field_pos_samples.set_range(0, wav_reader->sample_count() - 1); - field_scale.set_range(1, std::min(99999ul, wav_reader->sample_count() / 240)); + field_scale.set_range(1, std::min(99999ul, wav_reader->sample_count() / screen_width)); } bool ViewWavView::is_active() { @@ -262,6 +262,12 @@ void ViewWavView::on_playback_progress(const uint32_t progress) { ViewWavView::ViewWavView( NavigationView& nav) : nav_(nav) { + waveform_buffer.resize(screen_width); + amplitude_buffer.resize(screen_width); + waveform.set_length(screen_width); + waveform.set_data((int16_t*)waveform_buffer.data()); + for (auto& v : waveform_buffer) v = 0; + for (auto& v : amplitude_buffer) v = 0; baseband::run_prepared_image(portapack::memory::map::m4_code.base()); wav_reader = std::make_unique(); diff --git a/firmware/application/external/wav_view/ui_view_wav.hpp b/firmware/application/external/wav_view/ui_view_wav.hpp index a358c30db..a7fa7c624 100644 --- a/firmware/application/external/wav_view/ui_view_wav.hpp +++ b/firmware/application/external/wav_view/ui_view_wav.hpp @@ -73,8 +73,8 @@ class ViewWavView : public View { std::unique_ptr wav_reader{}; - int16_t waveform_buffer[240]{}; - uint8_t amplitude_buffer[240]{}; + std::vector waveform_buffer{}; + std::vector amplitude_buffer{}; int32_t scale{1}; uint64_t ns_per_pixel{}; uint64_t position{}; @@ -83,53 +83,54 @@ class ViewWavView : public View { bool waveform_update_needed{false}; Labels labels{ - {{0 * 8, 0 * 16}, "File:", Theme::getInstance()->fg_light->foreground}, + {{UI_POS_X(0), UI_POS_Y(0)}, "File:", Theme::getInstance()->fg_light->foreground}, {{2 * 8, 1 * 16}, "-bit mono", Theme::getInstance()->fg_light->foreground}, - {{0 * 8, 2 * 16}, "Title:", Theme::getInstance()->fg_light->foreground}, - {{0 * 8, 3 * 16}, "Duration:", Theme::getInstance()->fg_light->foreground}, - {{0 * 8, 12 * 16}, "Position: . s Scale:", Theme::getInstance()->fg_light->foreground}, - {{0 * 8, 13 * 16}, " Sample:", Theme::getInstance()->fg_light->foreground}, - {{0 * 8, 14 * 16}, "Cursor A:", Theme::getInstance()->fg_darkcyan->foreground}, - {{0 * 8, 15 * 16}, "Cursor B:", Theme::getInstance()->fg_magenta->foreground}, - {{0 * 8, 16 * 16}, "Delta:", Theme::getInstance()->fg_light->foreground}, - {{24 * 8, 18 * 16}, "Vol:", Theme::getInstance()->fg_light->foreground}}; + {{UI_POS_X(0), 2 * 16}, "Title:", Theme::getInstance()->fg_light->foreground}, + {{UI_POS_X(0), 3 * 16}, "Duration:", Theme::getInstance()->fg_light->foreground}, + {{UI_POS_X(0), 12 * 16}, "Position: . s Scale:", Theme::getInstance()->fg_light->foreground}, + {{UI_POS_X(0), 13 * 16}, " Sample:", Theme::getInstance()->fg_light->foreground}, + {{UI_POS_X(0), 14 * 16}, "Cursor A:", Theme::getInstance()->fg_darkcyan->foreground}, + {{UI_POS_X(0), 15 * 16}, "Cursor B:", Theme::getInstance()->fg_magenta->foreground}, + {{UI_POS_X(0), 16 * 16}, "Delta:", Theme::getInstance()->fg_light->foreground}, + {{UI_POS_X_RIGHT(6), UI_POS_Y_BOTTOM(2)}, "Vol:", Theme::getInstance()->fg_light->foreground}}; Text text_filename{ - {5 * 8, 0 * 16, 18 * 8, 16}, + {UI_POS_X(5), UI_POS_Y(0), UI_POS_WIDTH_REMAINING(12), UI_POS_HEIGHT(1)}, ""}; Text text_samplerate{ - {12 * 8, 1 * 16, 10 * 8, 16}, + {12 * 8, 1 * 16, 10 * 8, UI_POS_HEIGHT(1)}, ""}; Text text_title{ - {6 * 8, 2 * 16, 17 * 8, 16}, + {6 * 8, 2 * 16, 17 * 8, UI_POS_HEIGHT(1)}, ""}; Text text_duration{ - {9 * 8, 3 * 16, 20 * 8, 16}, + {9 * 8, 3 * 16, 20 * 8, UI_POS_HEIGHT(1)}, ""}; Text text_bits_per_sample{ - {0 * 8, 1 * 16, 2 * 8, 16}, + {UI_POS_X(0), 1 * 16, 2 * 8, UI_POS_HEIGHT(1)}, "16"}; Button button_open{ - {24 * 8, 8, 6 * 8, 2 * 16}, + {UI_POS_X_RIGHT(6), 8, UI_POS_WIDTH(6), UI_POS_HEIGHT(2)}, "Open"}; ImageButton button_play{ - {24 * 8, 17 * 16, 2 * 8, 1 * 16}, + {UI_POS_X_RIGHT(6), UI_POS_Y_BOTTOM(3), UI_POS_WIDTH(2), UI_POS_HEIGHT(1)}, &bitmap_play, Theme::getInstance()->fg_green->foreground, Theme::getInstance()->fg_green->background}; + AudioVolumeField field_volume{ - {screen_width - 2 * 8, 18 * 16}}; + {UI_POS_X_RIGHT(2), UI_POS_Y_BOTTOM(2)}}; Waveform waveform{ - {0, 5 * 16, screen_width, 64}, - waveform_buffer, + {0, UI_POS_Y(5), screen_width, UI_POS_HEIGHT(4)}, + waveform_buffer.data(), 240, 0, false, Theme::getInstance()->bg_darkest->foreground}; ProgressBar progressbar{ - {0 * 8, 11 * 16, screen_width, 4}}; + {UI_POS_X(0), 11 * 16, screen_width, 4}}; NumberField field_pos_seconds{ {9 * 8, 12 * 16}, diff --git a/firmware/application/external/wefax_rx/ui_wefax_rx.hpp b/firmware/application/external/wefax_rx/ui_wefax_rx.hpp index 36da65e2a..8e683ee7b 100644 --- a/firmware/application/external/wefax_rx/ui_wefax_rx.hpp +++ b/firmware/application/external/wefax_rx/ui_wefax_rx.hpp @@ -84,18 +84,18 @@ class WeFaxRxView : public View { }}; RFAmpField field_rf_amp{ - {13 * 8, 0 * 16}}; + {13 * 8, UI_POS_Y(0)}}; LNAGainField field_lna{ - {15 * 8, 0 * 16}}; + {15 * 8, UI_POS_Y(0)}}; VGAGainField field_vga{ - {18 * 8, 0 * 16}}; + {18 * 8, UI_POS_Y(0)}}; RSSI rssi{ - {21 * 8, 0, 6 * 8, 4}}; + {UI_POS_X(21), 0, UI_POS_WIDTH_REMAINING(24), 4}}; AudioVolumeField field_volume{ - {screen_width - 2 * 8, 0 * 16}}; + {screen_width - 2 * 8, UI_POS_Y(0)}}; RxFrequencyField field_frequency{ - {0 * 8, 0 * 16}, + {UI_POS_X(0), UI_POS_Y(0)}, nav_}; Labels labels{ @@ -124,11 +124,11 @@ class WeFaxRxView : public View { }}; Text txt_status{ - {0 * 8, 2 * 16, 20 * 8, 16}, + {UI_POS_X(0), 2 * 16, 20 * 8, 16}, }; Button button_ss{ - {190, 2 * 16, 5 * 8, 16}, + {UI_POS_X_RIGHT(6), UI_POS_Y(2), UI_POS_WIDTH(6), UI_POS_HEIGHT(1)}, LanguageHelper::currentMessages[LANG_START]}; MessageHandlerRegistration message_handler_stats{ diff --git a/firmware/application/mcuconf.h b/firmware/application/mcuconf.h old mode 100755 new mode 100644 index aef2f3417..247dad624 --- a/firmware/application/mcuconf.h +++ b/firmware/application/mcuconf.h @@ -49,10 +49,10 @@ */ #define LPC_ADC0_IRQ_PRIORITY 1 -//#define LPC_DMA_IRQ_PRIORITY 2 -//#define LPC_ADC1_IRQ_PRIORITY 3 +// #define LPC_DMA_IRQ_PRIORITY 2 +// #define LPC_ADC1_IRQ_PRIORITY 3 #define LPC43XX_GPT_TIMER0_IRQ_PRIORITY 2 -//#define LPC43XX_GPT_TIMER1_IRQ_PRIORITY 2 +// #define LPC43XX_GPT_TIMER1_IRQ_PRIORITY 2 #define LPC43XX_M0_I2C_I2C0_OR_I2C1_IRQ_PRIORITY 3 #define LPC43XX_PIN_INT4_IRQ_PRIORITY 3 @@ -62,6 +62,6 @@ #define LPC_RTC_IRQ_PRIORITY 3 #define LPC43XX_GPT_USE_TIMER0 TRUE -//#define LPC43XX_GPT_USE_TIMER1 TRUE +// #define LPC43XX_GPT_USE_TIMER1 TRUE #define LPC43XX_M4TXEVENT_IRQ_PRIORITY 3 diff --git a/firmware/application/protocols/encoders.hpp b/firmware/application/protocols/encoders.hpp index 130c2517d..f4d19ab6f 100644 --- a/firmware/application/protocols/encoders.hpp +++ b/firmware/application/protocols/encoders.hpp @@ -31,7 +31,6 @@ namespace encoders { #define ENC_TYPES_COUNT 14 #define OOK_SAMPLERATE 2280000U - #define ENCODER_UM3750 8 size_t make_bitstream(std::string& fragments); diff --git a/firmware/application/recent_entries.cpp b/firmware/application/recent_entries.cpp index 8f6abb95b..70a4b9abd 100644 --- a/firmware/application/recent_entries.cpp +++ b/firmware/application/recent_entries.cpp @@ -29,7 +29,7 @@ RecentEntriesColumns::RecentEntriesColumns( } RecentEntriesHeader::RecentEntriesHeader( - const RecentEntriesColumns& columns) + RecentEntriesColumns& columns) : _columns{columns} { } diff --git a/firmware/application/recent_entries.hpp b/firmware/application/recent_entries.hpp index b5a33fec1..8c5e35d31 100644 --- a/firmware/application/recent_entries.hpp +++ b/firmware/application/recent_entries.hpp @@ -159,12 +159,12 @@ class RecentEntriesColumns { class RecentEntriesHeader : public Widget { public: RecentEntriesHeader( - const RecentEntriesColumns& columns); + RecentEntriesColumns& columns); void paint(Painter& painter) override; private: - const RecentEntriesColumns& _columns; + RecentEntriesColumns& _columns; }; template @@ -175,8 +175,10 @@ class RecentEntriesTable : public Widget { std::function on_select{}; RecentEntriesTable( - Entries& recent) - : recent{recent} { + Entries& recent, + RecentEntriesColumns& columns) + : recent{recent}, + columns{columns} { } void paint(Painter& painter) override { @@ -199,7 +201,7 @@ class RecentEntriesTable : public Widget { const auto& entry = *p; const auto is_selected_key = (selected_key == entry.key()); const auto item_style = (has_focus() && is_selected_key) ? s.invert() : s; - draw(entry, target_rect, painter, item_style); + draw(entry, target_rect, painter, item_style, columns); target_rect += {0, target_rect.height()}; } @@ -246,7 +248,7 @@ class RecentEntriesTable : public Widget { private: Entries& recent; - + RecentEntriesColumns& columns; using EntryKey = typename Entry::Key; EntryKey selected_key = Entry::invalid_key; @@ -280,7 +282,8 @@ class RecentEntriesTable : public Widget { const Entry& entry, const Rect& target_rect, Painter& painter, - const Style& style); + const Style& style, + RecentEntriesColumns& columns); }; template @@ -291,10 +294,34 @@ class RecentEntriesView : public View { std::function on_select{}; RecentEntriesView( - const RecentEntriesColumns& columns, + RecentEntriesColumns& columns, Entries& recent) : _header{columns}, - _table{recent} { + _table{recent, columns} { + // Re calculate the widths if we got any zero-width columns (max 1). That means 'fill all the remaining space'. Only 1 col can have that + uint16_t total_width = 0; + uint8_t zero_width_count = 0; + for (const auto& column : columns) { + if (column.second == 0) { + zero_width_count++; + } else { + total_width += column.second; // char count + } + } + total_width += (columns.size() - 1); // spaces between columns + if (zero_width_count >= 1) { // we do only for the first, other 0-s will be ignored + const auto screen_width_chars = screen_width / UI_POS_DEFAULT_WIDTH; + const auto remaining_width = screen_width_chars > total_width ? screen_width_chars - total_width : 0; + uint8_t i = 0; + for (const auto& column : columns) { + if (column.second == 0) { + columns.set(i, column.first, remaining_width); + break; // only the first 0-width column gets the remaining space + } + i++; + } + } + add_children({ &_header, &_table, diff --git a/firmware/application/touch.cpp b/firmware/application/touch.cpp index 16e9c2189..3a40636f3 100644 --- a/firmware/application/touch.cpp +++ b/firmware/application/touch.cpp @@ -66,8 +66,11 @@ Metrics calculate_metrics(const Frame& frame) { } ui::Point Calibration::translate(const DigitizerPoint& p) const { - static constexpr range_t x_range{0, 240 - 1}; - static constexpr range_t y_range{0, 320 - 1}; + // static constexpr range_t x_range{0, 240 - 1}; + // static constexpr range_t y_range{0, 320 - 1}; + + static range_t x_range{0, screen_width - 1}; + static range_t y_range{0, screen_height - 1}; const int32_t x = (a * p.x + b * p.y + c) / k; const int32_t y = (d * p.x + e * p.y + f) / k; @@ -133,4 +136,4 @@ ui::Point Manager::filtered_point() const { return persistent_memory::touch_calibration().translate({filter_x.value(), filter_y.value()}); } -} /* namespace touch */ +} /* namespace touch */ \ No newline at end of file diff --git a/firmware/application/ui/ui_btngrid.cpp b/firmware/application/ui/ui_btngrid.cpp index 5d9f033cc..8ab4615c2 100644 --- a/firmware/application/ui/ui_btngrid.cpp +++ b/firmware/application/ui/ui_btngrid.cpp @@ -37,7 +37,9 @@ BtnGridView::BtnGridView( : keep_highlight{keep_highlight} { set_parent_rect(new_parent_rect); set_focusable(true); - + if (screen_height == 480) { + button_h = 64; + } button_pgup.set_focusable(false); button_pgup.on_select = [this](Button&) { if (arrow_up_enabled) { @@ -76,30 +78,11 @@ int BtnGridView::rows() { void BtnGridView::set_parent_rect(const Rect new_parent_rect) { View::set_parent_rect(new_parent_rect); - button_h = 48; // btn_h_min; - /* - // DISABLED FOR NOW. TODO fix next, prev button pos - int min_remainder = parent_rect().size().height(); - uint8_t max_button_count = 0; - - for (int h = btn_h_min; h <= btn_h_max; ++h) { - int count = parent_rect().size().height() / h; - int remainder = parent_rect().size().height() % h; - - // Prefer smaller remainder, then more buttons, then larger height - if (remainder < min_remainder || - (remainder == min_remainder && count > max_button_count) || - (remainder == min_remainder && count == max_button_count && h > button_h)) { - button_h = h; - min_remainder = remainder; - max_button_count = count; - } - } - */ + int space_available = parent_rect().size().height() - 16; // leave space for arrows displayed_max = (parent_rect().size().height() / button_h); - button_pgup.set_parent_rect({0, (Coord)(displayed_max * button_h), screen_width / 2, 16}); - button_pgdown.set_parent_rect({screen_width / 2, (Coord)(displayed_max * button_h), screen_width / 2, 16}); + button_pgup.set_parent_rect({0, (Coord)(space_available), screen_width / 2, 16}); + button_pgdown.set_parent_rect({screen_width / 2, (Coord)(space_available), screen_width / 2, 16}); displayed_max *= rows_; @@ -115,10 +98,10 @@ void BtnGridView::set_parent_rect(const Rect new_parent_rect) { for (size_t c = 0; c < displayed_max; c++) { auto item = std::make_unique(); add_child(item.get()); + item->set_vertical_center(true); item->set_parent_rect({(int)(c % rows_) * button_w, (int)(c / rows_) * button_h, button_w, button_h}); - menu_item_views.push_back(std::move(item)); } update_items(); diff --git a/firmware/application/ui/ui_btngrid.hpp b/firmware/application/ui/ui_btngrid.hpp index a10115991..a4a0be1f7 100644 --- a/firmware/application/ui/ui_btngrid.hpp +++ b/firmware/application/ui/ui_btngrid.hpp @@ -89,9 +89,8 @@ class BtnGridView : public View { bool blacklisted_app(GridItem new_item); void update_items(); - void set_btn_min_max_height(uint8_t min, uint8_t max) { - btn_h_min = min; - btn_h_max = max; + void set_btn_height_fixed(uint8_t h) { + button_h = h; } protected: @@ -99,8 +98,6 @@ class BtnGridView : public View { private: int rows_{3}; - uint8_t btn_h_min{40}; - uint8_t btn_h_max{60}; bool keep_highlight{false}; std::vector menu_items{}; diff --git a/firmware/application/ui/ui_freqlist.cpp b/firmware/application/ui/ui_freqlist.cpp index fcdc77e33..ce169e136 100644 --- a/firmware/application/ui/ui_freqlist.cpp +++ b/firmware/application/ui/ui_freqlist.cpp @@ -31,6 +31,7 @@ namespace ui { FreqManUIList::FreqManUIList(Rect parent_rect) : Widget{parent_rect}, visible_lines_{(unsigned)parent_rect.height() / char_height} { + line_max_length = (parent_rect.width() - 8) / char_width; this->set_focusable(true); } diff --git a/firmware/application/ui/ui_freqlist.hpp b/firmware/application/ui/ui_freqlist.hpp index dab751903..a29eca655 100644 --- a/firmware/application/ui/ui_freqlist.hpp +++ b/firmware/application/ui/ui_freqlist.hpp @@ -62,7 +62,7 @@ class FreqManUIList : public Widget { static constexpr int8_t char_height = 16; static constexpr int8_t char_width = 8; - static constexpr int8_t line_max_length = 29; + uint8_t line_max_length = 29; size_t visible_lines_{0}; FreqmanDB* db_{nullptr}; diff --git a/firmware/application/ui/ui_geomap.cpp b/firmware/application/ui/ui_geomap.cpp index cab77d9e3..a7cfd70a0 100644 --- a/firmware/application/ui/ui_geomap.cpp +++ b/firmware/application/ui/ui_geomap.cpp @@ -38,7 +38,7 @@ GeoPos::GeoPos( const alt_unit altitude_unit, const spd_unit speed_unit) : altitude_unit_(altitude_unit), speed_unit_(speed_unit) { - set_parent_rect({pos, {screen_width, 3 * 16}}); + set_parent_rect({pos, {screen_width, UI_POS_HEIGHT(3)}}); add_children({&labels_position, &label_spd_position, @@ -231,6 +231,8 @@ bool GeoMap::on_encoder(const EncoderEvent delta) { } void GeoMap::map_read_line_bin(ui::Color* buffer, uint16_t pixels) { + const auto r = screen_rect(); + ui::Dim width = r.width(); if (map_zoom == 1) { map_file.read(buffer, pixels << 1); } else if (map_zoom > 1) { @@ -242,7 +244,7 @@ void GeoMap::map_read_line_bin(ui::Color* buffer, uint16_t pixels) { // For 240 width, than means no check is needed for map_zoom values up to 6. // (Rectangle height must also divide evenly into map_zoom or we get black lines at end of screen) // Note that zooming in results in a map offset of (1/map_zoom) pixels to the right & downward directions (see zoom_pixel_offset). - for (int i = (geomap_rect_width / map_zoom) - 1; i >= 0; i--) { + for (int i = (width / map_zoom) - 1; i >= 0; i--) { for (int j = 0; j < map_zoom; j++) { buffer[(i * map_zoom) + j] = buffer[i]; } @@ -253,7 +255,7 @@ void GeoMap::map_read_line_bin(ui::Color* buffer, uint16_t pixels) { // Zoom out: Collapse each group of "-map_zoom" pixels into one pixel. // Future TODO: Average each group of pixels (in both X & Y directions if possible). - for (int i = 0; i < geomap_rect_width; i++) { + for (int i = 0; i < width; i++) { buffer[i] = zoom_out_buffer[i * (-map_zoom)]; } delete[] zoom_out_buffer; @@ -466,7 +468,8 @@ bool GeoMap::draw_osm_file(int zoom, int tile_x, int tile_y, int relative_x, int void GeoMap::paint(Painter& painter) { const auto r = screen_rect(); - std::array map_line_buffer; + std::vector map_line_buffer; + map_line_buffer.resize(r.width()); int16_t zoom_seek_x, zoom_seek_y; if (!use_osm) { diff --git a/firmware/application/ui/ui_geomap.hpp b/firmware/application/ui/ui_geomap.hpp index fd8347fd7..414474b21 100644 --- a/firmware/application/ui/ui_geomap.hpp +++ b/firmware/application/ui/ui_geomap.hpp @@ -42,8 +42,6 @@ namespace ui { #define INVALID_ANGLE 400 #define GEOMAP_BANNER_HEIGHT (3 * 16) -#define GEOMAP_RECT_WIDTH 240 -#define GEOMAP_RECT_HEIGHT (320 - 16 - GEOMAP_BANNER_HEIGHT) #define TILE_SIZE 256 @@ -115,31 +113,31 @@ class GeoPos : public View { spd_unit speed_unit_{}; Labels labels_position{ - {{1 * 8, 0 * 16}, "Alt:", Theme::getInstance()->fg_light->foreground}, + {{1 * 8, UI_POS_Y(0)}, "Alt:", Theme::getInstance()->fg_light->foreground}, {{1 * 8, 1 * 16}, "Lat: \xB0 ' \"", Theme::getInstance()->fg_light->foreground}, // 0xB0 is degree ° symbol in our 8x16 font {{1 * 8, 2 * 16}, "Lon: \xB0 ' \"", Theme::getInstance()->fg_light->foreground}, }; Labels label_spd_position{ - {{15 * 8, 0 * 16}, "Spd:", Theme::getInstance()->fg_light->foreground}, + {{15 * 8, UI_POS_Y(0)}, "Spd:", Theme::getInstance()->fg_light->foreground}, }; NumberField field_altitude{ - {6 * 8, 0 * 16}, + {6 * 8, UI_POS_Y(0)}, 5, {-1000, 50000}, 250, ' '}; NumberField field_speed{ - {19 * 8, 0 * 16}, + {19 * 8, UI_POS_Y(0)}, 4, {0, 5000}, 1, ' '}; Text text_alt_unit{ - {12 * 8, 0 * 16, 2 * 8, 16}, + {12 * 8, UI_POS_Y(0), 2 * 8, 16}, ""}; Text text_speed_unit{ - {25 * 8, 0 * 16, 5 * 8, 16}, + {25 * 8, UI_POS_Y(0), 5 * 8, 16}, ""}; NumberField field_lat_degrees{ @@ -243,10 +241,6 @@ class GeoMap : public Widget { void clear_markers(); MapMarkerStored store_marker(GeoMarker& marker); - static const Dim banner_height = GEOMAP_BANNER_HEIGHT; - static const Dim geomap_rect_width = GEOMAP_RECT_WIDTH; - static const Dim geomap_rect_height = GEOMAP_RECT_HEIGHT; - private: void draw_scale(Painter& painter); ui::Point item_rect_pixel(GeoMarker& item); @@ -370,7 +364,7 @@ class GeoMapView : public View { speed_unit_}; GeoMap geomap{ - {0, GeoMap::banner_height, GeoMap::geomap_rect_width, GeoMap::geomap_rect_height}}; + {0, GEOMAP_BANNER_HEIGHT, screen_width, screen_height - 16 - GEOMAP_BANNER_HEIGHT}}; Button button_ok{ {screen_width - 15 * 8, 0, 15 * 8, 1 * 16}, diff --git a/firmware/application/ui/ui_qrcode.cpp b/firmware/application/ui/ui_qrcode.cpp index d1add400d..25fea3a3a 100644 --- a/firmware/application/ui/ui_qrcode.cpp +++ b/firmware/application/ui/ui_qrcode.cpp @@ -55,7 +55,7 @@ QRCodeImage& QRCodeImage::operator=(const QRCodeImage& Image) { void QRCodeImage::paint(Painter& painter) { (void)painter; - + auto r = screen_rect(); // The structure to manage the QR code QRCode qrcode; @@ -66,12 +66,12 @@ void QRCodeImage::paint(Painter& painter) { qrcode_initText(&qrcode, qrcodeBytes, qr_version, ECC_HIGH, qr_text_); - display.fill_rectangle(Rect(57, 65, 126, 127), Color::white()); + display.fill_rectangle(Rect(r.left(), r.top(), 126, 127), Color::white()); // 57,63 for (uint8_t y = 0; y < qrcode.size; y++) { for (uint8_t x = 0; x < qrcode.size; x++) { if (qrcode_getModule(&qrcode, x, y)) { - display.fill_rectangle(Rect(63 + (x * 2), 71 + (y * 2), 2, 2), Color::black()); + display.fill_rectangle(Rect((r.left() + 6) + (x * 2), (r.top() + 8) + (y * 2), 2, 2), Color::black()); } } } diff --git a/firmware/application/ui/ui_qrcode.hpp b/firmware/application/ui/ui_qrcode.hpp index 6cd138eb9..90d9b5c11 100644 --- a/firmware/application/ui/ui_qrcode.hpp +++ b/firmware/application/ui/ui_qrcode.hpp @@ -69,7 +69,7 @@ class QRCodeView : public View { std::function on_close_{nullptr}; QRCodeImage qr_code{ - {50, 100, 100, 100}}; + {UI_POS_X_CENTER(16), 63, 100, 100}}; // Text text_qr { // { 0 * 8, 10 * 16, 32 * 8, 1 * 8 }, @@ -77,7 +77,7 @@ class QRCodeView : public View { // }; Button button_close{ - {9 * 8, 31 * 8, 12 * 8, 3 * 16}, + {UI_POS_X_CENTER(12), 31 * 8, 12 * 8, 3 * 16}, "Back"}; }; diff --git a/firmware/application/ui/ui_receiver.cpp b/firmware/application/ui/ui_receiver.cpp index 4c3d2f494..429ee7e17 100644 --- a/firmware/application/ui/ui_receiver.cpp +++ b/firmware/application/ui/ui_receiver.cpp @@ -256,6 +256,7 @@ bool FrequencyKeypadView::on_encoder(const EncoderEvent delta) { FrequencyKeypadView::FrequencyKeypadView( NavigationView& nav, const rf::Frequency value) { + button_w = screen_width / 3; add_child(&text_value); const auto button_fn = [this](Button& button) { diff --git a/firmware/application/ui/ui_receiver.hpp b/firmware/application/ui/ui_receiver.hpp index 6bba24955..084d0056d 100644 --- a/firmware/application/ui/ui_receiver.hpp +++ b/firmware/application/ui/ui_receiver.hpp @@ -222,7 +222,7 @@ class FrequencyKeypadView : public View { private: int16_t focused_button = 0; - static constexpr int button_w = 240 / 3; + int button_w = 240 / 3; static constexpr int button_h = 48; static constexpr int mhz_digits = 4; @@ -232,33 +232,33 @@ class FrequencyKeypadView : public View { static constexpr int text_digits = mhz_digits + 1 + submhz_digits; Text text_value{ - {0, 4, screen_width, 16}}; + {UI_POS_X(0), 4, screen_width, 16}}; std::array buttons{}; Button button_save_ghz{ - {0, 14 * 16, 80 / 2 - 1, 2 * 16}, + {UI_POS_X(0), 14 * 16, UI_POS_WIDTH_PERCENT(16) + 1, UI_POS_HEIGHT(2)}, "GHz"}; Button button_save_khz{ - {0 + 40 - 1, 14 * 16, 80 / 2, 2 * 16}, + {UI_POS_X(0) + UI_POS_WIDTH_PERCENT(16) + 1, 14 * 16, UI_POS_WIDTH_PERCENT(16) + 2, UI_POS_HEIGHT(2)}, "kHz"}; Button button_save_mhz{ - {0, 16 * 16, 80 - 1, 3 * 16}, + {UI_POS_X(0), 16 * 16, UI_POS_WIDTH_PERCENT(33), UI_POS_HEIGHT(3)}, "Save MHz"}; Button button_load{ - {80 + 1, 14 * 16, 80 - 2, 40}, + {UI_POS_WIDTH_PERCENT(33) + 1, 14 * 16, UI_POS_WIDTH_PERCENT(33), 40}, "Load"}; Button button_clear{ - {80 + 1, 264, 80 - 2, 40}, + {UI_POS_WIDTH_PERCENT(33) + 1, 264, UI_POS_WIDTH_PERCENT(33), 40}, "Clear"}; Button button_done_ghz{ - {160 + 1, 14 * 16, 80 / 2 - 1, 2 * 16}, + {UI_POS_WIDTH_PERCENT(66) + 1, 14 * 16, UI_POS_WIDTH_PERCENT(16) + 1, UI_POS_HEIGHT(2)}, "GHz"}; Button button_done_khz{ - {160 + 40, 14 * 16, 80 / 2 - 1, 2 * 16}, + {UI_POS_WIDTH_PERCENT(82) + 2, 14 * 16, UI_POS_WIDTH_PERCENT(16) + 2, UI_POS_HEIGHT(2)}, "kHz"}; Button button_done_mhz{ - {160 + 1, 16 * 16, 80 - 2, 3 * 16}, + {UI_POS_WIDTH_PERCENT(66) + 1, 16 * 16, UI_POS_WIDTH_PERCENT(33), UI_POS_HEIGHT(3)}, "Done MHz"}; /* TODO: Template arg required in enum?! */ @@ -312,29 +312,29 @@ class FrequencyOptionsView : public View { private: Text text_step{ - {0 * 8, 0 * 16, 4 * 8, 1 * 16}, + {UI_POS_X(0), UI_POS_Y(0), 4 * 8, 1 * 16}, "Step"}; FrequencyStepView field_step{ - {5 * 8, 0 * 16}, + {5 * 8, UI_POS_Y(0)}, }; void on_step_changed(rf::Frequency v); void on_reference_ppm_correction_changed(int32_t v); NumberField field_ppm{ - {23 * 8, 0 * 16}, + {23 * 8, UI_POS_Y(0)}, 3, {-99, 99}, 1, '0', }; Text text_ext{ - {23 * 8, 0 * 16, 3 * 8, 1 * 16}, + {23 * 8, UI_POS_Y(0), 3 * 8, 1 * 16}, "EXT", }; Text text_ppm{ - {27 * 8, 0 * 16, 3 * 8, 16}, + {27 * 8, UI_POS_Y(0), 3 * 8, 16}, "PPM", }; }; @@ -350,11 +350,11 @@ class RadioGainOptionsView : public View { private: Text label_rf_amp{ - {0 * 8, 0 * 16, 3 * 8, 1 * 16}, + {UI_POS_X(0), UI_POS_Y(0), 3 * 8, 1 * 16}, "Amp"}; RFAmpField field_rf_amp{ - {4 * 8, 0 * 16}, + {4 * 8, UI_POS_Y(0)}, }; }; diff --git a/firmware/application/ui/ui_spectrum.cpp b/firmware/application/ui/ui_spectrum.cpp index bf02d9dc3..f548c542b 100644 --- a/firmware/application/ui/ui_spectrum.cpp +++ b/firmware/application/ui/ui_spectrum.cpp @@ -61,7 +61,7 @@ void AudioSpectrumView::paint(Painter& painter) { // Cursor const Rect r_cursor{ - field_frequency.value() / (48000 / 240), r.bottom() - 32 - cursor_band_height, + field_frequency.value() / (48000 / screen_width), r.bottom() - 32 - cursor_band_height, 1, cursor_band_height}; painter.fill_rectangle( r_cursor, @@ -104,8 +104,8 @@ void FrequencyScale::set_channel_filter( void FrequencyScale::set_cursor_position(const int32_t position) { cursor_position = position; - cursor_position = std::min(cursor_position, 119); - cursor_position = std::max(cursor_position, -120); + cursor_position = std::min(cursor_position, screen_width / 2 - 1); + cursor_position = std::max(cursor_position, -1 * screen_width / 2); set_dirty(); } @@ -124,7 +124,7 @@ void FrequencyScale::paint(Painter& painter) { draw_frequency_ticks(painter, r); const Rect r_cursor{ - 118 + cursor_position, r.bottom() - filter_band_height, + (screen_width / 2 - 2) + cursor_position, r.bottom() - filter_band_height, 5, filter_band_height}; painter.fill_rectangle( r_cursor, @@ -222,8 +222,8 @@ void FrequencyScale::on_blur() { bool FrequencyScale::on_encoder(const EncoderEvent delta) { cursor_position += delta; - cursor_position = std::min(cursor_position, 119); - cursor_position = std::max(cursor_position, -120); + cursor_position = std::min(cursor_position, screen_width / 2 - 1); + cursor_position = std::max(cursor_position, -1 * screen_width / 2); set_dirty(); @@ -233,7 +233,7 @@ bool FrequencyScale::on_encoder(const EncoderEvent delta) { bool FrequencyScale::on_key(const KeyEvent key) { if (key == KeyEvent::Select) { if (on_select) { - on_select((cursor_position * spectrum_sampling_rate) / 240); + on_select((cursor_position * spectrum_sampling_rate) / screen_width); cursor_position = 0; set_dirty(); return true; @@ -246,7 +246,7 @@ bool FrequencyScale::on_key(const KeyEvent key) { bool FrequencyScale::on_touch(const TouchEvent touch) { if (touch.type == TouchEvent::Type::Start) { if (on_select) { - on_select((touch.point.x() * spectrum_sampling_rate) / 240); + on_select((touch.point.x() * spectrum_sampling_rate) / screen_width); } } return true; @@ -261,6 +261,8 @@ void WaterfallWidget::on_show() { const auto screen_r = screen_rect(); display.scroll_set_area(screen_r.top(), screen_r.bottom()); + + clear(); } void WaterfallWidget::on_hide() { @@ -268,27 +270,25 @@ void WaterfallWidget::on_hide() { * position? */ display.scroll_disable(); + clear(); } void WaterfallWidget::on_channel_spectrum( const ChannelSpectrum& spectrum) { /* TODO: static_assert that message.spectrum.db.size() >= pixel_row.size() */ - - std::array pixel_row; - for (size_t i = 0; i < 120; i++) { - const auto pixel_color = gradient.lut[spectrum.db[256 - 120 + i]]; + std::vector pixel_row(screen_width); + for (size_t i = 0; i < screen_width / 2; i++) { + const auto pixel_color = gradient.lut[spectrum.db[256 - screen_width / 2 + i]]; pixel_row[i] = pixel_color; } - for (size_t i = 120; i < 240; i++) { - const auto pixel_color = gradient.lut[spectrum.db[i - 120]]; + for (size_t i = screen_width / 2; i < screen_width; i++) { + const auto pixel_color = gradient.lut[spectrum.db[i - screen_width / 2]]; pixel_row[i] = pixel_color; } - const auto draw_y = display.scroll(1); - display.draw_pixels( - {{0, draw_y}, {pixel_row.size(), 1}}, + {{0, draw_y}, {(int)pixel_row.size(), 1}}, pixel_row); } diff --git a/firmware/application/ui/ui_spectrum.hpp b/firmware/application/ui/ui_spectrum.hpp index 11ec16ef4..9b8cf3ffc 100644 --- a/firmware/application/ui/ui_spectrum.hpp +++ b/firmware/application/ui/ui_spectrum.hpp @@ -51,10 +51,10 @@ class AudioSpectrumView : public View { int16_t audio_spectrum[128]{0}; Labels labels{ - {{6 * 8, 0 * 16}, "Hz", Theme::getInstance()->fg_light->foreground}}; + {{6 * 8, UI_POS_Y(0)}, "Hz", Theme::getInstance()->fg_light->foreground}}; NumberField field_frequency{ - {0 * 8, 0 * 16}, + {UI_POS_X(0), UI_POS_Y(0)}, 5, {0, 48000}, 48000 / 240, @@ -150,7 +150,7 @@ class WaterfallView : public View { private: void update_widgets_rect(); - const Rect audio_spectrum_view_rect{0 * 8, 0 * 16, screen_width, 2 * 16 + 20}; + const Rect audio_spectrum_view_rect{UI_POS_X(0), UI_POS_Y(0), UI_POS_MAXWIDTH, 2 * 16 + 20}; static constexpr Dim audio_spectrum_height = 16 * 2 + 20; static constexpr Dim scale_height = 20; diff --git a/firmware/application/ui/ui_transmitter.cpp b/firmware/application/ui/ui_transmitter.cpp index 84b294e48..fbc87e760 100644 --- a/firmware/application/ui/ui_transmitter.cpp +++ b/firmware/application/ui/ui_transmitter.cpp @@ -56,17 +56,21 @@ static const Style* get_style_for_gain(uint8_t tot_gain) { void TransmitterView::paint(Painter& painter) { size_t c; Point pos = {0, screen_pos().y()}; - - for (c = 0; c < 20; c++) { + Point pos2 = {0, screen_pos().y() + 32 + 8}; + // 24*8 pxstripes + for (c = 0; c < (size_t)(screen_width / 24 + 1); c++) { painter.draw_bitmap( pos, bitmap_stripes, Theme::getInstance()->fg_yellow->foreground, Theme::getInstance()->fg_yellow->background); - if (c != 9) - pos += {24, 0}; - else - pos = {0, screen_pos().y() + 32 + 8}; + painter.draw_bitmap( + pos2, + bitmap_stripes, + Theme::getInstance()->fg_yellow->foreground, + Theme::getInstance()->fg_yellow->background); + pos += {24, 0}; + pos2 += {24, 0}; } } @@ -225,16 +229,16 @@ TransmitterView2::TransmitterView2(Point pos, bool short_ui) { text_labels.set(short_ui ? "G: A:" : "Gain: Amp:"); text_labels.set_parent_rect( short_ui - ? Rect{0 * 8, 0 * 16, 7 * 8, 1 * 16} - : Rect{0 * 8, 0 * 16, 12 * 8, 1 * 16}); + ? Rect{UI_POS_X(0), UI_POS_Y(0), 7 * 8, 1 * 16} + : Rect{UI_POS_X(0), UI_POS_Y(0), 12 * 8, 1 * 16}); field_gain.set_parent_rect( short_ui - ? Rect{2 * 8, 0 * 16, 2 * 8, 1 * 16} - : Rect{5 * 8, 0 * 16, 2 * 8, 1 * 16}); + ? Rect{2 * 8, UI_POS_Y(0), 2 * 8, 1 * 16} + : Rect{5 * 8, UI_POS_Y(0), 2 * 8, 1 * 16}); field_amp.set_parent_rect( short_ui - ? Rect{7 * 8, 0 * 16, 2 * 8, 1 * 16} - : Rect{12 * 8, 0 * 16, 2 * 8, 1 * 16}); + ? Rect{7 * 8, UI_POS_Y(0), 2 * 8, 1 * 16} + : Rect{12 * 8, UI_POS_Y(0), 2 * 8, 1 * 16}); field_gain.set_value(transmitter_model.tx_gain()); field_gain.on_change = [this](uint32_t tx_gain) { diff --git a/firmware/application/ui/ui_transmitter.hpp b/firmware/application/ui/ui_transmitter.hpp index dd7f796fe..63c0285ad 100644 --- a/firmware/application/ui/ui_transmitter.hpp +++ b/firmware/application/ui/ui_transmitter.hpp @@ -115,7 +115,7 @@ class TransmitterView : public View { ' '}; Button button_start{ - {21 * 8, 1 * 8, 9 * 8, 32}, + {UI_POS_X_RIGHT(9), 1 * 8, 9 * 8, 32}, "START"}; FrequencyStepView field_frequency_step{ diff --git a/firmware/application/ui/ui_tv.hpp b/firmware/application/ui/ui_tv.hpp index dd94ad129..425c3c574 100644 --- a/firmware/application/ui/ui_tv.hpp +++ b/firmware/application/ui/ui_tv.hpp @@ -50,11 +50,11 @@ class TimeScopeView : public View { int16_t audio_spectrum[128]{0}; /*Labels labels { - { { 6 * 8, 0 * 16 }, "Hz", Theme::getInstance()->fg_light->foreground } + { { 6 * 8,UI_POS_Y(0) }, "Hz", Theme::getInstance()->fg_light->foreground } };*/ /* NumberField field_frequency { - { 0 * 8, 0 * 16 }, + { 0 * 8,UI_POS_Y(0) }, 5, { 0, 48000 }, 48000 / 240, @@ -108,7 +108,7 @@ class TVWidget : public View { void paint(Painter& painter) override; NumberField field_xcorr{ - {0 * 8, 0 * 16}, + {UI_POS_X(0), UI_POS_Y(0)}, 5, {0, 128}, 1, @@ -117,7 +117,7 @@ class TVWidget : public View { private: void update_widgets_rect(); - const Rect audio_spectrum_view_rect{0 * 8, 0 * 16, screen_width, 2 * 16 + 20}; + const Rect audio_spectrum_view_rect{UI_POS_X(0), UI_POS_Y(0), screen_width, 2 * 16 + 20}; static constexpr Dim audio_spectrum_height = 16 * 2 + 20; static constexpr Dim scale_height = 20; diff --git a/firmware/application/ui_baseband_stats_view.hpp b/firmware/application/ui_baseband_stats_view.hpp index ca8721f3f..365f5fbd7 100644 --- a/firmware/application/ui_baseband_stats_view.hpp +++ b/firmware/application/ui_baseband_stats_view.hpp @@ -36,7 +36,7 @@ class BasebandStatsView : public View { private: Text text_stats{ - {0 * 8, 0, (4 * 4 + 3) * 8, 1 * 16}, + {UI_POS_X(0), 0, (4 * 4 + 3) * 8, UI_POS_Y(1)}, "", }; diff --git a/firmware/application/ui_navigation.cpp b/firmware/application/ui_navigation.cpp index c3a03fe44..a7e67fb69 100644 --- a/firmware/application/ui_navigation.cpp +++ b/firmware/application/ui_navigation.cpp @@ -33,7 +33,6 @@ #include "ui_adsb_rx.hpp" #include "ui_aprs_rx.hpp" #include "ui_aprs_tx.hpp" -#include "ui_bht_tx.hpp" #include "ui_btle_rx.hpp" #include "ui_debug.hpp" #include "ui_encoders.hpp" @@ -136,7 +135,6 @@ const NavigationView::AppList NavigationView::appList = { {"weather", "Weather", RX, Color::green(), &bitmap_icon_thermometer, new ViewFactory()}, /* TX ********************************************************************/ {"aprstx", "APRS TX", TX, ui::Color::green(), &bitmap_icon_aprs, new ViewFactory()}, - {"bht", "BHT Xy/EP", TX, ui::Color::green(), &bitmap_icon_bht, new ViewFactory()}, {"bletx", "BLE Tx", TX, ui::Color::green(), &bitmap_icon_btle, new ViewFactory()}, {"ooktx", "OOK", TX, ui::Color::yellow(), &bitmap_icon_remote, new ViewFactory()}, {"pocsagtx", "POCSAG TX", TX, ui::Color::green(), &bitmap_icon_pocsag, new ViewFactory()}, @@ -903,7 +901,8 @@ void SystemMenuView::hackrf_mode(NavigationView& nav) { SystemMenuView::SystemMenuView(NavigationView& nav) : nav_(nav) { - set_max_rows(2); // allow wider buttons + set_btn_height_fixed((screen_height - 16 - 16) / 6); // this is for main menu height, to fill the screen + set_max_rows(2); // allow wider buttons show_arrows_enabled(false); } @@ -1041,22 +1040,26 @@ void SplashScreenView::focus() { SplashScreenView::SplashScreenView(NavigationView& nav) : nav_(nav) { - add_children({&button_done}); - + add_children({ + &button_done, + // &bmp_view + }); button_done.on_select = [this](Button&) { handle_pop(); }; } void SplashScreenView::paint(Painter&) { + constexpr const uint8_t zoom = 3; + // if (!bmp_view.load_bmp(splash_dot_bmp)) { //--too slow drawing, bc of the more bmp format support, and up-> down drawing if (!portapack::display.draw_bmp_from_sdcard_file({0, 0}, splash_dot_bmp)) // ^ try draw bmp file from sdcard at (0,0), and the (0,0) already bypassed the status bar, so actual pos is (0, STATUS_BAR_HEIGHT) - portapack::display.draw_bitmap({0, + portapack::display.draw_bitmap({screen_width / 2 - ((bitmap_titlebar_image.size.width() * zoom) / 2), screen_height / 2}, bitmap_titlebar_image.size, bitmap_titlebar_image.data, Theme::getInstance()->bg_darkest->foreground, - Theme::getInstance()->bg_darkest->background, 3); + Theme::getInstance()->bg_darkest->background, zoom); // ^ draw BMP HEX arr in firmware, note that the BMP HEX arr only cover the image part (instead of fill the screen with background, this position is located it in the center) } @@ -1151,8 +1154,8 @@ ModalMessageView::ModalMessageView( } void ModalMessageView::paint(Painter& painter) { - if (!compact) portapack::display.draw_bitmap({screen_width / 2 - 3 * 16 / 2, - screen_height / 2 - 3 * 16 / 2 - 100}, + if (!compact) portapack::display.draw_bitmap({UI_POS_X_CENTER(6), + UI_POS_Y(4)}, bitmap_icon_utilities.size, bitmap_icon_utilities.data, Theme::getInstance()->bg_darkest->foreground, diff --git a/firmware/application/ui_navigation.hpp b/firmware/application/ui_navigation.hpp index c3aaa87f1..21154bdac 100644 --- a/firmware/application/ui_navigation.hpp +++ b/firmware/application/ui_navigation.hpp @@ -44,6 +44,7 @@ #include "ui_dfu_menu.hpp" #include "bitmap.hpp" +#include "ui_bmpview.hpp" #include "ff.h" #include "diskio.h" #include "lfsr_random.hpp" @@ -205,11 +206,11 @@ class SystemStatusView : public View { NavigationView& nav_; Rectangle backdrop{ - {0 * 8, 0 * 16, ui::screen_width, 16}, + {UI_POS_X(0), UI_POS_Y(0), ui::screen_width, 16}, Theme::getInstance()->bg_dark->background}; ImageButton button_back{ - {0, 0 * 16, 12 * 8, 16}, // Back button also covers the title for easier touch. + {0, UI_POS_Y(0), 12 * 8, 16}, // Back button also covers the title for easier touch. &bitmap_icon_previous, Theme::getInstance()->bg_dark->foreground, Theme::getInstance()->bg_dark->background}; @@ -279,7 +280,7 @@ class SystemStatusView : public View { Theme::getInstance()->bg_dark->background}; ImageButton button_clock_status{ - {0, 0 * 16, 8, 1 * 16}, + {0, UI_POS_Y(0), 8, 1 * 16}, &bitmap_icon_clk_int, Theme::getInstance()->fg_light->foreground, Theme::getInstance()->bg_dark->background}; @@ -291,7 +292,7 @@ class SystemStatusView : public View { Theme::getInstance()->bg_dark->background}; SDCardStatusView sd_card_status_view{ - {0, 0 * 16, 2 * 8, 1 * 16}}; + {0, UI_POS_Y(0), 2 * 8, 1 * 16}}; BatteryTextField battery_text{{0, 0, 2 * 8, 1 * 16}, 102}; BatteryIcon battery_icon{{0, 0, 10, 1 * 16}, 102}; @@ -357,6 +358,7 @@ class SplashScreenView : public View { Button button_done{ {screen_width, 0, 1, 1}, ""}; + // BMPViewer bmp_view{ {0, 0, screen_width, screen_height - 16}}; }; class ReceiversMenuView : public BtnGridView { @@ -485,17 +487,17 @@ class ModalMessageView : public View { const bool compact; Button button_ok{ - {10 * 8, 14 * 16, 10 * 8, 48}, + {UI_POS_X_CENTER(10), UI_POS_Y_BOTTOM(5), UI_POS_WIDTH(10), UI_POS_HEIGHT(3)}, "OK", }; Button button_yes{ - {5 * 8, 14 * 16, 8 * 8, 48}, + {UI_POS_X_CENTER(8) - UI_POS_WIDTH(6), UI_POS_Y_BOTTOM(5), UI_POS_WIDTH(8), UI_POS_HEIGHT(3)}, "YES", }; Button button_no{ - {17 * 8, 14 * 16, 8 * 8, 48}, + {UI_POS_X_CENTER(8) + UI_POS_WIDTH(6), UI_POS_Y_BOTTOM(5), UI_POS_WIDTH(8), UI_POS_HEIGHT(3)}, "NO", }; }; diff --git a/firmware/application/ui_record_view.hpp b/firmware/application/ui_record_view.hpp index dae35af9c..365eb5cf9 100644 --- a/firmware/application/ui_record_view.hpp +++ b/firmware/application/ui_record_view.hpp @@ -116,36 +116,36 @@ class RecordView : public View { Theme::getInstance()->bg_darkest->background}; /*ImageButton button_pitch_rssi { - { 2, 0 * 16, 3 * 8, 1 * 16 }, + { 2, UI_POS_Y(0), 3 * 8, 1 * 16 }, &bitmap_rssipwm, Theme::getInstance()->fg_orange->foreground, Theme::getInstance()->fg_orange->background };*/ ImageButton button_record{ - //{ 4 * 8, 0 * 16, 2 * 8, 1 * 16 }, - {0 * 8, 0 * 16, 2 * 8, 1 * 16}, + //{ 4 * 8, UI_POS_Y(0), 2 * 8, 1 * 16 }, + {UI_POS_X(0), UI_POS_Y(0), UI_POS_WIDTH(2), UI_POS_HEIGHT(1)}, &bitmap_record, Theme::getInstance()->fg_red->foreground, Theme::getInstance()->fg_red->background}; Text text_record_filename{ - {7 * 8, 0 * 16, 8 * 8, 16}, + {UI_POS_X(7), UI_POS_Y(0), UI_POS_WIDTH(8), UI_POS_HEIGHT(1)}, "", }; Text text_record_dropped{ - {16 * 8, 0 * 16, 3 * 8, 16}, + {UI_POS_X(16), UI_POS_Y(0), UI_POS_WIDTH(3), UI_POS_HEIGHT(1)}, "", }; Text text_time_available{ - {21 * 8, 0 * 16, 9 * 8, 16}, + {UI_POS_X_RIGHT(9), UI_POS_Y(0), UI_POS_WIDTH(9), UI_POS_HEIGHT(1)}, "", }; Image gps_icon{ - {2 * 8 + 1, 0 * 16, 2 * 8, 1 * 16}, + {UI_POS_X(2) + 1, UI_POS_Y(0), UI_POS_WIDTH(2), UI_POS_HEIGHT(1)}, &bitmap_target, Theme::getInstance()->bg_darkest->foreground, Theme::getInstance()->bg_darkest->background}; diff --git a/firmware/application/ui_sd_card_debug.hpp b/firmware/application/ui_sd_card_debug.hpp index 4b744a894..c11e0a8f3 100644 --- a/firmware/application/ui_sd_card_debug.hpp +++ b/firmware/application/ui_sd_card_debug.hpp @@ -48,14 +48,14 @@ class SDCardDebugView : public View { std::string fetch_sdcard_format(); Labels labels{ - {{0 * 8, 1 * 16}, "Format", Theme::getInstance()->fg_light->foreground}, - {{0 * 8, 3 * 16}, "CSD", Theme::getInstance()->fg_light->foreground}, - {{0 * 8, 5 * 16}, "Bus width", Theme::getInstance()->fg_light->foreground}, - {{0 * 8, 6 * 16}, "Card type", Theme::getInstance()->fg_light->foreground}, - {{0 * 8, 8 * 16}, "Block size", Theme::getInstance()->fg_light->foreground}, - {{0 * 8, 9 * 16}, "Block count", Theme::getInstance()->fg_light->foreground}, - {{0 * 8, 10 * 16}, "Capacity", Theme::getInstance()->fg_light->foreground}, - {{0 * 8, 5 * 16}, "Bus width", Theme::getInstance()->fg_light->foreground}, + {{UI_POS_X(0), 1 * 16}, "Format", Theme::getInstance()->fg_light->foreground}, + {{UI_POS_X(0), 3 * 16}, "CSD", Theme::getInstance()->fg_light->foreground}, + {{UI_POS_X(0), 5 * 16}, "Bus width", Theme::getInstance()->fg_light->foreground}, + {{UI_POS_X(0), 6 * 16}, "Card type", Theme::getInstance()->fg_light->foreground}, + {{UI_POS_X(0), 8 * 16}, "Block size", Theme::getInstance()->fg_light->foreground}, + {{UI_POS_X(0), 9 * 16}, "Block count", Theme::getInstance()->fg_light->foreground}, + {{UI_POS_X(0), 10 * 16}, "Capacity", Theme::getInstance()->fg_light->foreground}, + {{UI_POS_X(0), 5 * 16}, "Bus width", Theme::getInstance()->fg_light->foreground}, }; diff --git a/firmware/application/usb_serial_shell.cpp b/firmware/application/usb_serial_shell.cpp index 64b51d3ca..8bd65afe1 100644 --- a/firmware/application/usb_serial_shell.cpp +++ b/firmware/application/usb_serial_shell.cpp @@ -160,7 +160,8 @@ static void cmd_flash(BaseSequentialStream* chp, int argc, char* argv[]) { // call nav with flash auto open_view = nav->push(); chprintf(chp, "Flashing started\r\n"); - chThdSleepMilliseconds(150); // to give display some time to paint the screen + chThdSleepMilliseconds(50); // to give display some time to paint the screen + open_view->wait_till_loaded(); // also wait for first frame sync if (!open_view->flash_firmware(path.native())) { chprintf(chp, "error\r\n"); } diff --git a/firmware/baseband/mcuconf.h b/firmware/baseband/mcuconf.h old mode 100755 new mode 100644 index 10d51a702..59022a556 --- a/firmware/baseband/mcuconf.h +++ b/firmware/baseband/mcuconf.h @@ -36,9 +36,9 @@ * DMA driver system settings. */ -//#define LPC_ADC0_IRQ_PRIORITY 2 +// #define LPC_ADC0_IRQ_PRIORITY 2 #define LPC_DMA_IRQ_PRIORITY 3 -//#define LPC_ADC1_IRQ_PRIORITY 4 +// #define LPC_ADC1_IRQ_PRIORITY 4 #define LPC43XX_M0APPTXEVENT_IRQ_PRIORITY 4 diff --git a/firmware/common/bmp.hpp b/firmware/common/bmp.hpp index dd032fb66..4ce606e92 100644 --- a/firmware/common/bmp.hpp +++ b/firmware/common/bmp.hpp @@ -19,7 +19,7 @@ * the Free Software Foundation, Inc., 51 Franklin Street, * Boston, MA 02110-1301, USA. */ - +#pragma once #pragma pack(push, 1) struct bmp_header_t { uint16_t signature; diff --git a/firmware/common/lcd_ili9341.cpp b/firmware/common/lcd_ili9341.cpp index ae84ab01b..a353df4e0 100644 --- a/firmware/common/lcd_ili9341.cpp +++ b/firmware/common/lcd_ili9341.cpp @@ -102,6 +102,56 @@ void lcd_init() { // LCDs are configured for IM[2:0] = 001 // 8080-I system, 16-bit parallel bus + io.lcd_data_write_command_and_data(0xE0, {0x00, 0x09, 0x0C, 0x03, 0x10, 0x06, 0x34, 0x68, + 0x49, 0x02, 0x0A, 0x07, 0x2C, 0x31, 0x0F}); + + io.lcd_data_write_command_and_data(0xE1, {0x00, 0x12, 0x15, 0x02, 0x10, 0x06, 0x35, 0x35, + 0x4A, 0x05, 0x10, 0x0C, 0x2F, 0x33, 0x0F}); + + io.lcd_data_write_command_and_data(0xC0, {0x0F, 0x0F}); // Vreg1out=4.5 Vreg2out=-4.5 + + io.lcd_data_write_command_and_data(0xC1, {0x44}); // VGH = 5*VCI VGL = -4*VCI + + io.lcd_data_write_command_and_data(0xC5, {0x00, 0x66, 0x80}); // VCOM + + io.lcd_data_write_command_and_data(0x36, { + (1 << 7) | // MY=1 + (0 << 6) | // MX=0 + (0 << 5) | // MV=0 + (1 << 4) | // ML=1: reverse vertical refresh to simplify scrolling logic + (1 << 3) // BGR=1: For Kingtech LCD, BGR filter. + }); + + // io.lcd_data_write_command_and_data(0x36, {0x48}); + io.lcd_data_write_command_and_data(0x3A, {0x65}); // 0x55 was the original + // 这里的刷新率过低? + // io.lcd_data_write_command_and_data(0xB1, {0x40, 0x1F}); // fix 60 fps + io.lcd_data_write_command_and_data(0xB1, {0xA0, 0x11}); // Frame rate source is 11fps + + io.lcd_data_write_command_and_data(0xB4, {0x02}); // 2 dot inversion + + io.lcd_data_write_command_and_data(0xEE, {0x00, 0x04}); + + io.lcd_data_write_command_and_data(0xE9, {0x00}); + + io.lcd_data_write_command_and_data(0xF7, {0xA9, 0x51, 0x2C, 0x82}); + + io.lcd_data_write_command_and_data(0x21, {}); // Display Inversion On + + io.lcd_data_write_command_and_data(0x11, {}); // Sleep Out + chThdSleepMilliseconds(120); // Delay 120ms + + io.lcd_data_write_command_and_data(0x29, {}); // Display On + chThdSleepMilliseconds(50); // Delay 50ms + + // Turn on Tearing Effect Line (TE) output signal. + io.lcd_data_write_command_and_data(0x35, {0b00000000}); +} + +void lcd_init_pp() { + // LCDs are configured for IM[2:0] = 001 + // 8080-I system, 16-bit parallel bus + // // 0x3a: DBI[2:0] = 101 // MDT[1:0] = XX (if not in 18-bit mode, right?) @@ -281,6 +331,16 @@ void lcd_vertical_scrolling_start_address( } // namespace +uint32_t ILI9341::lcd_read_display_id() { + io.lcd_data_write_command_and_data(0x04, {}); + io.lcd_read_data_raw(); + + uint32_t value3 = io.lcd_read_data_raw(); + uint32_t value4 = io.lcd_read_data_raw(); + uint32_t value5 = io.lcd_read_data_raw(); + return value5 + (value4 << 8) + (value3 << 16); +} + bool ILI9341::read_display_status() { lcd_reset(); uint32_t display_status = lcd_read_display_status(); @@ -302,7 +362,32 @@ bool ILI9341::read_display_status() { void ILI9341::init() { lcd_reset(); - lcd_init(); + bool hpp = false; + // detect method 1 + uint32_t id = lcd_read_display_id(); + if (id == 5537894) hpp = true; + + // detect method 2 - not working! + if (!hpp) { + draw_pixel({318, 478}, ui::Color::red()); + fill_rectangle_unrolled8({0, 0, 240, 320}, ui::Color::black()); + std::vector checker(1); + read_pixels({318, 478, 1, 1}, checker.data(), 1); + if (checker[0].b <= 10 && checker[0].g <= 10 && checker[0].r >= 100) + hpp = true; + } + // init screen + if (hpp) { + device_type = DEV_PORTARF; + lcd_init(); + screen_width = 320; + screen_height = 480; + } else { + device_type = DEV_PORTAPACK; + lcd_init_pp(); + screen_width = 240; + screen_height = 320; + } } void ILI9341::shutdown() { @@ -335,7 +420,7 @@ void ILI9341::fill_rectangle_unrolled8(ui::Rect r, const ui::Color c) { } } -void ILI9341::render_line(const ui::Point p, const uint8_t count, const ui::Color* line_buffer) { +void ILI9341::render_line(const ui::Point p, const uint16_t count, const ui::Color* line_buffer) { lcd_start_ram_write(p, {count, 1}); io.lcd_write_pixels(line_buffer, count); } @@ -473,8 +558,8 @@ bool ILI9341::draw_bmp_from_sdcard_file(const ui::Point p, const std::filesystem bmp_header_t bmp_header; uint8_t type = 0; char buffer[257]; - ui::Color line_buffer[240]; - + ui::Color line_buffer[320]; + int16_t start_x = p.x(); auto result = bmpimage.open(file); if (result.is_valid()) return false; @@ -508,8 +593,8 @@ bool ILI9341::draw_bmp_from_sdcard_file(const ui::Point p, const std::filesystem width = bmp_header.width; height = bmp_header.height; - - if (width != 240) + start_x = (screen_width - start_x - width) / 2 + start_x; // center horizontally + if (width > screen_width || width > 320) return false; file_pos = bmp_header.image_data; @@ -560,7 +645,7 @@ bool ILI9341::draw_bmp_from_sdcard_file(const ui::Point p, const std::filesystem if (read_size.value() != 256) break; } - render_line({p.x(), p.y() + py}, px, line_buffer); + render_line({start_x, p.y() + py}, px, line_buffer); px = 0; py--; @@ -668,7 +753,7 @@ void ILI9341::draw_bitmap( for (size_t i = 0; i < count; i++) { const auto pixel = pixels[i >> 3] & (1U << (i & 0x7)); if (pixel) { - draw_pixel(ui::Point(x, y), foreground); + if (x <= screen_width && y <= screen_height) draw_pixel(ui::Point(x, y), foreground); } // move to next px x++; @@ -740,8 +825,12 @@ ui::Coord ILI9341::scroll_area_y(const ui::Coord y) const { } void ILI9341::scroll_disable() { - lcd_vertical_scrolling_definition(0, height(), 0); - lcd_vertical_scrolling_start_address(0); + if (device_type == DEV_PORTAPACK) { + lcd_vertical_scrolling_definition(0, height(), 0); + lcd_vertical_scrolling_start_address(0); + } else { + io.lcd_data_write_command_and_data(0x13, {}); // normal mode + } } } /* namespace lcd */ diff --git a/firmware/common/lcd_ili9341.hpp b/firmware/common/lcd_ili9341.hpp index 2637b68b0..9add72f41 100644 --- a/firmware/common/lcd_ili9341.hpp +++ b/firmware/common/lcd_ili9341.hpp @@ -43,6 +43,7 @@ class ILI9341 { void operator=(const ILI9341&) = delete; bool read_display_status(); + uint32_t lcd_read_display_id(); void init(); void shutdown(); @@ -62,7 +63,7 @@ class ILI9341 { void draw_pixel(const ui::Point p, const ui::Color color); void draw_bmp_from_bmp_hex_arr(const ui::Point p, const uint8_t* bitmap, const uint8_t* transparency_color); bool draw_bmp_from_sdcard_file(const ui::Point p, const std::filesystem::path& file); - void render_line(const ui::Point p, const uint8_t count, const ui::Color* line_buffer); + void render_line(const ui::Point p, const uint16_t count, const ui::Color* line_buffer); void render_box(const ui::Point p, const ui::Size s, const ui::Color* line_buffer); template @@ -163,7 +164,6 @@ class ILI9341 { ui::Dim height; ui::Coord current_position; }; - scroll_t scroll_state; }; diff --git a/firmware/common/portapack_io.cpp b/firmware/common/portapack_io.cpp index 9403cdd60..26bbe18de 100644 --- a/firmware/common/portapack_io.cpp +++ b/firmware/common/portapack_io.cpp @@ -34,6 +34,42 @@ using namespace lpc43xx; namespace portapack { +DeviceType device_type = DEV_PORTAPACK; + +void IO::lcd_read_bytes(uint8_t* byte, size_t byte_count) { + if (portapack::device_type == portapack::DeviceType::DEV_PORTAPACK) { + size_t word_count = byte_count / 2; + while (word_count) { + const auto word = lcd_read_data(); + *(byte++) = word >> 8; + *(byte++) = word >> 0; + word_count--; + } + if (byte_count & 1) { + const auto word = lcd_read_data(); + *(byte++) = word >> 8; + } + return; + } + // prf + //--dummy read: + dir_read(); + lcd_rd_assert(); + halPolledDelay(71); + data_read(); + lcd_rd_deassert(); + size_t word_count = byte_count / 3; + for (size_t i = 0; i < word_count; i++) { + uint32_t word = lcd_read_data(); // reads 3 byte of data + uint8_t r = ((word >> 16) & 0xff); + uint8_t g = ((word >> 8) & 0xff); + uint8_t b = ((word >> 0) & 0xff); + *(byte++) = r; + *(byte++) = g << 2; + *(byte++) = b; + } +} + void IO::init() { data_mask_set(); data_write_high(0); diff --git a/firmware/common/portapack_io.hpp b/firmware/common/portapack_io.hpp index 229e1b994..d666a07a5 100644 --- a/firmware/common/portapack_io.hpp +++ b/firmware/common/portapack_io.hpp @@ -52,6 +52,12 @@ static const uint16_t darken_mask[4] = { namespace portapack { +enum DeviceType { + DEV_PORTAPACK, + DEV_PORTARF +}; +extern DeviceType device_type; + class IO { public: enum class TouchPinsConfig : uint8_t { @@ -140,12 +146,43 @@ class IO { lcd_write_data(d); } } + uint32_t lcd_read_data_raw() { + // NOTE: Assumes ADDR=1 from command phase. + dir_read(); + + /* Start read operation */ + lcd_rd_assert(); + /* Wait for passthrough data(15:8) to settle -- ~16ns (3 cycles) typical */ + /* Wait for read control L duration (355ns) */ + halPolledDelay(71); // 355ns + const auto value_high = data_read(); + + /* Latch data[7:0] */ + lcd_rd_deassert(); + /* Wait for latched data[7:0] to settle -- ~26ns (5 cycles) typical */ + /* Wait for read control H duration (90ns) */ + halPolledDelay(18); // 90ns + + const auto value_low = data_read(); + uint32_t original_value = (value_high << 8) | value_low; + + return original_value; + } void lcd_data_read_command_and_data( const uint_fast8_t command, uint16_t* const data, const size_t data_count) { lcd_command(command); + if (device_type == DEV_PORTARF) { + // dummy read + dir_read(); + lcd_rd_assert(); + halPolledDelay(71); + data_read(); + lcd_rd_deassert(); + halPolledDelay(71); + } for (size_t i = 0; i < data_count; i++) { data[i] = lcd_read_data(); } @@ -205,19 +242,7 @@ class IO { } } - void lcd_read_bytes(uint8_t* byte, size_t byte_count) { - size_t word_count = byte_count / 2; - while (word_count) { - const auto word = lcd_read_data(); - *(byte++) = word >> 8; - *(byte++) = word >> 0; - word_count--; - } - if (byte_count & 1) { - const auto word = lcd_read_data(); - *(byte++) = word >> 8; - } - } + void lcd_read_bytes(uint8_t* byte, size_t byte_count); uint32_t io_read() { io_stb_assert(); @@ -400,32 +425,40 @@ class IO { } uint32_t lcd_read_data() { - // NOTE: Assumes ADDR=1 from command phase. dir_read(); - /* Start read operation */ lcd_rd_assert(); /* Wait for passthrough data(15:8) to settle -- ~16ns (3 cycles) typical */ /* Wait for read control L duration (355ns) */ halPolledDelay(71); // 355ns - const auto value_high = data_read(); + if (portapack::device_type == portapack::DeviceType::DEV_PORTAPACK) { + const auto value_high = data_read(); + /* Latch data[7:0] */ + lcd_rd_deassert(); + /* Wait for latched data[7:0] to settle -- ~26ns (5 cycles) typical */ + /* Wait for read control H duration (90ns) */ + halPolledDelay(71); // 90ns + const auto value_low = data_read(); + uint32_t original_value = (value_high << 8) | value_low; + if (lcd_normally_black) return original_value; + + if (dark_cover_enabled) { + // this is read data, so if the fake brightness is enabled AKA get_dark_cover() == true, + // then shift to back side AKA UNDARKENED_PIXEL, to prevent read shifted darkern info + original_value = UNDARKENED_PIXEL(original_value, brightness); + } + return original_value; + } + const auto value_high = data_read(); /* Latch data[7:0] */ lcd_rd_deassert(); - /* Wait for latched data[7:0] to settle -- ~26ns (5 cycles) typical */ - /* Wait for read control H duration (90ns) */ - halPolledDelay(18); // 90ns - + halPolledDelay(71); const auto value_low = data_read(); - uint32_t original_value = (value_high << 8) | value_low; - - if (lcd_normally_black) return original_value; - - if (dark_cover_enabled) { - // this is read data, so if the fake brightness is enabled AKA get_dark_cover() == true, - // then shift to back side AKA UNDARKENED_PIXEL, to prevent read shifted darkern info - original_value = UNDARKENED_PIXEL(original_value, brightness); - } + lcd_rd_deassert(); + halPolledDelay(71); + const auto value_last_low = data_read(); + uint32_t original_value = (value_high << 16) | value_low << 8 | value_last_low; return original_value; } diff --git a/firmware/common/ui.hpp b/firmware/common/ui.hpp index 61a3ce31f..ff6fc1371 100644 --- a/firmware/common/ui.hpp +++ b/firmware/common/ui.hpp @@ -45,6 +45,8 @@ namespace ui { #define UI_POS_X_RIGHT(charnum) ((int)(screen_width - ((charnum)*UI_POS_DEFAULT_WIDTH))) // px position of the left character from the center of the screen (X) (for N character wide string) #define UI_POS_X_CENTER(charnum) ((int)((screen_width / 2) - ((charnum)*UI_POS_DEFAULT_WIDTH / 2))) +// px position of the currcol in a table with colnum number of columns, where one coloumn is charnum characters wide maximum +#define UI_POS_X_TABLE(colnum, currcol) ((currcol) * (screen_width / (colnum))) // px width of N characters #define UI_POS_WIDTH(charnum) ((int)((charnum)*UI_POS_DEFAULT_WIDTH)) // px width of the screen @@ -57,6 +59,8 @@ namespace ui { #define UI_POS_HEIGHT_REMAINING(linenum) ((int)(screen_height - ((linenum)*UI_POS_DEFAULT_HEIGHT))) // remaining px from the charnum-th character to the right of the screen #define UI_POS_WIDTH_REMAINING(charnum) ((int)(screen_width - ((charnum)*UI_POS_DEFAULT_WIDTH))) +// px width of the screen's percent +#define UI_POS_WIDTH_PERCENT(percent) ((int)(screen_width * (percent) / 100)) // px width of the screen #define UI_POS_MAXHEIGHT (screen_height) diff --git a/firmware/common/ui_widget.cpp b/firmware/common/ui_widget.cpp index f15b9ec84..41d277233 100644 --- a/firmware/common/ui_widget.cpp +++ b/firmware/common/ui_widget.cpp @@ -534,7 +534,7 @@ void BigFrequency::paint(Painter& painter) { _previous_frequency = _frequency; rf::Frequency frequency{_frequency}; - const auto rect = screen_rect(); + const auto rect = screen_rect(); // why not use screen_rect() directly for width, ...? it may be too small, but ... // Erase painter.fill_rectangle( @@ -544,7 +544,7 @@ void BigFrequency::paint(Painter& painter) { // Prepare digits if (!frequency) { digits.fill(10); // ----.--- - digit_pos = {0, rect.location().y()}; + digit_pos = {(screen_width - ((7 * digit_width) + 8)) / 2, rect.location().y()}; } else { frequency /= 1000; // GMMM.KKK(uuu) @@ -561,7 +561,7 @@ void BigFrequency::paint(Painter& painter) { break; } - digit_pos = {(Coord)(240 - ((7 * digit_width) + 8) - (i * digit_width)) / 2, rect.location().y()}; + digit_pos = {(Coord)(screen_width - ((7 * digit_width) + 8) - (i * digit_width)) / 2, rect.location().y()}; } segment_color = style().foreground; @@ -1334,24 +1334,57 @@ void NewButton::paint(Painter& painter) { style.background); int y = r.top(); - if (bitmap_) { - int offset_y = vertical_center_ ? (r.height() / 2) - (bitmap_->size.height() / 2) : 6; - Point bmp_pos = {r.left() + (r.width() / 2) - (bitmap_->size.width() / 2), r.top() + offset_y}; - y += bitmap_->size.height() - offset_y; + if (vertical_center_) { + const int bmp_h = bitmap_ ? bitmap_->size.height() : 0; + const int txt_h = !text_.empty() ? style.font.line_height() : 0; + int spacing = 0; + if (bmp_h > 0 && txt_h > 0) { + const int content_height = bmp_h + txt_h; + const int remaining_space = r.height() - content_height; + spacing = std::max(4, remaining_space / 3); + } + const int total_height = bmp_h + txt_h + spacing; + y += (r.height() - total_height) / 2; - painter.draw_bitmap( - bmp_pos, - *bitmap_, - color_, - style.background); - } + if (bitmap_) { + Point bmp_pos = {r.left() + (r.width() / 2) - (bitmap_->size.width() / 2), y}; + y += bitmap_->size.height(); - if (!text_.empty()) { - const auto label_r = style.font.size_of(text_); - painter.draw_string( - {r.left() + (r.width() - label_r.width()) / 2, y + (r.height() - label_r.height()) / 2}, - style, - text_); + painter.draw_bitmap( + bmp_pos, + *bitmap_, + color_, + style.background); + } + + if (!text_.empty()) { + const auto label_r = style.font.size_of(text_); + if (bitmap_) { + y += spacing; + } + painter.draw_string( + {r.left() + (r.width() - label_r.width()) / 2, y}, + style, + text_); + } + } else { // no valign + if (bitmap_) { + Point bmp_pos = {r.left() + (r.width() / 2) - (bitmap_->size.width() / 2), r.top() + 6}; + y += bitmap_->size.height() - 6; + painter.draw_bitmap( + bmp_pos, + *bitmap_, + color_, + style.background); + } + + if (!text_.empty()) { + const auto label_r = style.font.size_of(text_); + painter.draw_string( + {r.left() + (r.width() - label_r.width()) / 2, y + (r.height() - label_r.height()) / 2}, + style, + text_); + } } } @@ -2722,6 +2755,13 @@ bool Waveform::on_touch(const TouchEvent event) { } } +void Waveform::set_data(int16_t* new_data) { + if (new_data != data_) { + data_ = new_data; + set_dirty(); + } +} + void Waveform::paint(Painter& painter) { // previously it's upside down , low level is up and high level is down, which doesn't make sense, // if that was made for a reason, feel free to revert. diff --git a/firmware/common/ui_widget.hpp b/firmware/common/ui_widget.hpp index 09d7795c7..452d1b2cc 100644 --- a/firmware/common/ui_widget.hpp +++ b/firmware/common/ui_widget.hpp @@ -989,7 +989,7 @@ class Waveform : public Widget { void set_offset(const uint32_t new_offset); void set_length(const uint32_t new_length); void set_cursor(const uint32_t i, const int16_t position); - + void set_data(int16_t* new_data); bool is_paused() const; void set_paused(bool paused); bool is_clickable() const; @@ -1110,7 +1110,7 @@ class OptionTabView : public View { private: Checkbox check_enable{ - {2 * 8, 0 * 16}, + {2 * 8, UI_POS_Y(0)}, 20, "", false}; diff --git a/firmware/common/utility.hpp b/firmware/common/utility.hpp index 363ef0f27..5109f378c 100644 --- a/firmware/common/utility.hpp +++ b/firmware/common/utility.hpp @@ -127,9 +127,11 @@ struct is_flags_type { template constexpr bool is_flags_type_v = is_flags_type::value; -#define ENABLE_FLAGS_OPERATORS(type) \ - template <> \ - struct is_flags_type { static constexpr bool value = true; }; +#define ENABLE_FLAGS_OPERATORS(type) \ + template <> \ + struct is_flags_type { \ + static constexpr bool value = true; \ + }; template constexpr std::enable_if_t, TEnum> operator|(TEnum a, TEnum b) { diff --git a/firmware/standalone/common/ui/ui_widget.cpp b/firmware/standalone/common/ui/ui_widget.cpp index 1c8966faa..f509f9d9a 100644 --- a/firmware/standalone/common/ui/ui_widget.cpp +++ b/firmware/standalone/common/ui/ui_widget.cpp @@ -39,1074 +39,902 @@ // using namespace portapack; // using namespace rtc_time; -namespace ui -{ +namespace ui { - static bool ui_dirty = true; +static bool ui_dirty = true; - void dirty_set() - { - _api->set_dirty(); - ui_dirty = true; +void dirty_set() { + _api->set_dirty(); + ui_dirty = true; +} + +void dirty_clear() { + ui_dirty = false; +} + +bool is_dirty() { + return ui_dirty; +} + +/* Widget ****************************************************************/ + +const std::vector Widget::no_children{}; + +Point Widget::screen_pos() { + return screen_rect().location(); +} + +Size Widget::size() const { + return _parent_rect.size(); +} + +Rect Widget::screen_rect() const { + return parent() ? (parent_rect() + parent()->screen_pos()) : parent_rect(); +} + +Rect Widget::parent_rect() const { + return _parent_rect; +} + +void Widget::set_parent_rect(const Rect new_parent_rect) { + _parent_rect = new_parent_rect; + set_dirty(); +} + +Widget* Widget::parent() const { + return parent_; +} + +void Widget::set_parent(Widget* const widget) { + if (widget == parent_) { + return; } - void dirty_clear() - { - ui_dirty = false; + if (parent_ && !widget) { + // We have a parent, but are losing it. Update visible status. + dirty_overlapping_children_in_rect(screen_rect()); + visible(false); } - bool is_dirty() - { - return ui_dirty; - } + parent_ = widget; - /* Widget ****************************************************************/ + set_dirty(); +} - const std::vector Widget::no_children{}; +void Widget::set_dirty() { + flags.dirty = true; + dirty_set(); +} - Point Widget::screen_pos() - { - return screen_rect().location(); - } +bool Widget::dirty() const { + return flags.dirty; +} - Size Widget::size() const - { - return _parent_rect.size(); - } +void Widget::set_clean() { + flags.dirty = false; +} - Rect Widget::screen_rect() const - { - return parent() ? (parent_rect() + parent()->screen_pos()) : parent_rect(); - } +void Widget::hidden(bool hide) { + if (hide != flags.hidden) { + flags.hidden = hide; - Rect Widget::parent_rect() const - { - return _parent_rect; - } + // If parent is hidden, either of these is a no-op. + if (hide) { + // TODO: Instead of dirtying parent entirely, dirty only children + // that overlap with this widget. - void Widget::set_parent_rect(const Rect new_parent_rect) - { - _parent_rect = new_parent_rect; - set_dirty(); - } + // parent()->dirty_overlapping_children_in_rect(parent_rect()); - Widget *Widget::parent() const - { - return parent_; - } - - void Widget::set_parent(Widget *const widget) - { - if (widget == parent_) - { - return; - } - - if (parent_ && !widget) - { - // We have a parent, but are losing it. Update visible status. - dirty_overlapping_children_in_rect(screen_rect()); - visible(false); - } - - parent_ = widget; - - set_dirty(); - } - - void Widget::set_dirty() - { - flags.dirty = true; - dirty_set(); - } - - bool Widget::dirty() const - { - return flags.dirty; - } - - void Widget::set_clean() - { - flags.dirty = false; - } - - void Widget::hidden(bool hide) - { - if (hide != flags.hidden) - { - flags.hidden = hide; - - // If parent is hidden, either of these is a no-op. - if (hide) - { - // TODO: Instead of dirtying parent entirely, dirty only children - // that overlap with this widget. - - // parent()->dirty_overlapping_children_in_rect(parent_rect()); - - /* TODO: Notify self and all non-hidden children that they're - * now effectively hidden? - */ - } - else - { - set_dirty(); - /* TODO: Notify self and all non-hidden children that they're - * now effectively shown? - */ - } - } - } - - void Widget::focus() - { - context().focus_manager().set_focus_widget(this); - } - - void Widget::on_focus() - { - } - - void Widget::blur() - { - context().focus_manager().set_focus_widget(nullptr); - } - - void Widget::on_blur() - { - } - - bool Widget::focusable() const - { - return flags.focusable; - } - - void Widget::set_focusable(const bool value) - { - flags.focusable = value; - } - - bool Widget::has_focus() - { - return (context().focus_manager().focus_widget() == this); - } - - bool Widget::on_key(const KeyEvent event) - { - (void)event; - return false; - } - - bool Widget::on_encoder(const EncoderEvent event) - { - (void)event; - return false; - } - - bool Widget::on_touch(const TouchEvent event) - { - (void)event; - return false; - } - bool Widget::on_keyboard(const KeyboardEvent event) - { - (void)event; - return false; - } - - const std::vector &Widget::children() const - { - return no_children; - } - - Context &Widget::context() const - { - if (parent_ == nullptr) - _api->panic("parent__ is null"); - - return parent()->context(); - } - - void Widget::set_style(const Style *new_style) - { - if (new_style != style_) - { - style_ = new_style; - set_dirty(); - } - } - - const Style &Widget::style() const - { - if (style_ != nullptr) - return *style_; - else - { - auto p = parent(); - if (p == nullptr) - // TODO: debug - while (true) - ; - return p->style(); - } - } - - void Widget::visible(bool v) - { - if (v != flags.visible) - { - flags.visible = v; - - /* TODO: This on_show/on_hide implementation seems inelegant. - * But I need *some* way to take/configure resources when - * a widget becomes visible, and reverse the process when the - * widget becomes invisible, whether the widget (or parent) is - * hidden, or the widget (or parent) is removed from the tree. + /* TODO: Notify self and all non-hidden children that they're + * now effectively hidden? */ - if (v) - { - on_show(); - } - else - { - on_hide(); + } else { + set_dirty(); + /* TODO: Notify self and all non-hidden children that they're + * now effectively shown? + */ + } + } +} - // Set all children invisible too. - for (const auto child : children()) - { - child->visible(false); - } +void Widget::focus() { + context().focus_manager().set_focus_widget(this); +} + +void Widget::on_focus() { +} + +void Widget::blur() { + context().focus_manager().set_focus_widget(nullptr); +} + +void Widget::on_blur() { +} + +bool Widget::focusable() const { + return flags.focusable; +} + +void Widget::set_focusable(const bool value) { + flags.focusable = value; +} + +bool Widget::has_focus() { + return (context().focus_manager().focus_widget() == this); +} + +bool Widget::on_key(const KeyEvent event) { + (void)event; + return false; +} + +bool Widget::on_encoder(const EncoderEvent event) { + (void)event; + return false; +} + +bool Widget::on_touch(const TouchEvent event) { + (void)event; + return false; +} +bool Widget::on_keyboard(const KeyboardEvent event) { + (void)event; + return false; +} + +const std::vector& Widget::children() const { + return no_children; +} + +Context& Widget::context() const { + if (parent_ == nullptr) + _api->panic("parent__ is null"); + + return parent()->context(); +} + +void Widget::set_style(const Style* new_style) { + if (new_style != style_) { + style_ = new_style; + set_dirty(); + } +} + +const Style& Widget::style() const { + if (style_ != nullptr) + return *style_; + else { + auto p = parent(); + if (p == nullptr) + // TODO: debug + while (true); + return p->style(); + } +} + +void Widget::visible(bool v) { + if (v != flags.visible) { + flags.visible = v; + + /* TODO: This on_show/on_hide implementation seems inelegant. + * But I need *some* way to take/configure resources when + * a widget becomes visible, and reverse the process when the + * widget becomes invisible, whether the widget (or parent) is + * hidden, or the widget (or parent) is removed from the tree. + */ + if (v) { + on_show(); + } else { + on_hide(); + + // Set all children invisible too. + for (const auto child : children()) { + child->visible(false); } } } +} - bool Widget::highlighted() const - { - return flags.highlighted; - } +bool Widget::highlighted() const { + return flags.highlighted; +} - void Widget::set_highlighted(const bool value) - { - flags.highlighted = value; - } +void Widget::set_highlighted(const bool value) { + flags.highlighted = value; +} - void Widget::dirty_overlapping_children_in_rect(const Rect &child_rect) - { - for (auto child : children()) - { - if (!child_rect.intersect(child->parent_rect()).is_empty()) - { - child->set_dirty(); - } +void Widget::dirty_overlapping_children_in_rect(const Rect& child_rect) { + for (auto child : children()) { + if (!child_rect.intersect(child->parent_rect()).is_empty()) { + child->set_dirty(); } } +} - void Widget::getAccessibilityText(std::string &result) - { - result = ""; - } - void Widget::getWidgetName(std::string &result) - { - result = ""; - } - /* View ******************************************************************/ +void Widget::getAccessibilityText(std::string& result) { + result = ""; +} +void Widget::getWidgetName(std::string& result) { + result = ""; +} +/* View ******************************************************************/ - void View::paint(Painter &painter) - { +void View::paint(Painter& painter) { + painter.fill_rectangle( + screen_rect(), + style().background); +} + +void View::add_child(Widget* const widget) { + if (widget) { + if (widget->parent() == nullptr) { + widget->set_parent(this); + children_.push_back(widget); + } + } +} + +void View::add_children(const std::initializer_list children) { + children_.insert(std::end(children_), children); + for (auto child : children) { + child->set_parent(this); + } +} + +void View::remove_child(Widget* const widget) { + if (widget) { + children_.erase(std::remove(children_.begin(), children_.end(), widget), children_.end()); + widget->set_parent(nullptr); + } +} + +void View::remove_children(const std::vector& children) { + for (auto child : children) { + remove_child(child); + } +} + +const std::vector& View::children() const { + return children_; +} + +std::string View::title() const { + return ""; +}; + +/* OptionTabView *********************************************************/ + +OptionTabView::OptionTabView(Rect parent_rect) { + set_parent_rect(parent_rect); + + add_child(&check_enable); + hidden(true); + + check_enable.on_select = [this](Checkbox&, bool value) { + enabled = value; + }; +} + +void OptionTabView::set_enabled(bool value) { + check_enable.set_value(value); +} + +bool OptionTabView::is_enabled() { + return check_enable.value(); +} + +void OptionTabView::set_type(std::string type) { + check_enable.set_text("Transmit " + type); +} + +void OptionTabView::focus() { + check_enable.focus(); +} + +/* Rectangle *************************************************************/ + +Rectangle::Rectangle( + Color c) + : Widget{}, + color{c} { +} + +Rectangle::Rectangle( + Rect parent_rect, + Color c) + : Widget{parent_rect}, + color{c} { +} + +void Rectangle::set_color(const Color c) { + color = c; + set_dirty(); +} + +void Rectangle::set_outline(const bool outline) { + _outline = outline; + set_dirty(); +} + +void Rectangle::paint(Painter& painter) { + if (!_outline) { painter.fill_rectangle( screen_rect(), - style().background); + color); + } else { + painter.draw_rectangle( + screen_rect(), + color); } +} - void View::add_child(Widget *const widget) +/* Text ******************************************************************/ + +Text::Text( + Rect parent_rect, + std::string text) + : Widget{parent_rect}, + text{std::move(text)} { +} + +Text::Text( + Rect parent_rect) + : Text{parent_rect, {}} { +} + +void Text::set(std::string_view value) { + text = std::string{value}; + set_dirty(); +} + +void Text::getAccessibilityText(std::string& result) { + result = text; +} +void Text::getWidgetName(std::string& result) { + result = "Text"; +} +void Text::paint(Painter& painter) { + const auto rect = screen_rect(); + auto s = has_focus() ? style().invert() : style(); + auto max_len = (unsigned)rect.width() / s.font.char_width(); + auto text_view = std::string_view{text}; + + painter.fill_rectangle(rect, s.background); + + if (text_view.length() > max_len) + text_view = text_view.substr(0, max_len); + + painter.draw_string( + rect.location(), + s, + text_view); +} + +/* Labels ****************************************************************/ + +Labels::Labels( + std::initializer_list