diff --git a/build/depends.py b/build/depends.py index 3cafd41f9af..ac5e778feee 100644 --- a/build/depends.py +++ b/build/depends.py @@ -776,6 +776,7 @@ def sources(self, build): "src/preferences/effectsettingsmodel.cpp", "src/preferences/broadcastprofile.cpp", "src/preferences/upgrade.cpp", + "src/preferences/hotcuecolorpalettesettings.cpp", "src/preferences/dlgpreferencepage.cpp", "src/effects/effectmanifest.cpp", @@ -912,6 +913,8 @@ def sources(self, build): "src/controllers/softtakeover.cpp", "src/controllers/keyboard/keyboardeventfilter.cpp", "src/controllers/colorjsproxy.cpp", + "src/controllers/colormapper.cpp", + "src/controllers/colormapperjsproxy.cpp", "src/main.cpp", "src/mixxx.cpp", @@ -1254,6 +1257,7 @@ def sources(self, build): "src/util/movinginterquartilemean.cpp", "src/util/console.cpp", "src/util/color/color.cpp", + "src/util/color/colorpalette.cpp", "src/util/db/dbconnection.cpp", "src/util/db/dbconnectionpool.cpp", "src/util/db/dbconnectionpooler.cpp", @@ -1280,8 +1284,7 @@ def sources(self, build): "src/util/desktophelper.cpp", "src/util/widgetrendertimer.cpp", "src/util/workerthread.cpp", - "src/util/workerthreadscheduler.cpp", - "src/util/color/predefinedcolor.cpp" + "src/util/workerthreadscheduler.cpp" ] proto_args = { diff --git a/res/controllers/Roland_DJ-505-scripts.js b/res/controllers/Roland_DJ-505-scripts.js index e2600dacbaa..25efbbfb523 100644 --- a/res/controllers/Roland_DJ-505-scripts.js +++ b/res/controllers/Roland_DJ-505-scripts.js @@ -941,17 +941,24 @@ DJ505.PadColor = { DIM_MODIFIER: 0x10, }; -DJ505.PadColorMap = [ - DJ505.PadColor.OFF, - DJ505.PadColor.RED, - DJ505.PadColor.GREEN, - DJ505.PadColor.BLUE, - DJ505.PadColor.YELLOW, - DJ505.PadColor.CELESTE, - DJ505.PadColor.PURPLE, - DJ505.PadColor.APRICOT, - DJ505.PadColor.WHITE, -]; +DJ505.PadColorMap = new ColorMapper({ + '#FFCC0000': DJ505.PadColor.RED, + '#FFCC4400': DJ505.PadColor.CORAL, + '#FFCC8800': DJ505.PadColor.ORANGE, + '#FFCCCC00': DJ505.PadColor.YELLOW, + '#FF88CC00': DJ505.PadColor.GREEN, + '#FF00CC00': DJ505.PadColor.APPLEGREEN, + '#FF00CC88': DJ505.PadColor.AQUAMARINE, + '#FF00CCCC': DJ505.PadColor.TURQUOISE, + '#FF0088CC': DJ505.PadColor.CELESTE, + '#FF0000CC': DJ505.PadColor.BLUE, + '#FF4400CC': DJ505.PadColor.AZURE, + '#FF8800CC': DJ505.PadColor.PURPLE, + '#FFCC00CC': DJ505.PadColor.MAGENTA, + '#FFCC0044': DJ505.PadColor.RED, + '#FFFFCCCC': DJ505.PadColor.APRICOT, + '#FFFFFFFF': DJ505.PadColor.WHITE, +}); DJ505.PadSection = function (deck, offset) { // TODO: Add support for missing modes (flip, slicer, slicerloop) @@ -1190,7 +1197,6 @@ DJ505.HotcueMode = function (deck, offset) { this.ledControl = DJ505.PadMode.HOTCUE; this.color = DJ505.PadColor.WHITE; - var hotcueColors = [this.color].concat(DJ505.PadColorMap.slice(1)); this.pads = new components.ComponentContainer(); for (var i = 0; i <= 7; i++) { this.pads[i] = new components.HotcueButton({ @@ -1202,7 +1208,7 @@ DJ505.HotcueMode = function (deck, offset) { group: deck.currentDeck, on: this.color, off: this.color + DJ505.PadColor.DIM_MODIFIER, - colors: hotcueColors, + colorMapper: DJ505.PadColorMap, outConnect: false, }); } @@ -1226,7 +1232,6 @@ DJ505.CueLoopMode = function (deck, offset) { this.ledControl = DJ505.PadMode.HOTCUE; this.color = DJ505.PadColor.BLUE; - var cueloopColors = [this.color].concat(DJ505.PadColorMap.slice(1)); this.PerformancePad = function(n) { this.midi = [0x94 + offset, 0x14 + n]; this.number = n + 1; @@ -1241,7 +1246,7 @@ DJ505.CueLoopMode = function (deck, offset) { group: deck.currentDeck, on: this.color, off: this.color + DJ505.PadColor.DIM_MODIFIER, - colors: cueloopColors, + colorMapper: DJ505.PadColorMap, outConnect: false, unshift: function() { this.input = function (channel, control, value, status, group) { @@ -1492,14 +1497,13 @@ DJ505.PitchPlayMode = function (deck, offset) { this.color = DJ505.PadColor.GREEN; this.cuepoint = 1; this.range = PitchPlayRange.MID; - var pitchplayColors = [this.color].concat(DJ505.PadColorMap.slice(1)); this.PerformancePad = function(n) { this.midi = [0x94 + offset, 0x14 + n]; this.number = n + 1; this.on = this.color + DJ505.PadColor.DIM_MODIFIER; - this.colors = pitchplayColors; - this.colorIdKey = 'hotcue_' + this.number + '_color_id'; + this.colorMapper = DJ505.PadColorMap; + this.colorKey = 'hotcue_' + this.number + '_color'; components.Button.call(this); }; this.PerformancePad.prototype = new components.Button({ @@ -1510,10 +1514,10 @@ DJ505.PitchPlayMode = function (deck, offset) { mode: this, outConnect: false, off: DJ505.PadColor.OFF, - outputColor: function(id) { + outputColor: function(colorCode) { // For colored hotcues (shifted only) - var color = this.colors[id]; - this.send((this.mode.cuepoint === this.number) ? color : (color + DJ505.PadColor.DIM_MODIFIER)); + var midiColor = this.colorMapper.getNearestValue(colorCode); + this.send((this.mode.cuepoint === this.number) ? midiColor : (midiColor + DJ505.PadColor.DIM_MODIFIER)); }, unshift: function() { this.outKey = "pitch_adjust"; @@ -1561,8 +1565,8 @@ DJ505.PitchPlayMode = function (deck, offset) { this.outKey = "hotcue_" + this.number + "_enabled"; this.output = function (value, group, control) { var outval = this.outValueScale(value); - if (this.colorIdKey !== undefined && outval !== this.off) { - this.outputColor(engine.getValue(this.group, this.colorIdKey)); + if (this.colorKey !== undefined && outval !== this.off) { + this.outputColor(engine.getValue(this.group, this.colorKey)); } else { this.send(DJ505.PadColor.OFF); } @@ -1572,13 +1576,13 @@ DJ505.PitchPlayMode = function (deck, offset) { var previous_cuepoint = this.mode.cuepoint; this.mode.cuepoint = this.number; this.mode.pads[previous_cuepoint - 1].trigger(); - this.outputColor(engine.getValue(this.group, this.colorIdKey)); + this.outputColor(engine.getValue(this.group, this.colorKey)); } }; this.connect = function() { components.Button.prototype.connect.call(this); // call parent connect - if (undefined !== this.group && this.colorIdKey !== undefined) { - this.connections[1] = engine.makeConnection(this.group, this.colorIdKey, function (id) { + if (undefined !== this.group && this.colorKey !== undefined) { + this.connections[1] = engine.makeConnection(this.group, this.colorKey, function (id) { if (engine.getValue(this.group, this.outKey)) { this.outputColor(id); } diff --git a/res/controllers/midi-components-0.0.js b/res/controllers/midi-components-0.0.js index 7bb5c009578..c4685e75ea5 100644 --- a/res/controllers/midi-components-0.0.js +++ b/res/controllers/midi-components-0.0.js @@ -294,11 +294,8 @@ print('ERROR: No hotcue number specified for new HotcueButton.'); return; } - if (options.colors !== undefined || options.sendRGB !== undefined) { - this.colorIdKey = 'hotcue_' + options.number + '_color_id'; - if (options.colors === undefined) { - options.colors = color.predefinedColorsList(); - } + if (options.colorMapper !== undefined || options.sendRGB !== undefined) { + this.colorKey = 'hotcue_' + options.number + '_color'; } this.number = options.number; this.outKey = 'hotcue_' + this.number + '_enabled'; @@ -312,8 +309,8 @@ this.inKey = 'hotcue_' + this.number + '_clear'; }, getColor: function() { - if (this.colorIdKey !== undefined) { - return color.predefinedColorFromId(engine.getValue(this.group,this.colorIdKey)); + if (this.colorKey !== undefined) { + return color.colorFromHexCode(engine.getValue(this.group,this.colorKey)); } else { return null; } @@ -324,32 +321,29 @@ // and there is no hotcueColor for turning the LED // off. So the `send()` function is responsible for turning the // actual LED off. - if (this.colorIdKey !== undefined && outval !== this.off) { - this.outputColor(engine.getValue(this.group, this.colorIdKey)); + if (this.colorKey !== undefined && outval !== this.off) { + this.outputColor(engine.getValue(this.group, this.colorKey)); } else { this.send(outval); } }, - outputColor: function (id) { - var color = this.colors[id]; - if (color instanceof Array) { - if (color.length !== 3) { - print("ERROR: invalid color array for id: " + id); - return; - } + outputColor: function (colorCode) { + if (this.colorMapper !== undefined) { + var nearestColorValue = this.colorMapper.getNearestValue(colorCode); + print("COLOR = "+nearestColorValue); + this.send(nearestColorValue); + } else { if (this.sendRGB === undefined) { print("ERROR: no function defined for sending RGB colors"); return; } - this.sendRGB(color); - } else if (typeof color === 'number') { - this.send(color); + this.sendRGB(color.colorFromHexCode(colorCode)); } }, connect: function() { Button.prototype.connect.call(this); // call parent connect - if (undefined !== this.group && this.colorIdKey !== undefined) { - this.connections[1] = engine.makeConnection(this.group, this.colorIdKey, function (id) { + if (undefined !== this.group && this.colorKey !== undefined) { + this.connections[1] = engine.makeConnection(this.group, this.colorKey, function (id) { if (engine.getValue(this.group,this.outKey)) { this.outputColor(id); } diff --git a/res/schema.xml b/res/schema.xml index d5babba85aa..4940729fe1c 100644 --- a/res/schema.xml +++ b/res/schema.xml @@ -480,5 +480,30 @@ METADATA position INTEGER ); - + + + + Convert the PredefinedColor id to the actual RGBA value. + + + + UPDATE cues SET color=4278190080 WHERE color=0; + + UPDATE cues SET color=4291103240 WHERE color=1; + + UPDATE cues SET color=4281515588 WHERE color=2; + + UPDATE cues SET color=4278207743 WHERE color=3; + + UPDATE cues SET color=4294496768 WHERE color=4; + + UPDATE cues SET color=4282569972 WHERE color=5; + + UPDATE cues SET color=4289659084 WHERE color=6; + + UPDATE cues SET color=4294747863 WHERE color=7; + + UPDATE cues SET color=4294111999 WHERE color=8; + + diff --git a/res/skins/Deere/hotcue_button.xml b/res/skins/Deere/hotcue_button.xml index f5e47776226..19ba4bb08b4 100644 --- a/res/skins/Deere/hotcue_button.xml +++ b/res/skins/Deere/hotcue_button.xml @@ -46,8 +46,8 @@ false - ,hotcue__color_id - highlight + ,hotcue__color + backgroundColorRgba diff --git a/res/skins/Deere/style.qss b/res/skins/Deere/style.qss index c89a5eca6b4..944870dd38d 100644 --- a/res/skins/Deere/style.qss +++ b/res/skins/Deere/style.qss @@ -1583,7 +1583,6 @@ WPushButton:hover { /*"Pressed" state*/ WPushButton[value="1"], WPushButton[value="2"] { - /*color: #FDFDFD;*/ color: #FDFDFD; background-color: #006596; border: 0px solid #006596; @@ -1596,129 +1595,17 @@ WPushButton[value="2"]:hover { border: 0px solid #0080BE; } -/* Hotcue Color: No Color */ -#HotcueButton[value="1"][highlight="0"], -#HotcueButton[value="2"][highlight="0"] { - background-color: #006596; -} - -#HotcueButton[value="1"][highlight="0"]:hover, -#HotcueButton[value="2"][highlight="0"]:hover { - background-color: #0080BE; -} - -/* Hotcue Color: Red */ -#HotcueButton[value="1"][highlight="1"], -#HotcueButton[value="2"][highlight="1"] { - background-color: #c50a08; -} - -#HotcueButton[value="1"][highlight="1"]:hover, -#HotcueButton[value="2"][highlight="1"]:hover { - background-color: #e50c08; -} - -/* Hotcue Color: Green */ -#HotcueButton[value="1"][highlight="2"], -#HotcueButton[value="2"][highlight="2"] { - background-color: #32be44; -} - -#HotcueButton[value="1"][highlight="2"]:hover, -#HotcueButton[value="2"][highlight="2"]:hover { - background-color: #52de64; -} - -/* Hotcue Color: Blue */ -#HotcueButton[value="1"][highlight="3"], -#HotcueButton[value="2"][highlight="3"] { - background-color: #0044ff; -} - -#HotcueButton[value="1"][highlight="3"]:hover, -#HotcueButton[value="2"][highlight="3"]:hover { - background-color: #0064ff; -} - -/* Hotcue Color: Yellow */ -#HotcueButton[value="1"][highlight="4"], -#HotcueButton[value="2"][highlight="4"] { - color: #4B4B4B; - background-color: #f8d200; -} - -#HotcueButton[value="1"][highlight="4"]:hover, -#HotcueButton[value="2"][highlight="4"]:hover { - color: #4B4B4B; - background-color: #f8f200; -} - -/* Hotcue Color: Celeste */ -#HotcueButton[value="1"][highlight="5"], -#HotcueButton[value="2"][highlight="5"] { - color: #4B4B4B; - background-color: #42d4f4; +#HotcueButton { + qproperty-shouldHighlightBackgroundOnHover: true; } -#HotcueButton[value="1"][highlight="5"]:hover, -#HotcueButton[value="2"][highlight="5"]:hover { - color: #4B4B4B; - background-color: #62f4f4; -} - -/* Hotcue Color: Purple */ -#HotcueButton[value="1"][highlight="6"], -#HotcueButton[value="2"][highlight="6"] { - background-color: #af00cc; -} - -#HotcueButton[value="1"][highlight="6"]:hover, -#HotcueButton[value="2"][highlight="6"]:hover { - background-color: #cf00ec; -} - -/* Hotcue Color: Pink */ -#HotcueButton[value="1"][highlight="7"], -#HotcueButton[value="2"][highlight="7"] { - color: #4B4B4B; - background-color: #fca6d7; -} - -#HotcueButton[value="1"][highlight="7"]:hover, -#HotcueButton[value="2"][highlight="7"]:hover { - color: #4B4B4B; - background-color: #fcc6f7; -} - -/* Hotcue Color: White */ -#HotcueButton[value="1"][highlight="8"], -#HotcueButton[value="2"][highlight="8"] { - color: #4B4B4B; - background-color: #f2f2ff; -} - -#HotcueButton[value="1"][highlight="8"]:hover, -#HotcueButton[value="2"][highlight="8"]:hover { - color: #4B4B4B; - background-color: #ffffff; -} - -/*"Enabled" state, e.g. for recording status - 0 -- disconnected / off - 1 -- connecting / enabling - 2 -- connected / enabled -WPushButton[value="2"] { +#HotcueButton[backgroundIsDark=true][hasBackgroundColor=true] { color: #FDFDFD; - background-color: #4B4B4B; - border: 0px solid #006596; } -WPushButton[value="2"]:hover { - color: #FDFDFD; - background-color: #4B4B4B; - border: 0px solid #0080BE; +#HotcueButton[backgroundIsDark=false][hasBackgroundColor=true] { + color: #1f1e1e; } -*/ #PlayToggle[value="0"] { image: url(skin:/icon/ic_play_48px.svg) no-repeat center center; diff --git a/res/skins/LateNight/button_hotcue.xml b/res/skins/LateNight/button_hotcue.xml index 481b1ab72d7..88b031eabb2 100644 --- a/res/skins/LateNight/button_hotcue.xml +++ b/res/skins/LateNight/button_hotcue.xml @@ -28,8 +28,8 @@ false - ,hotcue__color_id - highlight + ,hotcue__color + backgroundColorRgba diff --git a/res/skins/LateNight/style.qss b/res/skins/LateNight/style.qss index b2101872d4e..1627fd42022 100644 --- a/res/skins/LateNight/style.qss +++ b/res/skins/LateNight/style.qss @@ -356,50 +356,6 @@ QHeaderView::down-arrow { #SpecialCueButton[displayValue="1"] { background-color: #0000D4; } -/* Hotcue Color: Red - this is also the default color if Auto-Color - in Preferences > Decks is Off */ -#HotcueButton[displayValue="1"][highlight="0"], -#HotcueButton[displayValue="2"][highlight="0"], -#HotcueButton[displayValue="1"][highlight="1"], -#HotcueButton[displayValue="2"][highlight="1"] { - background-color: #db0000; -} -/* Hotcue Color: Green */ -#HotcueButton[displayValue="1"][highlight="2"], -#HotcueButton[displayValue="2"][highlight="2"] { - background-color: #32be44; -} -/* Hotcue Color: Blue */ -#HotcueButton[displayValue="1"][highlight="3"], -#HotcueButton[displayValue="2"][highlight="3"] { - background-color: #0044ff; -} -/* Hotcue Color: Yellow */ -#HotcueButton[displayValue="1"][highlight="4"], -#HotcueButton[displayValue="2"][highlight="4"] { - background-color: #f8d200; -} -/* Hotcue Color: Celeste */ -#HotcueButton[displayValue="1"][highlight="5"], -#HotcueButton[displayValue="2"][highlight="5"] { - background-color: #42d4f4; -} -/* Hotcue Color: Purple */ -#HotcueButton[displayValue="1"][highlight="6"], -#HotcueButton[displayValue="2"][highlight="6"] { - background-color: #af00cc; -} -/* Hotcue Color: Pink */ -#HotcueButton[displayValue="1"][highlight="7"], -#HotcueButton[displayValue="2"][highlight="7"] { - background-color: #fca6d7; -} -/* Hotcue Color: White */ -#HotcueButton[displayValue="1"][highlight="8"], -#HotcueButton[displayValue="2"][highlight="8"] { - background-color: #f2f2ff; -} #BeatgridButtonsToggle { image: url(skin:/buttons/btn_beatgrid_buttons_toggle.svg) no-repeat center center; diff --git a/res/skins/Shade/btn/btn_hotcue_1.png b/res/skins/Shade/btn/btn_hotcue_1.png index bbcc063c525..86f572261d9 100644 Binary files a/res/skins/Shade/btn/btn_hotcue_1.png and b/res/skins/Shade/btn/btn_hotcue_1.png differ diff --git a/res/skins/Shade/btn/btn_hotcue_1_down.png b/res/skins/Shade/btn/btn_hotcue_1_down.png deleted file mode 100644 index b2b78e0b2cd..00000000000 Binary files a/res/skins/Shade/btn/btn_hotcue_1_down.png and /dev/null differ diff --git a/res/skins/Shade/btn/btn_hotcue_1_over.png b/res/skins/Shade/btn/btn_hotcue_1_over.png deleted file mode 100644 index 079f132f1da..00000000000 Binary files a/res/skins/Shade/btn/btn_hotcue_1_over.png and /dev/null differ diff --git a/res/skins/Shade/btn/btn_hotcue_1_overdown.png b/res/skins/Shade/btn/btn_hotcue_1_overdown.png deleted file mode 100644 index 78137be3a27..00000000000 Binary files a/res/skins/Shade/btn/btn_hotcue_1_overdown.png and /dev/null differ diff --git a/res/skins/Shade/btn/btn_hotcue_2.png b/res/skins/Shade/btn/btn_hotcue_2.png index f085b9f63a3..6c93b48fe2e 100644 Binary files a/res/skins/Shade/btn/btn_hotcue_2.png and b/res/skins/Shade/btn/btn_hotcue_2.png differ diff --git a/res/skins/Shade/btn/btn_hotcue_2_down.png b/res/skins/Shade/btn/btn_hotcue_2_down.png deleted file mode 100644 index 93b227bafe4..00000000000 Binary files a/res/skins/Shade/btn/btn_hotcue_2_down.png and /dev/null differ diff --git a/res/skins/Shade/btn/btn_hotcue_2_over.png b/res/skins/Shade/btn/btn_hotcue_2_over.png deleted file mode 100644 index 248fb1ea957..00000000000 Binary files a/res/skins/Shade/btn/btn_hotcue_2_over.png and /dev/null differ diff --git a/res/skins/Shade/btn/btn_hotcue_2_overdown.png b/res/skins/Shade/btn/btn_hotcue_2_overdown.png deleted file mode 100644 index a89346b0e30..00000000000 Binary files a/res/skins/Shade/btn/btn_hotcue_2_overdown.png and /dev/null differ diff --git a/res/skins/Shade/btn/btn_hotcue_3.png b/res/skins/Shade/btn/btn_hotcue_3.png index 2ccd932a164..b6494126f05 100644 Binary files a/res/skins/Shade/btn/btn_hotcue_3.png and b/res/skins/Shade/btn/btn_hotcue_3.png differ diff --git a/res/skins/Shade/btn/btn_hotcue_3_down.png b/res/skins/Shade/btn/btn_hotcue_3_down.png deleted file mode 100644 index 96b06c76596..00000000000 Binary files a/res/skins/Shade/btn/btn_hotcue_3_down.png and /dev/null differ diff --git a/res/skins/Shade/btn/btn_hotcue_3_over.png b/res/skins/Shade/btn/btn_hotcue_3_over.png deleted file mode 100644 index bc39f10974a..00000000000 Binary files a/res/skins/Shade/btn/btn_hotcue_3_over.png and /dev/null differ diff --git a/res/skins/Shade/btn/btn_hotcue_3_overdown.png b/res/skins/Shade/btn/btn_hotcue_3_overdown.png deleted file mode 100644 index 87587d95efa..00000000000 Binary files a/res/skins/Shade/btn/btn_hotcue_3_overdown.png and /dev/null differ diff --git a/res/skins/Shade/btn/btn_hotcue_4.png b/res/skins/Shade/btn/btn_hotcue_4.png index d973f1eb850..f3c47cc7ef8 100644 Binary files a/res/skins/Shade/btn/btn_hotcue_4.png and b/res/skins/Shade/btn/btn_hotcue_4.png differ diff --git a/res/skins/Shade/btn/btn_hotcue_4_down.png b/res/skins/Shade/btn/btn_hotcue_4_down.png deleted file mode 100644 index da33bba1f70..00000000000 Binary files a/res/skins/Shade/btn/btn_hotcue_4_down.png and /dev/null differ diff --git a/res/skins/Shade/btn/btn_hotcue_4_over.png b/res/skins/Shade/btn/btn_hotcue_4_over.png deleted file mode 100644 index 944eef1c632..00000000000 Binary files a/res/skins/Shade/btn/btn_hotcue_4_over.png and /dev/null differ diff --git a/res/skins/Shade/btn/btn_hotcue_4_overdown.png b/res/skins/Shade/btn/btn_hotcue_4_overdown.png deleted file mode 100644 index 6dd65f063cc..00000000000 Binary files a/res/skins/Shade/btn/btn_hotcue_4_overdown.png and /dev/null differ diff --git a/res/skins/Shade/btn/btn_hotcue_5.png b/res/skins/Shade/btn/btn_hotcue_5.png index dab5e3ce7b0..002cf0cc546 100644 Binary files a/res/skins/Shade/btn/btn_hotcue_5.png and b/res/skins/Shade/btn/btn_hotcue_5.png differ diff --git a/res/skins/Shade/btn/btn_hotcue_5_down.png b/res/skins/Shade/btn/btn_hotcue_5_down.png deleted file mode 100644 index b893778f314..00000000000 Binary files a/res/skins/Shade/btn/btn_hotcue_5_down.png and /dev/null differ diff --git a/res/skins/Shade/btn/btn_hotcue_5_over.png b/res/skins/Shade/btn/btn_hotcue_5_over.png deleted file mode 100644 index 6b9e23bedb8..00000000000 Binary files a/res/skins/Shade/btn/btn_hotcue_5_over.png and /dev/null differ diff --git a/res/skins/Shade/btn/btn_hotcue_5_overdown.png b/res/skins/Shade/btn/btn_hotcue_5_overdown.png deleted file mode 100644 index 8eaf2454685..00000000000 Binary files a/res/skins/Shade/btn/btn_hotcue_5_overdown.png and /dev/null differ diff --git a/res/skins/Shade/btn/btn_hotcue_6.png b/res/skins/Shade/btn/btn_hotcue_6.png index be5e45c35f8..dffdf0b39c3 100644 Binary files a/res/skins/Shade/btn/btn_hotcue_6.png and b/res/skins/Shade/btn/btn_hotcue_6.png differ diff --git a/res/skins/Shade/btn/btn_hotcue_6_down.png b/res/skins/Shade/btn/btn_hotcue_6_down.png deleted file mode 100644 index 22a37ba05d8..00000000000 Binary files a/res/skins/Shade/btn/btn_hotcue_6_down.png and /dev/null differ diff --git a/res/skins/Shade/btn/btn_hotcue_6_over.png b/res/skins/Shade/btn/btn_hotcue_6_over.png deleted file mode 100644 index 9e71774a01b..00000000000 Binary files a/res/skins/Shade/btn/btn_hotcue_6_over.png and /dev/null differ diff --git a/res/skins/Shade/btn/btn_hotcue_6_overdown.png b/res/skins/Shade/btn/btn_hotcue_6_overdown.png deleted file mode 100644 index 3dc5b66bd29..00000000000 Binary files a/res/skins/Shade/btn/btn_hotcue_6_overdown.png and /dev/null differ diff --git a/res/skins/Shade/btn/btn_hotcue_7.png b/res/skins/Shade/btn/btn_hotcue_7.png index f0821440d8c..998978cde5e 100644 Binary files a/res/skins/Shade/btn/btn_hotcue_7.png and b/res/skins/Shade/btn/btn_hotcue_7.png differ diff --git a/res/skins/Shade/btn/btn_hotcue_7_down.png b/res/skins/Shade/btn/btn_hotcue_7_down.png deleted file mode 100644 index c6da80d83a1..00000000000 Binary files a/res/skins/Shade/btn/btn_hotcue_7_down.png and /dev/null differ diff --git a/res/skins/Shade/btn/btn_hotcue_7_over.png b/res/skins/Shade/btn/btn_hotcue_7_over.png deleted file mode 100644 index b69e204f631..00000000000 Binary files a/res/skins/Shade/btn/btn_hotcue_7_over.png and /dev/null differ diff --git a/res/skins/Shade/btn/btn_hotcue_7_overdown.png b/res/skins/Shade/btn/btn_hotcue_7_overdown.png deleted file mode 100644 index 6090eb34550..00000000000 Binary files a/res/skins/Shade/btn/btn_hotcue_7_overdown.png and /dev/null differ diff --git a/res/skins/Shade/btn/btn_hotcue_8.png b/res/skins/Shade/btn/btn_hotcue_8.png index d4261196257..36542964c2e 100644 Binary files a/res/skins/Shade/btn/btn_hotcue_8.png and b/res/skins/Shade/btn/btn_hotcue_8.png differ diff --git a/res/skins/Shade/btn/btn_hotcue_8_down.png b/res/skins/Shade/btn/btn_hotcue_8_down.png deleted file mode 100644 index 9644ffd1ca1..00000000000 Binary files a/res/skins/Shade/btn/btn_hotcue_8_down.png and /dev/null differ diff --git a/res/skins/Shade/btn/btn_hotcue_8_over.png b/res/skins/Shade/btn/btn_hotcue_8_over.png deleted file mode 100644 index 62eee604366..00000000000 Binary files a/res/skins/Shade/btn/btn_hotcue_8_over.png and /dev/null differ diff --git a/res/skins/Shade/btn/btn_hotcue_8_overdown.png b/res/skins/Shade/btn/btn_hotcue_8_overdown.png deleted file mode 100644 index 3f67c1fb925..00000000000 Binary files a/res/skins/Shade/btn/btn_hotcue_8_overdown.png and /dev/null differ diff --git a/res/skins/Shade/deck_hotcue_button.xml b/res/skins/Shade/deck_hotcue_button.xml index 69c10a0fac1..f45560d2555 100644 --- a/res/skins/Shade/deck_hotcue_button.xml +++ b/res/skins/Shade/deck_hotcue_button.xml @@ -8,13 +8,13 @@ true 0 - skin:/btn/btn_hotcue__down.png + skin:/btn/btn_hotcue_.png skin:/btn/btn_hotcue_.png 1 - skin:/btn/btn_hotcue__overdown.png - skin:/btn/btn_hotcue__over.png + skin:/btn/btn_hotcue_.png + skin:/btn/btn_hotcue_.png [Channel],hotcue__activate @@ -33,8 +33,8 @@ false - [Channel],hotcue__color_id - highlight + [Channel],hotcue__color + backgroundColorRgba diff --git a/res/skins/Shade/style.qss b/res/skins/Shade/style.qss index 158e25eb970..07da25f2396 100644 --- a/res/skins/Shade/style.qss +++ b/res/skins/Shade/style.qss @@ -553,47 +553,6 @@ QPushButton#pushButtonRepeatPlaylist { } /* AutoDJ button icons */ -/* Hotcue Color: No Color */ -#HotcueButton[highlight="0"] { - background-color: #fd0564; -} - -/* Hotcue Color: Red */ -#HotcueButton[highlight="1"] { - background-color: #c50a08; -} - -/* Hotcue Color: Green */ -#HotcueButton[highlight="2"] { - background-color: #32be44; -} - -/* Hotcue Color: Blue */ -#HotcueButton[highlight="3"] { - background-color: #0044ff; -} - -/* Hotcue Color: Yellow */ -#HotcueButton[highlight="4"] { - background-color: #f8d200; -} - -/* Hotcue Color: Celeste */ -#HotcueButton[highlight="5"] { - background-color: #42d4f4; -} - -/* Hotcue Color: Purple */ -#HotcueButton[highlight="6"] { - background-color: #af00cc; -} - -/* Hotcue Color: Pink */ -#HotcueButton[highlight="7"] { - background-color: #fca6d7; -} - -/* Hotcue Color: White */ -#HotcueButton[highlight="8"] { - background-color: #f2f2ff; +#HotcueButton { + background-color: #aab2b7; } diff --git a/res/skins/Shade/style_dark.qss b/res/skins/Shade/style_dark.qss index fda2ce766cb..f9f10fc77fe 100644 --- a/res/skins/Shade/style_dark.qss +++ b/res/skins/Shade/style_dark.qss @@ -83,8 +83,3 @@ WLibrary QPushButton { WLibrary QRadioButton::indicator:checked { background: url(skin:/btn/btn_lib_radio_button_on_dark.svg) center center; } - -/* Hotcue Color: No Color */ -#HotcueButton[highlight="0"] { - background-color: #b39a00; -} diff --git a/res/skins/Shade/style_summer_sunset.qss b/res/skins/Shade/style_summer_sunset.qss index 903adc42713..e3d04bed986 100644 --- a/res/skins/Shade/style_summer_sunset.qss +++ b/res/skins/Shade/style_summer_sunset.qss @@ -71,8 +71,3 @@ WLibrary QPushButton { QPushButton#pushButtonAnalyze:hover { border: 1px solid #52F904; } - -/* Hotcue Color: No Color */ -#HotcueButton[highlight="0"] { - background-color: #52f904; -} diff --git a/res/skins/Tango/button_hotcue_deck.xml b/res/skins/Tango/button_hotcue_deck.xml index bb1009dbf42..eb0785f234c 100644 --- a/res/skins/Tango/button_hotcue_deck.xml +++ b/res/skins/Tango/button_hotcue_deck.xml @@ -38,8 +38,8 @@ Variables: false - ,hotcue__color_id - highlight + ,hotcue__color + backgroundColorRgba diff --git a/res/skins/Tango/style.qss b/res/skins/Tango/style.qss index dbde02e6c39..0b5b879dc8e 100644 --- a/res/skins/Tango/style.qss +++ b/res/skins/Tango/style.qss @@ -985,62 +985,12 @@ WLabel#TrackComment { border: 1px solid #eeeeee; } -/* Hotcue Color: No Color */ -#HotcueButton[displayValue="1"][highlight="0"], -#HotcueButton[displayValue="2"][highlight="0"] { - background-color: #666; -} - -/* Hotcue Color: Red */ -#HotcueButton[displayValue="1"][highlight="1"], -#HotcueButton[displayValue="2"][highlight="1"] { - background-color: #c50a08; -} - -/* Hotcue Color: Green */ -#HotcueButton[displayValue="1"][highlight="2"], -#HotcueButton[displayValue="2"][highlight="2"] { - background-color: #32be44; -} - -/* Hotcue Color: Blue */ -#HotcueButton[displayValue="1"][highlight="3"], -#HotcueButton[displayValue="2"][highlight="3"] { - background-color: #0044ff; -} - -/* Hotcue Color: Yellow */ -#HotcueButton[displayValue="1"][highlight="4"], -#HotcueButton[displayValue="2"][highlight="4"] { - color: #333; - background-color: #f8d200; -} - -/* Hotcue Color: Celeste */ -#HotcueButton[displayValue="1"][highlight="5"], -#HotcueButton[displayValue="2"][highlight="5"] { - color: #333; - background-color: #42d4f4; -} - -/* Hotcue Color: Purple */ -#HotcueButton[displayValue="1"][highlight="6"], -#HotcueButton[displayValue="2"][highlight="6"] { - background-color: #af00cc; -} - -/* Hotcue Color: Pink */ -#HotcueButton[displayValue="1"][highlight="7"], -#HotcueButton[displayValue="2"][highlight="7"] { - color: #333; - background-color: #fca6d7; +#HotcueButton[backgroundIsDark=true][hasBackgroundColor=true] { + color: #eeeeee; } -/* Hotcue Color: White */ -#HotcueButton[displayValue="1"][highlight="8"], -#HotcueButton[displayValue="2"][highlight="8"] { - color: #333; - background-color: #f2f2ff; +#HotcueButton[backgroundIsDark=false][hasBackgroundColor=true] { + color: #0f0f0f; } #CueButton { diff --git a/src/controllers/bulk/bulkcontroller.cpp b/src/controllers/bulk/bulkcontroller.cpp index d178d084877..01853742be4 100644 --- a/src/controllers/bulk/bulkcontroller.cpp +++ b/src/controllers/bulk/bulkcontroller.cpp @@ -68,15 +68,15 @@ static QString get_string(libusb_device_handle *handle, u_int8_t id) { return QString::fromLatin1((char*)buf); } - -BulkController::BulkController(libusb_context* context, - libusb_device_handle *handle, - struct libusb_device_descriptor *desc) - : m_context(context), +BulkController::BulkController(UserSettingsPointer pConfig, + libusb_context* context, + libusb_device_handle* handle, + struct libusb_device_descriptor* desc) + : Controller(pConfig), + m_context(context), m_phandle(handle), in_epaddr(0), - out_epaddr(0) -{ + out_epaddr(0) { vendor_id = desc->idVendor; product_id = desc->idProduct; diff --git a/src/controllers/bulk/bulkcontroller.h b/src/controllers/bulk/bulkcontroller.h index 480aa084a69..9d67c1041ea 100644 --- a/src/controllers/bulk/bulkcontroller.h +++ b/src/controllers/bulk/bulkcontroller.h @@ -42,8 +42,10 @@ class BulkReader : public QThread { class BulkController : public Controller { Q_OBJECT public: - BulkController(libusb_context* context, libusb_device_handle *handle, - struct libusb_device_descriptor *desc); + BulkController(UserSettingsPointer pConfig, + libusb_context* context, + libusb_device_handle* handle, + struct libusb_device_descriptor* desc); ~BulkController() override; QString presetExtension() override; diff --git a/src/controllers/bulk/bulkenumerator.cpp b/src/controllers/bulk/bulkenumerator.cpp index 2942c5b3ac7..27b5eea15d3 100644 --- a/src/controllers/bulk/bulkenumerator.cpp +++ b/src/controllers/bulk/bulkenumerator.cpp @@ -11,9 +11,8 @@ #include "controllers/bulk/bulkenumerator.h" #include "controllers/bulk/bulksupported.h" -BulkEnumerator::BulkEnumerator() - : ControllerEnumerator(), - m_context(NULL) { +BulkEnumerator::BulkEnumerator(UserSettingsPointer pConfig) + : ControllerEnumerator(), m_context(NULL), m_pConfig(pConfig) { libusb_init(&m_context); } @@ -55,7 +54,8 @@ QList BulkEnumerator::queryDevices() { continue; } - BulkController* currentDevice = new BulkController(m_context, handle, &desc); + BulkController* currentDevice = + new BulkController(m_pConfig, m_context, handle, &desc); m_devices.push_back(currentDevice); } } diff --git a/src/controllers/bulk/bulkenumerator.h b/src/controllers/bulk/bulkenumerator.h index 5e16dd67d11..2018a4a1181 100644 --- a/src/controllers/bulk/bulkenumerator.h +++ b/src/controllers/bulk/bulkenumerator.h @@ -14,7 +14,7 @@ struct libusb_context; class BulkEnumerator : public ControllerEnumerator { public: - BulkEnumerator(); + BulkEnumerator(UserSettingsPointer pConfig); virtual ~BulkEnumerator(); QList queryDevices(); @@ -22,6 +22,7 @@ class BulkEnumerator : public ControllerEnumerator { private: QList m_devices; libusb_context* m_context; + UserSettingsPointer m_pConfig; }; #endif diff --git a/src/controllers/colorjsproxy.cpp b/src/controllers/colorjsproxy.cpp index 4147cdcc124..f7ef331f704 100644 --- a/src/controllers/colorjsproxy.cpp +++ b/src/controllers/colorjsproxy.cpp @@ -1,36 +1,40 @@ #include "controllers/colorjsproxy.h" +#include "preferences/hotcuecolorpalettesettings.h" -ColorJSProxy::ColorJSProxy(QScriptEngine* pScriptEngine) - : m_pScriptEngine(pScriptEngine), - m_predefinedColorsList(makePredefinedColorsList(pScriptEngine)){}; - -ColorJSProxy::~ColorJSProxy() {}; +ColorJSProxy::ColorJSProxy(QScriptEngine* pScriptEngine, + HotcueColorPaletteSettings colorPaletteSettings) + : m_pScriptEngine(pScriptEngine), + m_JsHotcueColorPalette( + makeHotcueColorPalette(pScriptEngine, colorPaletteSettings)), + m_colorPaletteSettings(colorPaletteSettings) { +} -QScriptValue ColorJSProxy::predefinedColorFromId(int iId) { - PredefinedColorPointer color(Color::kPredefinedColorsSet.predefinedColorFromId(iId)); - return jsColorFrom(color); -}; +ColorJSProxy::~ColorJSProxy() = default; -Q_INVOKABLE QScriptValue ColorJSProxy::predefinedColorsList() { - return m_predefinedColorsList; +Q_INVOKABLE QScriptValue ColorJSProxy::hotcueColorPalette() { + return m_JsHotcueColorPalette; } -QScriptValue ColorJSProxy::jsColorFrom(PredefinedColorPointer predefinedColor) { +QScriptValue ColorJSProxy::colorFromHexCode(uint colorCode) { + QColor color = QColor::fromRgba(colorCode); QScriptValue jsColor = m_pScriptEngine->newObject(); - jsColor.setProperty("red", predefinedColor->m_defaultRgba.red()); - jsColor.setProperty("green", predefinedColor->m_defaultRgba.green()); - jsColor.setProperty("blue", predefinedColor->m_defaultRgba.blue()); - jsColor.setProperty("alpha", predefinedColor->m_defaultRgba.alpha()); - jsColor.setProperty("id", predefinedColor->m_iId); + jsColor.setProperty("red", color.red()); + jsColor.setProperty("green", color.green()); + jsColor.setProperty("blue", color.blue()); + jsColor.setProperty("alpha", color.alpha()); return jsColor; } -QScriptValue ColorJSProxy::makePredefinedColorsList(QScriptEngine* pScriptEngine) { - int numColors = Color::kPredefinedColorsSet.allColors.length(); - QScriptValue colorList = pScriptEngine->newArray(numColors); +QScriptValue ColorJSProxy::makeHotcueColorPalette(QScriptEngine* pScriptEngine, + HotcueColorPaletteSettings colorPaletteSettings) { + // TODO: make sure we get notified when the palette changes + QList colorList = + colorPaletteSettings.getHotcueColorPalette().m_colorList; + int numColors = colorList.length(); + QScriptValue jsColorList = pScriptEngine->newArray(numColors); for (int i = 0; i < numColors; ++i) { - PredefinedColorPointer color = Color::kPredefinedColorsSet.allColors.at(i); - colorList.setProperty(i, jsColorFrom(color)); + QColor color = colorList.at(i); + jsColorList.setProperty(i, colorFromHexCode(color.rgba())); } - return colorList; + return jsColorList; } diff --git a/src/controllers/colorjsproxy.h b/src/controllers/colorjsproxy.h index e6ebeda1d9b..dda4d693798 100644 --- a/src/controllers/colorjsproxy.h +++ b/src/controllers/colorjsproxy.h @@ -5,23 +5,29 @@ #include #include +#include "preferences/hotcuecolorpalettesettings.h" #include "util/color/color.h" -class ColorJSProxy: public QObject { +class ColorJSProxy final : public QObject { Q_OBJECT public: - ColorJSProxy(QScriptEngine* pScriptEngine); + ColorJSProxy(QScriptEngine* pScriptEngine, + HotcueColorPaletteSettings colorPaletteSettings); - virtual ~ColorJSProxy(); + ~ColorJSProxy() override; - Q_INVOKABLE QScriptValue predefinedColorFromId(int iId); - Q_INVOKABLE QScriptValue predefinedColorsList(); + Q_INVOKABLE QScriptValue hotcueColorPalette(); + // Return a JS object with the red, green, blue and alpha components + // of a color. The parameter is the hexadecimal representation of the color + // i.e. 0xAARRGGBB + Q_INVOKABLE QScriptValue colorFromHexCode(uint colorCode); private: - QScriptValue jsColorFrom(PredefinedColorPointer predefinedColor); - QScriptValue makePredefinedColorsList(QScriptEngine* pScriptEngine); + QScriptValue makeHotcueColorPalette(QScriptEngine* pScriptEngine, + HotcueColorPaletteSettings colorPaletteSettings); QScriptEngine* m_pScriptEngine; - QScriptValue m_predefinedColorsList; + QScriptValue m_JsHotcueColorPalette; + HotcueColorPaletteSettings m_colorPaletteSettings; }; #endif /* COLORJSPROXY_H */ diff --git a/src/controllers/colormapper.cpp b/src/controllers/colormapper.cpp new file mode 100644 index 00000000000..3caa515806e --- /dev/null +++ b/src/controllers/colormapper.cpp @@ -0,0 +1,59 @@ +#include "controllers/colormapper.h" + +#include +#include + +#include "util/debug.h" + +namespace { +double colorDistance(QRgb a, QRgb b) { + // This algorithm calculates the distance between two colors. In + // contrast to the L2 norm, this also tries take the human perception + // of colors into account. More accurate algorithms like the CIELAB2000 + // Delta-E rely on sophisticated color space conversions and need a lot + // of costly computations. In contrast, this is a low-cost + // approximation and should be sufficently accurate. + // More details: https://www.compuphase.com/cmetric.htm + long mean_red = ((long)qRed(a) + (long)qRed(b)) / 2; + long delta_red = (long)qRed(a) - (long)qRed(b); + long delta_green = (long)qGreen(a) - (long)qGreen(b); + long delta_blue = (long)qBlue(a) - (long)qBlue(b); + return sqrt( + (((512 + mean_red) * delta_red * delta_red) >> 8) + + (4 * delta_green * delta_green) + + (((767 - mean_red) * delta_blue * delta_blue) >> 8)); +} +} // namespace + +QPair ColorMapper::getNearestColor(QRgb desiredColor) { + // If desired color is already in cache, use cache entry + QMap::const_iterator i = m_cache.find(desiredColor); + QMap::const_iterator j; + if (i != m_cache.constEnd()) { + j = m_availableColors.find(i.value()); + DEBUG_ASSERT(j != m_availableColors.constEnd()); + qDebug() << "ColorMapper cache hit for" << desiredColor << ":" + << "Color =" << j.key() << "," + << "Value =" << j.value(); + return QPair(j.key(), j.value()); + } + + // Color is not cached + QMap::const_iterator nearestColorIterator; + double nearestColorDistance = qInf(); + for (j = m_availableColors.constBegin(); j != m_availableColors.constEnd(); j++) { + QRgb availableColor = j.key(); + double distance = colorDistance(desiredColor, availableColor); + if (distance < nearestColorDistance) { + nearestColorDistance = distance; + nearestColorIterator = j; + } + } + + DEBUG_ASSERT(nearestColorDistance < qInf()); + qDebug() << "ColorMapper found matching color for" << desiredColor << ":" + << "Color =" << nearestColorIterator.key() << "," + << "Value =" << nearestColorIterator.value(); + m_cache.insert(desiredColor, nearestColorIterator.key()); + return QPair(nearestColorIterator.key(), nearestColorIterator.value()); +} diff --git a/src/controllers/colormapper.h b/src/controllers/colormapper.h new file mode 100644 index 00000000000..c138edbf38d --- /dev/null +++ b/src/controllers/colormapper.h @@ -0,0 +1,30 @@ +#ifndef COLORMAPPER_H +#define COLORMAPPER_H + +#include +#include +#include +#include +#include + +#include "util/assert.h" + +class ColorMapper final : public QObject { + Q_OBJECT + public: + ColorMapper() = delete; + ColorMapper(const QMap availableColors) + : m_availableColors(availableColors) { + DEBUG_ASSERT(!m_availableColors.isEmpty()); + } + + ~ColorMapper() = default; + + QPair getNearestColor(QRgb desiredColor); + + private: + const QMap m_availableColors; + QMap m_cache; +}; + +#endif /* COLORMAPPER_H */ diff --git a/src/controllers/colormapperjsproxy.cpp b/src/controllers/colormapperjsproxy.cpp new file mode 100644 index 00000000000..f806cb9752e --- /dev/null +++ b/src/controllers/colormapperjsproxy.cpp @@ -0,0 +1,47 @@ +#include + +#include "controllers/colormapperjsproxy.h" + +ColorMapperJSProxy::ColorMapperJSProxy(QScriptEngine* pScriptEngine, QMap availableColors) + : m_pScriptEngine(pScriptEngine) { + m_colorMapper = new ColorMapper(availableColors); +} + +QScriptValue ColorMapperJSProxy::getNearestColor(uint colorCode) { + QPair result = m_colorMapper->getNearestColor(static_cast(colorCode)); + QScriptValue jsColor = m_pScriptEngine->newObject(); + jsColor.setProperty("red", qRed(result.first)); + jsColor.setProperty("green", qGreen(result.first)); + jsColor.setProperty("blue", qBlue(result.first)); + jsColor.setProperty("alpha", qAlpha(result.first)); + return jsColor; +} + +QScriptValue ColorMapperJSProxy::getNearestValue(uint colorCode) { + QPair result = m_colorMapper->getNearestColor(static_cast(colorCode)); + return m_pScriptEngine->toScriptValue(result.second); +} + +QScriptValue ColorMapperJSProxyConstructor(QScriptContext* pScriptContext, QScriptEngine* pScriptEngine) { + QMap availableColors; + DEBUG_ASSERT(pScriptContext->argumentCount() == 1); + QScriptValueIterator it(pScriptContext->argument(0)); + while (it.hasNext()) { + it.next(); + DEBUG_ASSERT(!it.value().isObject()); + QColor color(it.name()); + VERIFY_OR_DEBUG_ASSERT(color.isValid()) { + qWarning() << "Received invalid color name from controller script:" << it.name(); + continue; + } + availableColors.insert(color.rgb(), it.value().toVariant()); + } + + if (availableColors.isEmpty()) { + qWarning() << "Failed to create ColorMapper object: available colors mustn't be empty!"; + return pScriptEngine->undefinedValue(); + } + + QObject* colorMapper = new ColorMapperJSProxy(pScriptEngine, availableColors); + return pScriptEngine->newQObject(colorMapper, QScriptEngine::ScriptOwnership); +} diff --git a/src/controllers/colormapperjsproxy.h b/src/controllers/colormapperjsproxy.h new file mode 100644 index 00000000000..367d13bba3e --- /dev/null +++ b/src/controllers/colormapperjsproxy.h @@ -0,0 +1,30 @@ +#ifndef COLORMAPPERJS_H +#define COLORMAPPERJS_H + +#include +#include + +#include "controllers/colormapper.h" + +class ColorMapperJSProxy final : public QObject { + Q_OBJECT + public: + ColorMapperJSProxy() = delete; + ColorMapperJSProxy(QScriptEngine* pScriptEngine, QMap availableColors); + + ~ColorMapperJSProxy() { + delete m_colorMapper; + }; + + public slots: + QScriptValue getNearestColor(uint ColorCode); + QScriptValue getNearestValue(uint ColorCode); + + private: + QScriptEngine* m_pScriptEngine; + ColorMapper* m_colorMapper; +}; + +QScriptValue ColorMapperJSProxyConstructor(QScriptContext* context, QScriptEngine* engine); + +#endif /* COLORMAPPER_H */ diff --git a/src/controllers/controller.cpp b/src/controllers/controller.cpp index e30ff7a5f63..15a1e1dd5ec 100644 --- a/src/controllers/controller.cpp +++ b/src/controllers/controller.cpp @@ -13,14 +13,15 @@ #include "controllers/defs_controllers.h" #include "util/screensaver.h" -Controller::Controller() +Controller::Controller(UserSettingsPointer pConfig) : QObject(), m_pEngine(NULL), m_bIsOutputDevice(false), m_bIsInputDevice(false), m_bIsOpen(false), - m_bLearning(false) { - m_userActivityInhibitTimer.start(); + m_bLearning(false), + m_pConfig(pConfig) { + m_userActivityInhibitTimer.start(); } Controller::~Controller() { @@ -35,7 +36,7 @@ void Controller::startEngine() qWarning() << "Controller: Engine already exists! Restarting:"; stopEngine(); } - m_pEngine = new ControllerEngine(this); + m_pEngine = new ControllerEngine(this, m_pConfig); } void Controller::stopEngine() { diff --git a/src/controllers/controller.h b/src/controllers/controller.h index 9cc6be386ce..44a5218e2ae 100644 --- a/src/controllers/controller.h +++ b/src/controllers/controller.h @@ -23,7 +23,7 @@ class Controller : public QObject, ConstControllerPresetVisitor { Q_OBJECT public: - Controller(); + Controller(UserSettingsPointer pConfig); ~Controller() override; // Subclass should call close() at minimum. // Returns the extension for the controller (type) preset files. This is @@ -162,6 +162,8 @@ class Controller : public QObject, ConstControllerPresetVisitor { bool m_bLearning; QTime m_userActivityInhibitTimer; + UserSettingsPointer m_pConfig; + // accesses lots of our stuff, but in the same thread friend class ControllerManager; // For testing diff --git a/src/controllers/controllerengine.cpp b/src/controllers/controllerengine.cpp index 1d09be74657..e01e8943a1c 100644 --- a/src/controllers/controllerengine.cpp +++ b/src/controllers/controllerengine.cpp @@ -6,8 +6,8 @@ email : spappalardo@mixxx.org ***************************************************************************/ +#include "controllers/colormapperjsproxy.h" #include "controllers/controllerengine.h" - #include "controllers/controller.h" #include "controllers/controllerdebug.h" #include "control/controlobject.h" @@ -29,9 +29,11 @@ const int kDecks = 16; const int kScratchTimerMs = 1; const double kAlphaBetaDt = kScratchTimerMs / 1000.0; -ControllerEngine::ControllerEngine(Controller* controller) +ControllerEngine::ControllerEngine( + Controller* controller, UserSettingsPointer pConfig) : m_pEngine(nullptr), m_pController(controller), + m_pConfig(pConfig), m_bPopups(false), m_pBaClass(nullptr) { // Handle error dialog buttons @@ -213,9 +215,14 @@ void ControllerEngine::initializeScriptEngine() { engineGlobalObject.setProperty("midi", m_pEngine->newQObject(m_pController)); } - m_pColorJSProxy = std::make_unique(m_pEngine); + m_pColorJSProxy = std::make_unique( + m_pEngine, HotcueColorPaletteSettings(m_pConfig)); engineGlobalObject.setProperty("color", m_pEngine->newQObject(m_pColorJSProxy.get())); + QScriptValue constructor = m_pEngine->newFunction(ColorMapperJSProxyConstructor); + QScriptValue metaObject = m_pEngine->newQMetaObject(&ColorMapperJSProxy::staticMetaObject, constructor); + engineGlobalObject.setProperty("ColorMapper", metaObject); + m_pBaClass = new ByteArrayClass(m_pEngine); engineGlobalObject.setProperty("ByteArray", m_pBaClass->constructor()); } diff --git a/src/controllers/controllerengine.h b/src/controllers/controllerengine.h index 2359a0bcbb3..7eafb1763ea 100644 --- a/src/controllers/controllerengine.h +++ b/src/controllers/controllerengine.h @@ -80,7 +80,7 @@ class ScriptConnectionInvokableWrapper : public QObject { class ControllerEngine : public QObject { Q_OBJECT public: - ControllerEngine(Controller* controller); + ControllerEngine(Controller* controller, UserSettingsPointer pConfig); virtual ~ControllerEngine(); bool isReady(); @@ -198,6 +198,7 @@ class ControllerEngine : public QObject { double getDeckRate(const QString& group); Controller* m_pController; + UserSettingsPointer m_pConfig; bool m_bPopups; QList m_scriptFunctionPrefixes; QMap m_scriptErrors; diff --git a/src/controllers/controllermanager.cpp b/src/controllers/controllermanager.cpp index 54029398d48..397a9c7dc84 100644 --- a/src/controllers/controllermanager.cpp +++ b/src/controllers/controllermanager.cpp @@ -127,15 +127,15 @@ void ControllerManager::slotInitialize() { // Instantiate all enumerators. Enumerators can take a long time to // construct since they interact with host MIDI APIs. - m_enumerators.append(new PortMidiEnumerator()); + m_enumerators.append(new PortMidiEnumerator(m_pConfig)); #ifdef __HSS1394__ m_enumerators.append(new Hss1394Enumerator()); #endif #ifdef __BULK__ - m_enumerators.append(new BulkEnumerator()); + m_enumerators.append(new BulkEnumerator(m_pConfig)); #endif #ifdef __HID__ - m_enumerators.append(new HidEnumerator()); + m_enumerators.append(new HidEnumerator(m_pConfig)); #endif } diff --git a/src/controllers/hid/hidcontroller.cpp b/src/controllers/hid/hidcontroller.cpp index b07b5167c5e..094e31cae3f 100644 --- a/src/controllers/hid/hidcontroller.cpp +++ b/src/controllers/hid/hidcontroller.cpp @@ -47,8 +47,9 @@ void HidReader::run() { delete [] data; } -HidController::HidController(const hid_device_info deviceInfo) - : m_pHidDevice(NULL) { +HidController::HidController( + const hid_device_info deviceInfo, UserSettingsPointer pConfig) + : Controller(pConfig), m_pHidDevice(NULL) { // Copy required variables from deviceInfo, which will be freed after // this class is initialized by caller. hid_vendor_id = deviceInfo.vendor_id; diff --git a/src/controllers/hid/hidcontroller.h b/src/controllers/hid/hidcontroller.h index ac44aa9998b..0cefb8670d8 100644 --- a/src/controllers/hid/hidcontroller.h +++ b/src/controllers/hid/hidcontroller.h @@ -41,7 +41,8 @@ class HidReader : public QThread { class HidController final : public Controller { Q_OBJECT public: - HidController(const hid_device_info deviceInfo); + HidController( + const hid_device_info deviceInfo, UserSettingsPointer pConfig); ~HidController() override; QString presetExtension() override; diff --git a/src/controllers/hid/hidenumerator.cpp b/src/controllers/hid/hidenumerator.cpp index c1c2dc27834..802459f3c02 100644 --- a/src/controllers/hid/hidenumerator.cpp +++ b/src/controllers/hid/hidenumerator.cpp @@ -11,7 +11,8 @@ #include "controllers/hid/hidenumerator.h" #include "controllers/hid/hidblacklist.h" -HidEnumerator::HidEnumerator() : ControllerEnumerator() { +HidEnumerator::HidEnumerator(UserSettingsPointer pConfig) + : ControllerEnumerator(), m_pConfig(pConfig) { } HidEnumerator::~HidEnumerator() { @@ -98,7 +99,7 @@ QList HidEnumerator::queryDevices() { continue; } - HidController* currentDevice = new HidController(*cur_dev); + HidController* currentDevice = new HidController(*cur_dev, m_pConfig); m_devices.push_back(currentDevice); } hid_free_enumeration(devs); diff --git a/src/controllers/hid/hidenumerator.h b/src/controllers/hid/hidenumerator.h index 351e972beb1..9b39b75c947 100644 --- a/src/controllers/hid/hidenumerator.h +++ b/src/controllers/hid/hidenumerator.h @@ -12,13 +12,14 @@ class HidEnumerator : public ControllerEnumerator { public: - HidEnumerator(); + HidEnumerator(UserSettingsPointer pConfig); virtual ~HidEnumerator(); QList queryDevices(); private: QList m_devices; + UserSettingsPointer m_pConfig; }; #endif diff --git a/src/controllers/midi/midicontroller.cpp b/src/controllers/midi/midicontroller.cpp index 0176aa653ec..43dbaa65bed 100644 --- a/src/controllers/midi/midicontroller.cpp +++ b/src/controllers/midi/midicontroller.cpp @@ -17,8 +17,8 @@ #include "util/math.h" #include "util/screensaver.h" -MidiController::MidiController() - : Controller() { +MidiController::MidiController(UserSettingsPointer pConfig) + : Controller(pConfig) { setDeviceCategory(tr("MIDI Controller")); } diff --git a/src/controllers/midi/midicontroller.h b/src/controllers/midi/midicontroller.h index 93d91fdf443..b8ebb8b94b4 100644 --- a/src/controllers/midi/midicontroller.h +++ b/src/controllers/midi/midicontroller.h @@ -23,7 +23,7 @@ class MidiController : public Controller { Q_OBJECT public: - MidiController(); + MidiController(UserSettingsPointer pConfig); ~MidiController() override; QString presetExtension() override; diff --git a/src/controllers/midi/portmidicontroller.cpp b/src/controllers/midi/portmidicontroller.cpp index c4479a1ae68..4be78583c78 100644 --- a/src/controllers/midi/portmidicontroller.cpp +++ b/src/controllers/midi/portmidicontroller.cpp @@ -12,12 +12,11 @@ #include "controllers/controllerdebug.h" PortMidiController::PortMidiController(const PmDeviceInfo* inputDeviceInfo, - const PmDeviceInfo* outputDeviceInfo, - int inputDeviceIndex, - int outputDeviceIndex) - : MidiController(), - m_cReceiveMsg_index(0), - m_bInSysex(false) { + const PmDeviceInfo* outputDeviceInfo, + int inputDeviceIndex, + int outputDeviceIndex, + UserSettingsPointer pConfig) + : MidiController(pConfig), m_cReceiveMsg_index(0), m_bInSysex(false) { for (unsigned int k = 0; k < MIXXX_PORTMIDI_BUFFER_LEN; ++k) { // Can be shortened to `m_midiBuffer[k] = {}` with C++11. m_midiBuffer[k].message = 0; diff --git a/src/controllers/midi/portmidicontroller.h b/src/controllers/midi/portmidicontroller.h index 96da9afce1a..92d2954e18f 100644 --- a/src/controllers/midi/portmidicontroller.h +++ b/src/controllers/midi/portmidicontroller.h @@ -60,9 +60,10 @@ class PortMidiController : public MidiController { Q_OBJECT public: PortMidiController(const PmDeviceInfo* inputDeviceInfo, - const PmDeviceInfo* outputDeviceInfo, - int inputDeviceIndex, - int outputDeviceIndex); + const PmDeviceInfo* outputDeviceInfo, + int inputDeviceIndex, + int outputDeviceIndex, + UserSettingsPointer pConfig); ~PortMidiController() override; private slots: diff --git a/src/controllers/midi/portmidienumerator.cpp b/src/controllers/midi/portmidienumerator.cpp index 47e6a23a829..ba525ddeb73 100644 --- a/src/controllers/midi/portmidienumerator.cpp +++ b/src/controllers/midi/portmidienumerator.cpp @@ -21,7 +21,8 @@ bool shouldBlacklistDevice(const PmDeviceInfo* device) { deviceName.startsWith("Midi Through Port", Qt::CaseInsensitive); } -PortMidiEnumerator::PortMidiEnumerator() : MidiEnumerator() { +PortMidiEnumerator::PortMidiEnumerator(UserSettingsPointer pConfig) + : MidiEnumerator(), m_pConfig(pConfig) { PmError err = Pm_Initialize(); // Based on reading the source, it's not possible for this to fail. if (err != pmNoError) { @@ -257,9 +258,12 @@ QList PortMidiEnumerator::queryDevices() { // device (outputDeviceInfo != NULL). //.... so create our (aggregate) MIDI device! - PortMidiController *currentDevice = new PortMidiController( - inputDeviceInfo, outputDeviceInfo, - inputDevIndex, outputDevIndex); + PortMidiController* currentDevice = + new PortMidiController(inputDeviceInfo, + outputDeviceInfo, + inputDevIndex, + outputDevIndex, + m_pConfig); m_devices.push_back(currentDevice); } diff --git a/src/controllers/midi/portmidienumerator.h b/src/controllers/midi/portmidienumerator.h index 606c4feb4de..9566b54df1b 100644 --- a/src/controllers/midi/portmidienumerator.h +++ b/src/controllers/midi/portmidienumerator.h @@ -13,13 +13,14 @@ class PortMidiEnumerator : public MidiEnumerator { Q_OBJECT public: - PortMidiEnumerator(); + PortMidiEnumerator(UserSettingsPointer pConfig); virtual ~PortMidiEnumerator(); QList queryDevices(); private: QList m_devices; + UserSettingsPointer m_pConfig; }; // For testing. diff --git a/src/database/mixxxdb.cpp b/src/database/mixxxdb.cpp index 5a18ff73526..6dcd79a0a52 100644 --- a/src/database/mixxxdb.cpp +++ b/src/database/mixxxdb.cpp @@ -11,7 +11,7 @@ const QString MixxxDb::kDefaultSchemaFile(":/schema.xml"); //static -const int MixxxDb::kRequiredSchemaVersion = 30; +const int MixxxDb::kRequiredSchemaVersion = 31; namespace { diff --git a/src/engine/controls/cuecontrol.cpp b/src/engine/controls/cuecontrol.cpp index 3a160eded79..312ba3f3223 100644 --- a/src/engine/controls/cuecontrol.cpp +++ b/src/engine/controls/cuecontrol.cpp @@ -7,12 +7,13 @@ #include "engine/enginebuffer.h" #include "engine/controls/cuecontrol.h" +#include "control/controlindicator.h" #include "control/controlobject.h" #include "control/controlpushbutton.h" -#include "control/controlindicator.h" -#include "vinylcontrol/defs_vinylcontrol.h" -#include "util/sample.h" +#include "preferences/hotcuecolorpalettesettings.h" #include "util/color/color.h" +#include "util/sample.h" +#include "vinylcontrol/defs_vinylcontrol.h" // TODO: Convert these doubles to a standard enum // and convert elseif logic to switch statements @@ -23,19 +24,19 @@ static const double CUE_MODE_NUMARK = 3.0; static const double CUE_MODE_MIXXX_NO_BLINK = 4.0; static const double CUE_MODE_CUP = 5.0; -CueControl::CueControl(QString group, - UserSettingsPointer pConfig) : - EngineControl(group, pConfig), - m_bPreviewing(false), - // m_pPlay->toBoo() -> engine play state - // m_pPlay->set(1.0) -> emulate play button press - m_pPlay(ControlObject::getControl(ConfigKey(group, "play"))), - m_pStopButton(ControlObject::getControl(ConfigKey(group, "stop"))), - m_iCurrentlyPreviewingHotcues(0), - m_bypassCueSetByPlay(false), - m_iNumHotCues(NUM_HOT_CUES), - m_pLoadedTrack(), - m_mutex(QMutex::Recursive) { +CueControl::CueControl(QString group, UserSettingsPointer pConfig) + : EngineControl(group, pConfig), + m_colorPaletteSettings(HotcueColorPaletteSettings(pConfig)), + m_bPreviewing(false), + // m_pPlay->toBoo() -> engine play state + // m_pPlay->set(1.0) -> emulate play button press + m_pPlay(ControlObject::getControl(ConfigKey(group, "play"))), + m_pStopButton(ControlObject::getControl(ConfigKey(group, "stop"))), + m_iCurrentlyPreviewingHotcues(0), + m_bypassCueSetByPlay(false), + m_iNumHotCues(NUM_HOT_CUES), + m_pLoadedTrack(), + m_mutex(QMutex::Recursive) { // To silence a compiler warning about CUE_MODE_PIONEER. Q_UNUSED(CUE_MODE_PIONEER); createControls(); @@ -297,6 +298,8 @@ void CueControl::detachCue(HotcueControl* pControl) { } disconnect(pCue.get(), 0, this, 0); pControl->resetCue(); + // Reset the color CO to -1 + pControl->setColor(QColor()); } void CueControl::trackLoaded(TrackPointer pNewTrack) { @@ -579,14 +582,19 @@ void CueControl::hotcueSet(HotcueControl* pControl, double v) { pCue->setHotCue(hotcue); pCue->setLabel(""); pCue->setType(Cue::Type::HotCue); - // TODO(XXX) deal with spurious signals - attachCue(pCue, pControl); - if (getConfig()->getValue(ConfigKey("[Controls]", "auto_hotcue_colors"), false)) { - const QList predefinedColors = Color::kPredefinedColorsSet.allColors; - pCue->setColor(predefinedColors.at((hotcue % (predefinedColors.count() - 1)) + 1)); + auto hotcueColorPalette = m_colorPaletteSettings.getHotcueColorPalette(); + ConfigKey autoHotcueColorsKey("[Controls]", "auto_hotcue_colors"); + if (getConfig()->getValue(autoHotcueColorsKey, false)) { + auto colors = hotcueColorPalette.m_colorList; + pCue->setColor(colors.at((hotcue % (colors.count() - 1)) + 1)); + } else { + pCue->setColor(hotcueColorPalette.m_colorList.first()); }; + // TODO(XXX) deal with spurious signals + attachCue(pCue, pControl); + // If quantize is enabled and we are not playing, jump to the cue point // since it's not necessarily where we currently are. TODO(XXX) is this // potentially invalid for vinyl control? @@ -1734,8 +1742,9 @@ HotcueControl::HotcueControl(QString group, int i) m_hotcueEnabled = new ControlObject(keyForControl(i, "enabled")); m_hotcueEnabled->setReadOnly(); - // The id of the predefined color assigned to this color. - m_hotcueColor = new ControlObject(keyForControl(i, "color_id")); + // The rgba value of the color assigned to this color. + m_hotcueColor = new ControlObject(keyForControl(i, "color")); + m_hotcueColor->set(-1); connect(m_hotcueColor, &ControlObject::valueChanged, this, @@ -1824,9 +1833,9 @@ void HotcueControl::slotHotcuePositionChanged(double newPosition) { emit(hotcuePositionChanged(this, newPosition)); } -void HotcueControl::slotHotcueColorChanged(double newColorId) { - m_pCue->setColor(Color::kPredefinedColorsSet.predefinedColorFromId(newColorId)); - emit(hotcueColorChanged(this, newColorId)); +void HotcueControl::slotHotcueColorChanged(double newColor) { + m_pCue->setColor(QColor::fromRgba(newColor)); + emit(hotcueColorChanged(this, newColor)); } double HotcueControl::getPosition() const { @@ -1840,12 +1849,16 @@ void HotcueControl::setCue(CuePointer pCue) { // because we have a null check for valid data else where in the code m_pCue = pCue; } -PredefinedColorPointer HotcueControl::getColor() const { - return Color::kPredefinedColorsSet.predefinedColorFromId(m_hotcueColor->get()); +QColor HotcueControl::getColor() const { + return QColor::fromRgba(m_hotcueColor->get()); } -void HotcueControl::setColor(PredefinedColorPointer newColor) { - m_hotcueColor->set(static_cast(newColor->m_iId)); +void HotcueControl::setColor(const QColor& newColor) { + if (newColor.isValid()) { + m_hotcueColor->set(newColor.rgba()); + } else { + m_hotcueColor->set(-1); + } } void HotcueControl::resetCue() { // clear pCue first because we have a null check for valid data else where diff --git a/src/engine/controls/cuecontrol.h b/src/engine/controls/cuecontrol.h index ee9ce2412d4..eada3678368 100644 --- a/src/engine/controls/cuecontrol.h +++ b/src/engine/controls/cuecontrol.h @@ -7,9 +7,10 @@ #include #include +#include "control/controlproxy.h" #include "engine/controls/enginecontrol.h" +#include "preferences/hotcuecolorpalettesettings.h" #include "preferences/usersettings.h" -#include "control/controlproxy.h" #include "track/track.h" #define NUM_HOT_CUES 37 @@ -51,8 +52,8 @@ class HotcueControl : public QObject { void setCue(CuePointer pCue); void resetCue(); void setPosition(double position); - void setColor(PredefinedColorPointer newColor); - PredefinedColorPointer getColor() const; + void setColor(const QColor& newColor); + QColor getColor() const; // Used for caching the preview state of this hotcue control. inline bool isPreviewing() { @@ -77,7 +78,7 @@ class HotcueControl : public QObject { void slotHotcueActivatePreview(double v); void slotHotcueClear(double v); void slotHotcuePositionChanged(double newPosition); - void slotHotcueColorChanged(double newColorId); + void slotHotcueColorChanged(double newColor); signals: void hotcueSet(HotcueControl* pHotcue, double v); @@ -88,7 +89,7 @@ class HotcueControl : public QObject { void hotcueActivatePreview(HotcueControl* pHotcue, double v); void hotcueClear(HotcueControl* pHotcue, double v); void hotcuePositionChanged(HotcueControl* pHotcue, double newPosition); - void hotcueColorChanged(HotcueControl* pHotcue, double newColorId); + void hotcueColorChanged(HotcueControl* pHotcue, double newColor); void hotcuePlay(double v); private: @@ -197,6 +198,7 @@ class CueControl : public EngineControl { double quantizeCurrentPosition(QuantizeMode mode); TrackAt getTrackAt() const; + HotcueColorPaletteSettings m_colorPaletteSettings; bool m_bPreviewing; ControlObject* m_pPlay; ControlObject* m_pStopButton; diff --git a/src/library/dao/cuedao.cpp b/src/library/dao/cuedao.cpp index 5d81c215615..e43859645ee 100644 --- a/src/library/dao/cuedao.cpp +++ b/src/library/dao/cuedao.cpp @@ -11,8 +11,6 @@ #include "library/queryutil.h" #include "util/assert.h" #include "util/performancetimer.h" -#include "util/color/color.h" -#include "util/color/predefinedcolor.h" int CueDAO::cueCount() { qDebug() << "CueDAO::cueCount" << QThread::currentThread() << m_database.connectionName(); @@ -53,9 +51,16 @@ CuePointer CueDAO::cueFromRow(const QSqlQuery& query) const { int length = record.value(record.indexOf("length")).toInt(); int hotcue = record.value(record.indexOf("hotcue")).toInt(); QString label = record.value(record.indexOf("label")).toString(); - int iColorId = record.value(record.indexOf("color")).toInt(); - PredefinedColorPointer color = Color::kPredefinedColorsSet.predefinedColorFromId(iColorId); - CuePointer pCue(new Cue(id, trackId, (Cue::Type)type, position, length, hotcue, label, color)); + uint colorValue = record.value(record.indexOf("color")).toUInt(); + QColor color = QColor::fromRgba(colorValue); + CuePointer pCue(new Cue(id, + trackId, + (Cue::Type)type, + position, + length, + hotcue, + label, + color)); m_cues[id] = pCue; return pCue; } @@ -147,7 +152,7 @@ bool CueDAO::saveCue(Cue* cue) { query.bindValue(":length", cue->getLength()); query.bindValue(":hotcue", cue->getHotCue()); query.bindValue(":label", cue->getLabel()); - query.bindValue(":color", cue->getColor()->m_iId); + query.bindValue(":color", cue->getColor().rgba()); if (query.exec()) { int id = query.lastInsertId().toInt(); @@ -175,7 +180,7 @@ bool CueDAO::saveCue(Cue* cue) { query.bindValue(":length", cue->getLength()); query.bindValue(":hotcue", cue->getHotCue()); query.bindValue(":label", cue->getLabel()); - query.bindValue(":color", cue->getColor()->m_iId); + query.bindValue(":color", cue->getColor().rgba()); if (query.exec()) { cue->setDirty(false); diff --git a/src/library/dlgtrackinfo.cpp b/src/library/dlgtrackinfo.cpp index 6fafc712dbe..ab665aaffdf 100644 --- a/src/library/dlgtrackinfo.cpp +++ b/src/library/dlgtrackinfo.cpp @@ -1,17 +1,18 @@ +#include #include -#include #include -#include +#include #include "library/coverartcache.h" #include "library/coverartutils.h" #include "library/dlgtrackinfo.h" +#include "preferences/hotcuecolorpalettesettings.h" #include "sources/soundsourceproxy.h" #include "track/beatfactory.h" #include "track/cue.h" #include "track/keyfactory.h" #include "track/keyutils.h" -#include "util/color/color.h" +#include "util/color/colorpalette.h" #include "util/compatibility.h" #include "util/desktophelper.h" #include "util/duration.h" @@ -22,11 +23,12 @@ const int kMinBpm = 30; // Maximum allowed interval between beats (calculated from kMinBpm). const mixxx::Duration kMaxInterval = mixxx::Duration::fromMillis(1000.0 * (60.0 / kMinBpm)); -DlgTrackInfo::DlgTrackInfo(QWidget* parent) - : QDialog(parent), - m_pTapFilter(new TapFilter(this, kFilterLength, kMaxInterval)), - m_dLastTapedBpm(-1.), - m_pWCoverArtLabel(new WCoverArtLabel(this)) { +DlgTrackInfo::DlgTrackInfo(QWidget* parent, UserSettingsPointer pConfig) + : QDialog(parent), + m_pTapFilter(new TapFilter(this, kFilterLength, kMaxInterval)), + m_dLastTapedBpm(-1.), + m_pWCoverArtLabel(new WCoverArtLabel(this)), + m_pConfig(pConfig) { init(); } @@ -37,7 +39,6 @@ DlgTrackInfo::~DlgTrackInfo() { void DlgTrackInfo::init() { setupUi(this); - cueTable->hideColumn(0); coverBox->insertWidget(1, m_pWCoverArtLabel); connect(btnNext, &QPushButton::clicked, this, &DlgTrackInfo::slotNext); @@ -90,14 +91,6 @@ void DlgTrackInfo::init() { this, &DlgTrackInfo::slotKeyTextChanged); - connect(btnCueActivate, - &QPushButton::clicked, - this, - &DlgTrackInfo::cueActivate); - connect(btnCueDelete, - &QPushButton::clicked, - this, - &DlgTrackInfo::cueDelete); connect(bpmTap, &QPushButton::pressed, m_pTapFilter.data(), @@ -163,30 +156,6 @@ void DlgTrackInfo::slotPrev() { emit(previous()); } -void DlgTrackInfo::cueActivate() { - -} - -void DlgTrackInfo::cueDelete() { - QList selected = cueTable->selectedItems(); - QListIterator item_it(selected); - - QSet rowsToDelete; - while(item_it.hasNext()) { - QTableWidgetItem* item = item_it.next(); - rowsToDelete.insert(item->row()); - } - - QList rowsList = QList::fromSet(rowsToDelete); - std::sort(rowsList.begin(), rowsList.end()); - - QListIterator it(rowsList); - it.toBack(); - while (it.hasPrevious()) { - cueTable->removeRow(it.previous()); - } -} - void DlgTrackInfo::populateFields(const Track& track) { setWindowTitle(track.getArtist() % " - " % track.getTitle()); @@ -255,7 +224,6 @@ void DlgTrackInfo::loadTrack(TrackPointer pTrack) { m_pLoadedTrack = pTrack; populateFields(*m_pLoadedTrack); - populateCues(m_pLoadedTrack); m_pWCoverArtLabel->loadTrack(m_pLoadedTrack); // We already listen to changed() so we don't need to listen to individual @@ -307,130 +275,6 @@ void DlgTrackInfo::slotOpenInFileBrowser() { mixxx::DesktopHelper::openInFileBrowser(QStringList(m_pLoadedTrack->getLocation())); } -void DlgTrackInfo::populateCues(TrackPointer pTrack) { - int sampleRate = pTrack->getSampleRate(); - - QList listPoints; - const QList cuePoints = pTrack->getCuePoints(); - QListIterator it(cuePoints); - while (it.hasNext()) { - CuePointer pCue = it.next(); - Cue::Type type = pCue->getType(); - if (type == Cue::Type::HotCue || type == Cue::Type::MainCue || type == Cue::Type::Intro - || type == Cue::Type::Outro) { - listPoints.push_back(pCue); - } - } - it = QListIterator(listPoints); - cueTable->setSortingEnabled(false); - int row = 0; - - while (it.hasNext()) { - CuePointer pCue(it.next()); - - QString rowStr = QString("%1").arg(row); - - // All hotcues are stored in Cue's as 0-indexed, but the GUI presents - // them to the user as 1-indexex. Add 1 here. rryan 9/2010 - int iHotcue = pCue->getHotCue() + 1; - QString hotcue = ""; - hotcue = QString("%1").arg(iHotcue); - double position = pCue->getPosition(); - if (position == -1) { - continue; - } - - double totalSeconds = position / sampleRate / 2.0; - - bool negative = false; - if (totalSeconds < 0) { - totalSeconds *= -1; - negative = true; - } - - int iTotalSeconds = static_cast(totalSeconds); - int fraction = 100 * (totalSeconds - iTotalSeconds); - int seconds = iTotalSeconds % 60; - int mins = iTotalSeconds / 60; - //int hours = mins / 60; //Not going to worry about this for now. :) - - //Construct a nicely formatted duration string now. - QString duration = QString("%1%2:%3.%4").arg( - negative ? QString("-") : QString(), - QString::number(mins), - QString("%1").arg(seconds, 2, 10, QChar('0')), - QString("%1").arg(fraction, 2, 10, QChar('0'))); - - QTableWidgetItem* durationItem = new QTableWidgetItem(duration); - // Make the duration read only - durationItem->setFlags(Qt::NoItemFlags); - - // Decode cue type to display text - QString cueType; - switch (pCue->getType()) { - case Cue::Type::Invalid: - cueType = "?"; - break; - case Cue::Type::HotCue: - cueType = "Hotcue"; - break; - case Cue::Type::MainCue: - cueType = "Main Cue"; - break; - case Cue::Type::Beat: - cueType = "Beat"; - break; - case Cue::Type::Loop: - cueType = "Loop"; - break; - case Cue::Type::Jump: - cueType = "Jump"; - break; - case Cue::Type::Intro: - cueType = "Intro"; - break; - case Cue::Type::Outro: - cueType = "Outro"; - break; - default: - break; - } - - QTableWidgetItem* typeItem = new QTableWidgetItem(cueType); - // Make the type read only - typeItem->setFlags(Qt::NoItemFlags); - - QComboBox* colorComboBox = new QComboBox(); - const QList predefinedColors = Color::kPredefinedColorsSet.allColors; - for (int i = 0; i < predefinedColors.count(); i++) { - PredefinedColorPointer color = predefinedColors.at(i); - QColor defaultRgba = color->m_defaultRgba; - colorComboBox->addItem(color->m_sDisplayName, defaultRgba); - if (*color != *Color::kPredefinedColorsSet.noColor) { - QPixmap pixmap(80, 80); - pixmap.fill(defaultRgba); - QIcon icon(pixmap); - colorComboBox->setItemIcon(i, icon); - } - } - PredefinedColorPointer cueColor = pCue->getColor(); - colorComboBox->setCurrentIndex(Color::kPredefinedColorsSet.predefinedColorIndex(cueColor)); - - m_cueMap[row] = pCue; - cueTable->insertRow(row); - cueTable->setItem(row, 0, new QTableWidgetItem(rowStr)); - cueTable->setItem(row, 1, durationItem); - cueTable->setItem(row, 2, typeItem); - cueTable->setItem(row, 3, new QTableWidgetItem(hotcue)); - cueTable->setCellWidget(row, 4, colorComboBox); - cueTable->setItem(row, 5, new QTableWidgetItem(pCue->getLabel())); - row += 1; - } - cueTable->setSortingEnabled(true); - cueTable->horizontalHeader()->setStretchLastSection(true); - cueTable->horizontalHeader()->setSectionResizeMode(3, QHeaderView::ResizeToContents); -} - void DlgTrackInfo::saveTrack() { if (!m_pLoadedTrack) return; @@ -466,66 +310,6 @@ void DlgTrackInfo::saveTrack() { m_pLoadedTrack->setKeys(m_keysClone); - QSet updatedRows; - for (int row = 0; row < cueTable->rowCount(); ++row) { - QTableWidgetItem* rowItem = cueTable->item(row, 0); - QTableWidgetItem* hotcueItem = cueTable->item(row, 3); - QWidget* colorWidget = cueTable->cellWidget(row, 4); - QTableWidgetItem* labelItem = cueTable->item(row, 5); - - VERIFY_OR_DEBUG_ASSERT(rowItem && hotcueItem && colorWidget && labelItem) { - qWarning() << "unable to retrieve cells from cueTable row"; - continue; - } - - int oldRow = rowItem->data(Qt::DisplayRole).toInt(); - CuePointer pCue(m_cueMap.value(oldRow, CuePointer())); - if (!pCue) { - continue; - } - updatedRows.insert(oldRow); - - QVariant vHotcue = hotcueItem->data(Qt::DisplayRole); - bool ok; - int iTableHotcue = vHotcue.toInt(&ok); - if (ok) { - // The GUI shows hotcues as 1-indexed, but they are actually - // 0-indexed, so subtract 1 - pCue->setHotCue(iTableHotcue - 1); - } else { - pCue->setHotCue(-1); - } - - if (pCue->getType() == Cue::Type::HotCue) { - auto colorComboBox = qobject_cast(colorWidget); - if (colorComboBox) { - PredefinedColorPointer color = Color::kPredefinedColorsSet.allColors.at(colorComboBox->currentIndex()); - pCue->setColor(color); - } - } - // do nothing for now. - - QString label = labelItem->data(Qt::DisplayRole).toString(); - pCue->setLabel(label); - } - - QMutableHashIterator it(m_cueMap); - // Everything that was not processed above was removed. - while (it.hasNext()) { - it.next(); - int oldRow = it.key(); - - // If cue's old row is not in updatedRows then it must have been - // deleted. - if (updatedRows.contains(oldRow)) { - continue; - } - CuePointer pCue(it.value()); - it.remove(); - qDebug() << "Deleting cue" << pCue->getId() << pCue->getHotCue(); - m_pLoadedTrack->removeCue(pCue); - } - m_pLoadedTrack->setCoverInfo(m_loadedCoverInfo); // Reconnect changed signals now. @@ -575,10 +359,6 @@ void DlgTrackInfo::clear() { txtKey->setText(""); txtReplayGain->setText(""); - m_cueMap.clear(); - cueTable->clearContents(); - cueTable->setRowCount(0); - m_loadedCoverInfo = CoverInfo(); m_pWCoverArtLabel->setCoverArt(m_loadedCoverInfo, QPixmap()); } diff --git a/src/library/dlgtrackinfo.h b/src/library/dlgtrackinfo.h index 0ca2a44a292..9856dcbeeb3 100644 --- a/src/library/dlgtrackinfo.h +++ b/src/library/dlgtrackinfo.h @@ -18,7 +18,7 @@ class DlgTrackInfo : public QDialog, public Ui::DlgTrackInfo { Q_OBJECT public: - DlgTrackInfo(QWidget* parent); + DlgTrackInfo(QWidget* parent, UserSettingsPointer pConfig); virtual ~DlgTrackInfo(); public slots: @@ -39,9 +39,6 @@ class DlgTrackInfo : public QDialog, public Ui::DlgTrackInfo { void cancel(); void trackUpdated(); - void cueActivate(); - void cueDelete(); - void slotBpmDouble(); void slotBpmHalve(); void slotBpmTwoThirds(); @@ -69,12 +66,10 @@ class DlgTrackInfo : public QDialog, public Ui::DlgTrackInfo { private: void populateFields(const Track& track); void reloadTrackBeats(const Track& track); - void populateCues(TrackPointer pTrack); void saveTrack(); void unloadTrack(bool save); void clear(); void init(); - QHash m_cueMap; TrackPointer m_pLoadedTrack; BeatsPointer m_pBeatsClone; Keys m_keysClone; @@ -85,6 +80,7 @@ class DlgTrackInfo : public QDialog, public Ui::DlgTrackInfo { CoverInfo m_loadedCoverInfo; WCoverArtLabel* m_pWCoverArtLabel; + UserSettingsPointer m_pConfig; }; #endif /* DLGTRACKINFO_H */ diff --git a/src/library/dlgtrackinfo.ui b/src/library/dlgtrackinfo.ui index 4435134c7a0..caf42ac2c25 100644 --- a/src/library/dlgtrackinfo.ui +++ b/src/library/dlgtrackinfo.ui @@ -7,7 +7,7 @@ 0 0 700 - 588 + 595 @@ -41,7 +41,7 @@ - 0 + 2 @@ -376,7 +376,16 @@ QLayout::SetDefaultConstraint - + + 0 + + + 0 + + + 0 + + 0 @@ -798,90 +807,6 @@ Often results in higher quality beatgrids, but will not do well on tracks that h - - - Cuepoints - - - - - - - Cue Id - - - - - Position - - - - - Type - - - - - Hotcue - - - - - Color - - - - - Label - - - - - - - - - - - 125 - 0 - - - - Delete Cue - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - 125 - 0 - - - - Activate Cue - - - - - - - @@ -1030,9 +955,6 @@ Often results in higher quality beatgrids, but will not do well on tracks that h bpmThreeFourth bpmThreeHalves bpmFourThirds - cueTable - btnCueDelete - btnCueActivate diff --git a/src/preferences/configobject.cpp b/src/preferences/configobject.cpp index 6eecb2bb991..22900ac4e48 100644 --- a/src/preferences/configobject.cpp +++ b/src/preferences/configobject.cpp @@ -215,6 +215,18 @@ template void ConfigObject::save() { } } +template +QList ConfigObject::getKeysWithGroup(QString group) { + QWriteLocker lock(&m_valuesLock); + QList filteredList; + for (const ConfigKey& key : m_values.uniqueKeys()) { + if (key.group == group) { + filteredList.append(key); + } + } + return filteredList; +} + template ConfigObject::ConfigObject(const QDomNode& node) { if (!node.isNull() && node.isElement()) { QDomNode ctrl = node.firstChild(); @@ -270,6 +282,13 @@ void ConfigObject::setValue( set(key, ConfigValue(QString::number(value))); } +template<> +template<> +void ConfigObject::setValue( + const ConfigKey& key, const QColor& value) { + set(key, ConfigValue(value.name(QColor::NameFormat::HexArgb))); +} + template <> template <> bool ConfigObject::getValue( const ConfigKey& key, const bool& default_value) const { @@ -306,6 +325,24 @@ double ConfigObject::getValue( return ok ? result : default_value; } +template<> +template<> +QColor ConfigObject::getValue( + const ConfigKey& key, const QColor& default_value) const { + const ConfigValue value = get(key); + if (value.isNull()) { + return default_value; + } + auto color = QColor(value.value); + return color.isValid() ? color : default_value; +} + +template<> +template<> +QColor ConfigObject::getValue(const ConfigKey& key) const { + return getValue(key, QColor()); +} + // For string literal default template <> QString ConfigObject::getValue( diff --git a/src/preferences/configobject.h b/src/preferences/configobject.h index 3e4ba6aa20e..3791de6ecd7 100644 --- a/src/preferences/configobject.h +++ b/src/preferences/configobject.h @@ -187,6 +187,8 @@ template class ConfigObject { return m_settingsPath; } + QList getKeysWithGroup(QString group); + protected: // We use QMap because we want a sorted list in mixxx.cfg QMap m_values; diff --git a/src/preferences/hotcuecolorpalettesettings.cpp b/src/preferences/hotcuecolorpalettesettings.cpp new file mode 100644 index 00000000000..1da25a868f2 --- /dev/null +++ b/src/preferences/hotcuecolorpalettesettings.cpp @@ -0,0 +1,36 @@ +#include "preferences/hotcuecolorpalettesettings.h" + +const QString HotcueColorPaletteSettings::sGroup = "[HotcueColorPalette]"; + +ColorPalette HotcueColorPaletteSettings::getHotcueColorPalette() const { + QList colorList; + for (const ConfigKey& key : m_pConfig->getKeysWithGroup(sGroup)) { + QColor color = m_pConfig->getValue(key); + if (color.isValid()) { + colorList.append(color); + } + } + + // If no palette is defined in the settings, we use the default one. + if (colorList.isEmpty()) { + return ColorPalette::mixxxHotcuesPalette; + } + + return colorList; +} + +void HotcueColorPaletteSettings::setHotcueColorPalette( + const ColorPalette& colorPalette) { + removePalette(); + + for (int index = 0; index < colorPalette.m_colorList.count(); ++index) { + QColor color = colorPalette.m_colorList[index]; + m_pConfig->setValue(keyForIndex(index), color); + } +} + +void HotcueColorPaletteSettings::removePalette() { + for (const ConfigKey& key : m_pConfig->getKeysWithGroup(sGroup)) { + m_pConfig->remove(key); + } +} diff --git a/src/preferences/hotcuecolorpalettesettings.h b/src/preferences/hotcuecolorpalettesettings.h new file mode 100644 index 00000000000..9ed134146a2 --- /dev/null +++ b/src/preferences/hotcuecolorpalettesettings.h @@ -0,0 +1,26 @@ +#pragma once + +#include "preferences/usersettings.h" +#include "util/color/colorpalette.h" + +class HotcueColorPaletteSettings { + public: + explicit HotcueColorPaletteSettings(UserSettingsPointer pConfig) + : m_pConfig(pConfig) { + } + + ColorPalette getHotcueColorPalette() const; + + void setHotcueColorPalette(const ColorPalette& colorPalette); + + private: + static const QString sGroup; + + void removePalette(); + + ConfigKey keyForIndex(int index) { + return ConfigKey(sGroup, QString::number(index)); + } + + UserSettingsPointer m_pConfig; +}; diff --git a/src/skin/skincontext.h b/src/skin/skincontext.h index 7a81d296318..9c3f86f1918 100644 --- a/src/skin/skincontext.h +++ b/src/skin/skincontext.h @@ -12,7 +12,6 @@ #include #include -#include "../util/color/predefinedcolorsrepresentation.h" #include "preferences/usersettings.h" #include "skin/pixmapsource.h" #include "util/color/color.h" @@ -247,20 +246,6 @@ class SkinContext { return m_scaleFactor; } - PredefinedColorsRepresentation getCueColorRepresentation(const QDomNode& node, QColor defaultColor) const { - PredefinedColorsRepresentation colorRepresentation = Color::kPredefinedColorsSet.defaultRepresentation(); - for (PredefinedColorPointer color : Color::kPredefinedColorsSet.allColors) { - QString sColorName(color->m_sName); - QColor skinRgba = selectColor(node, "Cue" + sColorName); - if (skinRgba.isValid()) { - PredefinedColorPointer originalColor = Color::kPredefinedColorsSet.predefinedColorFromName(sColorName); - colorRepresentation.setCustomRgba(originalColor, skinRgba); - } - } - colorRepresentation.setCustomRgba(Color::kPredefinedColorsSet.noColor, defaultColor); - return colorRepresentation; - } - private: PixmapSource getPixmapSourceInner(const QString& filename) const; diff --git a/src/test/colorconfig_test.cpp b/src/test/colorconfig_test.cpp new file mode 100644 index 00000000000..477bab40a52 --- /dev/null +++ b/src/test/colorconfig_test.cpp @@ -0,0 +1,79 @@ +#include +#include + +#include "test/mixxxtest.h" + +#include +#include + +class ColorConfigTest : public MixxxTest {}; + +TEST_F(ColorConfigTest, SavingColorWithoutAlpha) { + ConfigKey key("[Color]", "color"); + QColor originalColor("#FF9900"); + config()->setValue(key, originalColor); + saveAndReloadConfig(); + QColor colorFromConfig = config()->getValue(key); + ASSERT_EQ(originalColor, colorFromConfig); +} + +TEST_F(ColorConfigTest, SavingColorWithAlpha) { + ConfigKey key("[Color]", "color"); + QColor originalColor("#66FF9900"); + config()->setValue(key, originalColor); + saveAndReloadConfig(); + QColor colorFromConfig = config()->getValue(key); + ASSERT_EQ(originalColor, colorFromConfig); +} + +TEST_F(ColorConfigTest, GetDefaultColorWhenNoStoredColor) { + ConfigKey key("[Color]", "color"); + QColor defaultColor("#66FF9900"); + QColor colorFromConfig = config()->getValue(key, defaultColor); + ASSERT_EQ(defaultColor, colorFromConfig); +} + +TEST_F(ColorConfigTest, SaveColorPalette) { + HotcueColorPaletteSettings colorPaletteSettings(config()); + ColorPalette originalColorPalette(QList{ + QColor("#66FF9900"), + QColor("#FF9900"), + QColor("#00000000"), + QColor("#FFFFFF"), + }); + ConfigKey key("[ColorPalette]", "colorPalette"); + colorPaletteSettings.setHotcueColorPalette(originalColorPalette); + saveAndReloadConfig(); + ColorPalette colorPaletteFromConfig = + colorPaletteSettings.getHotcueColorPalette(); + ASSERT_EQ(originalColorPalette, colorPaletteFromConfig); +} + +TEST_F(ColorConfigTest, ReplaceColorPalette) { + HotcueColorPaletteSettings colorPaletteSettings(config()); + ColorPalette colorPalette1(QList{ + QColor("#66FF9900"), + QColor("#FF9900"), + QColor("#00000000"), + QColor("#FFFFFF"), + }); + ColorPalette colorPalette2(QList{ + QColor("#0000FF"), + QColor("#FF0000"), + }); + ConfigKey key("[ColorPalette]", "colorPalette"); + colorPaletteSettings.setHotcueColorPalette(colorPalette1); + saveAndReloadConfig(); + colorPaletteSettings.setHotcueColorPalette(colorPalette2); + saveAndReloadConfig(); + ColorPalette colorPaletteFromConfig = + colorPaletteSettings.getHotcueColorPalette(); + ASSERT_EQ(colorPalette2, colorPaletteFromConfig); +} + +TEST_F(ColorConfigTest, DefaultColorPalette) { + HotcueColorPaletteSettings colorPaletteSettings(config()); + ColorPalette colorPaletteFromConfig = + colorPaletteSettings.getHotcueColorPalette(); + ASSERT_EQ(ColorPalette::mixxxHotcuesPalette, colorPaletteFromConfig); +} diff --git a/src/test/controller_preset_validation_test.cpp b/src/test/controller_preset_validation_test.cpp index d49ee3e50ba..970cf0158c9 100644 --- a/src/test/controller_preset_validation_test.cpp +++ b/src/test/controller_preset_validation_test.cpp @@ -15,7 +15,7 @@ class FakeController : public Controller { public: - FakeController(); + FakeController(UserSettingsPointer pConfig); ~FakeController() override; QString presetExtension() override { @@ -112,9 +112,8 @@ class FakeController : public Controller { HidControllerPreset m_hidPreset; }; -FakeController::FakeController() - : m_bMidiPreset(false), - m_bHidPreset(false) { +FakeController::FakeController(UserSettingsPointer pConfig) + : Controller(pConfig), m_bMidiPreset(false), m_bHidPreset(false) { } FakeController::~FakeController() { @@ -135,7 +134,7 @@ class ControllerPresetValidationTest : public MixxxTest { return false; } - FakeController controller; + FakeController controller(config()); controller.setDeviceName("Test Controller"); controller.startEngine(); controller.setPreset(*pPreset); diff --git a/src/test/controllerengine_test.cpp b/src/test/controllerengine_test.cpp index d84f736a6ff..ade6169ca5a 100644 --- a/src/test/controllerengine_test.cpp +++ b/src/test/controllerengine_test.cpp @@ -1,13 +1,14 @@ -#include #include +#include #include "control/controlobject.h" #include "control/controlpotmeter.h" -#include "preferences/usersettings.h" -#include "controllers/controllerengine.h" #include "controllers/controllerdebug.h" +#include "controllers/controllerengine.h" #include "controllers/softtakeover.h" +#include "preferences/usersettings.h" #include "test/mixxxtest.h" +#include "util/color/colorpalette.h" #include "util/memory.h" #include "util/time.h" @@ -17,7 +18,7 @@ class ControllerEngineTest : public MixxxTest { mixxx::Time::setTestMode(true); mixxx::Time::setTestElapsedTime(mixxx::Duration::fromMillis(10)); QThread::currentThread()->setObjectName("Main"); - cEngine = new ControllerEngine(nullptr); + cEngine = new ControllerEngine(nullptr, config()); pScriptEngine = cEngine->m_pEngine; ControllerDebug::enable(); cEngine->setPopups(false); @@ -618,24 +619,24 @@ TEST_F(ControllerEngineTest, connectionExecutesWithCorrectThisObject) { EXPECT_DOUBLE_EQ(1.0, pass->get()); } -TEST_F(ControllerEngineTest, colorProxy) { - QList allColors = Color::kPredefinedColorsSet.allColors; +TEST_F(ControllerEngineTest, colorProxyTestMixxxPalette) { + QList allColors = ColorPalette::mixxxHotcuesPalette.m_colorList; for (int i = 0; i < allColors.length(); ++i) { - PredefinedColorPointer color = allColors[i]; - qDebug() << "Testing color " << color->m_sName; - QScriptValue jsColor = pScriptEngine->evaluate("color.predefinedColorFromId(" + QString::number(color->m_iId) + ")"); - EXPECT_EQ(jsColor.property("red").toInt32(), color->m_defaultRgba.red()); - EXPECT_EQ(jsColor.property("green").toInt32(), color->m_defaultRgba.green()); - EXPECT_EQ(jsColor.property("blue").toInt32(), color->m_defaultRgba.blue()); - EXPECT_EQ(jsColor.property("alpha").toInt32(), color->m_defaultRgba.alpha()); - EXPECT_EQ(jsColor.property("id").toInt32(), color->m_iId); - - QScriptValue jsColor2 = pScriptEngine->evaluate("color.predefinedColorsList()[" - + QString::number(i) + "]"); - EXPECT_EQ(jsColor2.property("red").toInt32(), color->m_defaultRgba.red()); - EXPECT_EQ(jsColor2.property("green").toInt32(), color->m_defaultRgba.green()); - EXPECT_EQ(jsColor2.property("blue").toInt32(), color->m_defaultRgba.blue()); - EXPECT_EQ(jsColor2.property("alpha").toInt32(), color->m_defaultRgba.alpha()); - EXPECT_EQ(jsColor2.property("id").toInt32(), color->m_iId); + QColor color = allColors[i]; + qDebug() << "Testing color " << color.name(); + QString colorCode = QString::number(color.rgba()); + QScriptValue jsColor = pScriptEngine->evaluate( + "color.colorFromHexCode(" + colorCode + ")"); + EXPECT_EQ(jsColor.property("red").toInt32(), color.red()); + EXPECT_EQ(jsColor.property("green").toInt32(), color.green()); + EXPECT_EQ(jsColor.property("blue").toInt32(), color.blue()); + EXPECT_EQ(jsColor.property("alpha").toInt32(), color.alpha()); + + QScriptValue jsColor2 = pScriptEngine->evaluate( + "color.hotcueColorPalette()[" + QString::number(i) + "]"); + EXPECT_EQ(jsColor2.property("red").toInt32(), color.red()); + EXPECT_EQ(jsColor2.property("green").toInt32(), color.green()); + EXPECT_EQ(jsColor2.property("blue").toInt32(), color.blue()); + EXPECT_EQ(jsColor2.property("alpha").toInt32(), color.alpha()); } } diff --git a/src/test/midicontrollertest.cpp b/src/test/midicontrollertest.cpp index 696483c05fd..0d10c4c54a0 100644 --- a/src/test/midicontrollertest.cpp +++ b/src/test/midicontrollertest.cpp @@ -12,7 +12,8 @@ class MockMidiController : public MidiController { public: - MockMidiController() { } + MockMidiController(UserSettingsPointer pConfig) : MidiController(pConfig) { + } ~MockMidiController() override { } MOCK_METHOD0(open, int()); @@ -27,7 +28,7 @@ class MockMidiController : public MidiController { class MidiControllerTest : public MixxxTest { protected: void SetUp() override { - m_pController.reset(new MockMidiController()); + m_pController.reset(new MockMidiController(config())); } void addMapping(MidiInputMapping mapping) { diff --git a/src/test/portmidicontroller_test.cpp b/src/test/portmidicontroller_test.cpp index b8b9cb63594..8daf1f43d99 100644 --- a/src/test/portmidicontroller_test.cpp +++ b/src/test/portmidicontroller_test.cpp @@ -16,11 +16,15 @@ using ::testing::SetArrayArgument; class MockPortMidiController : public PortMidiController { public: MockPortMidiController(const PmDeviceInfo* inputDeviceInfo, - const PmDeviceInfo* outputDeviceInfo, - int inputDeviceIndex, - int outputDeviceIndex) : PortMidiController( - inputDeviceInfo, outputDeviceInfo, - inputDeviceIndex, outputDeviceIndex) { + const PmDeviceInfo* outputDeviceInfo, + int inputDeviceIndex, + int outputDeviceIndex, + UserSettingsPointer pConfig) + : PortMidiController(inputDeviceInfo, + outputDeviceInfo, + inputDeviceIndex, + outputDeviceIndex, + pConfig) { } ~MockPortMidiController() override { } @@ -71,9 +75,8 @@ class PortMidiControllerTest : public MixxxTest { m_outputDeviceInfo.output = 1; m_outputDeviceInfo.opened = 0; - m_pController.reset(new MockPortMidiController(&m_inputDeviceInfo, - &m_outputDeviceInfo, - 0, 0)); + m_pController.reset(new MockPortMidiController( + &m_inputDeviceInfo, &m_outputDeviceInfo, 0, 0, config())); m_pController->setPortMidiInputDevice(m_mockInput); m_pController->setPortMidiOutputDevice(m_mockOutput); } diff --git a/src/track/cue.cpp b/src/track/cue.cpp index f4d8711f85d..2c34957a2f2 100644 --- a/src/track/cue.cpp +++ b/src/track/cue.cpp @@ -28,12 +28,18 @@ Cue::Cue(TrackId trackId) m_sampleEndPosition(Cue::kNoPosition), m_iHotCue(-1), m_label(kDefaultLabel), - m_color(Color::kPredefinedColorsSet.noColor) { + m_color(QColor()) { DEBUG_ASSERT(!m_label.isNull()); } -Cue::Cue(int id, TrackId trackId, Cue::Type type, double position, double length, - int hotCue, QString label, PredefinedColorPointer color) +Cue::Cue(int id, + TrackId trackId, + Cue::Type type, + double position, + double length, + int hotCue, + QString label, + QColor color) : m_bDirty(false), m_iId(id), m_trackId(trackId), @@ -153,12 +159,12 @@ void Cue::setLabel(const QString label) { emit(updated()); } -PredefinedColorPointer Cue::getColor() const { +QColor Cue::getColor() const { QMutexLocker lock(&m_mutex); return m_color; } -void Cue::setColor(const PredefinedColorPointer color) { +void Cue::setColor(const QColor& color) { QMutexLocker lock(&m_mutex); m_color = color; m_bDirty = true; diff --git a/src/track/cue.h b/src/track/cue.h index ffc1c814690..9b6d133766a 100644 --- a/src/track/cue.h +++ b/src/track/cue.h @@ -6,7 +6,6 @@ #include #include "track/trackid.h" -#include "util/color/predefinedcolor.h" #include "util/memory.h" class CuePosition; @@ -54,8 +53,8 @@ class Cue : public QObject { QString getLabel() const; void setLabel(QString label); - PredefinedColorPointer getColor() const; - void setColor(PredefinedColorPointer color); + QColor getColor() const; + void setColor(const QColor& color); double getEndPosition() const; @@ -64,8 +63,14 @@ class Cue : public QObject { private: explicit Cue(TrackId trackId); - Cue(int id, TrackId trackId, Cue::Type type, double position, double length, - int hotCue, QString label, PredefinedColorPointer color); + Cue(int id, + TrackId trackId, + Cue::Type type, + double position, + double length, + int hotCue, + QString label, + QColor color); void setDirty(bool dirty); void setId(int id); void setTrackId(TrackId trackId); @@ -80,7 +85,7 @@ class Cue : public QObject { double m_sampleEndPosition; int m_iHotCue; QString m_label; - PredefinedColorPointer m_color; + QColor m_color; friend class Track; friend class CueDAO; diff --git a/src/util/color/color.cpp b/src/util/color/color.cpp index 4627fdf3d9a..92116acb748 100644 --- a/src/util/color/color.cpp +++ b/src/util/color/color.cpp @@ -4,8 +4,6 @@ namespace Color { -const PredefinedColorsSet kPredefinedColorsSet = PredefinedColorsSet(); - // algorithm by http://www.nbdtech.com/Blog/archive/2008/04/27/Calculating-the-Perceived-Brightness-of-a-Color.aspx // NOTE(Swiftb0y): please suggest if I should you use other methods // (like the W3C algorithm) or if this approach is to to performance hungry diff --git a/src/util/color/color.h b/src/util/color/color.h index 13401e05254..e5196a409e7 100644 --- a/src/util/color/color.h +++ b/src/util/color/color.h @@ -1,11 +1,9 @@ #pragma once -#include "util/color/predefinedcolorsset.h" +#include namespace Color { -extern const PredefinedColorsSet kPredefinedColorsSet; - int brightness(int red, int green, int blue); inline int brightness(const QColor& color) { diff --git a/src/util/color/colorpalette.cpp b/src/util/color/colorpalette.cpp new file mode 100644 index 00000000000..7334763b6f1 --- /dev/null +++ b/src/util/color/colorpalette.cpp @@ -0,0 +1,18 @@ +// +// Created by Ferran Pujol Camins on 27/10/2019. +// + +#include "colorpalette.h" + +ColorPalette::ColorPalette(QList colorList) : m_colorList(colorList) { +} + +const ColorPalette ColorPalette::mixxxHotcuesPalette = + ColorPalette(QList{QColor("#c50a08"), + QColor("#32be44"), + QColor("#0044ff"), + QColor("#f8d200"), + QColor("#42d4f4"), + QColor("#af00cc"), + QColor("#fca6d7"), + QColor("#f2f2ff")}); diff --git a/src/util/color/colorpalette.h b/src/util/color/colorpalette.h new file mode 100644 index 00000000000..eb276a2fecd --- /dev/null +++ b/src/util/color/colorpalette.h @@ -0,0 +1,21 @@ +// +// Created by Ferran Pujol Camins on 27/10/2019. +// + +#pragma once + +#include +#include "QList" + +class ColorPalette { + public: + ColorPalette(QList); + + static const ColorPalette mixxxHotcuesPalette; + + QList m_colorList; +}; + +inline bool operator==(const ColorPalette& lhs, const ColorPalette& rhs) { + return lhs.m_colorList == rhs.m_colorList; +} diff --git a/src/util/color/predefinedcolor.cpp b/src/util/color/predefinedcolor.cpp deleted file mode 100644 index 2a5db56e987..00000000000 --- a/src/util/color/predefinedcolor.cpp +++ /dev/null @@ -1,10 +0,0 @@ -#include "util/color/predefinedcolor.h" - -#include "util/color/color.h" - -PredefinedColor::PredefinedColor(QColor defaultRgba, QString sName, QString sDisplayName, int iId) - : m_defaultRgba(defaultRgba), - m_sName(sName), - m_sDisplayName(sDisplayName), - m_iId(iId) { -} diff --git a/src/util/color/predefinedcolor.h b/src/util/color/predefinedcolor.h deleted file mode 100644 index 4e5745c0883..00000000000 --- a/src/util/color/predefinedcolor.h +++ /dev/null @@ -1,35 +0,0 @@ -#pragma once - -#include - -#include "util/memory.h" - -// The PredefinedColor class is used to represent a Mixxx identificable color. -// A PredefinedColor can be uniquely identified with its m_iId property. -// -// PredefinedColors have a default Rgba value. A PredefinedColorsMap can provide with an alternative -// Rgba value for each PredefinedColor. Thus, a PredefinedColorsMap defines a particular way to render -// the PredefinedColors. -class PredefinedColor final { - public: - PredefinedColor(QColor defaultRgba, QString sName, QString sDisplayName, int iId); - - inline bool operator==(const PredefinedColor& other) const { - return m_iId == other.m_iId; - } - - inline bool operator!=(const PredefinedColor& other) const { - return m_iId != other.m_iId; - } - - // The QColor that is used by default to render this PredefinedColor. - const QColor m_defaultRgba; - // The name of the color used programmatically, e.g. on skin files. - const QString m_sName; - // The name of the color used on UI. - const QString m_sDisplayName; - // An Id uniquely identifying this predefined color. - // This value is used to identify a color on the DB and control objects. - const int m_iId; -}; -typedef std::shared_ptr PredefinedColorPointer; diff --git a/src/util/color/predefinedcolorsrepresentation.h b/src/util/color/predefinedcolorsrepresentation.h deleted file mode 100644 index 83f16405447..00000000000 --- a/src/util/color/predefinedcolorsrepresentation.h +++ /dev/null @@ -1,41 +0,0 @@ -#ifndef PREDEFINEDCOLORSREPRESENTATION_H -#define PREDEFINEDCOLORSREPRESENTATION_H - -#include -#include - -#include "util/color/predefinedcolor.h" - -// PredefinedColorsRepresentation defines a particular way to render Mixxx PredefinedColors. -// -// PredefinedColorsRepresentation maps a PredefinedColor to a custom Rgba color. -// Initially no color has a custom Rgba set. -// Call setCustomRgba(PredefinedColorPointer, QColor) to add a custom Rgba for a predefined color -// and customize the color map. -// -// This class uses the color's name() property as key, e.g. "#A9A9A9" -// Since QHash has copy-on-write, making a copy of PredefinedColorsRepresentation is fast. -// A deep copy of the QHash will be made when a copy is modified. -class PredefinedColorsRepresentation final { - public: - // Set a custom Rgba for a given color - void setCustomRgba(PredefinedColorPointer color, QColor cutomizedRgba) { - m_colorNameMap[color->m_defaultRgba.name()] = cutomizedRgba.name(); - } - - // Returns the custom Rgba of a color. - // If no custom Rgba is set for color, returns color->m_defaultRgba. - QColor representationFor(PredefinedColorPointer color) const { - QColor defaultRgba = color->m_defaultRgba; - if (m_colorNameMap.contains(defaultRgba.name())) { - return QColor(m_colorNameMap[defaultRgba.name()]); - } - return defaultRgba; - } - - - private: - QHash m_colorNameMap; -}; - -#endif /* PREDEFINEDCOLORSREPRESENTATION_H */ diff --git a/src/util/color/predefinedcolorsset.h b/src/util/color/predefinedcolorsset.h deleted file mode 100644 index f2dbff29ee2..00000000000 --- a/src/util/color/predefinedcolorsset.h +++ /dev/null @@ -1,120 +0,0 @@ -#pragma once - -#include -#include - -#include "predefinedcolorsrepresentation.h" -#include "util/color/predefinedcolor.h" - -// This class defines a set of predefined colors and provides some handy functions to work with them. -// A single global instance is create in the Color namespace. -// This class is thread-safe because all its methods and public properties are const. -class PredefinedColorsSet final { - public: - const PredefinedColorPointer noColor = std::make_shared( - QColor(), - QLatin1String("No Color"), - QObject::tr("No Color"), - 0); - const PredefinedColorPointer red = std::make_shared( - QColor("#c50a08"), - QLatin1String("Red"), - QObject::tr("Red"), - 1); - const PredefinedColorPointer green = std::make_shared( - QColor("#32be44"), - QLatin1String("Green"), - QObject::tr("Green"), - 2); - const PredefinedColorPointer blue = std::make_shared( - QColor("#0044ff"), - QLatin1String("Blue"), - QObject::tr("Blue"), - 3); - const PredefinedColorPointer yellow = std::make_shared( - QColor("#f8d200"), - QLatin1String("Yellow"), - QObject::tr("Yellow"), - 4); - const PredefinedColorPointer cyan = std::make_shared( - QColor("#42d4f4"), - QLatin1String("Celeste"), - QObject::tr("Celeste"), - 5); - const PredefinedColorPointer magenta = std::make_shared( - QColor("#af00cc"), - QLatin1String("Purple"), - QObject::tr("Purple"), - 6); - const PredefinedColorPointer pink = std::make_shared( - QColor("#fca6d7"), - QLatin1String("Pink"), - QObject::tr("Pink"), - 7); - const PredefinedColorPointer white = std::make_shared( - QColor("#f2f2ff"), - QLatin1String("White"), - QObject::tr("White"), - 8); - - // The list of the predefined colors. - const QList allColors{ - noColor, - red, - green, - yellow, - blue, - cyan, - magenta, - pink, - white, - }; - - PredefinedColorsSet() - : m_defaultRepresentation() { - for (PredefinedColorPointer color : allColors) { - m_defaultRepresentation.setCustomRgba(color, color->m_defaultRgba); - } - } - - // Returns the position of a PredefinedColor in the allColors list. - int predefinedColorIndex(PredefinedColorPointer searchedColor) const { - for (int position = 0; position < allColors.count(); ++position) { - PredefinedColorPointer color(allColors.at(position)); - if (*color == *searchedColor) { - return position; - } - } - return 0; - }; - - // Return a predefined color from its name. - // Return noColor if there's no color with such name. - PredefinedColorPointer predefinedColorFromName(QString name) const { - for (PredefinedColorPointer color : allColors) { - if (color->m_sName == name) { - return color; - } - } - return noColor; - }; - - // Return a predefined color from its id. - // Return noColor if there's no color with iId. - PredefinedColorPointer predefinedColorFromId(int iId) const { - for (PredefinedColorPointer color : allColors) { - if (color->m_iId == iId) { - return color; - } - } - return noColor; - }; - - // The default color representation, i.e. maps each predefined color to its default Rgba. - PredefinedColorsRepresentation defaultRepresentation() const { - return m_defaultRepresentation; - }; - - private: - PredefinedColorsRepresentation m_defaultRepresentation; -}; diff --git a/src/waveform/renderers/waveformrendermark.cpp b/src/waveform/renderers/waveformrendermark.cpp index 1d338688e08..26a840e8974 100644 --- a/src/waveform/renderers/waveformrendermark.cpp +++ b/src/waveform/renderers/waveformrendermark.cpp @@ -27,11 +27,6 @@ WaveformRenderMark::WaveformRenderMark( void WaveformRenderMark::setup(const QDomNode& node, const SkinContext& context) { WaveformSignalColors signalColors = *m_waveformRenderer->getWaveformSignalColors(); m_marks.setup(m_waveformRenderer->getGroup(), node, context, signalColors); - WaveformMarkPointer defaultMark(m_marks.getDefaultMark()); - QColor defaultColor = defaultMark - ? defaultMark->fillColor() - : signalColors.getAxesColor(); - m_predefinedColorsRepresentation = context.getCueColorRepresentation(node, defaultColor); } void WaveformRenderMark::draw(QPainter* painter, QPaintEvent* /*event*/) { @@ -128,7 +123,7 @@ void WaveformRenderMark::slotCuesUpdated() { } QString newLabel = pCue->getLabel(); - QColor newColor = m_predefinedColorsRepresentation.representationFor(pCue->getColor()); + QColor newColor = pCue->getColor(); if (pMark->m_text.isNull() || newLabel != pMark->m_text || !pMark->fillColor().isValid() || newColor != pMark->fillColor()) { pMark->m_text = newLabel; diff --git a/src/waveform/renderers/waveformrendermark.h b/src/waveform/renderers/waveformrendermark.h index 693332c8162..dc0fad17c90 100644 --- a/src/waveform/renderers/waveformrendermark.h +++ b/src/waveform/renderers/waveformrendermark.h @@ -35,8 +35,6 @@ class WaveformRenderMark : public QObject, public WaveformRendererAbstract { private: void generateMarkImage(WaveformMarkPointer pMark); - PredefinedColorsRepresentation m_predefinedColorsRepresentation; - WaveformMarkSet m_marks; DISALLOW_COPY_AND_ASSIGN(WaveformRenderMark); }; diff --git a/src/widget/colormenu.cpp b/src/widget/colormenu.cpp index 543b2710842..1ceabc888fe 100644 --- a/src/widget/colormenu.cpp +++ b/src/widget/colormenu.cpp @@ -1,39 +1,78 @@ #include "widget/colormenu.h" + #include "util/color/color.h" -ColorMenu::ColorMenu(QWidget *parent) - : QMenu(parent) { +ColorMenu::ColorMenu(QWidget* parent) + : QMenu(parent), m_pColorDialog(new QColorDialog(this)) { // If another title would be more appropriate in some context, setTitle // can be called again after construction. setTitle(tr("Set color")); - for (const auto& pColor : Color::kPredefinedColorsSet.allColors) { - if (*pColor == *Color::kPredefinedColorsSet.noColor) { - continue; - } + useColorPalette(ColorPalette::mixxxHotcuesPalette); + + connect(m_pColorDialog, + &QColorDialog::colorSelected, + this, + [this](const QColor& newColor) { + if (newColor.isValid()) { + emit(colorPicked(newColor)); + } + }); +} + +void ColorMenu::useColorPalette(const ColorPalette& colorPalette) { + clear(); + m_pActionGroup = new QActionGroup(this); + m_pActionGroup->setExclusive(true); + + createPaletteColorsActions(colorPalette); + createColorPickerAction(); +} - QAction* pColorAction = new QAction(pColor->m_sDisplayName, this); +void ColorMenu::setCurrentColor(QColor currentColor) { + m_currentColor = currentColor; + selectCurrentColorAction(currentColor); +} + +void ColorMenu::openColorDialog() { + m_pColorDialog->setCurrentColor(m_currentColor); + m_pColorDialog->open(); +} + +void ColorMenu::createPaletteColorsActions(const ColorPalette& colorPalette) { + for (const auto& color : colorPalette.m_colorList) { + QAction* pColorAction = new QAction(m_pActionGroup); QPixmap pixmap(80, 80); - pixmap.fill(pColor->m_defaultRgba); + pixmap.fill(color); pColorAction->setIcon(QIcon(pixmap)); + pColorAction->setCheckable(true); + pColorAction->setData(color); - m_pColorActions.insert(pColor, pColorAction); addAction(pColorAction); - connect(pColorAction, &QAction::triggered, this, [pColor, this]() { - emit(colorPicked(pColor)); + connect(pColorAction, &QAction::triggered, this, [color, this]() { + emit(colorPicked(color)); }); } } -void ColorMenu::useColorSet(PredefinedColorsRepresentation* pColorRepresentation) { - QMapIterator i(m_pColorActions); - while (i.hasNext()) { - i.next(); - QPixmap pixmap(80, 80); - if (pColorRepresentation == nullptr) { - pixmap.fill(i.key()->m_defaultRgba); - } else { - pixmap.fill(pColorRepresentation->representationFor(i.key())); +void ColorMenu::createColorPickerAction() { + m_pColorPickerAction = new QAction(m_pActionGroup); + m_pColorPickerAction->setText(tr("Other") + "…"); + m_pColorPickerAction->setCheckable(true); + addAction(m_pColorPickerAction); + connect(m_pColorPickerAction, + &QAction::triggered, + this, + &ColorMenu::openColorDialog); +} + +void ColorMenu::selectCurrentColorAction(const QColor& currentColor) const { + for (QAction* pAction : actions()) { + QColor color = pAction->data().value(); + // Other color action is the last one, if we find it, we didn't + // matched to any color action. + if (color == currentColor || pAction == m_pColorPickerAction) { + pAction->setChecked(true); + return; } - i.value()->setIcon(QIcon(pixmap)); } } diff --git a/src/widget/colormenu.h b/src/widget/colormenu.h index 4c82943cbb8..e661a8996f4 100644 --- a/src/widget/colormenu.h +++ b/src/widget/colormenu.h @@ -1,19 +1,31 @@ #pragma once +#include #include -#include "util/color/color.h" +#include "util/color/colorpalette.h" class ColorMenu : public QMenu { Q_OBJECT public: ColorMenu(QWidget *parent = nullptr); - void useColorSet(PredefinedColorsRepresentation* pColorRepresentation); + void useColorPalette(const ColorPalette& colorPalette); + void setCurrentColor(QColor currentColor); signals: - void colorPicked(PredefinedColorPointer pColor); + void colorPicked(QColor pColor); + + private slots: + void openColorDialog(); private: - QMap m_pColorActions; + void createPaletteColorsActions(const ColorPalette& colorPalette); + void createColorPickerAction(); + void selectCurrentColorAction(const QColor& currentColor) const; + + QColor m_currentColor; + QAction* m_pColorPickerAction; + QActionGroup* m_pActionGroup; + QColorDialog* m_pColorDialog; }; diff --git a/src/widget/cuemenu.cpp b/src/widget/cuemenu.cpp index b9d62a30911..53d88736e8a 100644 --- a/src/widget/cuemenu.cpp +++ b/src/widget/cuemenu.cpp @@ -37,14 +37,8 @@ void CueMenu::slotEditLabel() { } } -void CueMenu::slotChangeCueColor(PredefinedColorPointer pColor) { - VERIFY_OR_DEBUG_ASSERT(m_pCue != nullptr) { - return; - } - VERIFY_OR_DEBUG_ASSERT(pColor != nullptr) { - return; - } - m_pCue->setColor(pColor); +void CueMenu::slotChangeCueColor(QColor color) { + m_pCue->setColor(color); } void CueMenu::slotRemoveCue() { diff --git a/src/widget/cuemenu.h b/src/widget/cuemenu.h index 3802292e44a..5bf1a080649 100644 --- a/src/widget/cuemenu.h +++ b/src/widget/cuemenu.h @@ -14,22 +14,23 @@ class CueMenu : public QMenu { void setCue(CuePointer pCue) { m_pCue = pCue; + m_pColorMenu->setCurrentColor(m_pCue->getColor()); } void setTrack(TrackPointer pTrack) { m_pTrack = pTrack; } - void useColorSet(PredefinedColorsRepresentation* pColorRepresentation) { + void useColorSet(const ColorPalette& colorPalette) { if (m_pColorMenu != nullptr) { - m_pColorMenu->useColorSet(pColorRepresentation); + m_pColorMenu->useColorPalette(colorPalette); } } private slots: void slotEditLabel(); void slotRemoveCue(); - void slotChangeCueColor(PredefinedColorPointer pColor); + void slotChangeCueColor(QColor color); private: CuePointer m_pCue; diff --git a/src/widget/woverview.cpp b/src/widget/woverview.cpp index 62697f2ae01..e543197fa68 100644 --- a/src/widget/woverview.cpp +++ b/src/widget/woverview.cpp @@ -25,6 +25,7 @@ #include "control/controlproxy.h" #include "engine/engine.h" #include "mixer/playermanager.h" +#include "preferences/hotcuecolorpalettesettings.h" #include "track/track.h" #include "util/color/color.h" #include "util/compatibility.h" @@ -131,12 +132,9 @@ void WOverview::setup(const QDomNode& node, const SkinContext& context) { // setup hotcues and cue and loop(s) m_marks.setup(m_group, node, context, m_signalColors); - WaveformMarkPointer defaultMark(m_marks.getDefaultMark()); - QColor defaultColor = defaultMark - ? defaultMark->fillColor() - : m_signalColors.getAxesColor(); - m_predefinedColorsRepresentation = context.getCueColorRepresentation(node, defaultColor); - m_pCueMenu->useColorSet(&m_predefinedColorsRepresentation); + HotcueColorPaletteSettings colorPaletteSettings(m_pConfig); + auto colorPalette = colorPaletteSettings.getHotcueColorPalette(); + m_pCueMenu->useColorSet(colorPalette); for (const auto& pMark: m_marks) { if (pMark->isValid()) { @@ -339,7 +337,7 @@ void WOverview::updateCues(const QList &loadedCues) { const WaveformMarkPointer pMark = m_marks.getHotCueMark(currentCue->getHotCue()); if (pMark != nullptr && pMark->isValid() && pMark->isVisible() && pMark->getSamplePosition() != Cue::kNoPosition) { - QColor newColor = m_predefinedColorsRepresentation.representationFor(currentCue->getColor()); + QColor newColor = currentCue->getColor(); if (newColor != pMark->fillColor() || newColor != pMark->m_textColor) { pMark->setBaseColor(newColor); } diff --git a/src/widget/woverview.h b/src/widget/woverview.h index d5bcc4792ac..eaf5dca108c 100644 --- a/src/widget/woverview.h +++ b/src/widget/woverview.h @@ -167,7 +167,6 @@ class WOverview : public WWidget, public TrackDropTarget { QColor m_labelBackgroundColor; QColor m_endOfTrackColor; - PredefinedColorsRepresentation m_predefinedColorsRepresentation; // All WaveformMarks WaveformMarkSet m_marks; // List of visible WaveformMarks sorted by the order they appear in the track diff --git a/src/widget/wtracktableview.cpp b/src/widget/wtracktableview.cpp index 271dd42be02..ac8ce5a9103 100644 --- a/src/widget/wtracktableview.cpp +++ b/src/widget/wtracktableview.cpp @@ -764,7 +764,7 @@ void WTrackTableView::showTrackInfo(QModelIndex index) { if (m_pTrackInfo.isNull()) { // Give a NULL parent because otherwise it inherits our style which can // make it unreadable. Bug #673411 - m_pTrackInfo.reset(new DlgTrackInfo(nullptr)); + m_pTrackInfo.reset(new DlgTrackInfo(nullptr, m_pConfig)); connect(m_pTrackInfo.data(), SIGNAL(next()), this, SLOT(slotNextTrackInfo())); diff --git a/src/widget/wwidget.cpp b/src/widget/wwidget.cpp index 364b46280a1..5d523663139 100644 --- a/src/widget/wwidget.cpp +++ b/src/widget/wwidget.cpp @@ -26,7 +26,8 @@ WWidget::WWidget(QWidget* parent, Qt::WindowFlags flags) : QWidget(parent, flags), WBaseWidget(this), m_activeTouchButton(Qt::NoButton), - m_scaleFactor(1.0) { + m_scaleFactor(1.0), + m_bShouldHighlightBackgroundOnHover(false) { m_pTouchShift = new ControlProxy("[Controls]", "touch_shift"); setAttribute(Qt::WA_StaticContents); setAttribute(Qt::WA_AcceptTouchEvents); @@ -37,6 +38,31 @@ WWidget::~WWidget() { delete m_pTouchShift; } +double WWidget::getBackgroundColorRgba() const { + if (m_backgroundColorRgba >= 0) { + return m_backgroundColorRgba; + } + return -1; +} + +void WWidget::setBackgroundColorRgba(double rgba) { + QColor backgroundColor = QColor::fromRgba(rgba); + QColor highlightedBackgroundColor = backgroundColor.lighter(); + QString style = QString("WWidget { background-color: %1; }"); + if (m_bShouldHighlightBackgroundOnHover) { + style += "WWidget:hover { background-color: %2; }"; + } + + if (rgba >= 0) { + setStyleSheet(style.arg(backgroundColor.name()) + .arg(highlightedBackgroundColor.name())); + } else { + setStyleSheet(""); + } + m_backgroundColorRgba = rgba; + m_bBackgroundIsDark = Color::isDimmColor(backgroundColor); +} + bool WWidget::touchIsRightButton() { return (m_pTouchShift->get() != 0.0); } diff --git a/src/widget/wwidget.h b/src/widget/wwidget.h index cbadf8ab2fe..0fbf10631c2 100644 --- a/src/widget/wwidget.h +++ b/src/widget/wwidget.h @@ -23,6 +23,7 @@ #include #include "preferences/usersettings.h" +#include "util/color/color.h" #include "widget/wbasewidget.h" class ControlProxy; @@ -44,6 +45,21 @@ class WWidget : public QWidget, public WBaseWidget { ~WWidget() override; Q_PROPERTY(double value READ getControlParameterDisplay); + Q_PROPERTY(double backgroundColorRgba READ getBackgroundColorRgba WRITE + setBackgroundColorRgba); + Q_PROPERTY(bool shouldHighlightBackgroundOnHover MEMBER + m_bShouldHighlightBackgroundOnHover); + Q_PROPERTY(bool hasBackgroundColor READ hasBackgroundColor); + Q_PROPERTY(bool backgroundIsDark READ backgroundIsDark); + + double getBackgroundColorRgba() const; + void setBackgroundColorRgba(double rgba); + bool hasBackgroundColor() const { + return m_backgroundColorRgba >= 0; + } + bool backgroundIsDark() const { + return m_bBackgroundIsDark; + } protected: bool touchIsRightButton(); @@ -60,6 +76,9 @@ class WWidget : public QWidget, public WBaseWidget { private: ControlProxy* m_pTouchShift; double m_scaleFactor; + double m_backgroundColorRgba; + bool m_bBackgroundIsDark; + bool m_bShouldHighlightBackgroundOnHover; }; #endif