// 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; private var _screen as String; private var _lines as Array? = null; // wrapped, on first draw // For each wrapped line: true when it starts one of the text items. private var _starts as Array = [] as Array; private var _first as Number = 0; private var _visible as Number = 1; private var _shownLogged as Boolean = false; // screen names the view in "DIAG shown" lines for UI scripts. function initialize(title as String, text as Array, 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); if (next == _first) { return false; } _first = next; WatchUi.requestUpdate(); return true; } 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); 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; var bottom = Ui.isRound() ? h - h / 10 : h - 4; 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; 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); } 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 { AppState.activity(); var v = view(); if (v != null) { v.scroll(3); } return true; } function onPreviousPage() as Boolean { AppState.activity(); var v = view(); if (v != null) { v.scroll(-3); } return true; } function onBack() as Boolean { AppState.activity(); return false; } }