Skip to content

[widclkinfo] Ensure we cancel focus when widget_utils.swipeOn() pushes widget bar off screen #3680

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 25 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
0ab04e4
clock_info: Expose internal blur function as force_blur
Dec 1, 2024
e86fca8
widget_utils: Add shown and hidden events
Dec 1, 2024
e985056
widclkinfo: work in progress: Be aware of widget_utils.swipeOn
Dec 1, 2024
413b33f
Suggestions from review: emit events from Bangle and moving force_blu…
Dec 1, 2024
9bd9344
Also emit event when autohiding
Dec 2, 2024
b5ffb5a
Listen for widget events from Bangle
Dec 2, 2024
3a0de90
widget_utils: Emit shown and hidden events on show and hide functions
Dec 13, 2024
40059b7
widclkinfo: Remove references to widget_utils
Dec 13, 2024
b44fc18
widclkinfo: cleanup event listeners
Dec 13, 2024
4584330
widclkinfo: move draw function definition.
Dec 13, 2024
de845be
widclkinfo: Don't redraw widget if its clock_info is off screen
Dec 13, 2024
72a6b1d
widclkinfo: balance brackets
Dec 13, 2024
ba5d98a
widclkinfo: Make clockInfoMenu part of the widget
Jan 5, 2025
302e48b
widget_utils: Move events to anim function
Jun 18, 2025
86e6f86
widget_utils: format indentation
Jun 18, 2025
b99bc3e
clock_info: Use ensure_blur as public function in options
Aug 16, 2025
f1b8749
widclkinfo: update version and changelog
Aug 16, 2025
dd5fcd4
clock_info: update changelog
Aug 16, 2025
c7d7c8b
clock_info: bump version
Aug 17, 2025
89fb705
Remove test code comment
Aug 17, 2025
dbdf1a5
Note possible off by one error in detecting taps on clock_info
Aug 17, 2025
2c16883
widget_utils: Work to fix logic error in animation state handling
Aug 17, 2025
403ee4e
widclkinfo: Remove debug line
Aug 17, 2025
1358c99
widget_utils: fix missing bracket
Aug 17, 2025
71a98ac
widget_utils: Fix ambiguous if else
Aug 17, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions apps/clock_info/ChangeLog
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,5 @@
0.19: Fix Altitude ClockInfo after BLE added
Tapping Altitude now updates the reading
0.20: Altitude ClockInfo now uses the distance units set in locale.
0.21: Expose ensure_blur function

