Skip to content

Commit

Permalink
Add row dragging via ctrl+click on wire input indicators
Browse files Browse the repository at this point in the history
  • Loading branch information
Strilanc committed Mar 24, 2019
1 parent 58f5719 commit 7b1b065
Show file tree
Hide file tree
Showing 4 changed files with 149 additions and 21 deletions.
4 changes: 2 additions & 2 deletions src/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ let clickDownGateButtonKey = undefined;
canvasDiv.addEventListener('click', ev => {
let pt = eventPosRelativeTo(ev, canvasDiv);
let curInspector = displayed.get();
if (curInspector.isHandOverButtonKey() !== clickDownGateButtonKey) {
if (curInspector.tryGetHandOverButtonKey() !== clickDownGateButtonKey) {
return;
}
let clicked = syncArea(curInspector.withHand(curInspector.hand.withPos(pt))).tryClick();
Expand All @@ -182,7 +182,7 @@ watchDrags(canvasDiv,
let oldInspector = displayed.get();
let newHand = oldInspector.hand.withPos(pt);
let newInspector = syncArea(oldInspector.withHand(newHand));
clickDownGateButtonKey = newInspector.isHandOverButtonKey();
clickDownGateButtonKey = ev.ctrlKey ? undefined : newInspector.tryGetHandOverButtonKey();
if (clickDownGateButtonKey !== undefined) {
displayed.set(newInspector);
return;
Expand Down
114 changes: 110 additions & 4 deletions src/ui/DisplayedCircuit.js
Original file line number Diff line number Diff line change
Expand Up @@ -354,6 +354,8 @@ class DisplayedCircuit {
this._drawOutputDisplays(painter, stats, hand);
this._drawHintLabels(painter, stats);
}

this._drawRowDragHighlight(painter);
}

/**
Expand Down Expand Up @@ -606,14 +608,31 @@ class DisplayedCircuit {

_drawColumnDragHighlight(painter, col) {
if (this._highlightedSlot !== undefined &&
this._highlightedSlot.col === col &&
this._highlightedSlot.row === undefined) {
this._highlightedSlot.col === col &&
this._highlightedSlot.row === undefined) {
let rect = this.gateRect(0, col, 1, this._groundedWireCount()).paddedBy(3);
painter.fillRect(rect, 'rgba(255, 196, 112, 0.7)');
painter.strokeRect(rect, 'black');
}
}

/**
* @param {!Painter} painter
* @private
*/
_drawRowDragHighlight(painter) {
if (this._highlightedSlot !== undefined &&
this._highlightedSlot.col === undefined &&
this._highlightedSlot.row !== undefined) {

let row = this._highlightedSlot.row;
let w = this.gateRect(row, this.clampedCircuitColCount() + 1).x;
let rect = this.wireRect(row).takeLeft(w);
painter.fillRect(rect, 'rgba(255, 196, 112, 0.7)');
painter.strokeRect(rect, 'black');
}
}

/**
* @param {!Painter} painter
* @param {!int} columnIndex
Expand Down Expand Up @@ -649,11 +668,44 @@ class DisplayedCircuit {
* @returns {!DisplayedCircuit}
*/
previewDrop(hand) {
return hand.heldColumn !== undefined ? this._previewDropMovedGateColumn(hand) :
return hand.heldRow !== undefined ? this._previewDropMovedRow(hand) :
hand.heldColumn !== undefined ? this._previewDropMovedGateColumn(hand) :
hand.heldGate !== undefined ? this._previewDropMovedGate(hand) :
this._previewResizedGate(hand);
}

/**
* @param {!Hand} hand
* @returns {!DisplayedCircuit}
* @private
*/
_previewDropMovedRow(hand) {
if (hand.pos === undefined) {
return this;
}
let handWire = this.wireIndexAt(hand.pos.y);
if (handWire < 0 || handWire >= this.circuitDefinition.numWires) {
// Dragged the row out of the circuit.
return this;
}

let heldRowHeight = seq(hand.heldRow.gates).map(g => g === undefined ? 1 : g.height).max(1);
handWire = Math.min(handWire, this.circuitDefinition.numWires - heldRowHeight);

let newCols = [];
for (let c = 0; c < this.circuitDefinition.columns.length; c++) {
let gates = [...this.circuitDefinition.columns[c].gates];
gates.splice(handWire, 0, hand.heldRow.gates[c]);
gates.pop();
newCols.push(new GateColumn(gates));
}

let newCircuitDef = this.circuitDefinition.withColumns(newCols);

return this.withCircuit(newCircuitDef).
_withHighlightedSlot({row: handWire, col: undefined, resizeStyle: false});
}

/**
* @param {!Hand} hand
* @returns {!DisplayedCircuit}
Expand Down Expand Up @@ -840,7 +892,7 @@ class DisplayedCircuit {
}

/**
* @param {undefined|!{col: !int, row: undefined|!int, resizeStyle: !boolean}} slot
* @param {undefined|!{col: undefined|!int, row: undefined|!int, resizeStyle: !boolean}} slot
* @returns {!DisplayedCircuit}
* @private
*/
Expand Down Expand Up @@ -1017,6 +1069,10 @@ class DisplayedCircuit {
*/
tryGrab(hand, duplicate=false, wholeColumn=false, ignoreResizeTabs=false) {
if (wholeColumn) {
let grabRowResult = this._tryGrabRow(hand);
if (grabRowResult !== undefined) {
return grabRowResult;
}
return this._tryGrabWholeColumn(hand, duplicate) || {newCircuit: this, newHand: hand};
}

Expand All @@ -1033,6 +1089,56 @@ class DisplayedCircuit {
return newCircuit._tryGrabGate(newHand, duplicate) || {newCircuit, newHand};
}

/**
* @param {!Hand} hand
* @returns {undefined|!{newCircuit: !DisplayedCircuit, newHand: !Hand}}
*/
_tryGrabRow(hand) {
if (hand.pos === undefined) {
return undefined;
}

// Which wire is it? Is it one that's actually in the circuit?
let wire = this.wireIndexAt(hand.pos.y);
if (wire < 0 || wire >= this.circuitDefinition.numWires) {
return undefined;
}

// Is it inside the intended click area, instead of just off to the side?
let r = this._wireInitialStateClickableRect(wire);
if (!r.containsPoint(hand.pos)) {
return undefined;
}

let {new_circuit, row_gates} = this._cutRow(wire);
let holdOffset = new Point(0, hand.pos.y - r.y);
return {
newCircuit: this.withCircuit(new_circuit),
newHand: hand.withHeldRow(row_gates, holdOffset)
};
}

/**
* @param {!int} row
* @returns {!{new_circuit: !CircuitDefinition, row_gates: !GateColumn}}
* @private
*/
_cutRow(row) {
let row_gates = [];
let cols = [];
for (let i = 0; i < this.circuitDefinition.columns.length; i++) {
let col_gates = [...this.circuitDefinition.columns[i].gates];
row_gates.push(col_gates[row]);
col_gates.splice(row, 1);
col_gates.push(undefined);
cols.push(new GateColumn(col_gates));
}
return {
new_circuit: this.circuitDefinition.withColumns(cols),
row_gates: new GateColumn(row_gates)
};
}

/**
* @param {!Hand} hand
* @param {!boolean=} duplicate
Expand Down
2 changes: 1 addition & 1 deletion src/ui/DisplayedInspector.js
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ class DisplayedInspector {
/**
* @returns {undefined|!string}
*/
isHandOverButtonKey() {
tryGetHandOverButtonKey() {
if (this.hand.pos === undefined) {
return undefined;
}
Expand Down
50 changes: 36 additions & 14 deletions src/ui/Hand.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,26 +25,31 @@ class Hand {
* @param {undefined|!Gate} heldGate
* @param {undefined|!Point} holdOffset
* @param {undefined|!GateColumn} heldColumn
* @param {undefined|!GateColumn} heldRow
* @param {undefined|!Point} resizingGateSlot
*/
constructor(pos, heldGate, holdOffset, heldColumn, resizingGateSlot) {
constructor(pos, heldGate, holdOffset, heldColumn, heldRow, resizingGateSlot) {
let args = {pos, heldGate, holdOffset, heldColumn, heldRow, resizingGateSlot};
if (pos !== undefined && !(pos instanceof Point)) {
throw new DetailedError("Bad pos", {pos, heldGate, holdOffset, heldColumn, resizingGateSlot});
throw new DetailedError("Bad pos", args);
}
if (heldGate !== undefined && !(heldGate instanceof Gate)) {
throw new DetailedError("Bad heldGate", {pos, heldGate, holdOffset, heldColumn, resizingGateSlot});
throw new DetailedError("Bad heldGate", args);
}
if (holdOffset !== undefined && !(holdOffset instanceof Point)) {
throw new DetailedError("Bad holdOffset", {pos, heldGate, holdOffset, heldColumn, resizingGateSlot});
throw new DetailedError("Bad holdOffset", args);
}
if (resizingGateSlot !== undefined && !(resizingGateSlot instanceof Point)) {
throw new DetailedError("Bad resizingGateSlot", {pos, heldGate, holdOffset, heldColumn, resizingGateSlot});
throw new DetailedError("Bad resizingGateSlot", args);
}
if (heldColumn !== undefined && !(heldColumn instanceof GateColumn)) {
throw new DetailedError("Bad heldColumn", {pos, heldGate, holdOffset, heldColumn, resizingGateSlot});
throw new DetailedError("Bad heldColumn", args);
}
if (heldRow !== undefined && !(heldRow instanceof GateColumn)) {
throw new DetailedError("Bad heldRow", args);
}
if (heldGate !== undefined && this.resizingGateSlot !== undefined) {
throw new DetailedError("Holding AND resizing", {pos, heldGate, holdOffset, heldColumn, resizingGateSlot});
throw new DetailedError("Holding AND resizing", args);
}

/** @type {undefined|!Point} */
Expand All @@ -55,6 +60,8 @@ class Hand {
this.holdOffset = holdOffset;
/** @type {undefined|!GateColumn} */
this.heldColumn = heldColumn;
/** @type {undefined|!GateColumn} */
this.heldRow = heldRow;
/** @type {undefined|!Point} */
this.resizingGateSlot = resizingGateSlot;
}
Expand All @@ -74,7 +81,10 @@ class Hand {
* @returns {!boolean}
*/
isBusy() {
return this.heldGate !== undefined || this.heldColumn !== undefined || this.resizingGateSlot !== undefined;
return (this.heldGate !== undefined ||
this.heldColumn !== undefined ||
this.resizingGateSlot !== undefined ||
this.heldRow !== undefined);
}

/**
Expand All @@ -97,6 +107,7 @@ class Hand {
Util.CUSTOM_IS_EQUAL_TO_EQUALITY(this.holdOffset, other.holdOffset) &&
Util.CUSTOM_IS_EQUAL_TO_EQUALITY(this.heldGate, other.heldGate) &&
Util.CUSTOM_IS_EQUAL_TO_EQUALITY(this.heldColumn, other.heldColumn) &&
Util.CUSTOM_IS_EQUAL_TO_EQUALITY(this.heldRow, other.heldRow) &&
Util.CUSTOM_IS_EQUAL_TO_EQUALITY(this.resizingGateSlot, other.resizingGateSlot);
}

Expand All @@ -109,6 +120,7 @@ class Hand {
heldGate: this.heldGate,
holdOffset: this.holdOffset,
heldColumn: this.heldColumn,
heldRow: this.heldRow,
resizingGateSlot: this.resizingGateSlot
})}`;
}
Expand All @@ -118,14 +130,14 @@ class Hand {
* @returns {!Hand}
*/
withPos(newPos) {
return new Hand(newPos, this.heldGate, this.holdOffset, this.heldColumn, this.resizingGateSlot);
return new Hand(newPos, this.heldGate, this.holdOffset, this.heldColumn, this.heldRow, this.resizingGateSlot);
}

/**
* @returns {!Hand}
*/
withDrop() {
return new Hand(this.pos, undefined, undefined, undefined, undefined);
return new Hand(this.pos, undefined, undefined, undefined, undefined, undefined);
}

/**
Expand All @@ -134,7 +146,7 @@ class Hand {
* @returns {!Hand}
*/
withHeldGate(heldGate, heldGateOffset) {
return new Hand(this.pos, heldGate, heldGateOffset, undefined, undefined);
return new Hand(this.pos, heldGate, heldGateOffset, undefined, undefined, undefined);
}

/**
Expand All @@ -143,7 +155,16 @@ class Hand {
* @returns {!Hand}
*/
withHeldGateColumn(heldGateColumn, heldGateOffset) {
return new Hand(this.pos, undefined, heldGateOffset, heldGateColumn, undefined);
return new Hand(this.pos, undefined, heldGateOffset, heldGateColumn, undefined, undefined);
}

/**
* @param {!GateColumn} heldRow
* @param {!Point} heldGateOffset
* @returns {!Hand}
*/
withHeldRow(heldRow, heldGateOffset) {
return new Hand(this.pos, undefined, heldGateOffset, undefined, heldRow, undefined);
}

/**
Expand All @@ -152,7 +173,7 @@ class Hand {
* @returns {!Hand}
*/
withResizeSlot(resizeSlot, resizeTabOffset) {
return new Hand(this.pos, undefined, resizeTabOffset, undefined, resizeSlot);
return new Hand(this.pos, undefined, resizeTabOffset, undefined, undefined, resizeSlot);
}

/**
Expand All @@ -161,10 +182,11 @@ class Hand {
stableDuration() {
return this.heldGate !== undefined ? this.heldGate.stableDuration() :
this.heldColumn !== undefined ? this.heldColumn.stableDuration() :
this.heldRow !== undefined ? this.heldRow.stableDuration() :
Infinity;
}
}

Hand.EMPTY = new Hand(undefined, undefined, undefined, undefined, undefined);
Hand.EMPTY = new Hand(undefined, undefined, undefined, undefined, undefined, undefined);

export {Hand}

0 comments on commit 7b1b065

Please sign in to comment.