watchface settings and properties

Former Member
Former Member
Hi,

I don't know what happened to my other threads, please delete them.

Back to topic: I wan't to create some settings and properties to let the user choice some color for watchface.

I already create some settings and properties which works fine

<properties>
<property id="FONT_COLOR" type="number">1</property>
<property id="BACK_COLOR" type="number">2</property>
</properties>
<strings>
<string id="title_font_color">Font Color</string>
<string id="title_back_color">Background Color</string>

<string id="opt_color_1">White</string>
<string id="opt_color_2">Yellow</string>
<string id="opt_color_3">Orange</string>
<string id="opt_color_4">Red</string>
<string id="opt_color_5">Green</string>
<string id="opt_color_6">Blue</string>
<string id="opt_color_7">Black</string>
</strings>

<settings>
<setting propertyKey="@Properties.FONT_COLOR" title="@Strings.title_font_color">
<settingConfig type="list">
<listEntry value="1">Rez.Strings.opt_color_1</listEntry>
<listEntry value="2">Rez.Strings.opt_color_2</listEntry>
<listEntry value="3">Rez.Strings.opt_color_3</listEntry>
<listEntry value="4">Rez.Strings.opt_color_4</listEntry>
<listEntry value="5">Rez.Strings.opt_color_5</listEntry>
<listEntry value="6">Rez.Strings.opt_color_6</listEntry>
<listEntry value="7">Rez.Strings.opt_color_7</listEntry>
</settingConfig>
</setting>
<setting propertyKey="@Properties.BACK_COLOR" title="@Strings.title_back_color">
<settingConfig type="list">
<listEntry value="1">Rez.Strings.opt_color_1</listEntry>
<listEntry value="2">Rez.Strings.opt_color_2</listEntry>
<listEntry value="3">Rez.Strings.opt_color_3</listEntry>
<listEntry value="4">Rez.Strings.opt_color_4</listEntry>
<listEntry value="5">Rez.Strings.opt_color_5</listEntry>
<listEntry value="6">Rez.Strings.opt_color_6</listEntry>
<listEntry value="7">Rez.Strings.opt_color_7</listEntry>
</settingConfig>
</setting>
</settings>


How can I set the colors?
layout.xml
<label id="TimeLabel" x="center" y="center" font="Gfx.FONT_NUMBER_THAI_HOT" justification="Gfx.TEXT_JUSTIFY_CENTER" color="Gfx.COLOR_WHITE"></label>

watchface.mc
function onSettingsChanged() {
var font_color = App.getApp().getProperty("FONT_COLOR");
handleYourColorChangesHere(back_color);
var back_color = App.getApp().getProperty("BACK_COLOR");
handleYourColorChangesHere(back_color);
Ui.requestUpdate();
}


