From 4922e135e3865a5817d1f9c57e0c305c4c553bcd Mon Sep 17 00:00:00 2001 From: stweedo Date: Thu, 22 Jun 2023 00:36:02 -0500 Subject: [PATCH] Add new custom font library module --- apps/boxclk/ChangeLog | 2 +- apps/boxclk/README.md | 4 +-- apps/boxclk/app.js | 35 ++++++----------------- apps/boxclk/boxclk-1.json | 18 ++++++------ apps/boxclk/lib.js | 53 +++++++++++++++++++++++++++++++++++ apps/boxclk/metadata.json | 2 ++ apps/boxclk/screenshot-1.png | Bin 6017 -> 5782 bytes 7 files changed, 76 insertions(+), 38 deletions(-) create mode 100644 apps/boxclk/lib.js diff --git a/apps/boxclk/ChangeLog b/apps/boxclk/ChangeLog index f35b35819..105c8384c 100644 --- a/apps/boxclk/ChangeLog +++ b/apps/boxclk/ChangeLog @@ -1,3 +1,3 @@ 0.01: New App! 0.02: New config options such as step, meridian, short/long formats, custom prefix/suffix -0.03: Allows showing the month in short or long format by setting `"shortMonth"` to true or false +0.03: New Font Library Module! Also allows short or long month. diff --git a/apps/boxclk/README.md b/apps/boxclk/README.md index c72d932a4..5baa9c439 100644 --- a/apps/boxclk/README.md +++ b/apps/boxclk/README.md @@ -24,7 +24,7 @@ Here's an example of what a configuration might contain: { "customBox": { "string": "Your text here", - "font": "CustomFont", // Custom fonts must be removed in setUI + "font": "CustomFont", // Add custom fonts to "boxclk.lib" "fontSize": 1, "outline": 2, "color": "#FF9900", // Use 6 or 3 digit hex color codes @@ -54,7 +54,7 @@ __Breakdown of Parameters:__ * **string:** The text string to be displayed inside the box. This is only required for custom Box Names. -* **font:** The font name given to g.setFont(). +* **font:** The font name given to g.setFont(). To use a custom font, use the Espruino Font Converter and add it to "boxclk.lib" next to the other custom fonts. Use the font name beginning after "setFont" in your JSON config. * **fontSize:** The size of the font. diff --git a/apps/boxclk/app.js b/apps/boxclk/app.js index 0b0ca8e65..1d7d6c590 100644 --- a/apps/boxclk/app.js +++ b/apps/boxclk/app.js @@ -7,6 +7,7 @@ let storage = require("Storage"); let locale = require("locale"); let widgets = require("widget_utils"); + let customFonts = require("boxclk.lib"); let date = new Date(); let bgImage; let configNumber = (storage.readJSON("boxclk.json", 1) || {}).selectedConfig || 0; @@ -46,24 +47,7 @@ /** * --------------------------------------------------------------- - * 4. Font loading function - * --------------------------------------------------------------- - */ - let loadCustomFont = function() { - Graphics.prototype.setFontBrunoAce = function() { - // Actual height 23 (24 - 2) - return this.setFontCustom( - E.toString(require('heatshrink').decompress(atob('ABMHwADBh4DKg4bKgIPDAYUfAYV/AYX/AQMD/gmC+ADBn/AByE/GIU8AYUwLxcfAYX/8AnB//4JIP/FgMP4F+CQQBBjwJBFYRbBAd43DHoJpBh/g/xPEK4ZfDgEEORKDDAY8////wADLfZrTCgITBnhEBAYJMBAYMPw4DCM4QDjhwDCjwDBn0+AYMf/gDBh/4AYMH+ADBLpc4ToK/NGYZfnAYcfL4U/x5fBW4LvB/7vC+LvBgHAsBfIn76Cn4WBcYQDFEgJ+CQQYDyH4L/BAZbHLNYjjCAZc8ngDunycBZ4KkBa4KwBnEHY4UB+BfMgf/ZgMH/4XBc4cf4F/gE+ZgRjwAYcfj5jBM4U4M4RQBM4UA8BjIngDFEYJ8BAYUDAYQvCM4ZxBC4V+AYQvBnkBQ4M8gabBJQPAI4WAAYM/GYQaBAYJKCnqyCn5OCn4aBAYIaBAYJPCU4IABnBhIuDXCFAMD+Z/BY4IDBQwOPwEfv6TDAYUPAcwrDAYQ7BAYY/BI4cD8bLCK4RfEAA0BRYTeDcwIrFn0Pw43Bg4DugYDBjxBBU4SvDMYMH/5QBgP/LAQAP8EHN4UPwADHB4YAHA'))), - 46, - atob("CBEdChgYGhgaGBsaCQ=="), - 32|65536 - ); - }; - }; - - /** - * --------------------------------------------------------------- - * 5. Initial settings of boxes and their positions + * 4. Initial settings of boxes and their positions * --------------------------------------------------------------- */ for (let key in boxesConfig) { @@ -88,7 +72,7 @@ /** * --------------------------------------------------------------- - * 6. Text and drawing functions + * 5. Text and drawing functions * --------------------------------------------------------------- */ @@ -164,7 +148,7 @@ /** * --------------------------------------------------------------- - * 7. String forming helper functions + * 6. String forming helper functions * --------------------------------------------------------------- */ let isBool = function(val, defaultVal) { @@ -208,7 +192,7 @@ /** * --------------------------------------------------------------- - * 8. Main draw function + * 7. Main draw function * --------------------------------------------------------------- */ let draw = (function() { @@ -269,7 +253,7 @@ /** * --------------------------------------------------------------- - * 9. Helper function for touch event + * 8. Helper function for touch event * --------------------------------------------------------------- */ let touchInText = function(e, boxItem, boxKey) { @@ -293,7 +277,7 @@ /** * --------------------------------------------------------------- - * 10. Setup function to configure event handlers + * 9. Setup function to configure event handlers * --------------------------------------------------------------- */ let setup = function() { @@ -380,20 +364,19 @@ Bangle.removeListener('drag', dragHandler); if (drawTimeout) clearTimeout(drawTimeout); drawTimeout = undefined; - delete Graphics.prototype.setFontBrunoAce; + unloadCustomBoxclkFonts(); // Remove custom fonts // Restore original drawString function (no outlines) g.drawString = g_drawString; restoreSetColor(); widgets.show(); } }); - loadCustomFont(); draw(boxes); }; /** * --------------------------------------------------------------- - * 11. Main execution part + * 10. Main execution part * --------------------------------------------------------------- */ Bangle.loadWidgets(); diff --git a/apps/boxclk/boxclk-1.json b/apps/boxclk/boxclk-1.json index 99e225f04..d4fe65b25 100644 --- a/apps/boxclk/boxclk-1.json +++ b/apps/boxclk/boxclk-1.json @@ -1,15 +1,15 @@ { "time": { - "font": "6x8", - "fontSize": 3, + "font": "Orbitron", + "fontSize": 1, "outline": 2, "color": "#0ff", "outlineColor": "#00f", "border": "#0f0", - "xPadding": -1, - "yPadding": -2.5, - "xOffset": 2, - "yOffset": 0, + "xPadding": 0, + "yPadding": -3.5, + "xOffset": 0, + "yOffset": 2, "boxPos": { "x": "0.33", "y": "0.29" @@ -35,7 +35,7 @@ "dow": { "font": "6x8", "fontSize": 2, - "outline": 1, + "outline": 2, "color": "#000", "outlineColor": "#fff", "border": "#0f0", @@ -45,13 +45,13 @@ "yOffset": 1, "boxPos": { "x": "0.5", - "y": "0.82" + "y": "0.83" } }, "step": { "font": "6x8", "fontSize": 2, - "outline": 1, + "outline": 2, "color": "#000", "outlineColor": "#fff", "border": "#0f0", diff --git a/apps/boxclk/lib.js b/apps/boxclk/lib.js new file mode 100644 index 000000000..fec5f95e8 --- /dev/null +++ b/apps/boxclk/lib.js @@ -0,0 +1,53 @@ +/************************************************* + * Name: boxclk.lib + * Type: Font Library + * Desc: Add your custom fonts for Box Clock below + *************************************************/ + +// Create an empty object for exporting module's functions +var exports={}; + +// Array to hold the names of the custom fonts +let fontNames = []; + +// Add custom fonts below. Each font is a function that's added to the Graphics.prototype +// Use the Espruino Font Converter tool to convert your font into the appropriate format +// Use the name beginning after "setFont", for example "BrunoAce" in your JSON config +// This module and the main app will automatically delete the custom fonts in setUI + +Graphics.prototype.setFontBrunoAce = function() { + // Actual height 23 (24 - 2) + return this.setFontCustom( + E.toString(require('heatshrink').decompress(atob('ABMHwADBh4DKg4bKgIPDAYUfAYV/AYX/AQMD/gmC+ADBn/AByE/GIU8AYUwLxcfAYX/8AnB//4JIP/FgMP4F+CQQBBjwJBFYRbBAd43DHoJpBh/g/xPEK4ZfDgEEORKDDAY8////wADLfZrTCgITBnhEBAYJMBAYMPw4DCM4QDjhwDCjwDBn0+AYMf/gDBh/4AYMH+ADBLpc4ToK/NGYZfnAYcfL4U/x5fBW4LvB/7vC+LvBgHAsBfIn76Cn4WBcYQDFEgJ+CQQYDyH4L/BAZbHLNYjjCAZc8ngDunycBZ4KkBa4KwBnEHY4UB+BfMgf/ZgMH/4XBc4cf4F/gE+ZgRjwAYcfj5jBM4U4M4RQBM4UA8BjIngDFEYJ8BAYUDAYQvCM4ZxBC4V+AYQvBnkBQ4M8gabBJQPAI4WAAYM/GYQaBAYJKCnqyCn5OCn4aBAYIaBAYJPCU4IABnBhIuDXCFAMD+Z/BY4IDBQwOPwEfv6TDAYUPAcwrDAYQ7BAYY/BI4cD8bLCK4RfEAA0BRYTeDcwIrFn0Pw43Bg4DugYDBjxBBU4SvDMYMH/5QBgP/LAQAP8EHN4UPwADHB4YAHA'))), + 46, + atob("CBEdChgYGhgaGBsaCQ=="), + 32|65536 + ); +}; + +Graphics.prototype.setFontOrbitron = function() { + // Actual height 24 (25 - 2) + return this.setFontCustom( + E.toString(require('heatshrink').decompress(atob('AA3AAQMBAYwLDAA8DBYUHwADBjwLCngDCvADC+AWJh4OCDQYWGgPgDQsPGI0cMBUf///wE/AYPAAYc4BoIDCnoDCvIDC+IDBgPhAYMDAYfBAYMHwIDBh6HBnEeAYU8AYV4AYX4AYXwAYM58ADBnfAAYM/wADCI4RTDh4DBMhNAYIqeEQ40+XIx7HAYb/SV4MBC4M+gYDCg6fCg5vCAfxnBAYMf/wDBh/8SIYAJgRrDToOAS4KhBAYKpBf4IvChwD5n5jCj7TCh4DBdoK/BMo3wAQMBAYUDAY0HAYUPuB4CAYU8AYV4AYXwAYMB8ADBQIIDBRoIDBh4DCj8AAYKTBAYK7BJ4IDHFAIrCAZgAFO4I1BAYR/CAYK/7AZDABAYMHYYM4YZAAJb4RrBR4xn/gADDNYT0BKYL8BNYs4AfybHU4XAKwJbCXa8HQYQD+M42AYQK7FAA8H/BrC/0BNYQjDM/5rdMoMA4AdBAIwLCC44A='))), + 46, + atob("BxEbDRsaFxsaFRsbBw=="), + 32|65536 + ); +}; + +// Extract the names of the custom fonts added to the Graphics.prototype +for (let prop in Graphics.prototype) { + if (prop.startsWith('setFont')) { + fontNames.push(prop.slice(7)); // remove 'setFont' from the start + } +} + +// Function to remove the custom fonts from the Graphics.prototype +function unloadCustomBoxClkFonts() { + for (let i = 0; i < fontNames.length; i++) { + delete Graphics.prototype[fontNames[i]]; + } +} + +// Export the unload function +exports.unloadCustomBoxClkFonts = unloadCustomBoxClkFonts; \ No newline at end of file diff --git a/apps/boxclk/metadata.json b/apps/boxclk/metadata.json index 6717b79d8..2d2507e8d 100644 --- a/apps/boxclk/metadata.json +++ b/apps/boxclk/metadata.json @@ -12,11 +12,13 @@ "type": "clock", "tags": "clock", "supports": ["BANGLEJS2"], + "provides_modules" : ["boxclk.lib"], "readme": "README.md", "allow_emulator": true, "storage": [ {"name":"boxclk.app.js","url":"app.js"}, {"name":"boxclk.settings.js","url":"settings.js"}, + {"name":"boxclk.lib","url":"lib.js"}, {"name":"boxclk.img","url":"icon.js","evaluate":true}, {"name":"boxclk.beachhouse.img","url":"beachhouse.js","evaluate":true} ], diff --git a/apps/boxclk/screenshot-1.png b/apps/boxclk/screenshot-1.png index c6e22d26297899f6f7d588db4559a71f85aacb1b..25b911aa86557ed990215a0404cd9f7105c4cab9 100644 GIT binary patch literal 5782 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