-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathview_status.h
204 lines (175 loc) · 8.59 KB
/
view_status.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
#pragma once // Please format this file with clang before check-in to GitHub
/*
File: view_status.h
Software: Barry Hansen, K7BWH, [email protected], Seattle, WA
Hardware: John Vanderbeck, KM7O, Seattle, WA
Purpose: Show the grid square's attributes and how it is
displayed on the screen. It gives the user a sense of
how to interpret the bread crumb trail, and how far to
to within a 6-digit grid square.
+-----------------------------------+
| * Grid Size and Scale > |...yRow1
| |...yRow2 (unused)
| Size CN87: 93 x 69 mi |...yRow3
| Size CN87us: 3.9 x 2.9 mi |...yRow4
| |
| Scale: 1 pixel = 0.5 miles |...yRow5
| |
| Jan 01, 2020 01:01:01 GMT |...yRow7
+------------:--:-------------------+
labelX valueX
*/
#include <Arduino.h>
#include <Adafruit_ILI9341.h> // TFT color display library
#include "constants.h" // Griduino constants and colors
#include "logger.h" // conditional printing to Serial port
#include "grid_helper.h" // lat/long conversion routines
#include "model_gps.h" // Model of a GPS for model-view-controller
#include "TextField.h" // Optimize TFT display text for proportional fonts
#include "view.h" // Base class for all views
// ========== extern ===========================================
extern Logger logger; // Griduino.ino
extern Grids grid; // grid_helper.h
extern Model *model; // "model" portion of model-view-controller
void floatToCharArray(char *result, int maxlen, double fValue, int decimalPlaces); // Griduino.ino
extern void showDefaultTouchTargets(); // Griduino.ino
// ========== class ViewStatus =================================
class ViewStatus : public View {
public:
// ---------- public interface ----------
// This derived class must implement the public interface:
ViewStatus(Adafruit_ILI9341 *vtft, int vid) // ctor
: View{vtft, vid} {
background = cBACKGROUND; // every view can have its own background color
}
void updateScreen();
void startScreen();
bool onTouch(Point touch);
protected:
// ---------- local data for this derived class ----------
// color scheme: see constants.h
// ========== text screen layout ===================================
// vertical placement of text rows
const int space = 30;
const int half = space / 2;
const int yRow1 = 18;
const int yRow2 = yRow1 + space;
const int yRow3 = yRow2 + half;
const int yRow4 = yRow3 + space;
const int yRow5 = yRow4 + space + half;
const int yRow6 = yRow5 + space;
const int yRow7 = 230; // GMT date on bottom row, "230" will match other views
const int labelX = 122; // right-align labels, near their values
const int valueX = 140; // left-align values
const int label2 = 100;
const int value2 = 118;
// ----- screen text
// names for the array indexes, must be named in same order as array below
enum txtIndex {
TITLE = 0,
GRID4,
SIZE4,
GRID6,
SIZE6,
SCALE_LABEL,
SCALE,
GMT_DATE,
GMT_TIME,
GMT
};
// ----- static + dynamic screen text
// clang-format off
#define nStatusValues 10
TextField txtValues[nStatusValues] = {
{"Grid Size and Scale", -1, yRow1, cTITLE, ALIGNCENTER, eFONTSMALLEST}, // [TITLE] view title, centered
{"CN87:", labelX, yRow3, cLABEL, ALIGNRIGHT, eFONTSMALL}, // [GRID4]
{"101 x 69 mi", valueX, yRow3, cHIGHLIGHT, ALIGNLEFT, eFONTSMALL}, // [SIZE4]
{"CN87us:", labelX, yRow4, cLABEL, ALIGNRIGHT, eFONTSMALL}, // [GRID6]
{"4.4 x 3.3 mi", valueX, yRow4, cVALUE, ALIGNLEFT, eFONTSMALL}, // [SIZE6]
{"Screen:", label2, yRow5, cLABEL, ALIGNRIGHT, eFONTSMALL}, // [SCALE_LABEL]
{"1 pixel = 0.7 mi", value2, yRow5, cVALUE, ALIGNLEFT, eFONTSMALL}, // [SCALE]
{"Apr 26, 2021", 130, yRow7, cFAINT, ALIGNRIGHT, eFONTSMALLEST}, // [GMT_DATE]
{"02:34:56", 148, yRow7, cFAINT, ALIGNLEFT, eFONTSMALLEST}, // [GMT_TIME]
{"GMT", 232, yRow7, cFAINT, ALIGNLEFT, eFONTSMALLEST}, // [GMT]
};
// clang-format on
}; // end class ViewStatus
// ============== implement public interface ================
void ViewStatus::updateScreen() {
// called on every pass through main()
setFontSize(12);
char sUnits[] = "mi";
if (model->gMetric) {
strcpy(sUnits, "km");
}
// ----- 4-digit grid size
char sGrid[10]; // strlen("CN87us") = 6
grid.calcLocator(sGrid, model->gLatitude, model->gLongitude, 4);
strcat(sGrid, ":");
txtValues[GRID4].print(sGrid);
// all North-South distances are the same but we'll calculate it anyway
float nextNorth = grid.nextGridLineNorth(model->gLatitude);
float nextSouth = grid.nextGridLineSouth(model->gLatitude);
float nextEast = grid.nextGridLineEast(model->gLongitude);
float nextWest = grid.nextGridLineWest(model->gLongitude);
int nsDistance = (int)round(grid.calcDistanceLat(nextNorth, nextSouth, model->gMetric));
int ewDistance = (int)round(grid.calcDistanceLong(model->gLatitude, nextEast, nextWest, model->gMetric));
char msg[33];
snprintf(msg, sizeof(msg), "%d x %d %s", ewDistance, nsDistance, sUnits);
txtValues[SIZE4].print(msg);
// ----- 6-digit grid size
grid.calcLocator(sGrid, model->gLatitude, model->gLongitude, 6);
strcat(sGrid, ":");
txtValues[GRID6].print(sGrid);
nextNorth = grid.nextGrid6North(model->gLatitude);
nextSouth = grid.nextGrid6South(model->gLatitude);
nextEast = grid.nextGrid6East(model->gLongitude);
nextWest = grid.nextGrid6West(model->gLongitude);
float fNS = grid.calcDistanceLat(nextNorth, nextSouth, model->gMetric);
float fEW = grid.calcDistanceLong(model->gLatitude, nextEast, nextWest, model->gMetric);
char sNS[10], sEW[10];
floatToCharArray(sNS, 8, fNS, 1);
floatToCharArray(sEW, 8, fEW, 1);
snprintf(msg, sizeof(msg), "%s x %s %s", sEW, sNS, sUnits);
txtValues[SIZE6].print(msg);
// ----- scale of our screen map and the breadcrumb trail
// averaging together the scale in each direction
// this is the minimum real-world travel required to turn on the next pixel
const double minLong = gridWidthDegrees / gBoxWidth; // longitude degrees from one pixel to the next
const double minLat = gridHeightDegrees / gBoxHeight; // latitude degrees from one pixel to the next
float ewScale = grid.calcDistanceLong(model->gLatitude, 0.0, minLong, model->gMetric);
float nsScale = grid.calcDistanceLat(0.0, minLat, model->gMetric);
float scale = (ewScale + nsScale) / 2;
char sScale[10];
floatToCharArray(sScale, 8, scale, 1);
snprintf(msg, sizeof(msg), "1 pixel = %s %s", sScale, sUnits);
txtValues[SCALE].print(msg);
// ----- GMT date & time
char sDate[15]; // strlen("Jan 12, 2020") = 13
char sTime[10]; // strlen("19:54:14") = 8
model->getDate(sDate, sizeof(sDate));
model->getTime(sTime);
txtValues[GMT_DATE].print(sDate);
txtValues[GMT_TIME].print(sTime);
txtValues[GMT].print();
} // end updateScreen
void ViewStatus::startScreen() {
// called once each time this view becomes active
this->clearScreen(this->background); // clear screen
txtValues[0].setBackground(this->background); // set background for all TextFields in this view
TextField::setTextDirty(txtValues, nStatusValues); // make sure all fields get re-printed on screen change
setFontSize(eFONTSMALL);
drawAllIcons(); // draw gear (settings) and arrow (next screen)
showDefaultTouchTargets(); // optionally draw box around default button-touch areas
showMyTouchTargets(0, 0); // no real buttons on this view
showScreenBorder(); // optionally outline visible area
showScreenCenterline(); // optionally draw visual alignment bar
// ----- draw fields that have static text
txtValues[TITLE].print();
txtValues[SCALE_LABEL].print();
updateScreen(); // update UI immediately, don't wait for the main loop to eventually get around to it
}
bool ViewStatus::onTouch(Point touch) {
logger.log(CONFIG, INFO, "->->-> Touched status screen.");
return false; // true=handled, false=controller uses default action
} // end onTouch()