Commit 409fc9cc authored by Evan W. Patton's avatar Evan W. Patton

Fix arrangement/sort options in Blockly context menu

The context menu items for arranging blocks and sorting by category
did not have expected behavior due to the new Blockly events
system. This commit introduces two new events StartArrangeBlocks and
EndArrangeBlocks that will manipulate the appropriate flags so the
context menu options work correctly. We also group the arrangement
events in a single group. This makes the undo/redo menu options behave
correctly rather than undoing/redoing individual moves within the
larger arrangement action.

Change-Id: Icb832eef1f91aec5a12025707e9c39b3257987af
parent 28f2899c
...@@ -58,6 +58,18 @@ AI.Events.COMPONENT_MOVE = 'component.move'; ...@@ -58,6 +58,18 @@ AI.Events.COMPONENT_MOVE = 'component.move';
*/ */
AI.Events.COMPONENT_PROPERTY_CHANGE = 'property.change'; AI.Events.COMPONENT_PROPERTY_CHANGE = 'property.change';
/**
* Type identifier used for serializing StartArrangeBlocks events.
* @type {string}
*/
AI.Events.BLOCKS_ARRANGE_START = 'blocks.arrange.start';
/**
* Type identifier used for serializing EndArrangeBlocks events.
* @type {string}
*/
AI.Events.BLOCKS_ARRANGE_END = 'blocks.arrange.end';
/** /**
* Abstract class for all App Inventor events. * Abstract class for all App Inventor events.
* @constructor * @constructor
...@@ -164,6 +176,7 @@ AI.Events.ScreenEvent.prototype.isTransient = true; ...@@ -164,6 +176,7 @@ AI.Events.ScreenEvent.prototype.isTransient = true;
*/ */
AI.Events.ScreenSwitch = function(projectId, screenName) { AI.Events.ScreenSwitch = function(projectId, screenName) {
AI.Events.ScreenSwitch.superClass_.constructor.call(this, projectId, screenName); AI.Events.ScreenSwitch.superClass_.constructor.call(this, projectId, screenName);
this.recordUndo = false;
}; };
goog.inherits(AI.Events.ScreenSwitch, AI.Events.ScreenEvent); goog.inherits(AI.Events.ScreenSwitch, AI.Events.ScreenEvent);
...@@ -289,3 +302,91 @@ AI.Events.PropertyChange = function(projectId, component, property, oldValue, ne ...@@ -289,3 +302,91 @@ AI.Events.PropertyChange = function(projectId, component, property, oldValue, ne
this.oldValue = oldValue; this.oldValue = oldValue;
this.newValue = newValue; this.newValue = newValue;
}; };
/**
* StartArrangeBlocks is an event placed at the start of an event group created during an
* arrangement operation. Its purpose is to reset the Blockly.workspace_arranged* flags during an
* undo operation so that they reflect the state immediately preceding the arrangement.
* @param workspaceId The identifier of the workspace the event occurred on
* @constructor
*/
AI.Events.StartArrangeBlocks = function(workspaceId) {
AI.Events.StartArrangeBlocks.superClass_.constructor.call(this);
this.old_arranged_type = Blockly.workspace_arranged_type;
this.old_arranged_position = Blockly.workspace_arranged_position;
this.old_arranged_latest_position = Blockly.workspace_arranged_latest_position;
this.recordUndo = Blockly.Events.recordUndo;
this.workspaceId = workspaceId;
};
goog.inherits(AI.Events.StartArrangeBlocks, Blockly.Events.Ui);
AI.Events.StartArrangeBlocks.prototype.type = AI.Events.BLOCKS_ARRANGE_START;
AI.Events.StartArrangeBlocks.prototype.toJson = function() {
var json = AI.Events.StartArrangeBlocks.superClass_.toJson.call(this);
json['old_arranged_type'] = this.old_arranged_type;
json['old_arranged_position'] = this.old_arranged_position;
json['old_arranged_latest_position'] = this.old_arranged_latest_position;
return json;
};
AI.Events.StartArrangeBlocks.prototype.fromJson = function(json) {
AI.Events.StartArrangeBlocks.superClass_.fromJson.call(this, json);
this.old_arranged_type = json['old_arranged_type'];
this.old_arranged_position = json['old_arranged_position'];
this.old_arranged_latest_position = json['old_arranged_latest_position'];
};
AI.Events.StartArrangeBlocks.prototype.run = function(forward) {
if (!forward) {
Blockly.Events.FIRE_QUEUE_.length = 0;
setTimeout(function() {
Blockly.workspace_arranged_type = this.old_arranged_type;
Blockly.workspace_arranged_position = this.old_arranged_position;
Blockly.workspace_arranged_latest_position = this.old_arranged_latest_position;
}.bind(this));
}
};
/**
* EndArrangeBlocks is an event placed at the end of an event group created during an
* arrangement operation. Its purpose is to set the Blockly.workspace_arranged* flags to the
* appropriate values since they are reset by {@Blockly.WorkspaceSvg#fireChangeListener}.
* @param type The type of arrangement (either null or Blockly.BLKS_CATEGORY)
* @param layout The layout to be applied (either Blockly.BLKS_VERTICAL or Blockly.BLKS_HORIZONTAL)
* @constructor
*/
AI.Events.EndArrangeBlocks = function(workspaceId, type, layout) {
AI.Events.EndArrangeBlocks.superClass_.constructor.call(this);
this.new_type = type;
this.new_layout = layout;
this.recordUndo = Blockly.Events.recordUndo;
this.workspaceId = workspaceId;
};
goog.inherits(AI.Events.EndArrangeBlocks, Blockly.Events.Ui);
AI.Events.EndArrangeBlocks.prototype.type = AI.Events.BLOCKS_ARRANGE_END;
AI.Events.EndArrangeBlocks.prototype.toJson = function() {
var json = AI.Events.EndArrangeBlocks.superClass_.toJson.call(this);
json['new_type'] = this.new_type;
json['new_layout'] = this.new_layout;
return json;
};
AI.Events.EndArrangeBlocks.prototype.fromJson = function(json) {
AI.Events.EndArrangeBlocks.superClass_.fromJson.call(this, json);
this.new_type = json['new_type'];
this.new_layout = json['new_layout'];
};
AI.Events.EndArrangeBlocks.prototype.run = function(forward) {
if (forward) {
Blockly.Events.FIRE_QUEUE_.length = 0;
setTimeout(function() {
Blockly.workspace_arranged_type = this.new_type;
Blockly.workspace_arranged_position = this.new_layout;
Blockly.workspace_arranged_latest_position = this.new_layout;
}.bind(this));
}
};
...@@ -540,20 +540,18 @@ Blockly.WorkspaceSvg.prototype.customContextMenu = function(menuOptions) { ...@@ -540,20 +540,18 @@ Blockly.WorkspaceSvg.prototype.customContextMenu = function(menuOptions) {
// Arrange blocks in row order. // Arrange blocks in row order.
var arrangeOptionH = {enabled: (Blockly.workspace_arranged_position !== Blockly.BLKS_HORIZONTAL)}; var arrangeOptionH = {enabled: (Blockly.workspace_arranged_position !== Blockly.BLKS_HORIZONTAL)};
arrangeOptionH.text = Blockly.Msg.ARRANGE_H; arrangeOptionH.text = Blockly.Msg.ARRANGE_H;
arrangeOptionH.callback = function() { arrangeOptionH.callback = function(opt_type) {
Blockly.workspace_arranged_position = Blockly.BLKS_HORIZONTAL; opt_type = opt_type instanceof goog.events.Event ? null : opt_type;
Blockly.workspace_arranged_latest_position= Blockly.BLKS_HORIZONTAL; arrangeBlocks(opt_type? opt_type : Blockly.workspace_arranged_type, Blockly.BLKS_HORIZONTAL);
arrangeBlocks(Blockly.BLKS_HORIZONTAL);
}; };
menuOptions.push(arrangeOptionH); menuOptions.push(arrangeOptionH);
// Arrange blocks in column order. // Arrange blocks in column order.
var arrangeOptionV = {enabled: (Blockly.workspace_arranged_position !== Blockly.BLKS_VERTICAL)}; var arrangeOptionV = {enabled: (Blockly.workspace_arranged_position !== Blockly.BLKS_VERTICAL)};
arrangeOptionV.text = Blockly.Msg.ARRANGE_V; arrangeOptionV.text = Blockly.Msg.ARRANGE_V;
arrangeOptionV.callback = function() { arrangeOptionV.callback = function(opt_type) {
Blockly.workspace_arranged_position = Blockly.BLKS_VERTICAL; opt_type = opt_type instanceof goog.events.Event ? null : opt_type;
Blockly.workspace_arranged_latest_position = Blockly.BLKS_VERTICAL; arrangeBlocks(opt_type? opt_type : Blockly.workspace_arranged_type, Blockly.BLKS_VERTICAL);
arrangeBlocks(Blockly.BLKS_VERTICAL);
}; };
menuOptions.push(arrangeOptionV); menuOptions.push(arrangeOptionV);
...@@ -590,7 +588,15 @@ Blockly.WorkspaceSvg.prototype.customContextMenu = function(menuOptions) { ...@@ -590,7 +588,15 @@ Blockly.WorkspaceSvg.prototype.customContextMenu = function(menuOptions) {
} }
// Arranges block in layout (Horizontal or Vertical). // Arranges block in layout (Horizontal or Vertical).
function arrangeBlocks(layout) { function arrangeBlocks(type, layout) {
Blockly.Events.setGroup(true); // group these movements together
// start arrangement
var workspaceId = Blockly.mainWorkspace.id;
Blockly.Events.fire(new AI.Events.StartArrangeBlocks(workspaceId));
Blockly.workspace_arranged_type = type;
Blockly.workspace_arranged_position = layout;
Blockly.workspace_arranged_latest_position = layout;
var event = new AI.Events.EndArrangeBlocks(workspaceId, type, layout);
var SPACER = 25; var SPACER = 25;
var topblocks = Blockly.mainWorkspace.getTopBlocks(/* ordered */ false); var topblocks = Blockly.mainWorkspace.getTopBlocks(/* ordered */ false);
// If the blocks are arranged by Category, sort the array // If the blocks are arranged by Category, sort the array
...@@ -647,24 +653,30 @@ Blockly.WorkspaceSvg.prototype.customContextMenu = function(menuOptions) { ...@@ -647,24 +653,30 @@ Blockly.WorkspaceSvg.prototype.customContextMenu = function(menuOptions) {
break; break;
} }
} }
Blockly.Events.fire(event); // end arrangement
Blockly.Events.setGroup(false);
setTimeout(function() {
Blockly.workspace_arranged_type = type;
Blockly.workspace_arranged_position = layout;
Blockly.workspace_arranged_latest_position = layout;
}); // need to run after all events have run
} }
// Sort by Category. // Sort by Category.
var sortOptionCat = {enabled: (Blockly.workspace_arranged_type !== Blockly.BLKS_CATEGORY)}; var sortOptionCat = {enabled: (Blockly.workspace_arranged_type !== Blockly.BLKS_CATEGORY)};
sortOptionCat.text = Blockly.Msg.SORT_C; sortOptionCat.text = Blockly.Msg.SORT_C;
sortOptionCat.callback = function() { sortOptionCat.callback = function() {
Blockly.workspace_arranged_type = Blockly.BLKS_CATEGORY; rearrangeWorkspace(Blockly.BLKS_CATEGORY);
rearrangeWorkspace();
}; };
menuOptions.push(sortOptionCat); menuOptions.push(sortOptionCat);
// Called after a sort or collapse/expand to redisplay blocks. // Called after a sort or collapse/expand to redisplay blocks.
function rearrangeWorkspace() { function rearrangeWorkspace(opt_type) {
//default arrangement position set to Horizontal if it hasn't been set yet (is null) //default arrangement position set to Horizontal if it hasn't been set yet (is null)
if (Blockly.workspace_arranged_latest_position === null || Blockly.workspace_arranged_latest_position === Blockly.BLKS_HORIZONTAL) if (Blockly.workspace_arranged_latest_position === null || Blockly.workspace_arranged_latest_position === Blockly.BLKS_HORIZONTAL)
arrangeOptionH.callback(); arrangeOptionH.callback(opt_type);
else if (Blockly.workspace_arranged_latest_position === Blockly.BLKS_VERTICAL) else if (Blockly.workspace_arranged_latest_position === Blockly.BLKS_VERTICAL)
arrangeOptionV.callback(); arrangeOptionV.callback(opt_type);
} }
// Retrieve from backpack option. // Retrieve from backpack option.
...@@ -865,3 +877,13 @@ Blockly.WorkspaceSvg.prototype.getTopWorkspace = function() { ...@@ -865,3 +877,13 @@ Blockly.WorkspaceSvg.prototype.getTopWorkspace = function() {
} }
return parent; return parent;
}; };
Blockly.WorkspaceSvg.prototype.fireChangeListener = function(event) {
Blockly.WorkspaceSvg.superClass_.fireChangeListener.call(this, event);
if (event instanceof Blockly.Events.Move) {
// Reset arrangement parameters
Blockly.workspace_arranged_latest_position = null;
Blockly.workspace_arranged_position = null;
Blockly.workspace_arranged_type = null;
}
};
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment