diff --git a/.gitignore b/.gitignore index 574622dd..ba23cb91 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ /.lock-* /build/ /out/ +.idea/ diff --git a/appinfo.json b/appinfo.json index 64029118..44ecf8a9 100644 --- a/appinfo.json +++ b/appinfo.json @@ -11,17 +11,17 @@ "file": "images/menu_icon.png", "menuIcon": true, "name": "IMAGE_MENU_ICON", - "type": "bitmap" + "type": "png" }, { "file": "images/logo_splash.png", "name": "IMAGE_LOGO_SPLASH", - "type": "bitmap" + "type": "png" }, { "file": "images/tile_splash.png", "name": "IMAGE_TILE_SPLASH", - "type": "bitmap" + "type": "png" }, { "file": "fonts/UbuntuMono-Regular.ttf", diff --git a/resources/images/menu_icon.png b/resources/images/menu_icon.png index a9ed5033..91fc819f 100644 Binary files a/resources/images/menu_icon.png and b/resources/images/menu_icon.png differ diff --git a/src/js/app.js b/src/js/app.js index 9255c293..870d1934 100644 --- a/src/js/app.js +++ b/src/js/app.js @@ -7,6 +7,8 @@ var UI = require('ui'); var Vector2 = require('vector2'); +var TRIG_MAX_ANGLE = 0x10000; + var main = new UI.Card({ title: 'Pebble.js', icon: 'images/menu_icon.png', @@ -50,17 +52,11 @@ main.on('click', 'select', function(e) { size: new Vector2(140, 140), angle: 0, angle2: 300, - radius: 20, + radius: 10, backgroundColor: 'cyan', borderColor: 'celeste', borderWidth: 1, }); - var textfield = new UI.Text({ - size: new Vector2(140, 60), - font: 'gothic-24-bold', - text: 'Dynamic\nWindow', - textAlign: 'center' - }); var windSize = wind.size(); // Center the radial in the window var radialPos = radial.position() @@ -68,14 +64,34 @@ main.on('click', 'select', function(e) { .subSelf(radial.size()) .multiplyScalar(0.5); radial.position(radialPos); + var textfield = new UI.Text({ + size: new Vector2(140, 60), + font: 'gothic-18', + text: 'Dynamic\nWindow', + textAlign: 'center' + }); // Center the textfield in the window var textfieldPos = textfield.position() - .addSelf(windSize) - .subSelf(textfield.size()) - .multiplyScalar(0.5); + .addSelf(windSize) + .subSelf(textfield.size()) + .multiplyScalar(0.5) + .addSelf({ x: 0, y: 10 }); textfield.position(textfieldPos); wind.add(radial); wind.add(textfield); + // Add clock ticks inside radial using path fields + var centerPos = wind.size().multiplyScalar(0.5); + var outerSize = radial.size().y / 2 - radial.radius() - 15; + for (var i = 0; i < 12; i++) { + wind.add(new UI.Path({ + p0: { x: -2, y: outerSize - 7 }, + p1: { x: -2, y: outerSize + 7 }, + p2: { x: 2, y: outerSize + 7 }, + p3: { x: 2, y: outerSize - 7 }, + offset: centerPos, + rotation: Math.floor(TRIG_MAX_ANGLE * i / 12) + })); + } wind.show(); }); diff --git a/src/js/lib/struct.js b/src/js/lib/struct.js index 9989b161..192df1c9 100644 --- a/src/js/lib/struct.js +++ b/src/js/lib/struct.js @@ -171,8 +171,8 @@ struct.prototype._makeAccessor = function(field) { }; struct.prototype._makeMetaAccessor = function(name, transform) { - this[name] = function(value, field) { - transform.call(this, value, field); + this[name] = function(value) { + transform.call(this, value, name); return this; }; }; diff --git a/src/js/ui/element.js b/src/js/ui/element.js index c4759f29..4e5a05d5 100644 --- a/src/js/ui/element.js +++ b/src/js/ui/element.js @@ -38,6 +38,7 @@ var Types = [ 'TextType', 'ImageType', 'InverterType', + 'PathType' ]; Types.forEach(function(name, index) { diff --git a/src/js/ui/index.js b/src/js/ui/index.js index 8ba16893..4507fe40 100644 --- a/src/js/ui/index.js +++ b/src/js/ui/index.js @@ -14,5 +14,6 @@ UI.Image = require('ui/image'); UI.Inverter = require('ui/inverter'); UI.Vibe = require('ui/vibe'); UI.Light = require('ui/light'); +UI.Path = require('ui/path'); module.exports = UI; diff --git a/src/js/ui/path.js b/src/js/ui/path.js new file mode 100644 index 00000000..110184f0 --- /dev/null +++ b/src/js/ui/path.js @@ -0,0 +1,29 @@ +var util2 = require('util2'); +var myutil = require('myutil'); +var Propable = require('ui/propable'); +var StageElement = require('ui/element'); + +var pathProps = [ + 'rotation', + 'offset', + 'p0', + 'p1', + 'p2', + 'p3', +]; + +var defaults = { + backgroundColor: 'clear', + borderColor: 'white', +}; + +var Path = function(elementDef) { + StageElement.call(this, myutil.shadow(defaults, elementDef || {})); + this.state.type = StageElement.PathType; +}; + +util2.inherit(Path, StageElement); + +Propable.makeAccessors(pathProps, Path.prototype); + +module.exports = Path; diff --git a/src/js/ui/simply-pebble.js b/src/js/ui/simply-pebble.js index 648dc445..dacb935c 100644 --- a/src/js/ui/simply-pebble.js +++ b/src/js/ui/simply-pebble.js @@ -76,6 +76,11 @@ var SizeType = function(x) { this.sizeH(x.y); }; +var VectorType = function(vector, field) { + this[field + 'X'](vector.x); + this[field + 'Y'](vector.y); +}; + var namedColorMap = { 'clear': 0x00, 'black': 0xC0, @@ -732,6 +737,17 @@ var ElementAnimateDonePacket = new struct([ ['uint32', 'id'], ]); +var ElementPathPacket = new struct([ + [Packet, 'packet'], + ['uint32', 'id'], + ['int32', 'rotation'], + [GPoint, 'offset', VectorType], + [GPoint, 'p0', VectorType], + [GPoint, 'p1', VectorType], + [GPoint, 'p2', VectorType], + [GPoint, 'p3', VectorType], +]); + var VoiceDictationStartPacket = new struct([ [Packet, 'packet'], ['bool', 'enableConfirmation'], @@ -801,6 +817,7 @@ var CommandPackets = [ ElementImagePacket, ElementAnimatePacket, ElementAnimateDonePacket, + ElementPathPacket, VoiceDictationStartPacket, VoiceDictationStopPacket, VoiceDictationDataPacket, @@ -1305,6 +1322,19 @@ SimplyPebble.elementAnimate = function(id, def, animateDef, duration, easing) { SimplyPebble.sendPacket(ElementAnimatePacket); }; +SimplyPebble.elementPath = function(id, def) { + ElementPathPacket + .id(id) + .p0(def.p0) + .p1(def.p1) + .p2(def.p2) + .p3(def.p3) + .rotation(def.rotation) + .offset(def.offset || { x: 0, y: 0 }) + .prop(def); + SimplyPebble.sendPacket(ElementPathPacket); +}; + SimplyPebble.stageClear = function() { SimplyPebble.sendPacket(StageClearPacket); }; @@ -1333,6 +1363,9 @@ SimplyPebble.stageElement = function(id, type, def, index) { SimplyPebble.elementRadius(id, def); SimplyPebble.elementImage(id, def.image, def.compositing); break; + case StageElement.PathType: + SimplyPebble.elementPath(id, def); + break; } }; diff --git a/src/js/ui/stage.js b/src/js/ui/stage.js index f8ec59d0..573a64cb 100644 --- a/src/js/ui/stage.js +++ b/src/js/ui/stage.js @@ -9,11 +9,13 @@ var Stage = function(stageDef) { }; Stage.RectType = 1; -Stage.CircleType = 2; -Stage.RadialType = 6; -Stage.TextType = 3; -Stage.ImageType = 4; -Stage.InverterType = 5; +Stage.LineType = 2; +Stage.CircleType = 3; +Stage.RadialType = 4; +Stage.TextType = 4; +Stage.ImageType = 5; +Stage.InverterType = 6; +Stage.PathType = 7; util2.copy(Emitter.prototype, Stage.prototype); diff --git a/src/simply/simply_msg_commands.h b/src/simply/simply_msg_commands.h index c04f7aaa..8e54cb3d 100644 --- a/src/simply/simply_msg_commands.h +++ b/src/simply/simply_msg_commands.h @@ -55,6 +55,7 @@ enum Command { CommandElementImage, CommandElementAnimate, CommandElementAnimateDone, + CommandElementPath, CommandVoiceStart, CommandVoiceStop, CommandVoiceData, diff --git a/src/simply/simply_stage.c b/src/simply/simply_stage.c index 1b5106ad..f2ec4abb 100644 --- a/src/simply/simply_stage.c +++ b/src/simply/simply_stage.c @@ -111,6 +111,19 @@ struct __attribute__((__packed__)) ElementAnimateDonePacket { uint32_t id; }; +typedef struct ElementPathPacket ElementPathPacket; + +struct __attribute__((__packed__)) ElementPathPacket { + Packet packet; + uint32_t id; + int32_t rotation; + GPoint offset; + GPoint p0; + GPoint p1; + GPoint p2; + GPoint p3; +}; + static void simply_stage_clear(SimplyStage *self); static void simply_stage_update(SimplyStage *self); @@ -157,6 +170,15 @@ static void destroy_element(SimplyStage *self, SimplyElementCommon *element) { case SimplyElementTypeInverter: inverter_layer_destroy(((SimplyElementInverter*) element)->inverter_layer); break; + case SimplyElementTypePath: + if (((SimplyElementPath*) element)->path) { + if (((SimplyElementPath*) element)->path->points) { + free(((SimplyElementPath*) element)->path->points); + ((SimplyElementPath*) element)->path->points = NULL; + } + gpath_destroy(((SimplyElementPath*) element)->path); + } + break; } free(element); } @@ -289,6 +311,17 @@ static void image_element_draw(GContext *ctx, SimplyStage *self, SimplyElementIm graphics_context_set_compositing_mode(ctx, GCompOpAssign); } +static void path_element_draw(GContext *ctx, SimplyStage *self, SimplyElementPath *element) { + if (element->path && element->common.background_color.a) { + graphics_context_set_fill_color(ctx, gcolor8_get(element->common.background_color)); + gpath_draw_filled(ctx, element->path); + } + if (element->path && element->common.border_color.a) { + graphics_context_set_stroke_color(ctx, gcolor8_get(element->common.border_color)); + gpath_draw_outline(ctx, element->path); + } +} + static void layer_update_callback(Layer *layer, GContext *ctx) { SimplyStage *self = *(void**) layer_get_data(layer); @@ -332,6 +365,9 @@ static void layer_update_callback(Layer *layer, GContext *ctx) { break; case SimplyElementTypeInverter: break; + case SimplyElementTypePath: + path_element_draw(ctx, self, (SimplyElementPath*) element); + break; } element = (SimplyElementCommon*) element->node.next; } @@ -356,6 +392,7 @@ static size_t prv_get_element_size(SimplyElementType type) { case SimplyElementTypeText: return sizeof(SimplyElementText); case SimplyElementTypeImage: return sizeof(SimplyElementImage); case SimplyElementTypeInverter: return sizeof(SimplyElementInverter); + case SimplyElementTypePath: return sizeof(SimplyElementPath); } return 0; } @@ -596,6 +633,7 @@ static void handle_element_remove_packet(Simply *simply, Packet *data) { return; } simply_stage_remove_element(simply->stage, element); + destroy_element(simply->stage, element); simply_stage_update(simply->stage); } @@ -703,6 +741,38 @@ static void handle_element_animate_packet(Simply *simply, Packet *data) { simply_stage_animate_element(simply->stage, element, animation, packet->frame); } +static void handle_element_path_packet(Simply *simply, Packet *data) { + GPathInfo pathInfo; + GPath *path; + + ElementPathPacket *packet = (ElementPathPacket*) data; + SimplyElementPath *element = (SimplyElementPath*) simply_stage_get_element(simply->stage, packet->id); + if (!element) { + return; + } + pathInfo.num_points = 4; + pathInfo.points = malloc0(4 * sizeof(GPoint)); + if (pathInfo.points) { + memcpy(pathInfo.points, &packet->p0, 4 * sizeof(GPoint)); + path = gpath_create(&pathInfo); + if (path) { + if (element->path) { + if (element->path->points) { + free(element->path->points); + element->path->points = NULL; + } + gpath_destroy(element->path); + } + element->path = path; + gpath_move_to(element->path, packet->offset); + gpath_rotate_to(element->path, packet->rotation); + simply_stage_update(simply->stage); + } else { + free(pathInfo.points); + } + } +} + bool simply_stage_handle_packet(Simply *simply, Packet *packet) { switch (packet->type) { case CommandStageClear: @@ -738,6 +808,9 @@ bool simply_stage_handle_packet(Simply *simply, Packet *packet) { case CommandElementAnimate: handle_element_animate_packet(simply, packet); return true; + case CommandElementPath: + handle_element_path_packet(simply, packet); + return true; } return false; } diff --git a/src/simply/simply_stage.h b/src/simply/simply_stage.h index e9714f51..c00e804f 100644 --- a/src/simply/simply_stage.h +++ b/src/simply/simply_stage.h @@ -31,6 +31,7 @@ enum SimplyElementType { SimplyElementTypeText, SimplyElementTypeImage, SimplyElementTypeInverter, + SimplyElementTypePath, }; struct SimplyStageLayer { @@ -113,6 +114,13 @@ struct SimplyAnimation { AnimationCurve curve; }; +typedef struct SimplyElementPath SimplyElementPath; + +struct SimplyElementPath { + SimplyElementCommon common; + GPath* path; +}; + SimplyStage *simply_stage_create(Simply *simply); void simply_stage_destroy(SimplyStage *self);