1
0
Fork 0

Compare commits

...

10 commits

Author SHA1 Message Date
a7faa14345 main font: change to 'open_sans_light' 2025-01-02 11:55:15 +00:00
aa3401bd52 QuickSettings: add brief documention comment 2025-01-02 11:55:15 +00:00
14092ef883 justfile: add app: Calculator 2025-01-02 11:55:15 +00:00
17abbdd079 README: illustrate some Trax.im tweaks with screen-shots 2025-01-02 11:55:15 +00:00
6c59dd0d1c step-count (Analog, Digital, Terminal) grey-out when zero 2025-01-02 11:55:15 +00:00
5d15a0df3c Motion: change icon from 'M' to 'XYZ' 2025-01-02 11:55:15 +00:00
0745ebd82c Calculator: move 160 bytes from RAM to flash
Making the 'button matrix' arrays 'const' saves 160 bytes of non-stack
RAM, which in this context is a lot, and makes it fit (for me) in a
real-device build along with my choice of other apps.
2025-01-02 11:55:15 +00:00
JustScott
68384fc34e Add a new Calculator App
InfiniTime PR  "Calculator App"

Co-authored-by: Raupinger <fgrauper@gmail.com>
Co-authored-by: Florian <florian@florian-thinkpad.local>
Updated to 1.14 by: JustScott
2025-01-02 11:55:15 +00:00
bf786dac8e add a 'justfile' for building 2025-01-02 11:55:15 +00:00
a8a99bc462 docker: allow passing arguments to build 2025-01-02 11:55:15 +00:00
23 changed files with 560 additions and 28 deletions

View file

