Non-coding layman failed in putting steps in absolute numbers on plain watch face

Former Member
Former Member
Hi,

I've no experience in coding but I have been attempting to design my own watch face. So far, I have gotten the date and time the way I like it based on some stuff I found via google. But I have not had any success on steps. I have googled around and check various sources (including asking for tips from one of the senior members here) but couldn't figure out how to do it. The sample found in the sample folder of the SDK does it in terms of a bar and I can't for the life of me figure out how to change that to integers. So I was wondering if someone can see where I went wrong. I got the watch face to show, but the steps counter doesn't. And when I simulated the steps data, nothing changes until I get an error message:

"Failed invoking <symbol>
Invalid Font Specified
in onUpdate (C:\Users\....\workspace\bare_bones\source\bare_bonesView.mc:43)



using Toybox.WatchUi as Ui;
using Toybox.Graphics as Gfx;
using Toybox.System as Sys;
using Toybox.Lang as Lang;
using Toybox.Time as Time;
using Toybox.Time.Gregorian as Calendar;
using Toybox.ActivityMonitor as Act;

class bare_bonesView extends Ui.WatchFace {

function initialize() {
WatchFace.initialize();
}

// Load your resources here
function onLayout(dc) {
setLayout(Rez.Layouts.WatchFace(dc));
}

// Called when this View is brought to the foreground. Restore
// the state of this View and prepare it to be shown. This includes
// loading resources into memory.
function onShow() {
}

// Update the view
function onUpdate(dc) {
// Get and show the current day
var dateInfo = Calendar.info( Time.now(), Calendar.FORMAT_MEDIUM );
var dateString = Lang.format("$1$ $2$ $3$", [ dateInfo.day_of_week, dateInfo.day, dateInfo.month ]);
var date = View.findDrawableById("DateLabel"); date.setText(dateString);

// Get and show the current time
var clockTime = Sys.getClockTime();
var timeString = Lang.format("$1$:$2$", [clockTime.hour.format("%02d"), clockTime.min.format("%02d")]);
var view = View.findDrawableById("TimeLabel");
view.setText(timeString);

// Get and show the steps (this part's screwed)
var activityInfo = Act.getInfo();
var stepsString = activityInfo.steps;
dc.setColor(Gfx.COLOR_WHITE, Gfx.COLOR_BLACK);
dc.drawText(50, 100, stepsString, Gfx.FONT_SYSTEM_LARGE, Gfx.TEXT_JUSTIFY_RIGHT);
// end of screwed part


// Call the parent onUpdate function to redraw the layout
View.onUpdate(dc);
}

// Called when this View is removed from the screen. Save the
// state of this View here. This includes freeing resources from
// memory.
function onHide() {
}

// The user has just looked at their watch. Timers and animations may be started here.
function onExitSleep() {
}

// Terminate any active timers and prepare for slow updates.
function onEnterSleep() {
}

}
  • dc.drawText(145, 0, customFont, "P", Gfx.TEXT_JUSTIFY_RIGHT); //P for battery POWER


    It doesn't look like he's using a custom font,
    The VAHR has a 148×205 display, so I'm pretty confident the outline of the battery jaesurn is using is a glyph in a custom font.

    I only really noticed because I'm using the same approach, after picking up that idea reading one of your helpful posts in this forum. :)
  • The VAHR has a 148×205 display, so I'm pretty confident the outline of the battery jaesurn is using is a glyph in a custom font.

    I only really noticed because I'm using the same approach, after picking up that idea reading one of your helpful posts in this forum. :)


    But he's using fillrectangle() for the level! :)

    In my iconfont, I actually have 3 batteries - full, half, and empty. Switch to half at 50%, and at 25%, make it the "warn" color, and below 10% (IIRC) use the empty one in the warn color. I found that changing the level in the icon based on the specific level wasn't really needed or easy to tell) when the % is displayed next to it..
  • If using a font glyph for the battery border, it might make sense to draw the fill with a glyph as well...

    // `font' needs to have glyphs for "B01234". 'B' is the battery frame
    // and '0' to '4' are overlays for battery levels from low to high.

    // draw the battery frame in white
    dc.setColor(Gfx.COLOR_WHITE, Gfx.COLOR_TRANSPARENT);
    dc.drawText(x, y, font, "B", Gfx.TEXT_JUSTIFY_RIGHT);

    var systemStats = Sys.getSystemStats();

    // given battery 0.0 to 100.0, get a value from 0.0 to 1.0
    var battery0to1 = (systemStats.battery / 100);

    var colors = [
    Gfx.COLOR_RED,
    Gfx.COLOR_ORANGE,
    Gfx.COLOR_YELLOW,
    Gfx.COLOR_GREEN,
    Gfx.COLOR_GREEN,
    Gfx.COLOR_GREEN,
    Gfx.COLOR_GREEN,
    Gfx.COLOR_GREEN,
    Gfx.COLOR_GREEN,
    Gfx.COLOR_BLUE,
    ];

    // pick color based on battery level
    var color = colors[ battery0to1 * colors.size() ];

    var glyphs = [
    "0",
    "1",
    "2",
    "3",
    "4"
    ];

    // pick glyph based on battery level
    var glyph = glyphs[ battery0to1 * glyphs.size() ];

    // draw the battery fill area
    dc.setColor(color, Gfx.COLOR_TRANSPARENT);
    dc.drawText(x, y, font, glyph, Gfx.TEXT_JUSTIFY_RIGHT);


    Travis
  • I found that changing the level in the icon based on the specific level wasn't really needed or easy to tell) when the % is displayed next to it..
    In the case of my watch face, I don't have room to show the percentage. The rectangular space inside the battery cell outline is 20 pixels high, so I use it to represent the battery level in icosiles.



    On another note, I'm currently using layouts, but I'm finding the memory usage of the watch face to be quite over the top. Simply removing the battery cell from the layout, and instead performing an explicit [FONT=Courier New]drawText()[/FONT] to put the battery cell glyph in place, manages to shave 4.9kB off the memory usage. (Replacing the glyph with two explicit [FONT=Courier New]drawRectangle()[/FONT] calls, without stripping the glyph from the font altogether, has almost the same effect on memory usage.)

    it might make sense to draw the fill with a glyph as well&#8230;
    I'm not sure I can afford to put another 20 glyphs in the font, for fear of making the PRG file or the memory usage hit the respective limit. As it is, I'll probably have to do a complete rewrite that does not use layouts, and will pare the font files down as well to the bare minimum.
  • With your approach Travis, it is nice you can change the color based on battery level separate from the frame. As I said, I took approach with the outline and level as a single icon (well 3, one for full, half, and empty) and do it in the same color as the other icons (unless I use the "warn" color for a low condition), so there's no issue with something like the frame being lost if a white background is used for the watch face. Maybe make the frame color configurable if you allow background color configuration?
  • With your approach Travis, it is nice you can change the color based on battery level separate from the frame.

    Yes. It also makes placement of the fill color trivial (you just render the glyph at the same position), which makes it easier to support different devices or to tweak the look/feel of the battery display.

    so there's no issue with something like the frame being lost if a white background is used for the watch face. Maybe make the frame color configurable if you allow background color configuration?

    Yes. If you know the background color, you can draw the outline in a different color pretty easily.

    I'm not sure I can afford to put another 20 glyphs in the font

    You don't need 20 glyphs. In my example, you need 6.

    As it is, I'll probably have to do a complete rewrite that does not use layouts, and will pare the font files down as well to the bare minimum.

    Yeah, it has been noted many times that the layout system is costly. Using custom fonts is not free either. That said, you should pare the font files down to the minimum anyway; font data is quite expensive in terms of memory usage.

    There was a bug a while ago that each label in the layout got its own copy of the font data. If you used the same custom font for multiple things it was quite costly. The workaround was to set the font for each of the drawables in onLayout() so they shared the data. I'm curious if that is affecting you somehow. I also wonder if memory could be saved by loading/unloading the fonts as each element is drawn, instead of drawing them all with a call to View.onUpdate()...

    Travis
  • I'm not sure I can afford to put another 20 glyphs in the font, for fear of making the PRG file or the memory usage hit the respective limit. As it is, I'll probably have to do a complete rewrite that does not use layouts, and will pare the font files down as well to the bare minimum.


    With using custom fonts for the time, are you limiting what's included (just 0-9 and : for example)? The larger the font, the more space chars will take. Also, you can save some by not setting antialias=true. If things look a bit rough, you can clean it up by doing some pixel editing in the .png for the font (I just checked one of my WF's and antialias=true took and extra 4k or so for a large font for the time with just 0-9 and : )

    Removing layouts will likely save both memory and be a tiny bit fasters, and if you do so, you may want to consider how you do things for different devices. Basically, I set a few things in onLayout() (x/y/font) based on the screen.

    As a reference, my "Simple Big" watch face uses a large custom font for time (as well as my iconfont with maybe 20-25 icons), fully configurable color wise as well as what's displayed, no layouts, and a bit of extra code (I use some shared code and not all of it is used in all watchfaces when it comes to some settings/options, etc), and my peak usage is about 30K, and that's the same on all supported devices. Similar WFs that doesn't use the custom font for time but the same shared code, cut them down to the 25k range.
  • You don't need 20 glyphs. In my example, you need 6.
    I'm using the 20-pixel-tall space inside the battery cell outline is used to represent icosiles (so, one pixel for each 5% ‘grade’), so I'm not sure how six glyphs will suffice if I want to draw the remaining charge with a single [FONT=Courier New]drawText()[/FONT].

    By the way, the way you pick the colour is clever and I hadn't thought of that, so thank you very much for showing me.

    you should pare the font files down to the minimum anyway;
    Agreed, given the individual glyphs are not dynamically loaded as required. It just means a little more work to clone a more comprehensive font definition from my library each time and then tailor the cloned files that will be embedded in an app.

    If you used the same custom font for multiple things it was quite costly. The workaround was to set the font for each of the drawables in onLayout() so they shared the data. I'm curious if that is affecting you somehow.
    Thanks, I'll check that out this weekend and let you know.

    With using custom fonts for the time, are you limiting what's included (just 0-9 and : for example)?
    More or less. Right now I've unnecessarily included the period, the hyphen and the slash in the PNG file (but not the FNT file, or the filter string in the fonts.xml file).

    (I just checked one of my WF's and antialias=true took and extra 4k or so for a large font for the time with just 0-9 and : )
    When I tried that a few days ago, I had about the same 4kB saving by removing antialias=true for both of the extra large fonts (for hour and minutes) I'm using. They still looked OK in the simulator, but I haven't checked out the difference visually on Forerunner watches yet.

    Removing antialias=true for the icons font would make it look pretty crap, though.

    Removing layouts will likely save both memory and be a tiny bit fasters, and if you do so, you may want to consider how you do things for different devices. Basically, I set a few things in onLayout() (x/y/font) based on the screen.
    I only have a FR235 and a FR630, so (at least for this watch face, which is my first attempt at Connect&#8209;IQ) I'm only planning on it being released for FR230/235/630/735XT. Thanks for the tip, though.
  • When using custom fonts, you may want to check out Hermo's post
    https://developer.garmin.com/index.php/blog/post/connect-iq-pro-tip-custom-fonts-tricks

    I asked Hermo about antialias and what he saw with antialias on/off in general, and he told me he keeps it off to save memory, and just does pixel editing instead to make things look clean, so you may want to think about doing that. (that's what I did in my icon font long ago, and I use the same iconfont in a number of things so it paid off a number of times, but wasn't sure about larger custom fonts)

    When it comes to custom fonts and your icons, you may want to turn off anti alias, and do a bit of pixel editing to clean them up to save memory.
  • My NoFrills watch face more or less falls in the same category as yours in terms of a large font that dominates most of the screen.

    To give you hope:

    It uses 2 custom fonts, 1 large and 1 smaller for the clock
    It uses another font for icons
    It uses the layout system with the option to switch between large and smaller clock layouts.
    It does some drawing for the progress (water fill)

    And still manages to consume relatively little memory:

    fenix 3: 37.8kB , Peak: 42.4kB (4 different layouts)
    Fenix 5S, 5 and 5X: 40.0kB , Peak: 44.7kB (4 different layouts)

    fr 235: 35.9kB , Peak: 40.5kB (2 different layouts)
    fr 735xt: 36.2kB , Peak: 40.8kB (2 different layouts)