2023-10-02 22:23:30 +00:00
try { // For making it possiblie to run the test app in the following catch statement. It would complain on `exports` not being defined when uploading from Web IDE and not run otherwise.
2023-08-26 18:18:36 +00:00
2023-09-20 18:07:25 +00:00
exports . create = function ( cb , conf ) {
const R = Bangle . appRect ;
2023-10-02 22:23:30 +00:00
// Empty function added to cb if it's undefined.
2023-09-30 22:50:28 +00:00
if ( ! cb ) cb = ( ) => { } ;
2023-09-20 18:07:25 +00:00
let o = { } ;
o . v = { } ; // variables go here.
o . f = { } ; // functions go here.
2023-10-02 22:23:30 +00:00
// Default configuration for the indicator, modified by parameter `conf`:
2023-09-20 18:07:25 +00:00
o . c = Object . assign ( { // constants go here.
2023-10-10 20:15:12 +00:00
currLevel : null ,
2023-09-20 18:07:25 +00:00
horizontal : false ,
xStart : R . x2 - R . w / 4 - 4 ,
width : R . w / 4 ,
yStart : R . y + 4 ,
height : R . h - 10 ,
2023-10-04 17:44:50 +00:00
steps : 30 ,
dragableSlider : true ,
dragRect : R ,
useIncr : true ,
useMap : false ,
2023-09-20 18:07:25 +00:00
oversizeR : 0 ,
oversizeL : 0 ,
2023-10-04 17:44:50 +00:00
propagateDrag : false ,
2023-09-20 18:07:25 +00:00
timeout : 1 ,
2023-10-04 17:44:50 +00:00
drawableSlider : true ,
2023-09-20 18:07:25 +00:00
colorFG : g . theme . fg2 ,
colorBG : g . theme . bg2 ,
2023-10-01 00:42:44 +00:00
rounded : true ,
2023-09-20 18:07:25 +00:00
outerBorderSize : 2 ,
innerBorderSize : 2 ,
2023-10-04 17:44:50 +00:00
autoProgress : false ,
2023-09-20 18:07:25 +00:00
} , conf ) ;
2023-10-02 22:23:30 +00:00
// If horizontal, flip things around.
2023-09-20 18:07:25 +00:00
if ( o . c . horizontal ) {
2023-09-29 18:52:24 +00:00
let mediator = o . c . xStart ;
2023-09-20 18:07:25 +00:00
o . c . xStart = o . c . yStart ;
o . c . yStart = mediator ;
mediator = o . c . width ;
o . c . width = o . c . height ;
o . c . height = mediator ;
delete mediator ;
}
2023-08-24 23:05:34 +00:00
2023-10-02 22:23:30 +00:00
// Make room for the border. Underscore indicates the area for the actual indicator bar without borders.
2023-09-29 18:52:24 +00:00
let totalBorderSize = o . c . outerBorderSize + o . c . innerBorderSize ;
o . c . _xStart = o . c . xStart + totalBorderSize ;
o . c . _width = o . c . width - 2 * totalBorderSize ;
o . c . _yStart = o . c . yStart + totalBorderSize ;
o . c . _height = o . c . height - 2 * totalBorderSize ;
o . c . rounded = o . c . rounded ? 20 : 0 ;
o . c . STEP _SIZE = ( ( ! o . c . horizontal ? o . c . _height : o . c . _width ) - ( ! o . c . rounded ? 0 : ( 2 * o . c . rounded - 7 ) ) ) / o . c . steps ;
2023-10-02 22:23:30 +00:00
// Add a rectangle object with x, y, x2, y2, w and h values.
2023-09-20 18:07:25 +00:00
o . c . r = { x : o . c . xStart , y : o . c . yStart , x2 : o . c . xStart + o . c . width , y2 : o . c . yStart + o . c . height , w : o . c . width , h : o . c . height } ;
2023-09-04 21:17:53 +00:00
2023-09-20 18:07:25 +00:00
// Initialize the level
2023-10-10 20:15:12 +00:00
o . v . level = o . c . currLevel !== null ? o . c . currLevel : o . c . steps / 2 ;
2023-08-05 00:14:29 +00:00
2023-10-02 22:23:30 +00:00
// Only add interactivity if wanted.
2023-09-20 18:07:25 +00:00
if ( o . c . dragableSlider ) {
2023-10-08 22:09:53 +00:00
const Y _MAX = g . getHeight ( ) - 1 ; // Should this take users screen calibration into account?
2023-10-02 22:23:30 +00:00
2023-10-02 23:02:13 +00:00
o . v . ebLast = 0 ;
o . v . dy = 0 ;
2023-09-20 18:07:25 +00:00
o . f . wasOnDragRect = ( exFirst , eyFirst ) => {
"ram" ;
return exFirst > o . c . dragRect . x && exFirst < o . c . dragRect . x2 && eyFirst > o . c . dragRect . y && eyFirst < o . c . dragRect . y2 ;
} ;
2023-09-20 17:53:53 +00:00
2023-09-20 18:07:25 +00:00
o . f . wasOnIndicator = ( exFirst ) => {
"ram" ;
if ( ! o . c . horizontal ) return exFirst > o . c . _xStart - o . c . oversizeL * o . c . _width && exFirst < o . c . _xStart + o . c . _width + o . c . oversizeR * o . c . _width ;
if ( o . c . horizontal ) return exFirst > o . c . _yStart - o . c . oversizeL * o . c . _height && exFirst < o . c . _yStart + o . c . _height + o . c . oversizeR * o . c . _height ;
} ;
2023-08-24 23:05:34 +00:00
2023-10-02 22:23:30 +00:00
// Function to pass to `Bangle.on('drag', )`
2023-09-20 18:07:25 +00:00
o . f . dragSlider = e => {
"ram" ;
if ( o . v . ebLast == 0 ) {
exFirst = o . c . horizontal ? e . y : e . x ;
eyFirst = o . c . horizontal ? e . x : e . y ;
}
2023-08-26 21:31:19 +00:00
2023-10-02 22:23:30 +00:00
// Only react if on allowed area.
2023-09-20 18:07:25 +00:00
if ( o . f . wasOnDragRect ( exFirst , eyFirst ) ) {
o . v . dragActive = true ;
if ( ! o . c . propagateDrag ) E . stopEventPropagation && E . stopEventPropagation ( ) ;
2023-08-24 23:05:34 +00:00
2023-09-20 18:07:25 +00:00
if ( o . v . timeoutID ) { clearTimeout ( o . v . timeoutID ) ; o . v . timeoutID = undefined ; }
if ( e . b == 0 && ! o . v . timeoutID && ( o . c . timeout || o . c . timeout === 0 ) ) o . v . timeoutID = setTimeout ( o . f . remove , 1000 * o . c . timeout ) ;
2023-08-15 21:07:55 +00:00
2023-09-20 18:07:25 +00:00
if ( o . c . useMap && o . f . wasOnIndicator ( exFirst ) ) { // If draging starts on the indicator, adjust one-to-one.
2023-08-07 22:56:16 +00:00
2023-09-28 23:56:03 +00:00
let input = ! o . c . horizontal ?
2023-10-08 22:09:53 +00:00
Math . min ( ( Y _MAX - e . y ) - o . c . yStart - 3 * o . c . rounded / 4 , o . c . height ) :
2023-09-28 23:56:03 +00:00
Math . min ( e . x - o . c . xStart - 3 * o . c . rounded / 4 , o . c . width ) ;
2023-09-28 22:44:19 +00:00
input = Math . round ( input / o . c . STEP _SIZE ) ;
2023-09-28 23:56:03 +00:00
o . v . level = Math . min ( Math . max ( input , 0 ) , o . c . steps ) ;
2023-08-15 20:50:29 +00:00
2023-10-09 18:16:21 +00:00
o . v . cbObj = { mode : "map" , value : o . v . level } ;
2023-10-08 16:22:44 +00:00
2023-09-20 18:07:25 +00:00
} else if ( o . c . useIncr ) { // Heavily inspired by "updown" mode of setUI.
2023-08-08 18:38:22 +00:00
2023-09-20 18:07:25 +00:00
o . v . dy += o . c . horizontal ? - e . dx : e . dy ;
//if (!e.b) o.v.dy=0;
2023-08-08 18:38:22 +00:00
2023-09-20 18:07:25 +00:00
while ( Math . abs ( o . v . dy ) > 32 ) {
2023-09-29 19:06:50 +00:00
let incr ;
2023-09-20 18:07:25 +00:00
if ( o . v . dy > 0 ) { o . v . dy -= 32 ; incr = 1 ; }
else { o . v . dy += 32 ; incr = - 1 ; }
Bangle . buzz ( 20 ) ;
2023-08-08 18:38:22 +00:00
2023-09-20 18:07:25 +00:00
o . v . level = Math . min ( Math . max ( o . v . level - incr , 0 ) , o . c . steps ) ;
2023-10-08 16:22:44 +00:00
2023-10-09 18:16:21 +00:00
o . v . cbObj = { mode : "incr" , value : incr } ;
2023-09-20 18:07:25 +00:00
}
}
2023-10-09 16:17:35 +00:00
if ( o . v . level !== o . v . prevLevel || o . v . level === 0 || o . v . level === o . c . steps ) {
2023-10-09 18:16:21 +00:00
cb ( o . v . cbObj . mode , o . v . cbObj . value ) ;
2023-10-09 18:26:07 +00:00
o . f . draw && o . f . draw ( o . v . level ) ;
2023-10-08 16:22:44 +00:00
}
o . v . prevLevel = o . v . level ;
2023-09-20 18:07:25 +00:00
o . v . ebLast = e . b ;
2023-09-20 18:24:42 +00:00
}
} ;
2023-08-08 18:38:22 +00:00
2023-10-02 22:23:30 +00:00
// Cleanup.
2023-09-20 18:07:25 +00:00
o . f . remove = ( ) => {
Bangle . removeListener ( 'drag' , o . f . dragSlider ) ;
o . v . dragActive = false ;
2023-09-26 20:57:17 +00:00
o . v . timeoutID = undefined ;
2023-09-29 00:22:39 +00:00
cb ( "remove" , o . v . level ) ;
2023-09-20 18:07:25 +00:00
} ;
}
2023-08-08 18:38:22 +00:00
2023-10-02 22:23:30 +00:00
// Add standard slider graphics only if wanted.
2023-09-20 18:07:25 +00:00
if ( o . c . drawableSlider ) {
2023-08-05 00:14:29 +00:00
2023-10-02 22:23:30 +00:00
// Function for getting the indication bars size.
2023-09-20 18:07:25 +00:00
o . f . updateBar = ( levelHeight ) => {
"ram" ;
if ( ! o . c . horizontal ) return { x : o . c . _xStart , y : o . c . _yStart + o . c . _height - levelHeight , w : o . c . _width , y2 : o . c . _yStart + o . c . _height , r : o . c . rounded } ;
if ( o . c . horizontal ) return { x : o . c . _xStart , y : o . c . _yStart , w : levelHeight , h : o . c . _height , r : o . c . rounded } ;
} ;
2023-09-18 23:35:45 +00:00
2023-09-20 18:07:25 +00:00
o . c . borderRect = { x : o . c . _xStart - totalBorderSize , y : o . c . _yStart - totalBorderSize , w : o . c . _width + 2 * totalBorderSize , h : o . c . _height + 2 * totalBorderSize , r : o . c . rounded } ;
2023-09-18 23:35:45 +00:00
2023-09-20 18:07:25 +00:00
o . c . hollowRect = { x : o . c . _xStart - o . c . innerBorderSize , y : o . c . _yStart - o . c . innerBorderSize , w : o . c . _width + 2 * o . c . innerBorderSize , h : o . c . _height + 2 * o . c . innerBorderSize , r : o . c . rounded } ;
2023-09-02 09:17:04 +00:00
2023-10-02 22:23:30 +00:00
// Standard slider drawing method.
2023-09-20 18:07:25 +00:00
o . f . draw = ( level ) => {
"ram" ;
2023-08-05 00:14:29 +00:00
2023-09-29 00:22:39 +00:00
g . setColor ( o . c . colorFG ) . fillRect ( o . c . borderRect ) . // To get outer border...
setColor ( o . c . colorBG ) . fillRect ( o . c . hollowRect ) . // ... and here it's made hollow.
setColor ( 0 == level ? o . c . colorBG : o . c . colorFG ) . fillRect ( o . f . updateBar ( ( ! o . c . rounded ? 0 : ( 2 * o . c . rounded - 7 ) ) + level * o . c . STEP _SIZE ) ) ; // Here the bar is drawn.
2023-09-29 00:35:20 +00:00
if ( o . c . rounded && level === 0 ) { // Hollow circle indicates level zero when slider is rounded.
g . setColor ( o . c . colorFG ) . fillCircle ( o . c . _xStart + ( ! o . c . horizontal ? o . c . _width / 2 : o . c . rounded - 2 ) , o . c . _yStart + o . c . _height - o . c . rounded + 2 , o . c . rounded - o . c . innerBorderSize ) .
setColor ( o . c . colorBG ) . fillCircle ( o . c . _xStart + ( ! o . c . horizontal ? o . c . _width / 2 : o . c . rounded - 2 ) , o . c . _yStart + o . c . _height - o . c . rounded + 2 , o . c . rounded - o . c . innerBorderSize - 2 ) ;
2023-09-28 17:17:58 +00:00
}
2023-08-08 18:38:22 +00:00
2023-09-20 18:07:25 +00:00
//print(level);
//print(process.memory().usage);
} ;
}
2023-08-07 22:56:16 +00:00
2023-10-02 22:23:30 +00:00
// Add logic for auto progressing the slider only if wanted.
2023-09-20 18:07:25 +00:00
if ( o . c . autoProgress ) {
o . f . autoUpdate = ( ) => {
2023-10-03 22:31:03 +00:00
o . v . level = o . v . autoInitLevel + Math . round ( ( Date . now ( ) - o . v . autoInitTime ) / 1000 )
2023-09-30 19:11:39 +00:00
if ( o . v . level > o . c . steps ) o . v . level = o . c . steps ;
2023-09-20 18:07:25 +00:00
cb ( "auto" ) ;
2023-10-09 18:26:07 +00:00
o . f . draw && o . f . draw ( o . v . level ) ;
2023-09-20 18:07:25 +00:00
if ( o . v . level == o . c . steps ) { o . f . stopAutoUpdate ( ) ; }
} ;
2023-10-03 22:31:03 +00:00
o . f . initAutoValues = ( ) => {
o . v . autoInitTime = Date . now ( ) ;
o . v . autoInitLevel = o . v . level ;
} ;
2023-09-20 18:07:25 +00:00
o . f . startAutoUpdate = ( ) => {
o . f . stopAutoUpdate ( ) ;
2023-10-03 22:31:03 +00:00
o . f . initAutoValues ( ) ;
2023-09-30 19:11:39 +00:00
o . f . draw && o . f . draw ( o . v . level ) ;
2023-09-20 18:07:25 +00:00
o . v . autoIntervalID = setInterval ( o . f . autoUpdate , 1000 ) ;
} ;
2023-10-03 22:31:03 +00:00
o . f . stopAutoUpdate = ( ) => {
if ( o . v . autoIntervalID ) {
clearInterval ( o . v . autoIntervalID ) ;
o . v . autoIntervalID = undefined ;
}
o . v . autoInitLevel = undefined ;
o . v . autoInitTime = undefined ;
} ;
2023-09-20 18:07:25 +00:00
}
2023-09-02 09:17:04 +00:00
2023-09-20 18:07:25 +00:00
return o ;
2023-09-20 18:24:42 +00:00
} ;
2023-08-26 18:18:36 +00:00
2023-08-26 22:37:33 +00:00
} catch ( e ) {
2023-09-20 18:07:25 +00:00
print ( e ) ;
2023-09-28 23:12:17 +00:00
let appName = "spotrem" ;
2023-09-20 18:07:25 +00:00
eval ( require ( "Storage" ) . read ( appName + ".app.js" ) ) ;
2023-08-26 22:37:33 +00:00
}