@ -1,3 +1,17 @@
# The Trax.im fork of PineTime OS InfiniTime
Includes some tweaks such as:
|Analogue |Digital |Terminal
|---|---|---
|![](doc/img/InfiniSim_2024-08-27-ana.png)|![](doc/img/InfiniSim_2024-08-27-dig.png)|![](doc/img/InfiniSim_2024-08-27-term.png)
|Add [Calculator](https://github.com/InfiniTimeOrg/InfiniTime/pull/375) |Tweak Settings |Pulse: never say '0'
|---|---|---
|![](doc/img/InfiniSim_2024-08-28-calculator.png)|![](doc/img/InfiniSim_2024-08-28-settings1.png)|![](doc/img/InfiniSim_2024-08-27-nopulse-ana.png)
---
<div align="center">
![Header Image](doc/logo/watchface_collage.png)

8
doc/Calculator.md Normal file
View file

@ -0,0 +1,8 @@
# Calculator Manual
This is a simple Calculator with support for the four basic arithmetic operations, parenthesis and exponents.
Here is what you need to know to make full use of it:
- Swipe left to access parenthesis and exponents
- A long tap on the screen will reset the text field to `0`.
- If the entered term is invalid, the watch will vibrate.
- results are rounded to 4 digits after the decimal point
- **TIP:** you can use `^(1/2)` to calculate square roots

Binary file not shown.

After

(image error) Size: 6.8 KiB

Binary file not shown.

After

(image error) Size: 2.5 KiB

Binary file not shown.

After

(image error) Size: 6.3 KiB

Binary file not shown.

After

(image error) Size: 3.9 KiB

Binary file not shown.

After

(image error) Size: 4.3 KiB

Binary file not shown.

After

(image error) Size: 4.2 KiB

View file

@ -26,6 +26,7 @@ export GCC_ARM_PATH="gcc-arm-none-eabi-$GCC_ARM_VER"
main() {
local target="$1"
shift
mkdir -p "$TOOLS_DIR"
@ -35,7 +36,7 @@ main() {
mkdir -p "$BUILD_DIR"
CmakeGenerate
CmakeGenerate "$@"
CmakeBuild $target
BUILD_RESULT=$?
if [ "$DISABLE_POSTBUILD" != "true" -a "$BUILD_RESULT" == 0 ]; then
@ -68,13 +69,16 @@ CmakeGenerate() {
-DARM_NONE_EABI_TOOLCHAIN_PATH="$TOOLS_DIR/$GCC_ARM_PATH" \
-DNRF5_SDK_PATH="$TOOLS_DIR/$NRF_SDK_VER" \
-DBUILD_DFU=1 \
-DBUILD_RESOURCES=1
-DBUILD_RESOURCES=1 \
"$@"
}
# CmakeBuild [target [CMAKE_ARGS...]]
# CMAKE_ARGS can include a '--' arg, with the same meaning as it has in 'cmake'.
CmakeBuild() {
local target="$1"
[ -n "$target" ] && target="--target $target"
cmake --build "$BUILD_DIR" --config $BUILD_TYPE $target -- -j$(nproc)
[ -n "$target" ] && target="--target $target" && shift
cmake --build "$BUILD_DIR" --config $BUILD_TYPE $target "$@" -- -j$(nproc)
BUILD_RESULT=$?
return $BUILD_RESULT
}

48
justfile Normal file
View file

@ -0,0 +1,48 @@
set positional-arguments
default:
@just --list --justfile {{justfile()}}
# Specifying apps and watchfaces: see doc/code/Apps.md
# defaults: see src/displayapp/apps/CMakeLists.txt
USERAPPS_DEFAULT := "StopWatch Alarm Timer Steps HeartRate Music Paint Paddle Twos Metronome Navigation" # Weather Motion
export USERAPPS := "Steps Motion HeartRate StopWatch Alarm Timer Metronome Music Paint Paddle Calculator"
export WATCHFACES := "Analog Digital Terminal"
build-all: (build-app)
build-app \
ot="PineTime" ots="pine64" \
u=`python3 -c "print(','.join(['Apps::'+x for x in '${USERAPPS}'.split()]))"` \
w=`python3 -c "print(','.join(['WatchFace::'+x for x in '${WATCHFACES}'.split()]))"` \
: (build 'pinetime-mcuboot-app' '-DOWNER_TEXT='+ot '-DOWNER_TEXT_SHORT='+ots '-DENABLE_USERAPPS='+u '-DENABLE_WATCHFACES='+w)
test1: (build "pinetime-mcuboot-app" "-D`just get-userapps-arg '${USERAPPS}'`" "-D`just get-watchfaces-arg '${WATCHFACES}'`")
build *TARGET_AND_CMAKE_ARGS:
docker run --rm -it -v ${PWD}:/sources --user $(id -u):$(id -g) ${MY_REGISTRY}/infinitime-build /opt/build.sh "$@"
builder:
docker build -t ${MY_REGISTRY}/infinitime-build ./docker
#docker push
list-userapps-default:
sed -n -e 's/^ *\(#*\)set(DEFAULT_USER_APP_TYPES .*\(Apps::[^"]*\).*/\1\2/;T;p' < ./src/displayapp/apps/CMakeLists.txt
list-watchfaces-default:
sed -n -e 's/^ *\(#*\)set(DEFAULT_WATCHFACE_TYPES .*\(WatchFace::[^"]*\).*/\1\2/;T;p' < ./src/displayapp/apps/CMakeLists.txt
list-userapps-all:
sed -n -e '/enum class \<Apps\>.*{/,/}/p' < ./src/displayapp/apps/Apps.h.in
list-watchfaces-all:
sed -n -e '/enum class \<WatchFace\>.*{/,/}/p' < ./src/displayapp/apps/Apps.h.in
get-userapps-arg apps:
#!/usr/bin/env python3
print('ENABLE_USERAPPS=' + ','.join(['Apps::'+x for x in "{{apps}}".split()]), end='')
get-watchfaces-arg faces:
#!/usr/bin/env python3
print('ENABLE_WATCHFACES=' + ','.join(['WatchFace::'+x for x in "{{faces}}".split()]), end='')

View file

@ -390,6 +390,7 @@ list(APPEND SOURCE_FILES
displayapp/screens/CheckboxList.cpp
displayapp/screens/BatteryInfo.cpp
displayapp/screens/Steps.cpp
displayapp/screens/Calculator.cpp
displayapp/screens/Timer.cpp
displayapp/screens/Dice.cpp
displayapp/screens/PassKey.cpp
@ -610,6 +611,7 @@ set(INCLUDE_FILES
displayapp/screens/HeartRate.h
displayapp/screens/Metronome.h
displayapp/screens/Motion.h
displayapp/screens/Calculator.h
displayapp/screens/Timer.h
displayapp/screens/Dice.h
displayapp/screens/Alarm.h

View file

@ -23,6 +23,7 @@
#include "displayapp/screens/SystemInfo.h"
#include "displayapp/screens/Tile.h"
#include "displayapp/screens/Twos.h"
#include "displayapp/screens/Calculator.h"
#include "displayapp/screens/FlashLight.h"
#include "displayapp/screens/BatteryInfo.h"
#include "displayapp/screens/Steps.h"

View file

@ -26,6 +26,7 @@ namespace Pinetime {
StopWatch,
Metronome,
Motion,
Calculator,
Steps,
Dice,
Weather,

View file

@ -14,6 +14,7 @@ else ()
set(DEFAULT_USER_APP_TYPES "${DEFAULT_USER_APP_TYPES}, Apps::Metronome")
set(DEFAULT_USER_APP_TYPES "${DEFAULT_USER_APP_TYPES}, Apps::Navigation")
set(DEFAULT_USER_APP_TYPES "${DEFAULT_USER_APP_TYPES}, Apps::Weather")
set(DEFAULT_USER_APP_TYPES "${DEFAULT_USER_APP_TYPES}, Apps::Calculator")
#set(DEFAULT_USER_APP_TYPES "${DEFAULT_USER_APP_TYPES}, Apps::Motion")
set(USERAPP_TYPES "${DEFAULT_USER_APP_TYPES}" CACHE STRING "List of user apps to build into the firmware")
endif ()

View file

@ -2,17 +2,16 @@
"jetbrains_mono_bold_20": {
"sources": [
{
"file": "JetBrainsMono-Bold.ttf",
"file": "open_sans_light.ttf",
"range": "0x20-0x7e, 0x410-0x44f, 0xB0"
},
{
"file": "FontAwesome5-Solid+Brands+Regular.woff",
"range": "0xf294, 0xf242, 0xf54b, 0xf21e, 0xf1e6, 0xf017, 0xf129, 0xf03a, 0xf185, 0xf560, 0xf001, 0xf3fd, 0xf1fc, 0xf45d, 0xf59f, 0xf5a0, 0xf027, 0xf028, 0xf6a9, 0xf04b, 0xf04c, 0xf048, 0xf051, 0xf095, 0xf3dd, 0xf04d, 0xf2f2, 0xf024, 0xf252, 0xf569, 0xf06e, 0xf015, 0xf00c, 0xf0f3, 0xf522, 0xf743"
"range": "0xf294, 0xf242, 0xf54b, 0xf21e, 0xf1e6, 0xf017, 0xf129, 0xf03a, 0xf185, 0xf560, 0xf001, 0xf3fd, 0xf1fc, 0xf45d, 0xf59f, 0xf5a0, 0xf027, 0xf028, 0xf6a9, 0xf04b, 0xf04c, 0xf048, 0xf051, 0xf095, 0xf3dd, 0xf04d, 0xf2f2, 0xf024, 0xf252, 0xf569, 0xf06e, 0xf015, 0xf00c, 0xf0f3, 0xf522, 0xf743, 0xf743, 0xf1ec"
}
],
"bpp": 1,
"size": 20,
"patches": ["jetbrains_mono_bold_20.c_zero.patch", "jetbrains_mono_bold_20.c_M.patch"]
"size": 20
},
"jetbrains_mono_42": {
"sources": [

View file

@ -0,0 +1,394 @@
#include "Calculator.h"
#include <string>
#include <stack>
#include <cfloat>
#include <cmath>
#include <map>
#include <memory>
using namespace Pinetime::Applications::Screens;
// Anonymous Namespace for all the structs
namespace {
struct CalcTreeNode {
virtual double calculate() = 0;
};
struct NumNode : CalcTreeNode {
double value;
double calculate() override {
return value;
};
};
struct BinOp : CalcTreeNode {
std::shared_ptr<CalcTreeNode> left;
std::shared_ptr<CalcTreeNode> right;
char op;
double calculate() override {
// make sure we have actual numbers
if (!right || !left) {
errno = EINVAL;
return 0.0;
}
double rightVal = right->calculate();
double leftVal = left->calculate();
switch (op) {
case '^':
// detect overflow
if (log2(leftVal) + rightVal > 31) {
errno = ERANGE;
return 0.0;
}
return pow(leftVal, rightVal);
case 'x':
// detect over/underflowflow
if ((DBL_MAX / abs(rightVal)) < abs(leftVal)) {
errno = ERANGE;
return 0.0;
}
return leftVal * rightVal;
case '/':
// detect under/overflow
if ((DBL_MAX * abs(rightVal)) < abs(leftVal)) {
errno = ERANGE;
return 0.0;
}
// detect divison by zero
if (rightVal == 0.0) {
errno = EDOM;
return 0.0;
}
return leftVal / rightVal;
case '+':
// detect overflow
if ((DBL_MAX - rightVal) < leftVal) {
errno = ERANGE;
return 0.0;
}
return leftVal + rightVal;
case '-':
// detect underflow
if ((DBL_MIN + rightVal) > leftVal) {
errno = ERANGE;
return 0.0;
}
return leftVal - rightVal;
}
errno = EINVAL;
return 0.0;
};
};
uint8_t getPrecedence(char op) {
switch (op) {
case '^':
return 4;
case 'x':
case '/':
return 3;
case '+':
case '-':
return 2;
}
return 0;
}
bool leftAssociative(char op) {
switch (op) {
case '^':
return false;
case 'x':
case '/':
case '+':
case '-':
return true;
}
return false;
}
}
static void eventHandler(lv_obj_t* obj, lv_event_t event) {
auto calc = static_cast<Calculator*>(obj->user_data);
calc->OnButtonEvent(obj, event);
}
Calculator::~Calculator() {
lv_obj_clean(lv_scr_act());
}
static const char* const buttonMap1[] = {
"7", "8", "9", "/", "\n",
"4", "5", "6", "x", "\n",
"1", "2", "3", "-", "\n",
".", "0", "=", "+", "",
};
static const char* const buttonMap2[] = {
"7", "8", "9", "(", "\n",
"4", "5", "6", ")", "\n",
"1", "2", "3", "^", "\n",
".", "0", "=", "+", "",
};
Calculator::Calculator(Controllers::MotorController& motorController) : motorController {motorController} {
result = lv_label_create(lv_scr_act(), nullptr);
lv_label_set_long_mode(result, LV_LABEL_LONG_BREAK);
lv_label_set_text(result, "0");
lv_obj_set_size(result, 180, 60);
lv_obj_set_pos(result, 0, 0);
returnButton = lv_btn_create(lv_scr_act(), nullptr);
lv_obj_set_size(returnButton, 52, 52);
lv_obj_set_pos(returnButton, 186, 0);
lv_obj_t* returnLabel;
returnLabel = lv_label_create(returnButton, nullptr);
lv_label_set_text(returnLabel, "<=");
lv_obj_align(returnLabel, nullptr, LV_ALIGN_CENTER, 0, 0);
returnButton->user_data = this;
lv_obj_set_event_cb(returnButton, eventHandler);
buttonMatrix = lv_btnmatrix_create(lv_scr_act(), nullptr);
lv_btnmatrix_set_map(buttonMatrix, const_cast<const char **>(buttonMap1));
lv_obj_set_size(buttonMatrix, 240, 180);
lv_obj_set_pos(buttonMatrix, 0, 60);
lv_obj_set_style_local_pad_all(buttonMatrix, LV_BTNMATRIX_PART_BG, LV_STATE_DEFAULT, 0);
buttonMatrix->user_data = this;
lv_obj_set_event_cb(buttonMatrix, eventHandler);
}
void Calculator::eval() {
std::stack<char> input {};
for (int8_t i = position - 1; i >= 0; i--) {
input.push(text[i]);
}
std::stack<std::shared_ptr<CalcTreeNode>> output {};
std::stack<char> operators {};
bool expectingNumber = true;
int8_t sign = +1;
while (!input.empty()) {
if (input.top() == '.') {
input.push('0');
}
if (isdigit(input.top())) {
char numberStr[31];
uint8_t strln = 0;
uint8_t pointpos = 0;
while (!input.empty() && (isdigit(input.top()) || input.top() == '.')) {
if (input.top() == '.') {
if (pointpos != 0) {
motorController.RunForDuration(10);
return;
}
pointpos = strln;
} else {
numberStr[strln] = input.top();
strln++;
}
input.pop();
}
// replacement for strtod() since using that increased .txt by 76858 bzt
if (pointpos == 0) {
pointpos = strln;
}
double num = 0;
for (uint8_t i = 0; i < pointpos; i++) {
num += (numberStr[i] - '0') * pow(10, pointpos - i - 1);
}
for (uint8_t i = 0; i < strln - pointpos; i++) {
num += (numberStr[i + pointpos] - '0') / pow(10, i + 1);
}
auto number = std::make_shared<NumNode>();
number->value = sign * num;
output.push(number);
sign = +1;
expectingNumber = false;
continue;
}
if (expectingNumber && input.top() == '+') {
input.pop();
continue;
}
if (expectingNumber && input.top() == '-') {
sign *= -1;
input.pop();
continue;
}
char next = input.top();
input.pop();
switch (next) {
case '+':
case '-':
case '/':
case 'x':
case '^':
// while ((there is an operator at the top of the operator stack)
while (!operators.empty()
// and (the operator at the top of the operator stack is not a left parenthesis))
&& operators.top() != '('
// and ((the operator at the top of the operator stack has greater precedence)
&& (getPrecedence(operators.top()) > getPrecedence(next)
// or (the operator at the top of the operator stack has equal precedence and the token is left associative))
|| (getPrecedence(operators.top()) == getPrecedence(next) && leftAssociative(next)))) {
// need two elements on the output stack to add a binary operator
if (output.size() < 2) {
motorController.RunForDuration(10);
return;
}
auto node = std::make_shared<BinOp>();
node->right = output.top();
output.pop();
node->left = output.top();
output.pop();
node->op = operators.top();
operators.pop();
output.push(node);
}
operators.push(next);
expectingNumber = true;
break;
case '(':
// we expect there to be a binary operator here but found a left parenthesis. this occurs in terms like this: a+b(c). This should be
// interpreted as a+b*(c)
if (!expectingNumber) {
operators.push('x');
}
operators.push(next);
expectingNumber = true;
break;
case ')':
while (operators.top() != '(') {
// need two elements on the output stack to add a binary operator
if (output.size() < 2) {
motorController.RunForDuration(10);
return;
}
auto node = std::make_shared<BinOp>();
node->right = output.top();
output.pop();
node->left = output.top();
output.pop();
node->op = operators.top();
operators.pop();
output.push(node);
if (operators.empty()) {
motorController.RunForDuration(10);
return;
}
}
// discard the left parentheses
operators.pop();
}
}
while (!operators.empty()) {
char op = operators.top();
if (op == ')' || op == '(') {
motorController.RunForDuration(10);
return;
}
// need two elements on the output stack to add a binary operator
if (output.size() < 2) {
motorController.RunForDuration(10);
return;
}
auto node = std::make_shared<BinOp>();
node->right = output.top();
output.pop();
node->left = output.top();
output.pop();
node->op = op;
operators.pop();
output.push(node);
}
// perform the calculation
errno = 0;
double resultFloat = output.top()->calculate();
if (errno != 0) {
motorController.RunForDuration(10);
return;
}
// make sure the result fits in a 32 bit int
if (INT32_MAX < resultFloat || INT32_MIN > resultFloat) {
motorController.RunForDuration(10);
return;
}
// weird workaround because sprintf crashes when trying to use a float
int32_t upper = resultFloat;
int32_t lower = round(std::abs(resultFloat - upper) * 10000);
// round up to the next int value
if (lower >= 10000) {
lower = 0;
upper++;
}
// see if decimal places have to be printed
if (lower != 0) {
if (upper == 0 && resultFloat < 0) {
position = sprintf(text, "-%ld.%ld", upper, lower);
} else {
position = sprintf(text, "%ld.%ld", upper, lower);
}
// remove extra zeros
while (text[position - 1] == '0') {
position--;
}
} else {
position = sprintf(text, "%ld", upper);
}
}
void Calculator::OnButtonEvent(lv_obj_t* obj, lv_event_t event) {
if (event == LV_EVENT_CLICKED) {
if (obj == buttonMatrix) {
const char* buttonstr = lv_btnmatrix_get_active_btn_text(obj);
if (*buttonstr == '=') {
eval();
} else {
if (position >= 30) {
motorController.RunForDuration(10);
return;
}
text[position] = *buttonstr;
position++;
}
} else if (obj == returnButton) {
if (position > 1) {
position--;
} else {
position = 0;
lv_label_set_text(result, "0");
return;
}
}
text[position] = '\0';
lv_label_set_text(result, text);
}
}
bool Calculator::OnTouchEvent(Pinetime::Applications::TouchEvents event) {
if (event == Pinetime::Applications::TouchEvents::LongTap) {
position = 0;
lv_label_set_text(result, "0");
return true;
}
if (event == Pinetime::Applications::TouchEvents::SwipeLeft) {
lv_btnmatrix_set_map(buttonMatrix, const_cast<const char **>(buttonMap2));
return true;
}
if (event == Pinetime::Applications::TouchEvents::SwipeRight) {
lv_btnmatrix_set_map(buttonMatrix, const_cast<const char **>(buttonMap1));
return true;
}
return false;
}

View file

@ -0,0 +1,43 @@
#pragma once
#include "Screen.h"
#include "Symbols.h"
#include "components/motor/MotorController.h"
#include "systemtask/SystemTask.h"
#include <array>
#include <string>
namespace Pinetime::Applications {
namespace Screens {
class Calculator : public Screen {
public:
~Calculator() override;
Calculator(Controllers::MotorController& motorController);
void OnButtonEvent(lv_obj_t* obj, lv_event_t event);
bool OnTouchEvent(Pinetime::Applications::TouchEvents event) override;
private:
lv_obj_t *result, *returnButton, *buttonMatrix;
char text[31];
uint8_t position = 0;
void eval();
Controllers::MotorController& motorController;
};
}
template <>
struct AppTraits<Apps::Calculator> {
static constexpr Apps app = Apps::Calculator;
static constexpr const char* icon = Screens::Symbols::calculator;
static Screens::Screen* Create(AppControllers& controllers) {
return new Screens::Calculator(controllers.motorController);
};
};
}

View file

@ -36,7 +36,7 @@ namespace Pinetime {
template <>
struct AppTraits<Apps::Motion> {
static constexpr Apps app = Apps::Motion;
static constexpr const char* icon = "M";
static constexpr const char* icon = "XYZ";
static Screens::Screen* Create(AppControllers& controllers) {
return new Screens::Motion(controllers.motionController);

View file

@ -39,6 +39,7 @@ namespace Pinetime {
static constexpr const char* eye = "\xEF\x81\xAE";
static constexpr const char* home = "\xEF\x80\x95";
static constexpr const char* sleep = "\xEE\xBD\x84";
static constexpr const char* calculator = "\xEF\x87\xAC";
// fontawesome_weathericons.c
// static constexpr const char* sun = "\xEF\x86\x85";

View file

@ -172,16 +172,16 @@ WatchFaceAnalog::WatchFaceAnalog(Controllers::DateTime& dateTimeController,
heartbeatIcon = lv_label_create(lv_scr_act(), nullptr);
lv_label_set_text_static(heartbeatIcon, Symbols::heartBeat);
lv_obj_set_style_local_text_color(heartbeatIcon, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0xCE1B1B));
lv_obj_set_style_local_text_color(heartbeatIcon, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x1B1B1B));
lv_obj_align(heartbeatIcon, heartbeatValue, LV_ALIGN_OUT_TOP_LEFT, 0, 0);
stepValue = lv_label_create(lv_scr_act(), nullptr);
lv_obj_set_style_local_text_color(stepValue, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x00FFE7));
lv_label_set_text_static(stepValue, "0");
lv_label_set_text_static(stepValue, "");
lv_obj_align(stepValue, lv_scr_act(), LV_ALIGN_IN_BOTTOM_RIGHT, 0, 0);
stepIcon = lv_label_create(lv_scr_act(), nullptr);
lv_obj_set_style_local_text_color(stepIcon, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x00FFE7));
lv_obj_set_style_local_text_color(stepIcon, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x1B1B1B));
lv_label_set_text_static(stepIcon, Symbols::shoe);
lv_obj_align(stepIcon, stepValue, LV_ALIGN_OUT_TOP_RIGHT, 0, 0);
@ -309,15 +309,17 @@ void WatchFaceAnalog::Refresh() {
lv_obj_set_style_local_text_color(heartbeatIcon, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x1B1B1B));
lv_label_set_text_static(heartbeatValue, "");
}
lv_obj_realign(heartbeatIcon);
lv_obj_realign(heartbeatValue);
}
stepCount = motionController.NbSteps();
if (stepCount.IsUpdated()) {
lv_label_set_text_fmt(stepValue, "%lu", stepCount.Get());
lv_obj_realign(stepValue);
lv_obj_realign(stepIcon);
if (stepCount.Get()) {
lv_obj_set_style_local_text_color(stepIcon, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x00FFE7));
lv_label_set_text_fmt(stepValue, "%lu", stepCount.Get());
lv_obj_realign(stepValue);
} else {
lv_obj_set_style_local_text_color(stepIcon, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x1B1B1B));
lv_label_set_text_static(stepValue, "");
}
}
}

View file

@ -74,7 +74,7 @@ WatchFaceDigital::WatchFaceDigital(Controllers::DateTime& dateTimeController,
heartbeatIcon = lv_label_create(lv_scr_act(), nullptr);
lv_label_set_text_static(heartbeatIcon, Symbols::heartBeat);
lv_obj_set_style_local_text_color(heartbeatIcon, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0xCE1B1B));
lv_obj_set_style_local_text_color(heartbeatIcon, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x1B1B1B));
lv_obj_align(heartbeatIcon, lv_scr_act(), LV_ALIGN_IN_BOTTOM_LEFT, 0, 0);
heartbeatValue = lv_label_create(lv_scr_act(), nullptr);
@ -83,13 +83,13 @@ WatchFaceDigital::WatchFaceDigital(Controllers::DateTime& dateTimeController,
lv_obj_align(heartbeatValue, heartbeatIcon, LV_ALIGN_OUT_RIGHT_MID, 5, 0);
stepIcon = lv_label_create(lv_scr_act(), nullptr);
lv_obj_set_style_local_text_color(stepIcon, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x00FFE7));
lv_obj_set_style_local_text_color(stepIcon, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x1B1B1B));
lv_label_set_text_static(stepIcon, Symbols::shoe);
lv_obj_align(stepIcon, lv_scr_act(), LV_ALIGN_IN_BOTTOM_RIGHT, 0, 0);
stepValue = lv_label_create(lv_scr_act(), nullptr);
lv_obj_set_style_local_text_color(stepValue, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x00FFE7));
lv_label_set_text_static(stepValue, "0");
lv_label_set_text_static(stepValue, "");
lv_obj_align(stepValue, stepIcon, LV_ALIGN_OUT_LEFT_MID, -5, 0);
taskRefresh = lv_task_create(RefreshTaskCallback, LV_DISP_DEF_REFR_PERIOD, LV_TASK_PRIO_MID, this);
@ -171,16 +171,18 @@ void WatchFaceDigital::Refresh() {
lv_obj_set_style_local_text_color(heartbeatIcon, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x1B1B1B));
lv_label_set_text_static(heartbeatValue, "");
}
lv_obj_realign(heartbeatIcon);
lv_obj_realign(heartbeatValue);
}
stepCount = motionController.NbSteps();
if (stepCount.IsUpdated()) {
lv_label_set_text_fmt(stepValue, "%lu", stepCount.Get());
lv_obj_realign(stepValue);
lv_obj_realign(stepIcon);
if (stepCount.Get()) {
lv_obj_set_style_local_text_color(stepIcon, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x00FFE7));
lv_label_set_text_fmt(stepValue, "%lu", stepCount.Get());
lv_obj_realign(stepValue);
} else {
lv_obj_set_style_local_text_color(stepIcon, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x1B1B1B));
lv_label_set_text_static(stepValue, "");
}
}
currentWeather = weatherService.Current();

View file

@ -158,6 +158,10 @@ void WatchFaceTerminal::Refresh() {
stepCount = motionController.NbSteps();
if (stepCount.IsUpdated()) {
lv_label_set_text_fmt(stepValue, R_LTGREY("step=")"#ee3377 %lu steps#", stepCount.Get());
if (stepCount.Get()) {
lv_label_set_text_fmt(stepValue, R_LTGREY("step=")"#ee3377 %lu steps#", stepCount.Get());
} else {
lv_label_set_text_static(stepValue, R_LTGREY("step=---"));
}
}
}

View file

@ -1,5 +1,13 @@
#pragma once
// QuickSettings is the screen with four icons:
//
// - brightness
// - torch
// - notifications mode
// - settings
//
#include <cstdint>
#include "displayapp/screens/Screen.h"
#include <lvgl/lvgl.h>