diff --git a/src/css/main-dark.css b/src/css/main-dark.css index 1b99cb49..1d16d17d 100644 --- a/src/css/main-dark.css +++ b/src/css/main-dark.css @@ -11,6 +11,8 @@ --paper: url(../../images/paper-dark.jpg); --ledAccent: #6e6e6e; --ledBackground: #424242; + --gimbalBackground: var(--subtleAccent); + --gimbalCrosshair: var(--mutedText); --switcherysecond: #858585; } @@ -149,7 +151,7 @@ button { } .noUi-pips { - color: #dbdbdb; + color: var(--mutedText); } .jBox-container { @@ -164,3 +166,7 @@ button { text-shadow: 0 1px 1px #ffffff; color: white; } + +.jBox-pointer:after { + background: #393b3a; +} diff --git a/src/css/main.css b/src/css/main.css index 9f5aa3ba..c4c3bbbb 100644 --- a/src/css/main.css +++ b/src/css/main.css @@ -1,6 +1,6 @@ :root { --accent: #ffbb00; - --error: red; + --error: red; --subtleAccent: silver; --quietText: #ffffff; --quietHeader: #828885; @@ -13,6 +13,8 @@ --paper: url(../../images/paper.jpg); --ledAccent: #adadad; --ledBackground: #e9e9e9; + --gimbalBackground: #eee; + --gimbalCrosshair: var(--subtleAccent); --switcherysecond: #c4c4c4; } @@ -1155,7 +1157,7 @@ dialog { margin-left: auto; } -/** Hack to change the "display: none" by a "visibility:hidden", to apply +/** Hack to change the "display: none" by a "visibility:hidden", to apply the margin-left: auto needed by the first element to align to the right the buttons */ .toolbar_fixed_bottom .content_toolbar div[style='display: none;']:first-child { visibility: hidden; @@ -2141,3 +2143,33 @@ input { margin-top: 10px; margin-bottom: 10px; } + +/* + noUi slider stylings +*/ + +.noUi-target { + background: var(--alternativeBackground); + border-color: var(--subtleAccent); + box-shadow: none; +} + +.noUi-handle { + background: var(--sideBackground); + border-color: var(--subtleAccent); + box-shadow: none; +} + +.noUi-handle:before, +.noUi-handle:after { + background-color: var(--subtleAccent); +} + +.noUi-background { + background: var(--alternativeBackground); + box-shadow: none; +} + +.noUi-connect { + box-shadow: none; +} diff --git a/src/css/tabs-dark/adjustments-dark.css b/src/css/tabs-dark/adjustments-dark.css index e9674d45..b173841a 100644 --- a/src/css/tabs-dark/adjustments-dark.css +++ b/src/css/tabs-dark/adjustments-dark.css @@ -15,7 +15,3 @@ background-color: #3a3a3a; color: white; } - -.tab-adjustments .noUi-background { - background-color: #858585; -} diff --git a/src/css/tabs/adjustments.css b/src/css/tabs/adjustments.css index 0717d80a..492292e8 100644 --- a/src/css/tabs/adjustments.css +++ b/src/css/tabs/adjustments.css @@ -4,18 +4,10 @@ .tab-adjustments .range .marker, .tab-adjustments .channel-slider .noUi-connect { background: var(--accent); - box-shadow: inset 0 0px 2px rgba(0, 0, 0, 0.4), 0px 1px 0px rgba(255, 255, 255, 0.6); } .noUi-target { border-radius: 4px; - border: 1px solid #D3D3D3; - box-shadow: inset 0 0px 2px rgba(0, 0, 0, 0.4), 0px 1px 0px rgba(255, 255, 255, 0.6); -} - -.noUi-background { - box-shadow: inset 0 0px 2px rgba(0, 0, 0, 0.4), 0px 1px 0px rgba(255, 255, 255, 0.6); - background: #D2D2D2; } .tab-adjustments .adjustments { @@ -60,7 +52,7 @@ .tab-adjustments .adjustment select { /* outline: 1px solid silver; */ border-radius: 3px; - border: 1px solid var(--subtleAccent); + border: 1px solid var(--subtleAccent); } .tab-adjustments .adjustment td { diff --git a/src/css/tabs/receiver_msp.css b/src/css/tabs/receiver_msp.css index 36aa3c3a..6b9d3f6e 100644 --- a/src/css/tabs/receiver_msp.css +++ b/src/css/tabs/receiver_msp.css @@ -8,7 +8,7 @@ body { } .control-gimbals { - /* A generous padding around the window edges ensures that we continue to receive mousemove events (since + /* A generous padding around the window edges ensures that we continue to receive mousemove events (since * cursor stays in the window for longer) */ padding: 25px; @@ -21,7 +21,7 @@ body { position: relative; width: 120px; height: 120px; - background-color: #eee; + background-color: var(--gimbalBackground); margin-left: 1em; margin-right: 1em; margin-bottom: 2em; @@ -33,7 +33,7 @@ body { .crosshair { display: block; position: absolute; - background-color: var(--subtleAccent); + background-color: var(--gimbalCrosshair); } .crosshair-vert { diff --git a/src/js/DarkTheme.js b/src/js/DarkTheme.js index 8748be47..97ca49a6 100644 --- a/src/js/DarkTheme.js +++ b/src/js/DarkTheme.js @@ -29,27 +29,28 @@ var css_dark = [ var DarkTheme = { configEnabled: undefined, }; - -DarkTheme.setConfig = function(result) { +DarkTheme.isDarkThemeEnabled = function (val) { + return val === 0 || val === 2 && window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches; +} +DarkTheme.setConfig = function (result) { if (this.configEnabled != result) { this.configEnabled = result; - if (this.configEnabled === 0 || this.configEnabled === 2 && window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) { + if (this.isDarkThemeEnabled(this.configEnabled)) { this.applyDark(); } else { this.applyNormal(); } + + windowWatcherUtil.passValue(chrome.app.window.get("receiver_msp"), 'darkTheme', this.isDarkThemeEnabled(this.configEnabled)); + } }; -DarkTheme.applyDark = function() { - for (var i = 0; i < css_dark.length; i++) { - $('link[href="' + css_dark[i] + '"]').prop('disabled', false); - } +DarkTheme.applyDark = function () { + css_dark.forEach((el) => $('link[href="' + el + '"]').prop('disabled', false)); }; -DarkTheme.applyNormal = function() { - for (var i = 0; i < css_dark.length; i++) { - $('link[href="' + css_dark[i] + '"]').prop('disabled', true); - } +DarkTheme.applyNormal = function () { + css_dark.forEach((el) => $('link[href="' + el + '"]').prop('disabled', true)); }; diff --git a/src/js/tabs/receiver.js b/src/js/tabs/receiver.js index 27f94b67..1935f548 100644 --- a/src/js/tabs/receiver.js +++ b/src/js/tabs/receiver.js @@ -76,7 +76,7 @@ TABS.receiver.initialize = function (callback) { tab.yawDeadband = parseInt($(this).val()); }).change(); } - + if (semver.lt(CONFIG.apiVersion, "1.15.0")) { $('.sticks').hide(); } else { @@ -319,6 +319,9 @@ TABS.receiver.initialize = function (callback) { return false; } } + + windowWatcherUtil.passValue(createdWindow, 'darkTheme', DarkTheme.isDarkThemeEnabled(DarkTheme.configEnabled)); + }); }); @@ -350,7 +353,7 @@ TABS.receiver.initialize = function (callback) { if ($(this).val() == 1) { rcSmoothingnNumberElement.val(RX_CONFIG.rcSmoothingInputCutoff); $('.tab-receiver .rcSmoothing-input-cutoff').show(); - } + } }); $('.tab-receiver .rcSmoothing-derivative-cutoff').show(); @@ -578,7 +581,7 @@ TABS.receiver.initModelPreview = function () { if (CONFIG.flightControllerIdentifier == 'BTFL' && semver.lt(CONFIG.flightControllerVersion, '2.8.0')) { useOldRateCurve = true; } - + this.rateCurve = new RateCurve(useOldRateCurve); $(window).on('resize', $.proxy(this.model.resize, this.model)); @@ -665,4 +668,4 @@ function updateInterpolationView() { if (RX_CONFIG.rcSmoothingInputCutoff == 0) { $('.tab-receiver .rcSmoothing-input-cutoff').hide(); } -} \ No newline at end of file +} diff --git a/src/js/tabs/receiver_msp.js b/src/js/tabs/receiver_msp.js index 87a31992..39d4686a 100644 --- a/src/js/tabs/receiver_msp.js +++ b/src/js/tabs/receiver_msp.js @@ -1,10 +1,15 @@ "use strict"; +let css_dark = [ + '/css/main-dark.css' + ]; + + var CHANNEL_MIN_VALUE = 1000, CHANNEL_MID_VALUE = 1500, CHANNEL_MAX_VALUE = 2000, - + // What's the index of each channel in the MSP channel list? channelMSPIndexes = { Roll: 0, @@ -16,7 +21,7 @@ var Aux3: 6, Aux4: 7, }, - + // Set reasonable initial stick positions (Mode 2) stickValues = { Throttle: CHANNEL_MIN_VALUE, @@ -28,21 +33,31 @@ var Aux3: CHANNEL_MIN_VALUE, Aux4: CHANNEL_MIN_VALUE }, - + // First the vertical axis, then the horizontal: gimbals = [ ["Throttle", "Yaw"], ["Pitch", "Roll"], ], - + gimbalElems, sliderElems, - + enableTX = false; // This is a hack to get the i18n var of the parent, but the localizePage not works const i18n = opener.i18n; +let watchers = { + darkTheme: (val) => { + if (val) { + applyDarkTheme(); + } else { + applyNormalTheme(); + } + } +}; + $(document).ready(function () { $('[i18n]:not(.i18n-replaced)').each(function() { var element = $(this); @@ -50,20 +65,22 @@ $(document).ready(function () { element.html(i18n.getMessage(element.attr('i18n'))); element.addClass('i18n-replaced'); }); -}) + + windowWatcherUtil.bindWatchers(window, watchers); +}); function transmitChannels() { - var + var channelValues = [0, 0, 0, 0, 0, 0, 0, 0]; if (!enableTX) { return; } - + for (var stickName in stickValues) { channelValues[channelMSPIndexes[stickName]] = stickValues[stickName]; } - + // Callback given to us by the window creator so we can have it send data over MSP for us: if (!window.setRawRx(channelValues)) { // MSP connection has gone away @@ -73,7 +90,7 @@ function transmitChannels() { function stickPortionToChannelValue(portion) { portion = Math.min(Math.max(portion, 0.0), 1.0); - + return Math.round(portion * (CHANNEL_MAX_VALUE - CHANNEL_MIN_VALUE) + CHANNEL_MIN_VALUE); } @@ -85,15 +102,15 @@ function updateControlPositions() { for (var stickName in stickValues) { var stickValue = stickValues[stickName]; - + // Look for the gimbal which corresponds to this stick name for (var gimbalIndex in gimbals) { - var + var gimbal = gimbals[gimbalIndex], gimbalElem = gimbalElems.get(gimbalIndex), gimbalSize = $(gimbalElem).width(), stickElem = $(".control-stick", gimbalElem); - + if (gimbal[0] == stickName) { stickElem.css('top', (1.0 - channelValueToStickPortion(stickValue)) * gimbalSize + "px"); break; @@ -106,62 +123,70 @@ function updateControlPositions() { } function handleGimbalMouseDrag(e) { - var + var gimbal = $(gimbalElems.get(e.data.gimbalIndex)), gimbalOffset = gimbal.offset(), gimbalSize = gimbal.width(); - + stickValues[gimbals[e.data.gimbalIndex][0]] = stickPortionToChannelValue(1.0 - (e.pageY - gimbalOffset.top) / gimbalSize); stickValues[gimbals[e.data.gimbalIndex][1]] = stickPortionToChannelValue((e.pageX - gimbalOffset.left) / gimbalSize); - + updateControlPositions(); } function localizeAxisNames() { for (var gimbalIndex in gimbals) { - var + var gimbal = gimbalElems.get(gimbalIndex); - + $(".gimbal-label-vert", gimbal).text(i18n.getMessage("controlAxis" + gimbals[gimbalIndex][0])); $(".gimbal-label-horz", gimbal).text(i18n.getMessage("controlAxis" + gimbals[gimbalIndex][1])); } - + for (var sliderIndex = 0; sliderIndex < 4; sliderIndex++) { $(".slider-label", sliderElems.get(sliderIndex)).text(i18n.getMessage("controlAxisAux" + (sliderIndex + 1))); } } +function applyDarkTheme() { + css_dark.forEach((el) => $('link[href="' + el + '"]').prop('disabled', false)); +}; + +function applyNormalTheme() { + css_dark.forEach((el) => $('link[href="' + el + '"]').prop('disabled', true)); +}; + $(document).ready(function() { $(".button-enable .btn").click(function() { var shrinkHeight = $(".warning").height(); - + $(".warning").slideUp("short", function() { chrome.app.window.current().innerBounds.minHeight -= shrinkHeight; chrome.app.window.current().innerBounds.height -= shrinkHeight; chrome.app.window.current().innerBounds.maxHeight -= shrinkHeight; }); - + enableTX = true; }); - + gimbalElems = $(".control-gimbal"); sliderElems = $(".control-slider"); - + gimbalElems.each(function(gimbalIndex) { $(this).on('mousedown', {gimbalIndex: gimbalIndex}, function(e) { if (e.which == 1) { // Only move sticks on left mouse button handleGimbalMouseDrag(e); - + $(window).on('mousemove', {gimbalIndex: gimbalIndex}, handleGimbalMouseDrag); } }); }); - + $(".slider", sliderElems).each(function(sliderIndex) { - var + var initialValue = stickValues["Aux" + (sliderIndex + 1)]; - + $(this) .noUiSlider({ start: initialValue, @@ -171,27 +196,27 @@ $(document).ready(function() { } }).on('slide change set', function(e, value) { value = Math.round(parseFloat(value)); - + stickValues["Aux" + (sliderIndex + 1)] = value; - + $(".tooltip", this).text(value); }); - + $(this).append('
'); - + $(".tooltip", this).text(initialValue); }); - - /* + + /* * Mouseup handler needs to be bound to the window in order to receive mouseup if mouse leaves window. */ $(window).mouseup(function(e) { $(this).off('mousemove', handleGimbalMouseDrag); }); - + localizeAxisNames(); - + updateControlPositions(); - + setInterval(transmitChannels, 50); -}); \ No newline at end of file +}); diff --git a/src/js/utils/window_watchers.js b/src/js/utils/window_watchers.js new file mode 100644 index 00000000..15246711 --- /dev/null +++ b/src/js/utils/window_watchers.js @@ -0,0 +1,59 @@ +'use strict'; + +/* + This utility is intended to communicate between chrome windows. + One window could watch passed values from another window and react to them. +*/ + +var windowWatcherUtil = {}; + +windowWatcherUtil.invokeWatcher = function(bindingKey, bindingVal, watchersObject) { + if (watchersObject[bindingKey]) { + watchersObject[bindingKey](bindingVal); + } +} + +windowWatcherUtil.iterateOverBindings = function(bindings, watchersObject) { + let entries = Object.entries(bindings); + for (const [key, val] of entries) { + this.invokeWatcher(key, val, watchersObject) + } +} + +windowWatcherUtil.bindWatchers = function(windowObject, watchersObject) { + if (!windowObject.bindings) { + windowObject.bindings = {}; + } else { + this.iterateOverBindings(windowObject.bindings, watchersObject); + } + + windowObject.bindings = new Proxy(windowObject.bindings, { + set(target, prop, val, receiver) { + windowWatcherUtil.invokeWatcher(prop, val, watchersObject); + return Reflect.set(target, prop, val, receiver); + } + }); +} + +// 'Windows' here could be array or single window reference +windowWatcherUtil.passValue = function(windows, key, val) { + let applyBinding = function(win, key, val) { + if (!win) { + return; + } + + if (win.contentWindow.bindings) { + win.contentWindow.bindings[key] = val; + } else { + win.contentWindow.bindings = { + [key]: val + }; + } + } + + if (Array.isArray(windows)) { + windows.forEach((el) => applyBinding(el, key, val)); + } else { + applyBinding(windows, key, val) + } +} diff --git a/src/main.html b/src/main.html index e34936de..9f036852 100644 --- a/src/main.html +++ b/src/main.html @@ -85,6 +85,7 @@ + @@ -281,7 +282,7 @@