Greetings Ronny
  • It depends on how complicated a solution you want/need. This should work for simple cases...

    class MyBackground extends Ui.Drawable
    {
    hidden var callback;

    function initialize(params) {
    Drawable.initialize(params);
    callback = params[:callback];
    }

    function draw(dc) {
    var color = callback.invoke();

    dc.setColor(color, color);
    dc.clear();
    }
    }

    // extend Ui.Text, but throw away the specified color
    class MyText extends Ui.Text
    {
    hidden var callback;

    function initialize(params) {
    Text.initialize(params);
    callback = params[:callback];
    }

    function draw(dc) {
    Text.setColor(callback.invoke());
    Text.draw(dc);
    }
    }

    class MyApp extends App.AppBase
    {
    hidden var bgcolor;
    hidden var fgcolor;

    function onStart() {
    refreshColorSettings();
    }

    function onSettingsChanged() {
    refreshColorSettings();
    }

    function getInitialView() {
    return [ new MyView() ];
    }

    hidden function refreshColorSettings() {
    bgcolor = getProperty(BACK_COLOR);
    fgcolor = getProperty(FONT_COLOR);
    }

    function getBackgroundColor() {
    return bgcolor;
    }

    function getForegroundColor() {
    return fgcolor;
    }
    }


    Then define your layout to use the custom classes like this...

    <layout id="Layout">
    <drawable class="MyBackground">
    <param name="callback">App.getApp().method(:getBackgroundColor)</param>
    </drawable>
    <drawable class="MyText" id="TimeLabel">
    <param name="x">dc.getWidth() / 2</param>
    <param name="y">dc.getHeight() / 2</param>
    <param name="font">Gfx.FONT_NUMBER_THAI_HOT</param>
    <param name="justification">Gfx.TEXT_JUSTIFY_CENTER</param>
    <param name="callback">App.getApp().method(:getForegroundColor)</param>
    </drawable>
    </layout>


    Assuming I've not screwed this all up (I don't have access to a ConnectIQ build environment at the moment), it should just work.
  • Another option would be to use a Model-View-Controller design, which ends up being something like this (this is a lazy implementation).

    // just make these global to avoid having to keep calling through
    // App.getApp() to get them. they are technically part of the app
    // model, but we're being lazy.
    var global_bgcolor = Gfx.COLOR_BLACK;
    var global_fgcolor = Gfx.COLOR_WHITE;

    //
    // this class contains the application model data. that data can
    // be extracted into a separate class if it makes sense.
    //
    class MyApp extends App.AppBase
    {
    hidden var notify;

    function onStart() {
    refreshSettings();
    }

    function onSettingsChanged() {
    refreshSettings();
    }

    function onStop() {
    notify = null;
    }

    function getInitialView() {
    return [ new MyView() ];
    }

    hidden function refreshSettings() {
    global_bgcolor = getProperty(BACK_COLOR);
    global_fgcolor = getProperty(FONT_COLOR);

    if (notify != null) {
    notify.invoke(:color_changed);
    }

    // any other stuff
    }

    function setNotify(notify) {
    self.notify = notify;
    }

    function getBackgroundColor() {
    return bgcolor;
    }

    function getForegroundColor() {
    return fgcolor;
    }
    }

    class MyView extends Ui.View
    {
    hidden var layout;
    hidden var time_label;

    function initialize() {
    }

    function onLayout(dc) {
    layout = Rez.Layouts.MyViewLayout(dc);
    setLayout(layout);

    // get access to drawables as is necessary
    time_label = findDrawableById("TimeLabel");

    // set the initial colors. this could also have been done in the
    // layout specification.
    time_label.setColor(global_fgcolor);
    }

    function onShow() {
    // register ourselves with the model for update notifications
    App.getApp().setNotify(self.method(:notify));
    }

    function onHide() {
    // de-register ourselves from the model so we can be deallocated
    App.getApp().setNotify(null);
    }

    function notify(symbol) {
    if (symbol == :color_changed) {

    // apply colors as appropriate
    time_label.setColor(global_fgcolor);

    Ui.requestUpdate();
    }
    }

    function onUpdate(dc) {
    dc.setColor(global_bgcolor, global_bgcolor);
    dc.clear();

    // I believe that this will work, provided you don't explicitly create a background
    // drawable in your layout. If it does not work (you don't see your drawables), you
    // can disable this line and enable the ones that follow.
    View.onUpdate(dc);

    // for (var i = 0; i < layout.size(); ++i) {
    // layout.draw(dc);
    // }
    }
    }
    [/code]
  • Former Member
    Former Member
    4005

    Should I be honest? That's to much for me because I don't understand :( I'm not a programmer, just a person with some basics.

    If we say I just want to change the color of the timelabel, is there a way to replace the variable?

    <label id="TimeLabel" x="center" y="center" font="Gfx.FONT_NUMBER_THAI_HOT" justification="Gfx.TEXT_JUSTIFY_CENTER" color="Gfx.COLOR_WHITE"></label>

    I will try to explain my thoughts.

    • Initially one color is set (e.g. black)
    • User choose color blue in menu
    • variable color will set from Gfx.COLOR_WHITE to Gfx.COLOR_BLUE


    I want to keep it as simple as possible. I finally made the watchface primarily for myself and want to understand what I'm doing.

    Thanks & Greetings
  • Instead of referring to a color constant (Gfx.COLOR_WHITE in your example above), you can invoke a function or access a variable. Just make a global variable (say var gFontColor = Gfx.COLOR_WHITE; above your app class), and replace Gfx.COLOR_WHITE in the xml with gFontColor. If you do that, then every layout that you load will use the font color specified by that variable. This is a good start.

    The problem is now that the setting change notification (onSettingsChanged()) is sent to the app and you updated the color global, but the view does not know about the setting change. You need some way to tell the view to reload its layout or to walk through all of the drawables in the view and update the text color.

    Travis
  • Former Member
    Former Member
    How do I replace it in the xml- File?
  • With a text editor, just like you used to edit the file in the first place.

    <label id="TimeLabel" x="center" y="center" font="Gfx.FONT_NUMBER_THAI_HOT" justification="Gfx.TEXT_JUSTIFY_CENTER" color="gFontColor"></label>
  • Former Member
    Former Member
    I put in the variable

    ..
    using Toybox.Time.Gregorian as Calendar;
    using Toybox.ActivityMonitor as ActivityMonitor;

    var gFontColor = Gfx.COLOR_WHITE;

    class Simple_Digital_FaceView extends Ui.WatchFace {
    ..




    and get an error:

    BUILD: ERROR: Attribute 'color' in ID 'TimeLabel' has an invalid value 'gFontColor'
  • Up at the top, do you have

    "using Toybox.Graphics as Gfx;"?
  • Former Member
    Former Member
    I have

    Should I use custom classes @ layout?
  • Former Member
    Former Member
    If you have the label defined as
    <label id="TimeLabel" x="center" y="center" font="Gfx.FONT_NUMBER_THAI_HOT" justification="Gfx.TEXT_JUSTIFY_CENTER" color="Gfx.COLOR_WHITE"></label>
    You can get a reference to this label in your View class by calling
    var myLabel = View.findDrawableById("TimeLabel");
    This will return a Ui.Text object and save it to myLabel. The Ui.Text object has a function setColor(color). You can call this function and pass in a color value. After doing this you will need to call Ui.requestUpdate() to redraw the screen.
    function onUpdate(dc) {
    var myLabel = View.findDrawableById("TimeLabel");
    myLabel.setColor(Gfx.COLOR_BLUE);
    View.onUpdate(dc); // Calling the super class onUpdate function automatically calls Ui.requestUpdate()
    }