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
|
// Author: cirakg & claude ai
// Summary: A scrollable text screen with a title: settings results, entry IDs, clock status, about, errors.
import Toybox.Graphics;
import Toybox.Lang;
import Toybox.WatchUi;
class InfoView extends WatchUi.View {
private var _title as String;
private var _text as Array<String>;
private var _screen as String;
private var _lines as Array<String>? = null; // wrapped, on first draw
// For each wrapped line: true when it starts one of the text items.
private var _starts as Array<Boolean> = [] as Array<Boolean>;
private var _first as Number = 0;
private var _visible as Number = 1;
private var _shownLogged as Boolean = false;
private var _lineH as Number = 1;
// screen names the view in "DIAG shown" lines for UI scripts.
function initialize(title as String, text as Array<String>, screen as String) {
View.initialize();
_title = title;
_text = text;
_screen = screen;
}
function onShow() as Void {
_shownLogged = false;
}
// Scroll by delta lines; returns false when already at that end.
function scroll(delta as Number) as Boolean {
var lines = _lines;
if (lines == null) {
return false;
}
var max = lines.size() - _visible > 0 ? lines.size() - _visible : 0;
var next = _first + delta;
next = next < 0 ? 0 : (next > max ? max : next);
Diag.data("info_scroll", "first=" + next);
if (next == _first) {
return false;
}
_first = next;
WatchUi.requestUpdate();
return true;
}
// The height of a text line, as last drawn (for drags).
function lineHeight() as Number {
return _lineH;
}
function onUpdate(dc as Graphics.Dc) as Void {
Ui.clear(dc);
var w = dc.getWidth();
var h = dc.getHeight();
var font = Graphics.FONT_XTINY;
var lineH = dc.getFontHeight(font);
_lineH = lineH;
var y = Ui.isRound() ? h / 8 : 6;
dc.setColor(Ui.accent(), Graphics.COLOR_TRANSPARENT);
dc.drawText(w / 2, y, Graphics.FONT_SMALL, Ui.fit(dc, _title, Graphics.FONT_SMALL, 2 * Ui.halfWidthAt(dc, y)),
Graphics.TEXT_JUSTIFY_CENTER);
y += dc.getFontHeight(Graphics.FONT_SMALL) + 4;
// The same margin on rectangles: the Venu X1's rounded corners cut
// off a last line drawn at the very bottom, and the scroll arrow.
var bottom = h - h / 10;
var lines = _lines;
if (lines == null) {
// Wrap to the narrowest width the text area has.
var width = 2 * Ui.halfWidthBetween(dc, y, bottom) - 8;
lines = [] as Array<String>;
for (var i = 0; i < _text.size(); i++) {
var wrapped = Ui.wrap(dc, _text[i], font, width);
for (var k = 0; k < wrapped.size(); k++) {
_starts.add(k == 0);
}
lines.addAll(wrapped);
}
_lines = lines;
}
dc.setColor(Ui.fg(), Graphics.COLOR_TRANSPARENT);
// A third of a line between items, so wrapped items stay apart.
var gap = lineH / 3;
var shown = 0;
for (var i = _first; i < lines.size() && y + lineH <= bottom; i++) {
if (_starts[i] && i != _first) {
y += gap;
if (y + lineH > bottom) {
break;
}
}
dc.drawText(w / 2, y, font, lines[i], Graphics.TEXT_JUSTIFY_CENTER);
y += lineH;
shown++;
}
_visible = shown > 0 ? shown : 1;
if (_first + shown < lines.size()) {
var cx = w / 2;
dc.setColor(Ui.dim(), Graphics.COLOR_TRANSPARENT);
dc.fillPolygon([[cx - 6, bottom], [cx + 6, bottom], [cx, bottom + 6]] as Array<Graphics.Point2D>);
}
if (!_shownLogged) {
_shownLogged = true;
Diag.shown(_screen);
}
}
}
// Input on a text screen: scroll, BACK closes (and leaves the app when this
// is the only screen, as for start-up errors).
class InfoDelegate extends WatchUi.BehaviorDelegate {
function initialize() {
BehaviorDelegate.initialize();
}
private function view() as InfoView? {
var current = WatchUi.getCurrentView()[0];
return current instanceof InfoView ? current : null;
}
function onNextPage() as Boolean {
if (!Gesture.recent()) {
scrollBy(3);
}
return true;
}
function onPreviousPage() as Boolean {
if (!Gesture.recent()) {
scrollBy(-3);
}
return true;
}
private function scrollBy(lines as Number) as Void {
AppState.activity();
var v = view();
if (v != null) {
v.scroll(lines);
}
}
// A vertical swipe or drag scrolls too (the Forerunner 70 sends no page
// behaviour for them); Gesture makes a gesture count once.
function onSwipe(event as WatchUi.SwipeEvent) as Boolean {
if (!Gesture.isVertical(event)) {
return BehaviorDelegate.onSwipe(event);
}
var step = Gesture.swipeStep(event);
if (step != 0) {
scrollBy(3 * step);
}
return true;
}
function onDrag(event as WatchUi.DragEvent) as Boolean {
var v = view();
var step = Gesture.dragStep(event, v != null ? v.lineHeight() : 1);
if (step == 0) {
return false;
}
scrollBy(step);
return true;
}
function onBack() as Boolean {
AppState.activity();
return false;
}
}
|