diff --git a/src/js/tabs/osd.js b/src/js/tabs/osd.js index a622f9e2..bc06024d 100755 --- a/src/js/tabs/osd.js +++ b/src/js/tabs/osd.js @@ -1,6 +1,7 @@ 'use strict'; var SYM = SYM || {}; +SYM.BLANK = 0x20; SYM.VOLT = 0x06; SYM.RSSI = 0x01; SYM.AH_RIGHT = 0x02; @@ -465,6 +466,7 @@ OSD.constants = { name: 'MAIN_BATT_VOLTAGE', desc: 'osdDescElementMainBattVoltage', default_position: -29, + draw_order: 20, positionable: true, preview: FONT.symbol(SYM.BATTERY) + '16.8' + FONT.symbol(SYM.VOLT) }, @@ -472,6 +474,7 @@ OSD.constants = { name: 'RSSI_VALUE', desc: 'osdDescElementRssiValue', default_position: -59, + draw_order: 30, positionable: true, preview: FONT.symbol(SYM.RSSI) + '99' }, @@ -485,6 +488,7 @@ OSD.constants = { name: 'THROTTLE_POSITION', desc: 'osdDescElementThrottlePosition', default_position: -9, + draw_order: 110, positionable: true, preview: FONT.symbol(SYM.THR) + FONT.symbol(SYM.THR1) + '69' }, @@ -497,6 +501,7 @@ OSD.constants = { VTX_CHANNEL: { name: 'VTX_CHANNEL', default_position: 1, + draw_order: 120, positionable: true, preview: 'R:2:1' }, @@ -517,31 +522,88 @@ OSD.constants = { name: 'DISARMED', desc: 'osdDescElementDisarmed', default_position: -109, + draw_order: 280, positionable: true, preview: 'DISARMED' }, CROSSHAIRS: { name: 'CROSSHAIRS', desc: 'osdDescElementCrosshairs', - default_position: -1, - positionable: false + default_position: 193, + draw_order: 40, + positionable: function() { + return semver.gte(CONFIG.apiVersion, "1.38.0") ? true : false; + }, + preview: FONT.symbol(SYM.AH_CENTER_LINE) + FONT.symbol(SYM.AH_CENTER) + FONT.symbol(SYM.AH_CENTER_LINE_RIGHT) }, ARTIFICIAL_HORIZON: { name: 'ARTIFICIAL_HORIZON', desc: 'osdDescElementArtificialHorizon', - default_position: -1, - positionable: false + default_position: 194, + draw_order: 10, + positionable: function() { + return semver.gte(CONFIG.apiVersion, "1.38.0") ? true : false; + }, + preview: function() { + + var artificialHorizon = new Array(); + + for (var j = 1; j < 8; j++) { + for (var i = -4; i <= 4; i++) { + + var element; + + // Blank char to mark the size of the element + if (j != 4) { + element = {x: i, y : j, sym : SYM.BLANK}; + + // Sample of horizon + } else { + element = {x: i, y : j, sym : SYM.AH_BAR9_0 + 4}; + } + artificialHorizon.push(element); + } + } + return artificialHorizon; + } }, HORIZON_SIDEBARS: { name: 'HORIZON_SIDEBARS', desc: 'osdDescElementHorizonSidebars', - default_position: -1, - positionable: false + default_position: 194, + draw_order: 50, + positionable: function() { + return semver.gte(CONFIG.apiVersion, "1.38.0") ? true : false; + }, + preview: function(fieldPosition) { + + var horizonSidebar = new Array(); + + var hudwidth = OSD.constants.AHISIDEBARWIDTHPOSITION; + var hudheight = OSD.constants.AHISIDEBARHEIGHTPOSITION; + for (var i = -hudheight; i <= hudheight; i++) { + var element = {x: -hudwidth, y : i, sym : SYM.AH_DECORATION}; + horizonSidebar.push(element); + + element = {x: hudwidth, y : i, sym : SYM.AH_DECORATION}; + horizonSidebar.push(element); + } + + // AH level indicators + var element = {x: -hudwidth + 1, y : 0, sym : SYM.AH_LEFT}; + horizonSidebar.push(element); + + element = {x: hudwidth - 1, y : 0, sym : SYM.AH_RIGHT}; + horizonSidebar.push(element); + + return horizonSidebar; + } }, CURRENT_DRAW: { name: 'CURRENT_DRAW', desc: 'osdDescElementCurrentDraw', default_position: -23, + draw_order: 130, positionable: true, preview: function() { return semver.gte(CONFIG.apiVersion, "1.36.0") ? ' 42.00' + FONT.symbol(SYM.AMP) : FONT.symbol(SYM.AMP) + '42.0'; @@ -551,6 +613,7 @@ OSD.constants = { name: 'MAH_DRAWN', desc: 'osdDescElementMahDrawn', default_position: -18, + draw_order: 140, positionable: true, preview: function() { return semver.gte(CONFIG.apiVersion, "1.36.0") ? ' 690' + FONT.symbol(SYM.MAH) : FONT.symbol(SYM.MAH) + '690'; @@ -560,6 +623,7 @@ OSD.constants = { name: 'CRAFT_NAME', desc: 'osdDescElementCraftName', default_position: -77, + draw_order: 150, positionable: true, preview: function(osd_data) { return OSD.generateCraftName(osd_data, 1); @@ -569,6 +633,7 @@ OSD.constants = { name: 'ALTITUDE', desc: 'osdDescElementAltitude', default_position: 62, + draw_order: 160, positionable: true, preview: function(osd_data) { return '399.7' + FONT.symbol(osd_data.unit_mode === 0 ? SYM.FEET : SYM.METRE) @@ -592,6 +657,7 @@ OSD.constants = { name: 'FLYMODE', desc: 'osdDescElementFlyMode', default_position: -1, + draw_order: 90, positionable: true, preview: 'STAB' }, @@ -599,6 +665,7 @@ OSD.constants = { name: 'GPS_SPEED', desc: 'osdDescElementGPSSpeed', default_position: -1, + draw_order: 330, positionable: true, preview: ' 40K' }, @@ -606,6 +673,7 @@ OSD.constants = { name: 'GPS_SATS', desc: 'osdDescElementGPSSats', default_position: -1, + draw_order: 320, positionable: true, preview: FONT.symbol(SYM.GPS_SAT_L) + FONT.symbol(SYM.GPS_SAT_R) + '14' }, @@ -613,6 +681,7 @@ OSD.constants = { name: 'GPS_LON', desc: 'osdDescElementGPSLon', default_position: -1, + draw_order: 350, positionable: true, preview: FONT.symbol(SYM.ARROW_SOUTH) + '00.00000000' }, @@ -620,6 +689,7 @@ OSD.constants = { name: 'GPS_LAT', desc: 'osdDescElementGPSLat', default_position: -1, + draw_order: 340, positionable: true, preview: FONT.symbol(SYM.ARROW_EAST) + '00.00000000' }, @@ -627,6 +697,7 @@ OSD.constants = { name: 'DEBUG', desc: 'osdDescElementDebug', default_position: -1, + draw_order: 240, positionable: true, preview: 'DBG 0 0 0 0' }, @@ -634,6 +705,7 @@ OSD.constants = { name: 'PID_ROLL', desc: 'osdDescElementPIDRoll', default_position: 0x800 | (10 << 5) | 2, // 0x0800 | (y << 5) | x + draw_order: 170, positionable: true, preview: 'ROL 43 40 20' }, @@ -641,6 +713,7 @@ OSD.constants = { name: 'PID_PITCH', desc: 'osdDescElementPIDPitch', default_position: 0x800 | (11 << 5) | 2, // 0x0800 | (y << 5) | x + draw_order: 180, positionable: true, preview: 'PIT 58 50 22' }, @@ -648,6 +721,7 @@ OSD.constants = { name: 'PID_YAW', desc: 'osdDescElementPIDYaw', default_position: 0x800 | (12 << 5) | 2, // 0x0800 | (y << 5) | x + draw_order: 190, positionable: true, preview: 'YAW 70 45 20' }, @@ -655,6 +729,7 @@ OSD.constants = { name: 'POWER', desc: 'osdDescElementPower', default_position: (15 << 5) | 2, + draw_order: 200, positionable: true, preview: function() { return semver.gte(CONFIG.apiVersion, "1.36.0") ? ' 142W' : '142W'; @@ -664,13 +739,14 @@ OSD.constants = { name: 'PID_RATE_PROFILE', desc: 'osdDescElementPIDRateProfile', default_position: 0x800 | (13 << 5) | 2, // 0x0800 | (y << 5) | x + draw_order: 210, positionable: true, preview: '1-2' }, BATTERY_WARNING: { name: 'BATTERY_WARNING', desc: 'osdDescElementBatteryWarning', - default_position: -1, + default_position: -1, positionable: true, preview: 'LOW VOLTAGE' }, @@ -678,6 +754,7 @@ OSD.constants = { name: 'AVG_CELL_VOLTAGE', desc: 'osdDescElementAvgCellVoltage', default_position: 12 << 5, + draw_order: 230, positionable: true, preview: FONT.symbol(SYM.BATTERY) + '3.98' + FONT.symbol(SYM.VOLT) }, @@ -685,6 +762,7 @@ OSD.constants = { name: 'PITCH_ANGLE', desc: 'osdDescElementPitchAngle', default_position: -1, + draw_order: 250, positionable: true, preview: '-00.0' }, @@ -692,6 +770,7 @@ OSD.constants = { name: 'ROLL_ANGLE', desc: 'osdDescElementRollAngle', default_position: -1, + draw_order: 260, positionable: true, preview: '-00.0' }, @@ -699,6 +778,7 @@ OSD.constants = { name: 'MAIN_BATT_USAGE', desc: 'osdDescElementMainBattUsage', default_position: -17, + draw_order: 270, positionable: true, preview: FONT.symbol(SYM.PB_START) + FONT.symbol(SYM.PB_FULL) + FONT.symbol(SYM.PB_FULL) + FONT.symbol(SYM.PB_FULL) + FONT.symbol(SYM.PB_FULL) + FONT.symbol(SYM.PB_FULL) + FONT.symbol(SYM.PB_FULL) + FONT.symbol(SYM.PB_FULL) + FONT.symbol(SYM.PB_FULL) + FONT.symbol(SYM.PB_FULL) + FONT.symbol(SYM.PB_END) + FONT.symbol(SYM.PB_EMPTY) + FONT.symbol(SYM.PB_CLOSE) }, @@ -713,6 +793,7 @@ OSD.constants = { name: 'HOME_DIRECTION', desc: 'osdDescElementHomeDirection', default_position: -1, + draw_order: 370, positionable: true, preview: FONT.symbol(SYM.ARROW_SOUTH + 2) }, @@ -720,6 +801,7 @@ OSD.constants = { name: 'HOME_DISTANCE', desc: 'osdDescElementHomeDistance', default_position: -1, + draw_order: 360, positionable: true, preview: function(osd_data) { return '43' + FONT.symbol(osd_data.unit_mode === 0 ? SYM.FEET : SYM.METRE) @@ -729,6 +811,7 @@ OSD.constants = { name: 'NUMERICAL_HEADING', desc: 'osdDescElementNumericalHeading', default_position: -1, + draw_order: 290, positionable: true, preview: FONT.symbol(SYM.ARROW_EAST) + '90' }, @@ -736,6 +819,7 @@ OSD.constants = { name: 'NUMERICAL_VARIO', desc: 'osdDescElementNumericalVario', default_position: -1, + draw_order: 300, positionable: true, preview: FONT.symbol(SYM.ARROW_NORTH) + '8.7' }, @@ -743,6 +827,7 @@ OSD.constants = { name: 'COMPASS_BAR', desc: 'osdDescElementCompassBar', default_position: -1, + draw_order: 310, positionable: true, preview: function(osd_data) { return FONT.symbol(SYM.HEADING_W) + FONT.symbol(SYM.HEADING_LINE) + FONT.symbol(SYM.HEADING_DIVIDED_LINE) + @@ -754,6 +839,7 @@ OSD.constants = { name: 'WARNINGS', desc: 'osdDescElementWarnings', default_position: -1, + draw_order: 220, positionable: true, preview: 'LOW VOLTAGE' }, @@ -761,6 +847,7 @@ OSD.constants = { name: 'ESC_TEMPERATURE', desc: 'osdDescElementEscTemperature', default_position: -1, + draw_order: 380, positionable: true, preview: FONT.symbol(SYM.TEMP_C) + '45' }, @@ -768,6 +855,7 @@ OSD.constants = { name: 'ESC_RPM', desc: 'osdDescElementEscRpm', default_position: -1, + draw_order: 390, positionable: true, preview: '226000' }, @@ -775,6 +863,7 @@ OSD.constants = { name: 'REMAINING_TIME_ESTIMATE', desc: 'osdDescElementRemaningTimeEstimate', default_position: -1, + draw_order: 80, positionable: true, preview: '01:13' }, @@ -782,6 +871,7 @@ OSD.constants = { name: 'RTC_DATE_TIME', desc: 'osdDescElementRtcDateTime', default_position: -1, + draw_order: 400, positionable: true, preview: '2017-11-11 16:20:00' }, @@ -789,6 +879,7 @@ OSD.constants = { name: 'ADJUSTMENT_RANGE', desc: 'osdDescElementAdjustmentRange', default_position: -1, + draw_order: 410, positionable: true, preview: 'PITCH/ROLL P: 42' }, @@ -796,6 +887,7 @@ OSD.constants = { name: 'TIMER_1', desc: 'osdDescElementTimer1', default_position: -1, + draw_order: 60, positionable: true, preview: function(osd_data) { return OSD.generateTimerPreview(osd_data, 0); @@ -805,6 +897,7 @@ OSD.constants = { name: 'TIMER_2', desc: 'osdDescElementTimer2', default_position: -1, + draw_order: 70, positionable: true, preview: function(osd_data) { return OSD.generateTimerPreview(osd_data, 1); @@ -814,6 +907,7 @@ OSD.constants = { name: 'CORE_TEMPERATURE', desc: 'osdDescElementCoreTemperature', default_position: -1, + draw_order: 420, positionable: true, preview: function(osd_data) { return OSD.generateTemperaturePreview(osd_data, 33); @@ -926,6 +1020,20 @@ OSD.constants = { ] }; +OSD.searchLimitsElement = function(arrayElements) { + // Search minimum and maximum + var limits = {minX: 0, maxX: 0, minY:0, maxY: 0}; + + arrayElements.forEach(function(valor, indice, array) { + limits.minX = Math.min(valor.x, limits.minX); + limits.maxX = Math.max(valor.x, limits.maxX); + limits.minY = Math.min(valor.y, limits.minY); + limits.maxY = Math.max(valor.y, limits.maxY); + }); + + return limits; + } + // Pick display fields by version, order matters, so these are going in an array... pry could iterate the example map instead OSD.chooseFields = function () { var F = OSD.constants.ALL_DISPLAY_FIELDS; @@ -1082,6 +1190,24 @@ OSD.updateDisplaySize = function() { }; }; +OSD.drawByOrder = function(selectedPosition, field, charCode) { + + // Check if there is other field at the same position + if (OSD.data.preview[selectedPosition] !== undefined) { + var oldField = OSD.data.preview[selectedPosition][0]; + if (oldField != null) { + if (oldField.draw_order !== undefined) { + if ((field.draw_order === undefined) || (field.draw_order < oldField.draw_order)) { + // Not overwrite old field + return; + } + } + } + + // Default action, overwrite old field + OSD.data.preview[selectedPosition++] = [field, charCode]; + } +} OSD.msp = { /** @@ -1241,6 +1367,7 @@ OSD.msp = { name: suffix ? c.name + suffix : c.name, desc: c.desc, index: j, + draw_order: c.draw_order, positionable: c.positionable, preview: suffix ? c.preview + suffix : c.preview }, this.helpers.unpack.position(v, c))); @@ -1295,11 +1422,14 @@ OSD.msp = { } } - // Generate OSD element previews that are defined by a function + // Generate OSD element previews and positionable that are defined by a function for (let item of d.display_items) { if (typeof(item.preview) === 'function') { item.preview = item.preview(d); } + if (typeof(item.positionable) === 'function') { + item.positionable = item.positionable(d); + } } OSD.updateDisplaySize(); @@ -1318,8 +1448,18 @@ OSD.GUI.preview = { }, onDragStart: function(e) { var ev = e.originalEvent; + + var display_item = OSD.data.display_items[$(ev.target).data('field').index]; + var offsetX = 6; + var offsetY = 9; + if (display_item.preview.constructor === Array) { + var arrayElements = display_item.preview; + var limits = OSD.searchLimitsElement(arrayElements); + offsetX -= limits.minX*12; + offsetY -= limits.minY*12; + } ev.dataTransfer.setData("text/plain", $(ev.target).data('field').index); - ev.dataTransfer.setDragImage($(this).data('field').preview_img, 6, 9); + ev.dataTransfer.setDragImage($(this).data('field').preview_img, offsetX, offsetY); }, onDragOver: function(e) { var ev = e.originalEvent; @@ -1338,10 +1478,35 @@ OSD.GUI.preview = { var position = $(this).removeAttr('style').data('position'); var field_id = parseInt(ev.dataTransfer.getData('text/plain')) var display_item = OSD.data.display_items[field_id]; - var overflows_line = FONT.constants.SIZES.LINE - ((position % FONT.constants.SIZES.LINE) + display_item.preview.length); - if (overflows_line < 0) { - position += overflows_line; + + var overflows_line = 0; + // Standard preview, string type + if (display_item.preview.constructor !== Array) { + overflows_line = FONT.constants.SIZES.LINE - ((position % FONT.constants.SIZES.LINE) + display_item.preview.length); + if (overflows_line < 0) { + position += overflows_line; + } + + // Advanced preview, array type + } else { + var arrayElements = display_item.preview; + var limits = OSD.searchLimitsElement(arrayElements); + + var selectedPositionX = position % FONT.constants.SIZES.LINE; + var selectedPositionY = Math.trunc(position / FONT.constants.SIZES.LINE); + + if ((limits.minX < 0) && ((selectedPositionX + limits.minX) < 0)) { + position += Math.abs(selectedPositionX + limits.minX); + } else if ((limits.maxX > 0) && ((selectedPositionX + limits.maxX) >= FONT.constants.SIZES.LINE)) { + position -= (selectedPositionX + limits.maxX + 1) - FONT.constants.SIZES.LINE; + } + if ((limits.minY < 0) && ((selectedPositionY + limits.minY) < 0)) { + position += Math.abs(selectedPositionY + limits.minY)*FONT.constants.SIZES.LINE; + } else if ((limits.maxY > 0) && ((selectedPositionY + limits.maxY) >= OSD.data.display_size.y)) { + position -= (selectedPositionY + limits.maxY - OSD.data.display_size.y + 1)*FONT.constants.SIZES.LINE; + } } + if (semver.gte(CONFIG.apiVersion, "1.21.0")) { // unsigned now } else { @@ -1721,53 +1886,75 @@ TABS.osd.initialize = function (callback) { OSD.data.preview[i * 30 + j] = [{name: 'LOGO', positionable: false}, x++]; } } + // draw all the displayed items and the drag and drop preview images for(let field of OSD.data.display_items) { - if (!field.preview || !field.isVisible) { continue; } - var j = (field.position >= 0) ? field.position : field.position + OSD.data.display_size.total; + + if (!field.preview || !field.isVisible) { + continue; + } + + var selectedPosition = (field.position >= 0) ? field.position : field.position + OSD.data.display_size.total; + // create the preview image field.preview_img = new Image(); var canvas = document.createElement('canvas'); var ctx = canvas.getContext("2d"); - // fill the screen buffer - for(var i = 0; i < field.preview.length; i++) { - var charCode = field.preview.charCodeAt(i); - OSD.data.preview[j++] = [field, charCode]; - // draw the preview - var img = new Image(); - img.src = FONT.draw(charCode); - ctx.drawImage(img, i*12, 0); + + // Standard preview, type String + if (field.preview.constructor !== Array) { + + + // fill the screen buffer + for(var i = 0; i < field.preview.length; i++) { + + // Add the character to the preview + var charCode = field.preview.charCodeAt(i); + OSD.drawByOrder(selectedPosition++, field, charCode); + + // Image used when "dragging" the element + if (field.positionable) { + var img = new Image(); + img.src = FONT.draw(charCode); + ctx.drawImage(img, i*12, 0); + } + } + } else { + var arrayElements = field.preview; + + // The array can have negative and positive positions, search limits... + var limits = OSD.searchLimitsElement(arrayElements); + + var offsetX = 0; + var offsetY = 0; + if (limits.minX < 0) { + offsetX = -limits.minX; + } + if (limits.minY < 0) { + offsetY = -limits.minY; + } + + for (var i=0; i < arrayElements.length; i++) { + + // Add the character to the preview + var element = arrayElements[i]; + var charCode = element.sym; + OSD.drawByOrder(selectedPosition + element.x + element.y*FONT.constants.SIZES.LINE, field, charCode); + + // Image used when "dragging" the element + if (field.positionable) { + var img = new Image(); + img.src = FONT.draw(charCode); + ctx.drawImage(img, (element.x + offsetX)*12, (element.y + offsetY)*12); + } + } } field.preview_img.src = canvas.toDataURL('image/png'); - // Required for NW.js - Otherwise the will - // consume drag/drop events. - field.preview_img.style.pointerEvents = 'none'; - } - var centerishPosition = 194; - // artificial horizon - if ($('input[name="ARTIFICIAL_HORIZON"]').prop('checked')) { - for (var i = 0; i < 9; i++) { - OSD.data.preview[centerishPosition - 4 + i] = SYM.AH_BAR9_0 + 4; - } - } - // crosshairs - if ($('input[name="CROSSHAIRS"]').prop('checked')) { - OSD.data.preview[centerishPosition - 1] = SYM.AH_CENTER_LINE; - OSD.data.preview[centerishPosition + 1] = SYM.AH_CENTER_LINE_RIGHT; - OSD.data.preview[centerishPosition] = SYM.AH_CENTER; - } - // sidebars - if ($('input[name="HORIZON_SIDEBARS"]').prop('checked')) { - var hudwidth = OSD.constants.AHISIDEBARWIDTHPOSITION; - var hudheight = OSD.constants.AHISIDEBARHEIGHTPOSITION; - for (var i = -hudheight; i <= hudheight; i++) { - OSD.data.preview[centerishPosition - hudwidth + (i * FONT.constants.SIZES.LINE)] = SYM.AH_DECORATION; - OSD.data.preview[centerishPosition + hudwidth + (i * FONT.constants.SIZES.LINE)] = SYM.AH_DECORATION; - } - // AH level indicators - OSD.data.preview[centerishPosition-hudwidth+1] = SYM.AH_LEFT; - OSD.data.preview[centerishPosition+hudwidth-1] = SYM.AH_RIGHT; + // Required for NW.js - Otherwise the will + //consume drag/drop events. + field.preview_img.style.pointerEvents = 'none'; } + // render var $preview = $('.display-layout .preview').empty(); var $row = $('
');