BangleApps_old/apps/mtimer/scroller.lib.js

351 lines
11 KiB
JavaScript

exports.Scroller = function(options) {
this.getScrollerY = function() {
return Bangle.appRect.y + this.topBarH;
}
this.dragging = false;
this.firstTouch = true;
this.selectedX = 0;
this.h = options.h;
this.w = Bangle.appRect.w;
this.c = options.c;
this.drawFunc = options.draw;
this.drawEmpty = options.drawEmpty;
this.selectFunc = options.select;
this.touchX = undefined;
this.button1Func = options.button1;
this.button2Func = options.button2;
this.drawButton1 = options.drawButton1;
this.drawButton2 = options.drawButton2;
this.buttonW = 55;
this.buttonsW = (this.drawButton1 !== undefined ? this.buttonW : 0) + (this.drawButton2 !== undefined ? this.buttonW : 0);
this.touchStart = true;
this.mode = undefined;
this.selected = undefined;
/**
minH:
maxH;
drawMin: function(r)
drawMax: function(r)
handler: function(r)
*/
this.topBar = options.topBar;
this.topBarH = this.topBar.maxH;
this.topBarIsMax = true;
this.topBarIsMin = false;
this.maxCountOneScreen = Math.floor((Bangle.appRect.y2 - Bangle.appRect.y - this.topBar.maxH) / this.h);
this.y0 = Bangle.appRect.y + this.topBarH;
this.x = 0;
this.dy = 0;
this.clearScroller = function() {
g.clearRect(Bangle.appRect.x, Bangle.appRect.y + this.topBarH, Bangle.appRect.x2, Bangle.appRect.y2);
};
this.getMinY = function() {
let minY = Bangle.appRect.y2 - (this.c * this.h);
return minY > this.topBarH ? this.topBarH : minY;
};
this.minY = this.getMinY();
this.dragHandler = function(d) {
if (this.firstTouch) {
this.firstTouch = false;
if (d.b === 0) return;
}
if (d.b === 1) this.dragging = true;
else this.dragging = false;
if (this.mode === undefined) {
if (this.topBarIsMax && d.y < Bangle.appRect.y + this.topBar.maxH) {
this.topBar.handler(d, {x: Bangle.appRect.x, y: Bangle.appRect.y, x2: Bangle.appRect.x2, y2: Bangle.appRect.y + this.topBar.maxH - 1});
return;
}
this.selected = Math.floor((d.y - this.y0) / this.h);
if (this.selected > this.c - 1 || this.selected < 0) this.selected = undefined;
if (d.x != undefined) this.touchX = d.x; // need to save the last touch position
if (d.dy === 0 && d.dx === 0 && this.touchStart === true) { // initial finger press, we'll check after release
if (d.b === 0) { // finger released, must have been no dragging, call select func
if (this.selected !== undefined) {
const idx = this.selected;
const bounds = this.getBounds(idx);
this.selectFunc(idx, bounds, this.touchX);
}
return;
}
} else if (Math.abs(d.dy) > Math.abs(d.dx)) { // drag up/down
this.touchStart = false;
this.selectedX = this.x;
this.mode = "ud";
} else if (Math.abs(d.dx) > Math.abs(d.dy) && this.selected != undefined) { // drag left/right
this.touchStart = false;
this.mode = "lr";
}
}
if (this.mode === "ud") this.udHandler(d);
else if (this.mode === "lr") this.lrHandler(d);
else if (this.mode === "button") this.buttonHandler(d);
};
this.udHandler = function(d) {
this.minY = this.getMinY();
if (d.b === 0) { // released finger
if (this.c > this.maxCountOneScreen) {
if (this.topBarH > (this.topBar.maxH + this.topBar.minH) / 2) {
this.topBarH = this.topBar.maxH;
this.topBarIsMax = true;
this.topBarIsMin = false;
} else {
this.topBarH = this.topBar.minH;
this.topBarIsMax = false;
this.topBarIsMin = true;
} // make sure the top bar ends in one of the 2 states when we stop scrolling
}
let done = false;
this.y0 += this.dy; // move a little extra after finger release. Feels more natural.
if (this.y0 > this.topBarH) {
this.y0 = this.topBarH; // overscroll top
done = true;
} else if (this.y0 < this.minY) {
this.y0 = this.minY; // overscroll bottom
done = true;
}
this.drawAll();
this.touchStart = true; // released finger, touchStart is true for next time
this.mode = undefined; // reset mode, we don't know if we will continue scrolling up/down
if (done) {
return;
}
}
this.x = 0;
this.y0 += (d.dy * 1.2); // scroll a little faster than finger is moving to compensate for lag
if (this.c > this.maxCountOneScreen) {
if (d.dy > 0 && this.topBarH < this.topBar.maxH) { // drag top to bottom
this.topBarH += d.dy;
if (this.topBarH > this.topBar.maxH) {
this.topBarH = this.topBar.maxH;
}
}
else if (d.dy < 0 && this.topBarH > this.topBar.minH) { // drag bottom to top
this.topBarH += d.dy;
if (this.topBarH < this.topBar.minH) {
this.topBarH = this.topBar.minH;
}
}
}
if (this.y0 > this.topBarH + 30) this.y0 = this.topBarH + 30; // max overscroll top
else if (this.y0 < this.minY-30) this.y0 = this.minY-30; // max overscroll bottom
this.clearScroller();
this.drawAll();
};
this.lrHandler = function(d) {
if (d.b === 0) { // released finger
if (this.buttonsW === 0) return;
if (this.selectedX <= -(this.buttonsW) + 20) { // left swipe buttons width less 20px
this.selectedX = -(this.buttonsW);
this.drawLR(this.selected);
this.mode = "button";
} else {
this.selectedX = this.x;
this.mode = undefined;
this.drawAll();
}
this.touchStart = true;
return;
}
if (d.dx > 0 && this.selectedX >= 0) return; // ignore swipe right
else {
this.selectedX += d.dx;
this.drawLR(this.selected);
}
};
this.buttonHandler = function(d) {
if (Math.abs(d.dx > 1)) {
this.mode = "lr";
return;
}
if (this.touchStart && d.b === 0) {
this.mode = undefined;
this.selectedX = this.x;
let b1x1, b1x2, b2x1, b2x2;
if (this.drawButton2 === undefined) {
b1x1 = this.x + this.w - this.buttonW;
b1x2 = this.x + this.w;
} else {
b1x1 = this.x + this.w - this.buttonsW;
b1x2 = this.x + this.w - this.buttonW;
}
b2x1 = this.x + this.w - this.buttonW;
if (d.x > b1x1 && d.x < b1x2) { // button 1: delete
this.removingSelection = this.selected;
this.c--;
this.minY = this.getMinY();
this.animateDelete(this.selected);
this.button1Func(this.selected);
} else if (d.x > b2x1) {
this.button2Func(this.selected);
}
return;
}
};
this.getBounds = function(idx) {
let x = this.x;
let y = this.y0 + (idx * this.h);
let x2 = x + this.w - 1;
let y2 = y + this.h - 1;
return {x: x, y: y, x2: x2, y2: y2, w: this.w, h: this.h};
}
this.draw = function(idx) {
this.drawFunc(idx, this.getBounds(idx), this.dragging);
};
this.scrollToCenter = function(idx) {
this.idxMidY = this.getScrollerY() + ((Bangle.appRect.h - this.h) / 2) - (idx * this.h);
this.dir = this.idxMidY > this.y0 ? 1 : -1;
const scroll = function(idx) {
let done = false;
this.y0 += 30 * this.dir;
if (Math.abs(this.y0) - Math.abs(this.idxMidY) <= 30) {
this.y0 = this.idxMidY;
done = true;
}
this.drawAll();
if (done) {
delete this.idxMidY;
delete this.dir;
delete Bangle.scroll;
return;
} else {
setTimeout(() => Bangle.scroll(idx), 30);
return;
}
}
Bangle.scroll = scroll.bind(this, idx);
Bangle.scroll(idx);
};
this.drawTopBar = function() {
if (this.c <= this.maxCountOneScreen) {
this.topBarH = this.topBar.maxH;
this.topBarIsMax = true;
this.topBarIsMin = false;
}
g.setClipRect(Bangle.appRect.x, Bangle.appRect.y, Bangle.appRect.x2, Bangle.appRect.y + this.topBarH - 1);
if (this.topBarH < this.topBar.maxH) {
this.topBar.drawMin({x: Bangle.appRect.x, y: Bangle.appRect.y, x2: Bangle.appRect.x2, y2: Bangle.appRect.y + this.topBarH - 1})
} else {
this.topBar.drawMax({x: Bangle.appRect.x, y: Bangle.appRect.y, x2: Bangle.appRect.x2, y2: Bangle.appRect.y + this.topBarH - 1})
}
};
this.drawAll = function() {
this.drawTopBar();
g.setClipRect(Bangle.appRect.x, this.getScrollerY(), Bangle.appRect.x2, Bangle.appRect.y2);
this.clearScroller();
if (this.c === 0) {
this.drawEmpty({x: Bangle.appRect.x, y: this.getScrollerY(), x2: Bangle.appRect.x2, y2: Bangle.appRect.y2});
return;
}
let x = this.x;
let y = this.y0;
let x2 = x + this.w - 1;
let y2 = y + this.h - 1;
const startIdx = this.y0 < 0 ? Math.floor(-this.y0 / this.h) : 0; // How many are fully off screen? We won't draw them.
const count = Math.ceil(Bangle.appRect.h / this.h); // How many we should draw
let drawn = 0;
for (let i = 0; i < this.c; i++) {
if (i >= startIdx && drawn <= count) {
if (i === this.removingSelection) {
g.clearRect(x, y, x2, y + this.removingH - 1);
y += this.removingH;
y2 += this.removingH;
}
if ( (this.mode === "lr" || this.mode === "button") && i === this.selected ) {
this.drawLR(this.selected);
}
else {
this.drawFunc(i, {
x: x, y: y, x2: x2, y2: y2, w: this.w, h: this.h
}, this.dragging);
}
drawn++;
}
y += this.h;
y2 += this.h;
}
};
this.drawLR = function(selected) {
// const idx = selected;
// const x = this.x;
const y = this.y0 + (this.h * selected);
const x2 = this.x + this.w - 1;
const y2 = y + this.h - 1;
// const w = this.buttonW;
// const h = this.h;
g.clearRect(this.x, y, x2, y2);
this.drawMenu(selected,
{ x: x2 - (2 * this.buttonW), y: y, x2: x2 - this.buttonW - 1, y2: y2, w: this.buttonW, h: this.h },
{ x: x2 - this.buttonW, y: y, x2: x2, y2: y2, w: this.buttonW, h: this.h });
this.drawFunc(this.selected, {
x: this.selectedX, y: y, x2: this.selectedX + this.w - 1, y2: y2, h: this.h, w: this.w
});
};
this.animateDelete = function(selected) {
Bangle.animateDelete = this.animateDelete.bind(this);
this.removingSelection = selected;
this.minY = this.getMinY();
this.removingH = this.removingH === undefined ? this.h : this.removingH - 30;
if (this.removingH < 1) this.removingH = 1;
if (selected === this.c || this.y0 < this.minY) {
if (this.y0 < Bangle.appRect.y + this.topBarH) this.y0 += this.removingH;
if (this.y0 > Bangle.appRect.y + this.topBarH) this.y0 -= this.removingH;
}
this.drawAll();
if (this.removingH > 1) {
setTimeout(() => Bangle.animateDelete(selected), 30);
} else {
if (this.y0 < this.minY) {
this.y0 = this.minY;
}
this.removingSelection = undefined;
this.removingH = undefined;
delete Bangle.animateDelete;
this.drawAll();
}
};
this.drawMenu = function(_idx, r1, r2) {
const col = g.getColor();
if (this.drawButton2 !== undefined) this.drawButton2(r2);
else r1 = r2;
if (this.drawButton1 !== undefined) this.drawButton1(r1);
g.setColor(col);
};
this.remove = function() {
Bangle.setUI();
g.reset();
};
this.reload = function() {
this.minY = this.getMinY();
Bangle.setUI();
this.firstTouch = true;
Bangle.dragHandler = this.dragHandler.bind(this);
Bangle.setUI({mode: "custom", drag: Bangle.dragHandler,
btn: (_n) => Bangle.showClock()
});
};
} // Scroller