From b82d3a24b2487454397535c6fefda250d4ff6114 Mon Sep 17 00:00:00 2001 From: Terry Truong Date: Mon, 28 Mar 2022 01:15:53 +1100 Subject: Enable auto-mode to trigger expand/collapse-fail animations Done by sending a signal to a failed-operation's LayoutNode's Tile component via a watched property on LayoutNode. Also added code to prevent auto-mode from retrying a failed expand/collapse. Not an ideal solution, but signalling via LayoutNode is more general, and arguably cleaner, than the previous method of triggering fail animations by getting Tile to pass a callback upward as event data. --- src/App.vue | 29 +++++++++++++++++------------ src/components/Tile.vue | 28 ++++++++++++++++++---------- src/lib.ts | 4 ++++ 3 files changed, 39 insertions(+), 22 deletions(-) diff --git a/src/App.vue b/src/App.vue index 295bceb..45f7a03 100644 --- a/src/App.vue +++ b/src/App.vue @@ -120,6 +120,7 @@ export default defineComponent({ animationActive: false, autoWaitTime: 500, //ms (in auto mode, time to wait after an action ends) autoPrevAction: null as Action | null, // Used in auto-mode for reducing action cycles + autoPrevActionFail: false, // Used in auto-mode to avoid re-trying a failed expand/collapse helpOpen: false, // Options layoutOptions: {...defaultLayoutOptions}, @@ -205,20 +206,20 @@ export default defineComponent({ } }, // For tile expand/collapse events - onInnerLeafClicked({layoutNode, failCallback}: {layoutNode: LayoutNode, failCallback?: () => void}){ + onInnerLeafClicked(layoutNode: LayoutNode){ let success = tryLayout(this.activeRoot, this.layoutMap, this.tileAreaPos, this.tileAreaDims, this.layoutOptions, false, {type: 'expand', node: layoutNode}); - if (!success && failCallback != null){ - failCallback(); + if (!success){ + layoutNode.expandFailFlag = !layoutNode.expandFailFlag; // Triggers failure animation } return success; }, - onInnerHeaderClicked({layoutNode, failCallback}: {layoutNode: LayoutNode, failCallback?: () => void}){ + onInnerHeaderClicked(layoutNode: LayoutNode){ let oldChildren = layoutNode.children; let success = tryLayout(this.activeRoot, this.layoutMap, this.tileAreaPos, this.tileAreaDims, this.layoutOptions, false, {type: 'collapse', node: layoutNode}); - if (!success && failCallback != null){ - failCallback(); + if (!success){ + layoutNode.collapseFailFlag = !layoutNode.collapseFailFlag; // Triggers failure animation } return success; }, @@ -328,7 +329,7 @@ export default defineComponent({ return; } // Attempt tile-expand - let success = this.onInnerLeafClicked({layoutNode}); + let success = this.onInnerLeafClicked(layoutNode); if (success){ setTimeout(() => this.expandToTolNode(tolNode), this.componentOptions.transitionDuration); return; @@ -393,7 +394,7 @@ export default defineComponent({ } else { actionWeights = { 'move across': 1, 'move down': 2, 'move up': 1, - 'collapse': 1, 'expand to view': 1, 'expand parent bar': 1 + 'collapse': 1, 'expand to view': 0.5, 'expand parent bar': 0.5 }; // Zero weights for disallowed actions if (node == this.activeRoot || node.parent!.children.length == 1){ @@ -417,6 +418,9 @@ export default defineComponent({ if (revAction != null && revAction in actionWeights){ actionWeights[revAction as keyof typeof actionWeights] = 0; } + if (this.autoPrevActionFail){ + actionWeights[this.autoPrevAction as keyof typeof actionWeights] = 0; + } } // Choose action let actionList = Object.getOwnPropertyNames(actionWeights); @@ -427,24 +431,25 @@ export default defineComponent({ action = actionList[randWeightedChoice(weightList)!] as Action; } // Perform action + this.autoPrevActionFail = false; switch (action){ case 'move across': let siblings = node.parent!.children.filter(n => n != node); let siblingWeights = siblings.map(n => n.dCount + 1); - this.setLastFocused(siblings[randWeightedChoice(siblingWeights)]); + this.setLastFocused(siblings[randWeightedChoice(siblingWeights)!]); break; case 'move down': let childWeights = node.children.map(n => n.dCount + 1); - this.setLastFocused(node.children[randWeightedChoice(childWeights)]); + this.setLastFocused(node.children[randWeightedChoice(childWeights)!]); break; case 'move up': this.setLastFocused(node.parent!); break; case 'expand': - this.onInnerLeafClicked({layoutNode: node}); + this.autoPrevActionFail = !this.onInnerLeafClicked(node); break; case 'collapse': - this.onInnerHeaderClicked({layoutNode: node}); + this.autoPrevActionFail = !this.onInnerHeaderClicked(node); break; case 'expand to view': this.onInnerHeaderClickHeld(node); diff --git a/src/components/Tile.vue b/src/components/Tile.vue index 99994d1..df124e9 100644 --- a/src/components/Tile.vue +++ b/src/components/Tile.vue @@ -120,6 +120,20 @@ export default defineComponent({ }; } }, + collapseFailFlag(){ + return this.layoutNode.collapseFailFlag; + }, + expandFailFlag(){ + return this.layoutNode.expandFailFlag; + }, + }, + watch: { + expandFailFlag(newVal){ + this.triggerAnimation('animate-expand-shrink'); + }, + collapseFailFlag(newVal){ + this.triggerAnimation('animate-shrink-expand'); + }, }, methods: { // Leaf click handling @@ -143,10 +157,7 @@ export default defineComponent({ return; } this.prepForTransition(); - this.$emit('leaf-clicked', { - layoutNode: this.layoutNode, - failCallback: () => {this.triggerAnimation('animate-expand-shrink')} - }); + this.$emit('leaf-clicked', this.layoutNode); }, onLeafClickHold(){ if (!this.isExpandable){ @@ -174,10 +185,7 @@ export default defineComponent({ }, onHeaderClick(){ this.prepForTransition(); - this.$emit('header-clicked', { - layoutNode: this.layoutNode, - failCallback: () => {this.triggerAnimation('animate-shrink-expand')} - }); + this.$emit('header-clicked', this.layoutNode); }, onHeaderClickHold(){ this.prepForTransition(); @@ -195,10 +203,10 @@ export default defineComponent({ this.nonLeafHighlight = false; }, // Child event propagation - onInnerLeafClicked(data: {layoutNode: LayoutNode, failCallback: () => void}){ + onInnerLeafClicked(data: LayoutNode){ this.$emit('leaf-clicked', data); }, - onInnerHeaderClicked(data: {layoutNode: LayoutNode, failCallback: () => void}){ + onInnerHeaderClicked(data: LayoutNode){ this.$emit('header-clicked', data); }, onInnerLeafClickHeld(data: LayoutNode){ diff --git a/src/lib.ts b/src/lib.ts index 450eb90..a82bbd9 100644 --- a/src/lib.ts +++ b/src/lib.ts @@ -30,6 +30,8 @@ export class LayoutNode { sepSweptArea: SepSweptArea | null; hidden: boolean; hasFocus: boolean; + collapseFailFlag: boolean; // Used to trigger failure animations + expandFailFlag: boolean; // Used to trigger failure animations // Used for layout heuristics and info display dCount: number; // Number of descendant leaf nodes depth: number; // Number of ancestor nodes @@ -45,6 +47,8 @@ export class LayoutNode { this.sepSweptArea = null; this.hidden = false; this.hasFocus = false; + this.collapseFailFlag = false; + this.expandFailFlag = false; this.dCount = children.length == 0 ? 1 : arraySum(children.map(n => n.dCount)); this.depth = 0; this.empSpc = 0; -- cgit v1.2.3