Making a Rectangular Selection Tool
-
Hello! I had an idea to make a rectangular or box selection tool so you can click and drag over multiple components and change their settings all at once in a HISE instrument.
The snippet below includes a panel with my box-drawing script, and two buttons.
The goal is as follows:
While clicking and dragging - set the button value to 1 if the rectangle intersects with the button. Set the button value back to 0 if the rectangle no longer intersects with the button. This is basically collision detection.
On mouse release - do nothing to the button statesMaybe the box drawing can be optimized. I am interested to hear people's thoughts and solutions!
HiseSnippet 1740.3ocsXstaaTDEd2jrArKEnBd.l1eTsoJI0tsDPJAHM2Zsn4B0oswBgJa1cr8nrdlUyNabrph3u7XwiBOB7F.myL683bgHp+gkmys4aNy4xb7ARgOMNVHsrab3jHpk8m4zcBWMbygdLtUmsrr+JmWS8Ud7AIgdxtzPXg0FSh7hioAV11y9BTP6FyYo+72+3FdgdbeZAIKq2JX9zWwFwTETOX8ehEFtiW.8P1nRR+r063K3aJBEI.nl0okUjm+IdCn64ghMiik87aGvTBYWkmhFaYO2FhfIcGJFyMx+VVL63PJtnsUWvPFx6HBCPDiTs1bHKL3frCerEXkCJbEyZbEesytr.VN8BWxWpYPJznr+vdlpva1JvqcY30pD7lBjrKAo4LP5dNc8krHUAGDO2woCWQk88.2dYnXj0Zl+3NNaJ.I3pkG4cBcGIrHWC2UZ0ZQB70Bq1DjJVDRW1Oj5IcABMe7iIaIHbghHS3DFmnFRIdIALA7KI0KnoNbY4.ZepbSuvvigaqXWkLglpNZSH7QE2zG+E4TOI4Uc1a620YqCeI46IsWsFiM2+U6+ZfQqyVYkczeLF5ETkd2eSGhuXTjfSqZzHdHnU1Ac.UsYlTtO.38fEJuQGq3stToQlWP71Wk3sQwMGWvon.WDIBhV.ulHQw3ThRPBjdiA5RS1THkLdHkS7CY9mv3CdLvdv.3GMArtbLUc.p+qMp61Og6qXBt6fEZ9glMZ1.1IVes6XjHIlRXwFKQCHiYpgoWTQdbZXyFfjt.s3kC7TdKmJ2BMa7AvPnkjz3Te6wIJkfCG4vDZLw02iSNFOL8ERRnPDs.pA5dPD9VTJWLtQSqccZFim5P.uXL5EhGJjfOjDqR52Gj.8tiYApgf6UiQv29Nbsq1FH6gT1fgpR7eolPg.LNScTFe8YTSoL6dWfcuL1GIogU3hDxX1qNydkXdjehrpl.gbMqyrWJSiaoalGWjvChIh9DnDAoOnHRsHHAuBhURpxeXpgoACn3ocWOHuSBRQcQDuXdR0hZ+4RZePtOB0pWUs5UUKiaVqVub03zAaOk8CNJ02u5pTeypphYyJElrKTXp1Qe7PlhBbGfAVlNBt4EHJo5VXhEpZrt6DjlTXjUMh7yIdP9EjO9Lh6H.TT3KlOTtZh1NXBh9x7GRikd3CM2el08fbkFPZGBELKFaH59KZIWTyeQ8ch96d+5hEk2zF+7ZP3oDf.MDSZy1109OrsY2Ho6rVwkxIdyfP6Uu.DpexWqDDHS+nmcQmd50xmgjqGCOY0q0Mr1MwMjihZdha.bPD8HxgPji+PHVfBwoXIJUhjWpZHjbxM0pA4ypRWKTUbJUm3NBJWBrigZVR58IO5wlcASKBECX9Goa3Q.+PTHz6cH9lDYYAvrlV0EfXrB5mtut36frBsKf9pTKm8qB+U0B0syN2mWz.wz7HIpYwMw8mZmhZIo5FKDEbQFG4Igtf5zz9vK5ddXnaqyZk9A2xyadN1bLss1t3Nl8Pgh9ZzSAin6sgXPupdipKfKyiGz.Kq+Edoy7BIPzC73HRjHloqGnu9PAzpRF5EEQ4wULZVSDyde1pWfYublSxYJo517tlyYIriwIF+FfN35BQD5mHGQ73Ajd4PqJHR6nLULzqLODBoVlSGWbPEkeTPpW.JyCU6NUGaq0prmppKJ0yVESlVfoGryHKUuO6B0vYE4mbA46sv069z3+MQW0UeqKwJPvVVTEj3tmPQ2m6Zd0z4MI0Y0u+T4gOzSJfRBxoxFGYQdUJ5xSFcLUtn4kT4BBuiu5vANW9vAkmcw27tyRBJ3c.W49PT7kMQiU5iUgAIlIEUfnJ8XDed5XDanKvYwfoEl2AqVXowKLM17YytYcS0s8Tz8Mc1BtzvQYRAC.vHpTwvyt8VzSg4BMC1zvYKZ7IJQDrO4Os1x9SL64cyF6AK6n2RGGndh0YEyMt25SJu.JvHFmOOhk8W3.ElHEDzOYIWg+52W27fjBSLYcqR3XtqyuUfj+LrLRBpXl4uNW3kXl0iVuJfs9sK.30V25hyKBSpJBfA2UUGeEmYOkAD1VYlQbtPNTRXR4Y5+eal1aJDumyAL3EuSGiyLELhQ4eDvX5+Dvcc1teenYeA.myYmi93L1ukYluAvamkLHdvYujQcgm95SgcmCw+XYC6YvLSy5V3ZzCzkxCzK9G3SJy13Z6TlsyXZMxyWJduuI0F+uF9TME.Sb8eyRCmcw0j7jZGmVK2xZDKf8dee73uDTyY557jagNO8VnyytE57M2BcV4Vny2dKz46tRcv+wommnDiLoC.gC1VWb01datGDYoiBs9WvRwf6t
Here is the code. I now see things I would change immediately, like where I am setting the vars.
Content.makeFrontInterface(600, 600); Console.clear(); // Do not run in the audio thread Synth.deferCallbacks(true); // Constants const var LINEWIDTH = 1; const var LINECOLOR = 0x66FFFFFF; // Get the UI components const var pnl = Content.getComponent("pnl"); const var btn0 = Content.getComponent("btn0"); const var btn1 = Content.getComponent("btn1"); // Create a paint routine to draw a rectangle when clicking/dragging pnl.setPaintRoutine(function(g) { // if the mouse is clicked within the panel if (this.data.clicked) { // reset the button values (can be a for loop) btn0.setValue(0); btn1.setValue(0); // Create vars to shorten stuff var width = this.getWidth(); var height = this.getHeight(); var initX = this.data.initX; var initY = this.data.initY; var Xrel = this.data.Xrel; var Yrel = this.data.Yrel; var Xcur = this.data.Xcur; var Ycur = this.data.Ycur; // Set the bounds of how far the rectangle can stretch var edgeX = Math.range(Xrel,LINEWIDTH,width-initX); var edgeY = Math.range(Yrel,LINEWIDTH,height-initY); var negEdgeX = Math.range(Xcur,LINEWIDTH,width); var negEdgeY = Math.range(Ycur,LINEWIDTH,height); // Make the rectangle white g.setColour(LINECOLOR); // Draw the selection rectangle; // Quadrant 4 (mathematically); if (Xcur > initX && Ycur > initY) { g.drawRect([initX,initY,edgeX,edgeY], LINEWIDTH); } // Quadrant 3 else if (Xcur < initX && Ycur > initY) { g.drawRect([negEdgeX,initY,initX-negEdgeX,edgeY], LINEWIDTH); } // Quadrant 1; else if (Xcur > initX && Ycur < initY) { g.drawRect([initX,negEdgeY,edgeX,initY-negEdgeY], LINEWIDTH); } // Quadrant 2; else if (Xcur < initX && Ycur < initY) { g.drawRect([negEdgeX,negEdgeY,initX-negEdgeX,initY-negEdgeY], LINEWIDTH); } /* The challenge to turn the buttons on when dragging the rectangle over them begins here! */ var logicX = 1; // placeholder var logicY = 0; // placeholder if (!btn0.getValue() && logicX && logicY) { btn0.setValue(1); } } // if mouse up else if (!this.data.clicked) { // Make the panel transparent g.fillAll(0x00000000); } }); pnl.setMouseCallback(function(event) { if (event.clicked) { this.data.clicked = 1; // set the initial cursor position when the click happens this.data.initX = event.x; this.data.initY = event.y; this.repaint(); } if (event.drag) { // get current X and Y positions this.data.Xcur = event.x; this.data.Ycur = event.y; // get new position of the mouse cursor relative to // the initial position when clicked this.data.Xrel = (event.x - this.data.initX); this.data.Yrel = (event.y - this.data.initY); this.repaint(); } if (event.mouseUp) { this.data.clicked = 0; this.repaint(); } });
-
@ericchesek The snippet isn't working here...
A simpler way to do it is using a path and the
getIntersection()
method I added not long ago.
Note that since you said "intersect with the button", this is only edge detection, meaning that if the buttons are inside the rectangle they will no longer intersect the path, so another method has to be used.HiseSnippet 1422.3ocsW0saaaCElJIpc1acXEaWsq3xECxXdN1ccYCKqXdwNo0X4Gi5zeFFJ5nkns4BMo.EURLJxiydO1izdC1NjTxRN0IovnUWHHd9iemCOmCOpuRFRSRjJjWkSlESQdeh+fYB8jNSHLApWWj2m6+TZnlHFmxIpATNr.s6rXRRBMB44s9iMB5UYCj84e+kcIbhHjVPBgdtjEROfMkoKn1u8uw378IQzSXSKI8Ca2KTJ5H4xT.Tq62DESBOkLldDwH1Z9Hu6rWDSKUCzDMMA4swtxnYClHOW3j+4rD1PN0rnEZ.XHG48k7HChMTQclv3Q8yc9DDXk9Egh0cghuv+PVDaN8hPxmYYfKznb7vasEg25K.uVkgWyRvaIPxqDj1vAo66OHTwh0EbL34i86IzT0HBD1KCEmrn096652QBRHzMlRNktuBVLWifsa1rNFdUampfTIRNsQHmRTA.gpasEtqDKjZrJUfYBrdBESRiXR3KEkDU0ltzHhNhp5P37gvoURfVkRyT2XSH8QmTMz7E9LhBePui16E85dxSvOB2ZmqvnywGb7SAFMuX6s2293LzioZ6t+rd3P4zXoftnQiEbPqbGcLU2IWpfMAdaVq7FMTKR.o+ikKNvs4l0pWsB7fwWqLs1r1q.rAFuQDQSZDWZ+Cgfil1mnmjGG6XofI3XHOBhmxTMSPwZINRQNGnqb0YbJ97ITANjyBOkIFuEvd7X3C6FkP08M5+Tm5AiREgZlTDLtV02TsxXi.tpmf4AS..UpVgMBGnmvRbXkk3pkA6VC7ywMLfvB2BYhqiKsv3+6JSEQIAsZzrV8hSQv9WZbxp4H7PYZBMOan.hzyfHiElFrXW0v5kzHCFJ1KHgQo6KMmPu4hexI3E0wyx9b1kycHGASDBrvaVvHk7PvNlLxcVfeXpRcK6QkETHtntXQxV3dD87AoCuRDL2QLVdITm8VlhCmomHCdKT9d1.4DeGze4tv0afPtLglGILbubwypolbimEW6FNqFQ3IzaOmMMFnR2MUqgxZ2oRUmMUTaMVPQhISXbLbdpH9J5ZxIGIU3fgldbllCYoSPc6ekB8LHPyuoDnJjnlgslJqW3TVTDTwJGYWMzZPnRNVQS.u03OLcBNVlvL6KXQtLjvyD7.vPCLwWSqnglBrGykCI79Yh+x.nLaIz+8fZ3uww3IT13Ifmt0Cd0NKw56Ihv3q254V4ErHyo0JsYrD6cIIYGelZsEaZTvF5AbEGu9hXst6rO6rrxPSyjmS3ozfqrKe0iJI4vFgSfFmznr7sKqdCOySAjhijZ5wB2oOnD9prFMZo7Ls3URNmpVJay7LpaRw.Q5zgTUc3hHvylKHbI+hSN3e8SNTdvlP2MNkDTJ5IX5iiohqabGT10TvTFqkgJPTscFiOMaFCWwAhAiRbGeyMhHKdKOXG5cU2VKQ2m0qKjhXlyICL..ioJMy36dcomACM5l5oheWZxoZYLrOyuAF4cW2ddu7YhHBJ2tk99v8PnKJuayJu.tXRd97gUPdeoeGysPI0wOQdFUg+Zb2rqbQmaJL.K58x0PnI1z+7UnRPYiaKzU.l+g2t.LsosKal6baQwqyLwsy.ZtS9msyAaNketM5smmDljUFAC1qWb7VyL8YLfL2ElozTEJfFCyJOy+6sYdeWg3886yzgSVNFWaIXzjn+A.iY+ov872azHnyTA.2ve+W9g42BPtI+FeHQqXP9f+QoSG.i6ERgcW.k.lNGd17S25ll0lHv.pHxt3+fmLlsLq8xX1JmIZJITIecnq517uHejkBfIg82vp3enYMddcsueyFMQvkgrWGFZb+uEZ6rbcdvJny2sB57vUPmueEzY6UPmeXEz4GuQcL+Q5ulpkSckC.g96Y6u54smf.YV1rPz+Silsop
-
@ericchesek I call b.changed() in the mouseCB making it fire at each drag movement which is bad. The button callback should be called when mouse is released, or using a check to see if the button state changed so the CB doesn't trigger constantly...
pnl.setMouseCallback(function(event) { if (event.clicked) this.data.startPos = {x:event.x, y:event.y}; if (event.drag) { this.data.isSelecting = true; this.data.currPos = {x:event.x, y:event.y}; this.data.p.clear(); this.data.p.startNewSubPath(this.data.startPos.x, this.data.startPos.y); this.data.p.lineTo(this.data.currPos.x, this.data.startPos.y); this.data.p.lineTo(this.data.currPos.x, this.data.currPos.y); this.data.p.lineTo(this.data.startPos.x, this.data.currPos.y); this.data.p.closeSubPath(); } if (this.data.isSelecting) updateButtons(event.mouseUp); if (event.mouseUp) this.data.isSelecting = false; this.repaint(); }); inline function updateButtons(isRelease) { for (b in btns) { // just an imaginary line in the middle of the button representing its position local buttonLineStart = [b.getGlobalPositionX(), b.getGlobalPositionY() + b.getHeight()/2]; local buttonLineEnd = [b.getGlobalPositionX() + b.getWidth(), b.getGlobalPositionY() + b.getHeight()/2]; local isIntersecting = this.data.p.getIntersection(buttonLineStart, buttonLineEnd, true); b.setValue(isIntersecting != false); if (isRelease) b.changed(); } }