11 changes: 10 additions & 1 deletion apps/clock_info/lib.js
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,10 @@ exports.addInteractive = function(menu, options) {
redraw = false;
if (redraw) options.redraw();
};
// better to only call blur when we know it's focused. Could reuse this logic in this file
options.ensure_blur = () => {
if (options.focus) blur()
}
const focus = () => {
let redraw = true;
Bangle.CLKINFO_FOCUS = (0 | Bangle.CLKINFO_FOCUS) + 1;
Expand All @@ -344,10 +348,15 @@ exports.addInteractive = function(menu, options) {
if (redraw) options.redraw();
};
let touchHandler, lockHandler;
// debug touch handler next
if (options.x!==undefined && options.y!==undefined && options.w && options.h) {
touchHandler = function(_,e) {
if (e.x<options.x || e.y<options.y ||
e.x>(options.x+options.w) || e.y>(options.y+options.h)) {
e.x>(options.x+options.w-1) || e.y>(options.y+options.h-1)) {
// touch at y=0 focuses when widclkinfo is off screen
// may have off by one error here
// options.y is -24
// options.h is 24
Comment on lines +355 to +359
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Want to be sure this is consistent with how we define rectangles for widgets and app area etc

if (options.focus)
blur();
return; // outside area
Expand Down
2 changes: 1 addition & 1 deletion apps/clock_info/metadata.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{ "id": "clock_info",
"name": "Clock Info Module",
"shortName": "Clock Info",
"version":"0.20",
"version":"0.21",
"description": "A library used by clocks to provide extra information on the clock face (Altitude, BPM, etc)",
"icon": "app.png",
"type": "module",
Expand Down
3 changes: 2 additions & 1 deletion apps/widclkinfo/ChangeLog
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
0.01: New Widget!
0.02: Now use an app ID (to avoid conflicts with clocks that also use ClockInfo)
0.03: Fix widget clearing too far down
0.04: If a small font is needed, use 6x8 but twice as high
0.04: If a small font is needed, use 6x8 but twice as high
0.05: Defocus the clock_info if widget_utils is used to move widgets off screen
2 changes: 1 addition & 1 deletion apps/widclkinfo/metadata.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{ "id": "widclkinfo",
"name": "Clock Info Widget",
"version":"0.04",
"version":"0.05",
"description": "Use 'Clock Info' in the Widget bar. Tap on the widget to select, then drag up/down/left/right to choose what information is displayed.",
"icon": "widget.png",
"screenshots" : [ { "url":"screenshot.png" }],
Expand Down
127 changes: 85 additions & 42 deletions apps/widclkinfo/widget.js
Original file line number Diff line number Diff line change
@@ -1,50 +1,93 @@
if (!require("clock_info").loadCount) { // don't load if a clock_info was already loaded
// Load the clock infos
let clockInfoItems = require("clock_info").load();
// Add the
let clockInfoMenu = require("clock_info").addInteractive(clockInfoItems, {
app : "widclkinfo",
// Add the dimensions we're rendering to here - these are used to detect taps on the clock info area
x : 0, y: 0, w: 72, h:24,
// You can add other information here you want to be passed into 'options' in 'draw'
// This function draws the info
draw : (itm, info, options) => {
// itm: the item containing name/hasRange/etc
// info: data returned from itm.get() containing text/img/etc
// options: options passed into addInteractive
clockInfoInfo = info;
if (WIDGETS["clkinfo"])
WIDGETS["clkinfo"].draw(WIDGETS["clkinfo"]);
}
});
let clockInfoInfo; // when clockInfoMenu.draw is called we set this up
(() => {
if (!require("clock_info").loadCount) { // don't load if a clock_info was already loaded
const clock_info = require("clock_info");

// The actual widget we're displaying
WIDGETS["clkinfo"] = {
area:"tl",
width: clockInfoMenu.w,
draw:function(e) {
clockInfoMenu.x = e.x;
clockInfoMenu.y = e.y;
var o = clockInfoMenu;
// Clear the background
area: "tl",
width: 0, //this.clockInfoMenu.w,
init: function() {
this.width = this.clockInfoMenu.w;
delete this.init;
return this;
},
clockInfoInfo: undefined, // defined during clockInfoMenu.draw()
clockInfoMenu: clock_info.addInteractive(clock_info.load(), {
app: "widclkinfo",
// Add the dimensions we're rendering to here - these are used to detect taps on the clock info area
x: 0,
y: 0, // TODO how know if offscreen to start?
w: 72,
h: 23, // workaround off by one error in clock_info
// You can add other information here you want to be passed into 'options' in 'draw'
// This function draws the info
draw: (itm, info, options) => {
// itm: the item containing name/hasRange/etc
// info: data returned from itm.get() containing text/img/etc
// options: options passed into addInteractive
var wi = WIDGETS["clkinfo"];
wi.clockInfoInfo = info;
wi.clockInfoMenu.y = options.y;
if (WIDGETS["clkinfo"] && wi.clockInfoMenu.y > -24) {
WIDGETS["clkinfo"].draw(WIDGETS["clkinfo"]);
Comment on lines +27 to +31
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any way to not refer to the global values here?

}
}
}),
draw: function(e) {
this.clockInfoMenu.x = e.x;
var o = this.clockInfoMenu;
g.reset();
// indicate focus - make background reddish
//if (clockInfoMenu.focus) g.setBgColor(g.blendColor(g.theme.bg, "#f00", 0.25));
if (clockInfoMenu.focus) g.setColor("#f00");
g.clearRect(o.x, o.y, o.x+o.w-1, o.y+o.h-1);
if (clockInfoInfo) {
// indicate focus
if (this.clockInfoMenu.focus) {
g.setColor("#f00");
}
g.clearRect(o.x, o.y, o.x + o.w - 1, o.y + o.h - 1);
if (this.clockInfoInfo) {
var x = o.x;
if (clockInfoInfo.img) {
g.drawImage(clockInfoInfo.img, x,o.y); // draw the image
x+=24;
if (this.clockInfoInfo.img) {
g.drawImage(this.clockInfoInfo.img, x, o.y); // draw the image
x += 24;
}
var availableWidth = o.x+clockInfoMenu.w - (x+2);
g.setFont("6x8:2").setFontAlign(-1,0);
if (g.stringWidth(clockInfoInfo.text) > availableWidth)
var availableWidth = o.x + this.clockInfoMenu.w - (x + 2);
g.setFont("6x8:2").setFontAlign(-1, 0);
if (g.stringWidth(this.clockInfoInfo.text) > availableWidth)
g.setFont("6x8:1x2");
g.drawString(clockInfoInfo.text, x+2,o.y+12); // draw the text
g.drawString(this.clockInfoInfo.text, x + 2, o.y + 12); // draw the text
}
}
};
}
}.init();

// We make clock info touch area active on the start of the animation
Bangle.on("widgets-start-show", () => {
var wi = WIDGETS["clkinfo"];
if (wi) {
wi.clockInfoMenu.y = 0;
wi.draw(wi);
}
});

Bangle.on("widgets-shown", () => {
var wi = WIDGETS["clkinfo"];
if (wi) {
wi.clockInfoMenu.y = 0;
wi.draw(wi);
}
});

Bangle.on("widgets-start-hide", () => {
var wi = WIDGETS["clkinfo"];
if (wi) {
wi.clockInfoMenu.ensure_blur(); // let user see defocus cue before hiding
wi.clockInfoMenu.y = -24;
wi.draw(wi);
}
});

Bangle.on("widgets-hidden", () => {
var wi = WIDGETS["clkinfo"];
if (wi) {
wi.clockInfoMenu.ensure_blur();
wi.clockInfoMenu.y = -24;
wi.draw(wi);
}
});
}})()
40 changes: 32 additions & 8 deletions modules/widget_utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ exports.hide = function() {
w.area = "";
if (w.x!=undefined) g.clearRect(w.x,w.y,w.x+w.width-1,w.y+23);
}
Bangle.emit("widgets-hidden");
};

/// Show any hidden widgets
Expand All @@ -25,6 +26,7 @@ exports.show = function() {
delete w._area;
w.draw(w);
}
Bangle.emit("widgets-shown");
};

/// Remove anything not needed if the overlay was removed
Expand Down Expand Up @@ -129,19 +131,41 @@ exports.swipeOn = function(autohide) {
function anim(dir, callback) {
if (exports.animInterval) clearInterval(exports.animInterval);
exports.animInterval = setInterval(function() {
exports.offset += dir;
// exports.offset < -23 + |dir| > -|dir| otherwise
// dir >0 start showing shown this step showing
// <0 hidden this step start hiding hiding
Comment on lines +134 to +136
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is probably a more efficient way to do these nested conditions

let stop = false;
if (dir>0 && exports.offset>=0) { // fully down
stop = true;
exports.offset = 0;
} else if (dir<0 && exports.offset<-23) { // fully up
stop = true;
exports.offset = -24;
if (dir > 0) {
if (exports.offset >= -dir) {
// nearly shown
stop = true;
exports.offset = 0; // clamp
Bangle.emit("widgets-shown");
} else if (exports.offset < -23 + dir) {
Bangle.emit("widgets-start-show");
} else {
Bangle.emit("widgets-anim-step");
}
} else if (dir < 0) {
if (exports.offset > dir) {
Bangle.emit("widgets-start-hide");
} else if (exports.offset < -23 - dir) {
// nearly hidden
stop = true;
exports.offset = -24; // clamp
Bangle.emit("widgets-hidden");
} else {
Bangle.emit("widgets-anim-step");
}
}
if (stop) {
clearInterval(exports.animInterval);
delete exports.animInterval;
if (callback) callback();
if (callback) {
callback();
}
} else {
exports.offset += dir;
}
queueDraw();
}, 50);
Expand Down