From 0eb96aead852bb8ce7287afddc730f75fc5c31e7 Mon Sep 17 00:00:00 2001 From: MatCat Date: Mon, 8 Mar 2021 00:38:27 -0800 Subject: [PATCH] WIP:0.4.3 Multi-Select --- README.md | 4 ++ js/baseclasses.js | 89 +++++++++++++++++++++++++++++++------------ js/logicengine.js | 96 +++++++++++++++++++++++++++++++++++------------ js/main.js | 6 ++- 4 files changed, 147 insertions(+), 48 deletions(-) diff --git a/README.md b/README.md index 180162b..b6848eb 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,10 @@ To be decided, but at this moment this code is open source and free to use for n ## Changelog +### 0.4.3 + +* There is now multi-selection, including movement, deleting, disconnecting, etc. + ### 0.4.2 * Added Hex keypad with 4 bit output, also has 3 function keys diff --git a/js/baseclasses.js b/js/baseclasses.js index 988224c..9c9b0eb 100644 --- a/js/baseclasses.js +++ b/js/baseclasses.js @@ -80,6 +80,15 @@ class elementContainer { this.ICOutputs = 0; } + isSelected(element) { + if (this.Selected) { + for (let a = 0; a < this.Selected.length; a++) { + if (this.Selected[a] == element) return true; + } + } + return false; + } + isHigh(element,input) { let isHigh = false; for (let a = 0; a < this.Elements.length; a++) { @@ -121,15 +130,30 @@ class elementContainer { } DeleteElement(element) { - // Can pass object or Designator - for (let a = 0; a < this.Elements.length; a++) { - if ((this.Elements[a] == element) || (this.Elements[a].Designator == element)) { - this.Elements[a].Delete(); - this.Elements.splice(a,1); - return true; + // Can pass object or Designator, or array + if (Array.isArray(element)) { + let returnval = false; + for (let a = 0; a < element.length; a++) { + for (let b = 0; b < this.Elements.length; b++) { + if ((this.Elements[b] == element[a]) || (this.Elements[b].Designator == element[a])) { + this.Elements[b].Delete(); + this.Elements.splice(b, 1); + b--; + returnval = true; + } + } } + return returnval; + } else { + for (let a = 0; a < this.Elements.length; a++) { + if ((this.Elements[a] == element) || (this.Elements[a].Designator == element)) { + this.Elements[a].Delete(); + this.Elements.splice(a, 1); + return true; + } + } + return false; } - return false; } HasElement(element) { @@ -144,8 +168,17 @@ class elementContainer { Disconnect(element) { if (!element) return false; - for (let a = 0; a < this.Elements.length; a++) { - this.Elements[a].Disconnect(element); + + if (Array.isArray(element)) { + for (let a = 0; a < this.Elements.length; a++) { + for (let b = 0; b < element.length; b++) { + this.Elements[a].Disconnect(element[b]); + } + } + } else { + for (let a = 0; a < this.Elements.length; a++) { + this.Elements[a].Disconnect(element); + } } } @@ -163,7 +196,7 @@ class elementContainer { if (this.Elements[a] instanceof ICOutput) ICOuts++; if (this.Elements[a].isVisible()) { ctx.save(); - if (this.Elements[a] == this.Selected) this.Elements[a].drawBorderBox(ctx, this.Elements[a].X - 2, this.Elements[a].Y - 2, this.Elements[a].Width + 4, this.Elements[a].Height + 4, 1, "rgba(100,200,255,0.25)", "rgba(100,200,255,0.25)"); + if (this.isSelected(this.Elements[a])) this.Elements[a].drawBorderBox(ctx, this.Elements[a].X - 2, this.Elements[a].Y - 2, this.Elements[a].Width + 4, this.Elements[a].Height + 4, 1, "rgba(100,200,255,0.25)", "rgba(100,200,255,0.25)"); this.Elements[a].drawElement(this.Elements[a].X, this.Elements[a].Y, ctx); ctx.restore(); } @@ -171,8 +204,10 @@ class elementContainer { this.ICOutputs = ICOuts; if (!this.Selected) { - let PropertiesBox = document.getElementById("PropertiesBox"); - if (PropertiesBox.style.display != "none") PropertiesBox.style.display = "none"; + if (this.Selected.length == 1) { + let PropertiesBox = document.getElementById("PropertiesBox"); + if (PropertiesBox.style.display != "none") PropertiesBox.style.display = "none"; + } } if (logicEngine.Settings.TopConnections && !logicEngine.Settings.HideConnections) { @@ -184,29 +219,37 @@ class elementContainer { } + SelectWithin(x1,y1,x2,y2) { + let selectedArray = new Array(); + for (let a = 0; a < this.Elements.length; a++) { + if ((this.Elements[a].X >= x1) && ((this.Elements[a].X + this.Elements[a].Width) <= x2) && (this.Elements[a].Y >= y1) && ((this.Elements[a].Y + this.Elements[a].Height) <= y2)) selectedArray.push(this.Elements[a]); + } + this.Selected = selectedArray; + } + Select(element) { - this.Selected = element; + this.Selected = new Array(element); let PropertiesBox = document.getElementById("PropertiesBox"); let PropertiesBoxTitle = document.getElementById("PropertiesBoxTitle"); let PropertiesBoxContent = document.getElementById("PropertiesBoxContent"); - PropertiesBoxTitle.innerText = this.Selected.Designator + " Properties"; + PropertiesBoxTitle.innerText = this.Selected[0].Designator + " Properties"; let contentString = ""; - for (let a = 0; a < this.Selected.Properties.length;a++) { - contentString += "
" + this.Selected.Properties[a].Name + ""; - switch (this.Selected.Properties[a].Type) { + for (let a = 0; a < this.Selected[0].Properties.length;a++) { + contentString += "
" + this.Selected[0].Properties[a].Name + ""; + switch (this.Selected[0].Properties[a].Type) { case "int": - contentString += ""; + contentString += ""; break; case "string": - contentString += ''; + contentString += ''; break; case "color": - contentString += ''; + contentString += ''; break; case "list": - contentString += ''; + for (let b = 0; b < this.Selected[0].Properties[a].Values.length; b++) { + contentString += ''; } contentString += ''; break; diff --git a/js/logicengine.js b/js/logicengine.js index 500feee..363f2f9 100644 --- a/js/logicengine.js +++ b/js/logicengine.js @@ -75,12 +75,17 @@ class LogicEngine { let element = this.ActiveContainer.checkMouseBounds(mousePos); if (element) { this.MouseDown = true; - this.ActiveContainer.Select(element); - this.MovingElement = element; - this.MovingElementStartX = element.X; - this.MovingElementStartY = element.Y; - this.MovingElementMouseStartX = mousePos.x; - this.MovingElementMouseStartY = mousePos.y; + if (this.ActiveContainer.Selected.length <= 1) this.ActiveContainer.Select(element); + this.MovingElement = new Array(this.ActiveContainer.Selected.length); + + for (let a = 0; a < this.ActiveContainer.Selected.length; a++) { + this.MovingElement[a] = { + StartX: this.ActiveContainer.Selected[a].X, + StartY: this.ActiveContainer.Selected[a].Y + }; + this.MovingElementMouseStartX = mousePos.x; + this.MovingElementMouseStartY = mousePos.y; + } element.MouseDown(mousePos); } else { this.MouseDown = true; @@ -90,6 +95,15 @@ class LogicEngine { this.Panning.StartOffsetY = this.Panning.OffsetY; this.Panning.StartX = mousePos.x; this.Panning.StartY = mousePos.y; + } else { + + let cmPos = this.getCanvasMousePos(mousePos); + this.MultiSelectStart.InProgress = true; + this.MultiSelectStart.x = cmPos.x; + this.MultiSelectStart.y = cmPos.y; + this.ActiveContainer.Selected = false; + let PropertiesBox = document.getElementById("PropertiesBox"); + PropertiesBox.style.display = "none"; } } if (this.ActiveContainer.Selected) { @@ -102,18 +116,35 @@ class LogicEngine { Mouse_Up(evt) { let mousePos = getMousePos(this.Canvas, evt); - if (this.MovingElement) this.MovingElement.MouseUp(mousePos); + if (this.MovingElement) { + let element = this.ActiveContainer.checkMouseBounds(mousePos); + if (element) element.MouseUp(mousePos); + } + if (this.MovingElement && (this.MovingElement.X == this.MovingElementStartX) && (this.MovingElement.Y == this.MovingElementStartY)) { if ((performance.now() - this.MouseDownTime) < 3000) { // Presume this was a click - this.MovingElement.MouseClick(mousePos); + let element = this.ActiveContainer.checkMouseBounds(mousePos); + if (element) element.MouseClick(mousePos); } //console.log("Mouse Up"); } - if (!this.MovingElement && evt.which === 1) this.ActiveContainer.Selected = false; + if (!this.MovingElement && evt.which === 1) { + this.ActiveContainer.Selected = false; + let PropertiesBox = document.getElementById("PropertiesBox"); + PropertiesBox.style.display = "none"; + } this.MovingElement = false; this.MouseDown = false; + + if (this.MultiSelectStart.InProgress) { + this.MultiSelectStart.InProgress = false; + let cmStartPos = {x: this.MultiSelectStart.x, y: this.MultiSelectStart.y}; + let cmEndPos = this.getCanvasMousePos(mousePos); + this.ActiveContainer.SelectWithin(cmStartPos.x,cmStartPos.y,cmEndPos.x,cmEndPos.y); + } + if (this.ActiveContainer.Selected) { disableSelectedRCMs(false); } else { @@ -128,18 +159,26 @@ class LogicEngine { if(this.MouseDown) { //console.log('Mouse at position: ' + mousePos.x + ',' + mousePos.y); if (this.MovingElement) { + if ((performance.now() - this.MouseDownTime) > 100) { let xOffset = mousePos.x - this.MovingElementMouseStartX; let yOffset = mousePos.y - this.MovingElementMouseStartY; - let diffxOffset = this.MovingElementMouseStartX - this.MovingElementStartX; - let diffyOffset = this.MovingElementMouseStartY - this.MovingElementStartY; - let actualPosX = (this.MovingElementMouseStartX + xOffset) - diffxOffset; - let actualPosY = (this.MovingElementMouseStartY + yOffset) - diffyOffset; - if (!this.ControlPressed && this.Settings.SnapGrid) actualPosX = Math.round(actualPosX/this.Settings.GridSize)*this.Settings.GridSize; - if (!this.ControlPressed && this.Settings.SnapGrid) actualPosY = Math.round(actualPosY/this.Settings.GridSize)*this.Settings.GridSize; - this.MovingElement.X = actualPosX; - this.MovingElement.Y = actualPosY; + + for (let a = 0; a < this.ActiveContainer.Selected.length; a++) { + let diffxOffset = this.MovingElementMouseStartX - this.MovingElement[a].StartX; + let diffyOffset = this.MovingElementMouseStartY - this.MovingElement[a].StartY; + let actualPosX = (this.MovingElementMouseStartX + xOffset) - diffxOffset; + let actualPosY = (this.MovingElementMouseStartY + yOffset) - diffyOffset; + + if (!this.ControlPressed && this.Settings.SnapGrid) actualPosX = Math.round(actualPosX / this.Settings.GridSize) * this.Settings.GridSize; + if (!this.ControlPressed && this.Settings.SnapGrid) actualPosY = Math.round(actualPosY / this.Settings.GridSize) * this.Settings.GridSize; + + this.ActiveContainer.Selected[a].X = actualPosX; + this.ActiveContainer.Selected[a].Y = actualPosY; + } } + + } else { if (this.ControlPressed) { let distX = mousePos.x - this.Panning.StartX; @@ -181,6 +220,8 @@ class LogicEngine { if (this.ActiveContainer.Selected) { this.ActiveContainer.DeleteElement(this.ActiveContainer.Selected); this.ActiveContainer.Selected = false; + let PropertiesBox = document.getElementById("PropertiesBox"); + PropertiesBox.style.display = "none"; } } } @@ -200,8 +241,6 @@ class LogicEngine { this.MouseDownTime = 0; this.MovingElementContainer = false; this.MovingElement = false; - this.MovingElementStartX = 0; - this.MovingElementStartY = 0; this.MovingElementMouseStartX = 0; this.MovingElementMouseStartY = 0; this.ActiveContainer = new elementContainer(); @@ -212,20 +251,26 @@ class LogicEngine { this.Canvas.setAttribute('tabindex','0'); this.ControlPressed = false; this.Panning = {OffsetX: 0, OffsetY: 0,StartOffsetX: 0, StartOffsetY: 0, StartX: 0, StartY: 0}; + this.MultiSelectStart = { + x: 0, + y: 0, + InProgress: false + }; + } Link(input = 0) { if (this.ActiveLink) { if (this.ActiveContainer.Selected && (this.ActiveContainer.Selected != this.ActiveLink)) { - this.ActiveLink.addConnection(this.ActiveContainer,this.ActiveContainer.Selected,input,this.ActiveLink.OutputLink.Output); + this.ActiveLink.addConnection(this.ActiveContainer,this.ActiveContainer.Selected[0],input,this.ActiveLink.OutputLink.Output); this.ActiveLink = false; } else { this.ActiveLink = false; } } else { - if (this.ActiveContainer.Selected) { - this.ActiveLink = this.ActiveContainer.Selected; + if (this.ActiveContainer.Selected.length == 1) { + this.ActiveLink = this.ActiveContainer.Selected[0]; } } @@ -242,6 +287,12 @@ class LogicEngine { } let startLoop = performance.now(); this.Ctx.clearRect(0- this.Panning.OffsetX,0- this.Panning.OffsetY,this.Canvas.width,this.Canvas.height); + let ct = new CanvasTools(); + + if (this.MultiSelectStart.InProgress) { + let cmPos = this.getCanvasMousePos(this.Mouse); + ct.drawBorderBox(this.Ctx,this.MultiSelectStart.x,this.MultiSelectStart.y,cmPos.x - this.MultiSelectStart.x,cmPos.y - this.MultiSelectStart.y,0,"rgba(100,200,255,0.25)","rgba(100,200,255,0.25)"); + } this.ActiveContainer.DrawAll(this.Ctx,this.Settings); let tfm_CreateIC = document.getElementById("tfm_CreateIC"); @@ -275,7 +326,6 @@ class LogicEngine { this.Ctx.stroke(); this.Ctx.restore(); } - let ct = new CanvasTools(); let FPSOffset = 5 - this.Panning.OffsetX; if (this.Settings.ShowFPS) { ct.drawText(this.Ctx, FPSOffset, 15 - this.Panning.OffsetY, "FPS: " + this.FPS, "12px console", "#00ff00"); diff --git a/js/main.js b/js/main.js index 38034db..8612657 100644 --- a/js/main.js +++ b/js/main.js @@ -2,7 +2,7 @@ MatCat BrowserLogic Simulator */ -let Version = "0.4.2"; +let Version = "0.4.3"; let spanVersion = document.getElementById("version"); spanVersion.innerText = Version; // get the canvas and get the engine object going @@ -145,7 +145,9 @@ tfm_Delete.addEventListener('click', function(evt) { let rcm_Disconect = document.getElementById("rcm_Disconnect"); rcm_Disconect.addEventListener('click', function(evt) { - logicEngine.ActiveContainer.Selected.Disconnect(); + for (let a = 0; a < logicEngine.ActiveContainer.Selected.length;a++) { + logicEngine.ActiveContainer.Selected[a].Disconnect(); + } logicEngine.ActiveContainer.Disconnect(logicEngine.ActiveContainer.Selected); });