From ec07a36e4434825608f377065d75bd1409636871 Mon Sep 17 00:00:00 2001 From: alschmiedt Date: Wed, 11 Sep 2019 18:16:05 -0700 Subject: [PATCH] Update cursor api (#3002) * Move over to new implementation for cursor --- blockly_compressed.js | 132 ++++++------- blockly_uncompressed.js | 10 +- core/flyout_base.js | 4 + core/gesture.js | 2 +- core/inject.js | 3 - core/keyboard_nav/cursor.js | 56 +++--- core/keyboard_nav/cursor_svg.js | 149 ++++++-------- core/keyboard_nav/flyout_cursor.js | 103 ++++++++++ core/keyboard_nav/marker_cursor.js | 79 ++++++++ core/keyboard_nav/navigation.js | 269 ++++++++++---------------- core/workspace.js | 53 +++-- core/workspace_svg.js | 45 +++-- tests/mocha/cursor_test.js | 3 +- tests/mocha/navigation_modify_test.js | 94 ++++----- tests/mocha/navigation_test.js | 103 +++++----- 15 files changed, 618 insertions(+), 487 deletions(-) create mode 100644 core/keyboard_nav/flyout_cursor.js create mode 100644 core/keyboard_nav/marker_cursor.js diff --git a/blockly_compressed.js b/blockly_compressed.js index 8329090b7..c2a26b681 100644 --- a/blockly_compressed.js +++ b/blockly_compressed.js @@ -44,9 +44,9 @@ Blockly.utils.dom.createSvgElement=function(a,b,c){a=document.createElementNS(Bl Blockly.utils.dom.removeClass=function(a,b){var c=a.getAttribute("class");if(-1==(" "+c+" ").indexOf(" "+b+" "))return!1;c=c.split(/\s+/);for(var d=0;dc)){var d=b.getSvgXY(a.getSvgRoot());a.outputConnection?(d.x+=(a.RTL?3:-3)*c,d.y+=13*c):a.previousConnection&&(d.x+=(a.RTL?-23:23)*c,d.y+=3*c);a=Blockly.utils.dom.createSvgElement("circle",{cx:d.x,cy:d.y,r:0,fill:"none",stroke:"#888","stroke-width":10},b.getParentSvg());Blockly.blockAnimations.connectionUiStep_(a,new Date,c)}}; Blockly.blockAnimations.connectionUiStep_=function(a,b,c){var d=(new Date-b)/150;1=this.remainingCapacity()||(this.currentGesture_&&this.currentGesture_.cancel(),"comment"==a.tagName.toLowerCase()?this.pasteWorkspaceComment_(a):this.pasteBlock_(a))}; -Blockly.WorkspaceSvg.prototype.pasteBlock_=function(a){Blockly.Events.disable();try{var b=Blockly.Xml.domToBlock(a,this),c=Blockly.navigation.marker_.getCurNode();if(Blockly.keyboardAccessibilityMode&&c){Blockly.navigation.insertBlock(b,c.getLocation());return}var d=parseInt(a.getAttribute("x"),10),e=parseInt(a.getAttribute("y"),10);if(!isNaN(d)&&!isNaN(e)){this.RTL&&(d=-d);do{a=!1;var f=this.getAllBlocks(!1);c=0;for(var g;g=f[c];c++){var h=g.getRelativeToSurfaceXY();if(1>=Math.abs(d-h.x)&&1>=Math.abs(e- -h.y)){a=!0;break}}if(!a){var k=b.getConnections_(!1);c=0;for(var l;l=k[c];c++)if(l.closest(Blockly.SNAP_RADIUS,new Blockly.utils.Coordinate(d,e)).connection){a=!0;break}}a&&(d=this.RTL?d-Blockly.SNAP_RADIUS:d+Blockly.SNAP_RADIUS,e+=2*Blockly.SNAP_RADIUS)}while(a);b.moveBy(d,e)}}finally{Blockly.Events.enable()}Blockly.Events.isEnabled()&&!b.isShadow()&&Blockly.Events.fire(new Blockly.Events.BlockCreate(b));b.select()}; +Blockly.WorkspaceSvg.prototype.pasteBlock_=function(a){Blockly.Events.disable();try{var b=Blockly.Xml.domToBlock(a,this),c=this.getMarker().getCurNode();if(Blockly.keyboardAccessibilityMode&&c){Blockly.navigation.insertBlock(b,c.getLocation());return}var d=parseInt(a.getAttribute("x"),10),e=parseInt(a.getAttribute("y"),10);if(!isNaN(d)&&!isNaN(e)){this.RTL&&(d=-d);do{a=!1;var f=this.getAllBlocks(!1);c=0;for(var g;g=f[c];c++){var h=g.getRelativeToSurfaceXY();if(1>=Math.abs(d-h.x)&&1>=Math.abs(e-h.y)){a= +!0;break}}if(!a){var k=b.getConnections_(!1);c=0;for(var l;l=k[c];c++)if(l.closest(Blockly.SNAP_RADIUS,new Blockly.utils.Coordinate(d,e)).connection){a=!0;break}}a&&(d=this.RTL?d-Blockly.SNAP_RADIUS:d+Blockly.SNAP_RADIUS,e+=2*Blockly.SNAP_RADIUS)}while(a);b.moveBy(d,e)}}finally{Blockly.Events.enable()}Blockly.Events.isEnabled()&&!b.isShadow()&&Blockly.Events.fire(new Blockly.Events.BlockCreate(b));b.select()}; Blockly.WorkspaceSvg.prototype.pasteWorkspaceComment_=function(a){Blockly.Events.disable();try{var b=Blockly.WorkspaceCommentSvg.fromXml(a,this),c=parseInt(a.getAttribute("x"),10),d=parseInt(a.getAttribute("y"),10);isNaN(c)||isNaN(d)||(this.RTL&&(c=-c),b.moveBy(c+50,d+50))}finally{Blockly.Events.enable()}Blockly.Events.isEnabled();b.select()}; Blockly.WorkspaceSvg.prototype.refreshToolboxSelection=function(){var a=this.isFlyout?this.targetWorkspace:this;a&&!a.currentGesture_&&a.toolbox_&&a.toolbox_.flyout_&&a.toolbox_.refreshSelection()};Blockly.WorkspaceSvg.prototype.renameVariableById=function(a,b){Blockly.WorkspaceSvg.superClass_.renameVariableById.call(this,a,b);this.refreshToolboxSelection()};Blockly.WorkspaceSvg.prototype.deleteVariableById=function(a){Blockly.WorkspaceSvg.superClass_.deleteVariableById.call(this,a);this.refreshToolboxSelection()}; Blockly.WorkspaceSvg.prototype.createVariable=function(a,b,c){a=Blockly.WorkspaceSvg.superClass_.createVariable.call(this,a,b,c);this.refreshToolboxSelection();return a};Blockly.WorkspaceSvg.prototype.recordDeleteAreas=function(){this.deleteAreaTrash_=this.trashcan&&this.svgGroup_.parentNode?this.trashcan.getClientRect():null;this.deleteAreaToolbox_=this.flyout_?this.flyout_.getClientRect():this.toolbox_?this.toolbox_.getClientRect():null}; @@ -1049,48 +1049,47 @@ case Blockly.ASTNode.types.INPUT:return Blockly.ASTNode.createBlockNode(this.loc Blockly.user.keyMap.getActionByKeyCode=function(a){return Blockly.user.keyMap.map_[a]};Blockly.user.keyMap.getKeyByAction=function(a){for(var b=Object.keys(Blockly.user.keyMap.map_),c=0,d;d=b[c];c++)if(Blockly.user.keyMap.map_[d].name===a.name)return d;return null};Blockly.user.keyMap.serializeKeyEvent=function(a){for(var b=Blockly.utils.object.values(Blockly.user.keyMap.modifierKeys),c="",d=0,e;e=b[d];d++)a.getModifierState(e)&&(c+=e);return c+=a.keyCode}; Blockly.user.keyMap.createSerializedKey=function(a,b){for(var c="",d=Blockly.utils.object.values(Blockly.user.keyMap.modifierKeys),e=0,f;f=b[e];e++)if(-1b.viewBottom||b.contentLeftb.viewRight){c=null;a&&(c=Blockly.Events.getGroup(),Blockly.Events.setGroup(a.group));switch(a.type){case Blockly.Events.BLOCK_CREATE:case Blockly.Events.BLOCK_MOVE:var f=e.getBlockById(a.blockId);f=f.getRootBlock();break;case Blockly.Events.COMMENT_CREATE:case Blockly.Events.COMMENT_MOVE:f= -e.getCommentById(a.commentId)}if(f){d=f.getBoundingRectangle();d.height=d.bottom-d.top;d.width=d.right-d.left;var m=b.viewTop,n=b.viewBottom-d.height;n=Math.max(m,n);m=Blockly.utils.math.clamp(m,d.top,n)-d.top;n=b.viewLeft;var p=b.viewRight-d.width;b.RTL?n=Math.min(p,n):p=Math.max(n,p);b=Blockly.utils.math.clamp(n,d.left,p)-d.left;f.moveBy(b,m)}a&&(a.group||console.log("WARNING: Moved object in bounds but there was no event group. This may break undo."),Blockly.Events.setGroup(c))}}});Blockly.svgResize(e); -Blockly.WidgetDiv.createDom();Blockly.DropDownDiv.createDom();Blockly.Tooltip.createDom();return e}; -Blockly.init_=function(a){var b=a.options,c=a.getParentSvg();Blockly.bindEventWithChecks_(c.parentNode,"contextmenu",null,function(a){Blockly.utils.isTargetInput(a)||a.preventDefault()});c=Blockly.bindEventWithChecks_(window,"resize",null,function(){Blockly.hideChaff(!0);Blockly.svgResize(a)});a.setResizeHandlerWrapper(c);Blockly.inject.bindDocumentEvents_();b.languageTree&&(a.toolbox_?a.toolbox_.init(a):a.flyout_&&(a.flyout_.init(a),a.flyout_.show(b.languageTree.childNodes),a.flyout_.scrollToStart())); -c=Blockly.Scrollbar.scrollbarThickness;b.hasTrashcan&&(c=a.trashcan.init(c));b.zoomOptions&&b.zoomOptions.controls&&a.zoomControls_.init(c);b.moveOptions&&b.moveOptions.scrollbars?(a.scrollbar=new Blockly.ScrollbarPair(a),a.scrollbar.resize()):a.setMetrics({x:.5,y:.5});b.hasSounds&&Blockly.inject.loadSounds_(b.pathToMedia,a)}; +Blockly.createMainWorkspace_=function(a,b,c,d){b.parentWorkspace=null;var e=new Blockly.WorkspaceSvg(b,c,d);e.scale=b.zoomOptions.startScale;a.appendChild(e.createDom("blocklyMainBackground"));!b.hasCategories&&b.languageTree&&(c=e.addFlyout_("svg"),Blockly.utils.dom.insertAfter(c,a));b.hasTrashcan&&e.addTrashcan();b.zoomOptions&&b.zoomOptions.controls&&e.addZoomControls();e.translate(0,0);Blockly.mainWorkspace=e;b.readOnly||e.isMovable()||e.addChangeListener(function(a){if(!e.isDragging()&&!e.isMovable()&& +-1!=Blockly.Events.BUMP_EVENTS.indexOf(a.type)){var b=Object.create(null),c=e.getMetrics(),d=e.scale;b.RTL=e.RTL;b.viewLeft=c.viewLeft/d;b.viewTop=c.viewTop/d;b.viewRight=(c.viewLeft+c.viewWidth)/d;b.viewBottom=(c.viewTop+c.viewHeight)/d;e.isContentBounded()?(c=e.getBlocksBoundingBox(),b.contentLeft=c.left,b.contentTop=c.top,b.contentRight=c.right,b.contentBottom=c.bottom):(b.contentLeft=c.contentLeft/d,b.contentTop=c.contentTop/d,b.contentRight=(c.contentLeft+c.contentWidth)/d,b.contentBottom=(c.contentTop+ +c.contentHeight)/d);if(b.contentTopb.viewBottom||b.contentLeftb.viewRight){c=null;a&&(c=Blockly.Events.getGroup(),Blockly.Events.setGroup(a.group));switch(a.type){case Blockly.Events.BLOCK_CREATE:case Blockly.Events.BLOCK_MOVE:var f=e.getBlockById(a.blockId);f=f.getRootBlock();break;case Blockly.Events.COMMENT_CREATE:case Blockly.Events.COMMENT_MOVE:f=e.getCommentById(a.commentId)}if(f){d=f.getBoundingRectangle();d.height=d.bottom-d.top;d.width= +d.right-d.left;var m=b.viewTop,n=b.viewBottom-d.height;n=Math.max(m,n);m=Blockly.utils.math.clamp(m,d.top,n)-d.top;n=b.viewLeft;var p=b.viewRight-d.width;b.RTL?n=Math.min(p,n):p=Math.max(n,p);b=Blockly.utils.math.clamp(n,d.left,p)-d.left;f.moveBy(b,m)}a&&(a.group||console.log("WARNING: Moved object in bounds but there was no event group. This may break undo."),Blockly.Events.setGroup(c))}}});Blockly.svgResize(e);Blockly.WidgetDiv.createDom();Blockly.DropDownDiv.createDom();Blockly.Tooltip.createDom(); +return e}; +Blockly.init_=function(a){var b=a.options,c=a.getParentSvg();Blockly.bindEventWithChecks_(c.parentNode,"contextmenu",null,function(a){Blockly.utils.isTargetInput(a)||a.preventDefault()});c=Blockly.bindEventWithChecks_(window,"resize",null,function(){Blockly.hideChaff(!0);Blockly.svgResize(a)});a.setResizeHandlerWrapper(c);Blockly.inject.bindDocumentEvents_();b.languageTree&&(a.toolbox_?a.toolbox_.init(a):a.flyout_&&(a.flyout_.init(a),a.flyout_.show(b.languageTree.childNodes),a.flyout_.scrollToStart()));c= +Blockly.Scrollbar.scrollbarThickness;b.hasTrashcan&&(c=a.trashcan.init(c));b.zoomOptions&&b.zoomOptions.controls&&a.zoomControls_.init(c);b.moveOptions&&b.moveOptions.scrollbars?(a.scrollbar=new Blockly.ScrollbarPair(a),a.scrollbar.resize()):a.setMetrics({x:.5,y:.5});b.hasSounds&&Blockly.inject.loadSounds_(b.pathToMedia,a)}; Blockly.inject.bindDocumentEvents_=function(){Blockly.documentEventsBound_||(Blockly.bindEventWithChecks_(document,"scroll",null,function(){for(var a=Blockly.Workspace.getAll(),b=0,c;c=a[b];b++)c.updateInverseScreenCTM&&c.updateInverseScreenCTM()}),Blockly.bindEventWithChecks_(document,"keydown",null,Blockly.onKeyDown_),Blockly.bindEvent_(document,"touchend",null,Blockly.longStop_),Blockly.bindEvent_(document,"touchcancel",null,Blockly.longStop_),Blockly.utils.userAgent.IPAD&&Blockly.bindEventWithChecks_(window, "orientationchange",document,function(){Blockly.svgResize(Blockly.getMainWorkspace())}));Blockly.documentEventsBound_=!0}; Blockly.inject.loadSounds_=function(a,b){var c=b.getAudioManager();c.load([a+"click.mp3",a+"click.wav",a+"click.ogg"],"click");c.load([a+"disconnect.wav",a+"disconnect.mp3",a+"disconnect.ogg"],"disconnect");c.load([a+"delete.mp3",a+"delete.ogg",a+"delete.wav"],"delete");var d=[],e=function(){for(;d.length;)Blockly.unbindEvent_(d.pop());c.preload()};d.push(Blockly.bindEventWithChecks_(document,"mousemove",null,e,!0));d.push(Blockly.bindEventWithChecks_(document,"touchstart",null,e,!0))}; diff --git a/blockly_uncompressed.js b/blockly_uncompressed.js index ffe39e201..e0c43b15c 100644 --- a/blockly_uncompressed.js +++ b/blockly_uncompressed.js @@ -69,7 +69,7 @@ goog.addDependency("../../../" + dir + "/core/field_number.js", ['Blockly.FieldN goog.addDependency("../../../" + dir + "/core/field_registry.js", ['Blockly.fieldRegistry'], []); goog.addDependency("../../../" + dir + "/core/field_textinput.js", ['Blockly.FieldTextInput'], ['Blockly.Events', 'Blockly.Events.BlockChange', 'Blockly.Field', 'Blockly.fieldRegistry', 'Blockly.Msg', 'Blockly.utils', 'Blockly.utils.aria', 'Blockly.utils.Coordinate', 'Blockly.utils.dom', 'Blockly.utils.object', 'Blockly.utils.Size', 'Blockly.utils.userAgent']); goog.addDependency("../../../" + dir + "/core/field_variable.js", ['Blockly.FieldVariable'], ['Blockly.Events', 'Blockly.Events.BlockChange', 'Blockly.FieldDropdown', 'Blockly.fieldRegistry', 'Blockly.Msg', 'Blockly.utils', 'Blockly.utils.object', 'Blockly.utils.Size', 'Blockly.VariableModel', 'Blockly.Variables', 'Blockly.Xml']); -goog.addDependency("../../../" + dir + "/core/flyout_base.js", ['Blockly.Flyout'], ['Blockly.Block', 'Blockly.Events', 'Blockly.Events.BlockCreate', 'Blockly.Events.VarCreate', 'Blockly.FlyoutButton', 'Blockly.Gesture', 'Blockly.Tooltip', 'Blockly.Touch', 'Blockly.utils', 'Blockly.utils.Coordinate', 'Blockly.utils.dom', 'Blockly.WorkspaceSvg', 'Blockly.Xml']); +goog.addDependency("../../../" + dir + "/core/flyout_base.js", ['Blockly.Flyout'], ['Blockly.Block', 'Blockly.Events', 'Blockly.Events.BlockCreate', 'Blockly.Events.VarCreate', 'Blockly.FlyoutButton', 'Blockly.FlyoutCursor', 'Blockly.Gesture', 'Blockly.MarkerCursor', 'Blockly.Tooltip', 'Blockly.Touch', 'Blockly.utils', 'Blockly.utils.Coordinate', 'Blockly.utils.dom', 'Blockly.WorkspaceSvg', 'Blockly.Xml']); goog.addDependency("../../../" + dir + "/core/flyout_button.js", ['Blockly.FlyoutButton'], ['Blockly.utils', 'Blockly.utils.Coordinate', 'Blockly.utils.dom']); goog.addDependency("../../../" + dir + "/core/flyout_dragger.js", ['Blockly.FlyoutDragger'], ['Blockly.utils.object', 'Blockly.WorkspaceDragger']); goog.addDependency("../../../" + dir + "/core/flyout_horizontal.js", ['Blockly.HorizontalFlyout'], ['Blockly.Block', 'Blockly.Flyout', 'Blockly.FlyoutButton', 'Blockly.utils', 'Blockly.utils.object', 'Blockly.utils.Rect']); @@ -85,8 +85,10 @@ goog.addDependency("../../../" + dir + "/core/keyboard_nav/action.js", ['Blockly goog.addDependency("../../../" + dir + "/core/keyboard_nav/ast_node.js", ['Blockly.ASTNode'], []); goog.addDependency("../../../" + dir + "/core/keyboard_nav/cursor.js", ['Blockly.Cursor'], []); goog.addDependency("../../../" + dir + "/core/keyboard_nav/cursor_svg.js", ['Blockly.CursorSvg'], ['Blockly.Cursor', 'Blockly.utils.object']); +goog.addDependency("../../../" + dir + "/core/keyboard_nav/flyout_cursor.js", ['Blockly.FlyoutCursor'], ['Blockly.Cursor', 'Blockly.utils.object']); goog.addDependency("../../../" + dir + "/core/keyboard_nav/key_map.js", ['Blockly.user.keyMap'], ['Blockly.utils.KeyCodes', 'Blockly.utils.object']); -goog.addDependency("../../../" + dir + "/core/keyboard_nav/navigation.js", ['Blockly.navigation'], ['Blockly.Action', 'Blockly.ASTNode', 'Blockly.user.keyMap']); +goog.addDependency("../../../" + dir + "/core/keyboard_nav/marker_cursor.js", ['Blockly.MarkerCursor'], ['Blockly.Cursor', 'Blockly.utils.object']); +goog.addDependency("../../../" + dir + "/core/keyboard_nav/navigation.js", ['Blockly.navigation'], ['Blockly.Action', 'Blockly.ASTNode', 'Blockly.utils.Coordinate', 'Blockly.user.keyMap']); goog.addDependency("../../../" + dir + "/core/msg.js", ['Blockly.Msg'], []); goog.addDependency("../../../" + dir + "/core/mutator.js", ['Blockly.Mutator'], ['Blockly.Bubble', 'Blockly.Events', 'Blockly.Events.BlockChange', 'Blockly.Events.Ui', 'Blockly.Icon', 'Blockly.utils', 'Blockly.utils.dom', 'Blockly.utils.global', 'Blockly.utils.object', 'Blockly.utils.xml', 'Blockly.WorkspaceSvg', 'Blockly.Xml']); goog.addDependency("../../../" + dir + "/core/names.js", ['Blockly.Names'], ['Blockly.Msg']); @@ -153,7 +155,7 @@ goog.addDependency("../../../" + dir + "/core/variables.js", ['Blockly.Variables goog.addDependency("../../../" + dir + "/core/variables_dynamic.js", ['Blockly.VariablesDynamic'], ['Blockly.Variables', 'Blockly.Blocks', 'Blockly.Msg', 'Blockly.utils.xml', 'Blockly.VariableModel', 'Blockly.Xml']); goog.addDependency("../../../" + dir + "/core/warning.js", ['Blockly.Warning'], ['Blockly.Bubble', 'Blockly.Events', 'Blockly.Events.Ui', 'Blockly.Icon', 'Blockly.utils.dom', 'Blockly.utils.object']); goog.addDependency("../../../" + dir + "/core/widgetdiv.js", ['Blockly.WidgetDiv'], ['Blockly.Css', 'Blockly.utils.style']); -goog.addDependency("../../../" + dir + "/core/workspace.js", ['Blockly.Workspace'], ['Blockly.Cursor', 'Blockly.Events', 'Blockly.Themes.Classic', 'Blockly.utils', 'Blockly.utils.math', 'Blockly.VariableMap', 'Blockly.WorkspaceComment']); +goog.addDependency("../../../" + dir + "/core/workspace.js", ['Blockly.Workspace'], ['Blockly.Cursor', 'Blockly.MarkerCursor', 'Blockly.Events', 'Blockly.Themes.Classic', 'Blockly.utils', 'Blockly.utils.math', 'Blockly.VariableMap', 'Blockly.WorkspaceComment']); goog.addDependency("../../../" + dir + "/core/workspace_audio.js", ['Blockly.WorkspaceAudio'], ['Blockly.utils', 'Blockly.utils.global', 'Blockly.utils.userAgent']); goog.addDependency("../../../" + dir + "/core/workspace_comment.js", ['Blockly.WorkspaceComment'], ['Blockly.Events', 'Blockly.Events.CommentChange', 'Blockly.Events.CommentCreate', 'Blockly.Events.CommentDelete', 'Blockly.Events.CommentMove', 'Blockly.utils', 'Blockly.utils.Coordinate', 'Blockly.utils.xml']); goog.addDependency("../../../" + dir + "/core/workspace_comment_render_svg.js", ['Blockly.WorkspaceCommentSvg.render'], ['Blockly.utils', 'Blockly.utils.Coordinate', 'Blockly.utils.dom', 'Blockly.WorkspaceCommentSvg']); @@ -227,6 +229,7 @@ goog.require('Blockly.FieldTextInput'); goog.require('Blockly.FieldVariable'); goog.require('Blockly.Flyout'); goog.require('Blockly.FlyoutButton'); +goog.require('Blockly.FlyoutCursor'); goog.require('Blockly.FlyoutDragger'); goog.require('Blockly.Generator'); goog.require('Blockly.Gesture'); @@ -235,6 +238,7 @@ goog.require('Blockly.HorizontalFlyout'); goog.require('Blockly.Icon'); goog.require('Blockly.Input'); goog.require('Blockly.InsertionMarkerManager'); +goog.require('Blockly.MarkerCursor'); goog.require('Blockly.Menu'); goog.require('Blockly.MenuItem'); goog.require('Blockly.Msg'); diff --git a/core/flyout_base.js b/core/flyout_base.js index 4fc58bfc9..16f1d12b5 100644 --- a/core/flyout_base.js +++ b/core/flyout_base.js @@ -31,7 +31,9 @@ goog.require('Blockly.Events'); goog.require('Blockly.Events.BlockCreate'); goog.require('Blockly.Events.VarCreate'); goog.require('Blockly.FlyoutButton'); +goog.require('Blockly.FlyoutCursor'); goog.require('Blockly.Gesture'); +goog.require('Blockly.MarkerCursor'); goog.require('Blockly.Tooltip'); goog.require('Blockly.Touch'); goog.require('Blockly.utils'); @@ -56,6 +58,8 @@ Blockly.Flyout = function(workspaceOptions) { */ this.workspace_ = new Blockly.WorkspaceSvg(workspaceOptions); this.workspace_.isFlyout = true; + this.workspace_.setCursor(new Blockly.FlyoutCursor()); + this.workspace_.setMarker(new Blockly.MarkerCursor()); /** * Is RTL vs LTR. diff --git a/core/gesture.js b/core/gesture.js index ed2899132..3d7a5665d 100644 --- a/core/gesture.js +++ b/core/gesture.js @@ -769,7 +769,7 @@ Blockly.Gesture.prototype.doWorkspaceClick_ = function(e) { var screenCoord = new Blockly.utils.Coordinate(e.clientX, e.clientY); var wsCoord = Blockly.utils.screenToWsCoordinates(ws, screenCoord); var wsNode = Blockly.ASTNode.createWorkspaceNode(ws, wsCoord); - ws.cursor.setLocation(wsNode); + ws.getCursor().setLocation(wsNode); } else if (Blockly.selected) { Blockly.selected.unselect(); } diff --git a/core/inject.js b/core/inject.js index 3231193b0..4c5bd2eea 100644 --- a/core/inject.js +++ b/core/inject.js @@ -232,9 +232,6 @@ Blockly.createMainWorkspace_ = function(svg, options, blockDragSurface, mainWorkspace.addZoomControls(); } - Blockly.navigation.setCursor(mainWorkspace.cursor); - Blockly.navigation.setMarker(mainWorkspace.marker); - // A null translation will also apply the correct initial scale. mainWorkspace.translate(0, 0); Blockly.mainWorkspace = mainWorkspace; diff --git a/core/keyboard_nav/cursor.js b/core/keyboard_nav/cursor.js index 790e802c9..bcfd8cd3c 100644 --- a/core/keyboard_nav/cursor.js +++ b/core/keyboard_nav/cursor.js @@ -21,6 +21,7 @@ /** * @fileoverview The class representing a cursor. * Used primarily for keyboard navigation. + * @author aschmiedt@google.com (Abby Schmiedt) */ 'use strict'; @@ -29,39 +30,39 @@ goog.provide('Blockly.Cursor'); /** * Class for a cursor. - * @param {boolean=} opt_marker True if the cursor is a marker. A marker is used - * to save a location and is an immovable cursor. False or undefined if the - * cursor is not a marker. + * A cursor controls how a user navigates the Blockly AST. * @constructor */ -Blockly.Cursor = function(opt_marker) { +Blockly.Cursor = function() { /* * The current location of the cursor. - * @type {Blockly.Field|Blockly.Connection|Blockly.Block} + * @type {Blockly.ASTNode} * @private */ this.curNode_ = null; /** - * Whether or not the cursor is a marker. - * @type {boolean} True if the cursor is a marker. False otherwise. + * The object in charge of drawing the visual representation of the current node. + * @type {Blockly.CursorSvg} * @private */ - this.isMarker_ = !!opt_marker; + this.drawer_ = null; }; /** - * Object holding different types for a cursor. + * Sets the object in charge of drawing the cursor. + * @param {Blockly.CursorSvg} drawer The object in charge of drawing the cursor. */ -Blockly.Cursor.prototype.types = { - FIELD: 'field', - BLOCK: 'block', - INPUT: 'input', - OUTPUT: 'output', - NEXT: 'next', - PREVIOUS: 'previous', - STACK: 'stack', - WORKSPACE: 'workspace' +Blockly.Cursor.prototype.setDrawer = function(drawer) { + this.drawer_ = drawer; +}; + +/** + * Get the current drawer for the cursor. + * @return {Blockly.CursorSvg} The object in charge of drawing the cursor. + */ +Blockly.Cursor.prototype.getDrawer = function() { + return this.drawer_; }; /** @@ -81,20 +82,19 @@ Blockly.Cursor.prototype.getCurNode = function() { */ Blockly.Cursor.prototype.setLocation = function(newNode) { this.curNode_ = newNode; - this.update_(); + if (this.drawer_) { + this.drawer_.draw(this.getCurNode()); + } }; /** - * Update method to be overwritten in cursor_svg. - * @protected + * Hide the cursor svg. */ -Blockly.Cursor.prototype.update_ = function() {}; - -/** - * Hide method to be overwritten in cursor_svg. - * @protected - */ -Blockly.Cursor.prototype.hide = function() {}; +Blockly.Cursor.prototype.hide = function() { + if (this.drawer_) { + this.drawer_.hide(); + } +}; /** * Find the next connection, field, or block. diff --git a/core/keyboard_nav/cursor_svg.js b/core/keyboard_nav/cursor_svg.js index 16173f6e6..a3da835d5 100644 --- a/core/keyboard_nav/cursor_svg.js +++ b/core/keyboard_nav/cursor_svg.js @@ -32,20 +32,37 @@ goog.require('Blockly.utils.object'); /** * Class for a cursor. - * @param {!Blockly.Workspace} workspace The workspace to sit in. + * @param {!Blockly.Workspace} workspace The workspace the cursor belongs to. * @param {boolean=} opt_marker True if the cursor is a marker. A marker is used * to save a location and is an immovable cursor. False or undefined if the * cursor is not a marker. - * @extends {Blockly.Cursor} * @constructor */ Blockly.CursorSvg = function(workspace, opt_marker) { - Blockly.CursorSvg.superClass_.constructor.call(this, opt_marker); + /** + * The workspace the cursor belongs to. + * @type {!Blockly.Workspace} + * @private + */ this.workspace_ = workspace; - this.constants = new Blockly.blockRendering.ConstantProvider(); - this.constants.init(); + + /** + * True if the cursor should be drawn as a marker, false otherwise. + * A marker is drawn as a solid blue line, while the cursor is drawns as a + * flashing red one. + * @type {boolean} + * @private + */ + this.isMarker_ = opt_marker; + + /** + * The constants necessary to draw the cursor. + * @type {Blockly.blockRendering.ConstantProvider} + * @private + */ + this.constants_ = new Blockly.blockRendering.ConstantProvider(); + this.constants_.init(); }; -Blockly.utils.object.inherits(Blockly.CursorSvg, Blockly.Cursor); /** * Height of the horizontal cursor. @@ -181,11 +198,11 @@ Blockly.CursorSvg.prototype.setParent_ = function(newParent) { /** * Show the cursor using coordinates. + * @param {!Blockly.ASTNode} curNode The node that we want to draw the cursor for. * @private */ -Blockly.CursorSvg.prototype.showWithCoordinates_ = function() { - var workspaceNode = this.getCurNode(); - var wsCoordinate = workspaceNode.getWsCoordinate(); +Blockly.CursorSvg.prototype.showWithCoordinates_ = function(curNode) { + var wsCoordinate = curNode.getWsCoordinate(); this.currentCursorSvg = this.cursorSvgLine_; this.setParent_(this.workspace_.svgBlockCanvas_); this.positionLine_(wsCoordinate.x, wsCoordinate.y, @@ -194,11 +211,12 @@ Blockly.CursorSvg.prototype.showWithCoordinates_ = function() { }; /** - * Show the cursor using a block + * Show the cursor using a block. + * @param {!Blockly.ASTNode} curNode The node that we want to draw the cursor for. * @private */ -Blockly.CursorSvg.prototype.showWithBlock_ = function() { - var block = this.getCurNode().getLocation(); +Blockly.CursorSvg.prototype.showWithBlock_ = function(curNode) { + var block = curNode.getLocation(); this.currentCursorSvg = this.cursorSvgRect_; this.setParent_(block.getSvgRoot()); @@ -207,15 +225,16 @@ Blockly.CursorSvg.prototype.showWithBlock_ = function() { }; /** - * Show the cursor using a connection with input or output type + * Show the cursor using a connection with input or output type. + * @param {!Blockly.ASTNode} curNode The node that we want to draw the cursor for. * @private */ -Blockly.CursorSvg.prototype.showWithInputOutput_ = function() { +Blockly.CursorSvg.prototype.showWithInputOutput_ = function(curNode) { var connection = /** @type {Blockly.Connection} */ - (this.getCurNode().getLocation()); + (curNode.getLocation()); this.currentCursorSvg = this.cursorInputOutput_; var path = Blockly.utils.svgPaths.moveTo(0, 0) + - this.constants.shapeFor(connection).pathDown; + this.constants_.shapeFor(connection).pathDown; this.cursorInputOutput_.setAttribute('d', path); this.setParent_(connection.getSourceBlock().getSvgRoot()); this.positionInputOutput_(connection); @@ -223,11 +242,12 @@ Blockly.CursorSvg.prototype.showWithInputOutput_ = function() { }; /** - * Show the cursor using a next connection + * Show the cursor using a next connection. + * @param {!Blockly.ASTNode} curNode The node that we want to draw the cursor for. * @private */ -Blockly.CursorSvg.prototype.showWithNext_ = function() { - var connection = this.getCurNode().getLocation(); +Blockly.CursorSvg.prototype.showWithNext_ = function(curNode) { + var connection = curNode.getLocation(); var targetBlock = connection.getSourceBlock(); var x = 0; var y = connection.getOffsetInBlock().y; @@ -241,10 +261,11 @@ Blockly.CursorSvg.prototype.showWithNext_ = function() { /** * Show the cursor using a previous connection. + * @param {!Blockly.ASTNode} curNode The node that we want to draw the cursor for. * @private */ -Blockly.CursorSvg.prototype.showWithPrev_ = function() { - var connection = this.getCurNode().getLocation(); +Blockly.CursorSvg.prototype.showWithPrev_ = function(curNode) { + var connection = curNode.getLocation(); var targetBlock = connection.getSourceBlock(); var width = targetBlock.getHeightWidth().width; @@ -256,10 +277,11 @@ Blockly.CursorSvg.prototype.showWithPrev_ = function() { /** * Show the cursor using a field. + * @param {!Blockly.ASTNode} curNode The node that we want to draw the cursor for. * @private */ -Blockly.CursorSvg.prototype.showWithField_ = function() { - var field = this.getCurNode().getLocation(); +Blockly.CursorSvg.prototype.showWithField_ = function(curNode) { + var field = curNode.getLocation(); var width = field.borderRect_.width.baseVal.value; var height = field.borderRect_.height.baseVal.value; @@ -271,10 +293,11 @@ Blockly.CursorSvg.prototype.showWithField_ = function() { /** * Show the cursor using a stack. + * @param {!Blockly.ASTNode} curNode The node that we want to draw the cursor for. * @private */ -Blockly.CursorSvg.prototype.showWithStack_ = function() { - var block = this.getCurNode().getLocation(); +Blockly.CursorSvg.prototype.showWithStack_ = function(curNode) { + var block = curNode.getLocation(); // Gets the height and width of entire stack. var heightWidth = block.getHeightWidth(); @@ -361,30 +384,30 @@ Blockly.CursorSvg.prototype.hide = function() { /** * Update the cursor. + * @param {!Blockly.ASTNode} curNode The node that we want to draw the cursor for. * @package */ -Blockly.CursorSvg.prototype.update_ = function() { - if (!this.getCurNode()) { +Blockly.CursorSvg.prototype.draw = function(curNode) { + if (!curNode) { return; } - var curNode = this.getCurNode(); if (curNode.getType() === Blockly.ASTNode.types.BLOCK) { - this.showWithBlock_(); - //This needs to be the location type because next connections can be input - //type but they need to draw like they are a next statement + this.showWithBlock_(curNode); + // This needs to be the location type because next connections can be input + // type but they need to draw like they are a next statement } else if (curNode.getLocation().type === Blockly.INPUT_VALUE || curNode.getType() === Blockly.ASTNode.types.OUTPUT) { - this.showWithInputOutput_(); + this.showWithInputOutput_(curNode); } else if (curNode.getLocation().type === Blockly.NEXT_STATEMENT) { - this.showWithNext_(); + this.showWithNext_(curNode); } else if (curNode.getType() === Blockly.ASTNode.types.PREVIOUS) { - this.showWithPrev_(); + this.showWithPrev_(curNode); } else if (curNode.getType() === Blockly.ASTNode.types.FIELD) { - this.showWithField_(); + this.showWithField_(curNode); } else if (curNode.getType() === Blockly.ASTNode.types.WORKSPACE) { - this.showWithCoordinates_(); + this.showWithCoordinates_(curNode); } else if (curNode.getType() === Blockly.ASTNode.types.STACK) { - this.showWithStack_(); + this.showWithStack_(curNode); } }; @@ -470,58 +493,6 @@ Blockly.CursorSvg.prototype.createCursorSvg_ = function() { return this.cursorSvg_; }; -/** - * Find the next connection, field, or block. - * Does nothing if this cursor is an immovable marker. - * @return {Blockly.ASTNode} The next element, or null if the current node is - * not set or there is no next value. - */ -Blockly.CursorSvg.prototype.next = function() { - if (this.isMarker_) { - return null; - } - return Blockly.CursorSvg.superClass_.next.call(this); -}; - -/** - * Find the in connection or field. - * Does nothing if this cursor is an immovable marker. - * @return {Blockly.ASTNode} The in element, or null if the current node is - * not set or there is no in value. - */ -Blockly.CursorSvg.prototype.in = function() { - if (this.isMarker_) { - return null; - } - return Blockly.CursorSvg.superClass_.in.call(this); -}; - -/** - * Find the previous connection, field, or block. - * Does nothing if this cursor is an immovable marker. - * @return {Blockly.ASTNode} The previous element, or null if the current node - * is not set or there is no previous value. - */ -Blockly.CursorSvg.prototype.prev = function() { - if (this.isMarker_) { - return null; - } - return Blockly.CursorSvg.superClass_.prev.call(this); -}; - -/** - * Find the out connection, field, or block. - * Does nothing if this cursor is an immovable marker. - * @return {Blockly.ASTNode} The out element, or null if the current node is - * not set or there is no out value. - */ -Blockly.CursorSvg.prototype.out = function() { - if (this.isMarker_) { - return null; - } - return Blockly.CursorSvg.superClass_.out.call(this); -}; - /** * Dispose of this cursor. */ diff --git a/core/keyboard_nav/flyout_cursor.js b/core/keyboard_nav/flyout_cursor.js new file mode 100644 index 000000000..a9e62ea35 --- /dev/null +++ b/core/keyboard_nav/flyout_cursor.js @@ -0,0 +1,103 @@ +/** + * @license + * Visual Blocks Editor + * + * Copyright 2019 Google Inc. + * https://developers.google.com/blockly/ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @fileoverview The class representing a cursor used to navigate the flyout. + * Used primarily for keyboard navigation. + * @author aschmiedt@google.com (Abby Schmiedt) + */ +'use strict'; + +goog.provide('Blockly.FlyoutCursor'); + +goog.require('Blockly.Cursor'); +goog.require('Blockly.utils.object'); + + +/** + * Class for a flyout cursor. + * This controls how a user navigates blocks in the flyout. + * @constructor + * @extends {Blockly.Cursor} + */ +Blockly.FlyoutCursor = function() { + Blockly.FlyoutCursor.superClass_.constructor.call(this); +}; +Blockly.utils.object.inherits(Blockly.FlyoutCursor, Blockly.Cursor); + +/** + * Find the next connection, field, or block. + * @return {Blockly.ASTNode} The next element, or null if the current node is + * not set or there is no next value. + * @override + */ +Blockly.FlyoutCursor.prototype.next = function() { + if (!this.isMarker_) { + var curNode = this.getCurNode(); + if (!curNode) { + return null; + } + var newNode = curNode.next(); + + if (newNode) { + this.setLocation(newNode); + } + return newNode; + } +}; + +/** + * This is a no-op since a flyout cursor can not go in. + * @return {null} Always null. + * @override + */ +Blockly.FlyoutCursor.prototype.in = function() { + return null; +}; + +/** + * Find the previous connection, field, or block. + * @return {Blockly.ASTNode} The previous element, or null if the current node + * is not set or there is no previous value. + * @override + */ +Blockly.FlyoutCursor.prototype.prev = function() { + if (!this.isMarker_) { + var curNode = this.getCurNode(); + if (!curNode) { + return null; + } + var newNode = curNode.prev(); + + if (newNode) { + this.setLocation(newNode); + } + return newNode; + } +}; + +/** + * This is a no-op since a flyout cursor can not go out. + * @return {null} Always null. + * @override + */ +Blockly.FlyoutCursor.prototype.out = function() { + return null; +}; diff --git a/core/keyboard_nav/marker_cursor.js b/core/keyboard_nav/marker_cursor.js new file mode 100644 index 000000000..17fa338ed --- /dev/null +++ b/core/keyboard_nav/marker_cursor.js @@ -0,0 +1,79 @@ +/** + * @license + * Visual Blocks Editor + * + * Copyright 2019 Google Inc. + * https://developers.google.com/blockly/ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @fileoverview The class representing a cursor used for marking a location. + * Used primarily for keyboard navigation. + * @author aschmiedt@google.com (Abby Schmiedt) + */ +'use strict'; + +goog.provide('Blockly.MarkerCursor'); + +goog.require('Blockly.Cursor'); +goog.require('Blockly.utils.object'); + + +/** + * Class for a marker. + * This is used in keyboard navigation to save a location in the Blockly AST. + * @constructor + * @extends {Blockly.Cursor} + */ +Blockly.MarkerCursor = function() { + Blockly.MarkerCursor.superClass_.constructor.call(this); +}; +Blockly.utils.object.inherits(Blockly.MarkerCursor, Blockly.Cursor); + +/** + * This is a no-op since markers do not move. + * @return {null} Always null. + * @override + */ +Blockly.MarkerCursor.prototype.next = function() { + return null; +}; + +/** + * This is a no-op since markers do not move. + * @return {null} Always null. + * @override + */ +Blockly.MarkerCursor.prototype.in = function() { + return null; +}; + +/** + * This is a no-op since markers do not move. + * @return {null} Always null. + * @override + */ +Blockly.MarkerCursor.prototype.prev = function() { + return null; +}; + +/** + * This is a no-op since markers do not move. + * @return {null} Always null. + * @override + */ +Blockly.MarkerCursor.prototype.out = function() { + return null; +}; diff --git a/core/keyboard_nav/navigation.js b/core/keyboard_nav/navigation.js index ad85e5593..73969c15b 100644 --- a/core/keyboard_nav/navigation.js +++ b/core/keyboard_nav/navigation.js @@ -18,26 +18,21 @@ * limitations under the License. */ +/** + * @fileoverview The class in charge of handling actions related to keyboard + * navigation. + * @author aschmiedt@google.com (Abby Schmiedt) + */ +'use strict'; + goog.provide('Blockly.navigation'); goog.require('Blockly.Action'); goog.require('Blockly.ASTNode'); +goog.require('Blockly.utils.Coordinate'); goog.require('Blockly.user.keyMap'); -/** - * The cursor for keyboard navigation. - * @type {Blockly.Cursor} - * @private - */ -Blockly.navigation.cursor_ = null; - -/** - * The marker that shows where a user has marked while navigating blocks. - * @type {!Blockly.Cursor} - */ -Blockly.navigation.marker_ = null; - /** * The current selected category if the toolbox is open or * last selected category if focus is on a different element. @@ -46,13 +41,6 @@ Blockly.navigation.marker_ = null; */ Blockly.navigation.currentCategory_ = null; -/** - * The current selected block in the flyout. - * @type {Blockly.BlockSvg} - * @private - */ -Blockly.navigation.flyoutBlock_ = null; - /** * A function to call to give feedback to the user about logs, warnings, and * errors. You can override this to customize feedback (e.g. warning sounds, @@ -108,33 +96,14 @@ Blockly.navigation.actionNames = { EXIT: 'exit', TOGGLE_KEYBOARD_NAV: 'toggle_keyboard_nav' }; -/** - * Set the navigation cursor. - * @param {Blockly.Cursor} cursor The cursor to navigate through blocks on a - * workspace. - * @package - */ -Blockly.navigation.setCursor = function(cursor) { - Blockly.navigation.cursor_ = cursor; -}; - -/** - * Set the navigation marker. - * @param {Blockly.CursorSvg} marker The marker that shows where a user has - * marked while navigating blocks. - * @package - */ -Blockly.navigation.setMarker = function(marker) { - Blockly.navigation.marker_ = marker; -}; /** * Move the marker to the cursor's current location. * @package */ Blockly.navigation.markAtCursor = function() { - Blockly.navigation.marker_.setLocation( - Blockly.navigation.cursor_.getCurNode()); + Blockly.getMainWorkspace().getMarker().setLocation( + Blockly.getMainWorkspace().getCursor().getCurNode()); }; /** @@ -142,15 +111,15 @@ Blockly.navigation.markAtCursor = function() { * @package */ Blockly.navigation.removeMark = function() { - Blockly.navigation.marker_.setLocation(null); - Blockly.navigation.marker_.hide(); + Blockly.getMainWorkspace().getMarker().setLocation(null); + Blockly.getMainWorkspace().getMarker().hide(); }; /** * Gets the top node on a block. * This is either the previous connection, output connection or the block. - * @param {Blockly.Block} block The block to find the top most ast node on. - * @return {Blockly.ASTNode} The ast node holding the top most node on the + * @param {Blockly.Block} block The block to find the top most AST node on. + * @return {Blockly.ASTNode} The AST node holding the top most node on the * block. * @package */ @@ -181,7 +150,7 @@ Blockly.navigation.focusToolbox = function() { var workspace = Blockly.getMainWorkspace(); var toolbox = workspace.getToolbox(); - if (!Blockly.navigation.marker_.getCurNode()) { + if (!Blockly.getMainWorkspace().getMarker().getCurNode()) { Blockly.navigation.markAtCursor(); } if (workspace && !Blockly.navigation.currentCategory_) { @@ -282,10 +251,9 @@ Blockly.navigation.focusFlyout = function() { Blockly.navigation.currentState_ = Blockly.navigation.STATE_FLYOUT; var workspace = Blockly.getMainWorkspace(); var toolbox = workspace.getToolbox(); - var cursor = Blockly.navigation.cursor_; var flyout = toolbox ? toolbox.flyout_ : workspace.getFlyout(); - if (!Blockly.navigation.marker_.getCurNode()) { + if (!Blockly.getMainWorkspace().getMarker().getCurNode()) { Blockly.navigation.markAtCursor(); } @@ -293,74 +261,25 @@ Blockly.navigation.focusFlyout = function() { var topBlocks = flyout.getWorkspace().getTopBlocks(); if (topBlocks.length > 0) { topBlock = topBlocks[0]; - Blockly.navigation.flyoutBlock_ = topBlock; - var astNode = Blockly.ASTNode.createBlockNode(Blockly.navigation.flyoutBlock_); - cursor.setLocation(astNode); + var astNode = Blockly.ASTNode.createStackNode(topBlock); + Blockly.navigation.getFlyoutCursor_().setLocation(astNode); } } }; /** - * Select the next block in the flyout. + * Get the cursor from the flyouts workspace. + * @return {Blockly.FlyoutCursor} The flyouts cursor or null if no flyout exists. */ -Blockly.navigation.selectNextBlockInFlyout = function() { - if (!Blockly.navigation.flyoutBlock_) { - return; - } - var blocks = Blockly.navigation.getFlyoutBlocks_(); - var curBlock = Blockly.navigation.flyoutBlock_; - var curIdx = blocks.indexOf(curBlock); - var cursor = Blockly.navigation.cursor_; - var nextBlock; - - if (curIdx > -1 && blocks[++curIdx]) { - nextBlock = blocks[curIdx]; - } - - if (nextBlock) { - Blockly.navigation.flyoutBlock_ = nextBlock; - var astNode = Blockly.ASTNode.createBlockNode(nextBlock); - cursor.setLocation(astNode); - } -}; - -/** - * Select the previous block in the flyout. - */ -Blockly.navigation.selectPreviousBlockInFlyout = function() { - if (!Blockly.navigation.flyoutBlock_) { - return; - } - var blocks = Blockly.navigation.getFlyoutBlocks_(); - var curBlock = Blockly.navigation.flyoutBlock_; - var curIdx = blocks.indexOf(curBlock); - var cursor = Blockly.navigation.cursor_; - var prevBlock; - - if (curIdx > -1 && blocks[--curIdx]) { - prevBlock = blocks[curIdx]; - } - - if (prevBlock) { - Blockly.navigation.flyoutBlock_ = prevBlock; - var astNode = Blockly.ASTNode.createBlockNode(prevBlock); - cursor.setLocation(astNode); - } -}; - -/** - * Get a list of all blocks in the flyout. - * @return {!Array} List of blocks in the flyout. - */ -Blockly.navigation.getFlyoutBlocks_ = function() { +Blockly.navigation.getFlyoutCursor_ = function() { var workspace = Blockly.getMainWorkspace(); - var toolbox = workspace.getToolbox(); - var topBlocks = []; - var flyout = toolbox ? toolbox.flyout_ : workspace.getFlyout(); - if (flyout && flyout.getWorkspace()) { - topBlocks = flyout.getWorkspace().getTopBlocks(); + var cursor = null; + if (workspace.rendered) { + var toolbox = workspace.getToolbox(); + var flyout = toolbox ? toolbox.flyout_ : workspace.getFlyout(); + cursor = flyout ? flyout.workspace_.getCursor() : null; } - return topBlocks; + return cursor; }; /** @@ -369,7 +288,6 @@ Blockly.navigation.getFlyoutBlocks_ = function() { * it on the workspace. */ Blockly.navigation.insertFromFlyout = function() { - var flyout = Blockly.getMainWorkspace().getFlyout(); if (!flyout || !flyout.isVisible()) { Blockly.navigation.warn('Trying to insert from the flyout when the flyout does not ' + @@ -377,21 +295,22 @@ Blockly.navigation.insertFromFlyout = function() { return; } - var newBlock = flyout.createBlock(Blockly.navigation.flyoutBlock_); + var newBlock = flyout.createBlock( + Blockly.navigation.getFlyoutCursor_().getCurNode().getLocation()); // Render to get the sizing right. newBlock.render(); // Connections are hidden when the block is first created. Normally there's // enough time for them to become unhidden in the user's mouse movements, // but not here. newBlock.setConnectionsHidden(false); - Blockly.navigation.cursor_.setLocation( + Blockly.getMainWorkspace().getCursor().setLocation( Blockly.ASTNode.createBlockNode(newBlock)); if (!Blockly.navigation.modify_()) { Blockly.navigation.warn('Something went wrong while inserting a block from the flyout.'); } Blockly.navigation.focusWorkspace(); - Blockly.navigation.cursor_.setLocation(Blockly.navigation.getTopNode(newBlock)); + Blockly.getMainWorkspace().getCursor().setLocation(Blockly.navigation.getTopNode(newBlock)); Blockly.navigation.removeMark(); }; @@ -400,11 +319,11 @@ Blockly.navigation.insertFromFlyout = function() { * @param {boolean} shouldHide True if the flyout should be hidden. */ Blockly.navigation.resetFlyout = function(shouldHide) { - var cursor = Blockly.navigation.cursor_; - Blockly.navigation.flyoutBlock_ = null; - cursor.hide(); - if (shouldHide) { - cursor.workspace_.getFlyout().hide(); + if (Blockly.navigation.getFlyoutCursor_()) { + Blockly.navigation.getFlyoutCursor_().hide(); + if (shouldHide) { + Blockly.getMainWorkspace().getFlyout().hide(); + } } }; @@ -419,8 +338,8 @@ Blockly.navigation.resetFlyout = function(shouldHide) { * @private */ Blockly.navigation.modifyWarn_ = function() { - var markerNode = Blockly.navigation.marker_.getCurNode(); - var cursorNode = Blockly.navigation.cursor_.getCurNode(); + var markerNode = Blockly.getMainWorkspace().getMarker().getCurNode(); + var cursorNode = Blockly.getMainWorkspace().getCursor().getCurNode(); if (!markerNode) { Blockly.navigation.warn('Cannot insert with no marked node.'); @@ -487,8 +406,8 @@ Blockly.navigation.moveBlockToWorkspace_ = function(block, wsNode) { * @private */ Blockly.navigation.modify_ = function() { - var markerNode = Blockly.navigation.marker_.getCurNode(); - var cursorNode = Blockly.navigation.cursor_.getCurNode(); + var markerNode = Blockly.getMainWorkspace().getMarker().getCurNode(); + var cursorNode = Blockly.getMainWorkspace().getCursor().getCurNode(); if (!Blockly.navigation.modifyWarn_()) { return false; } @@ -690,7 +609,7 @@ Blockly.navigation.insertBlock = function(block, destConnection) { * @package */ Blockly.navigation.disconnectBlocks = function() { - var curNode = Blockly.navigation.cursor_.getCurNode(); + var curNode = Blockly.getMainWorkspace().getCursor().getCurNode(); if (!curNode.isConnection()) { Blockly.navigation.log('Cannot disconnect blocks when the cursor is not on a connection'); return; @@ -717,7 +636,7 @@ Blockly.navigation.disconnectBlocks = function() { rootBlock.bringToFront(); var connectionNode = Blockly.ASTNode.createConnectionNode(superiorConnection); - Blockly.navigation.cursor_.setLocation(connectionNode); + Blockly.getMainWorkspace().getCursor().setLocation(connectionNode); }; /*************************/ @@ -729,7 +648,8 @@ Blockly.navigation.disconnectBlocks = function() { * block or a set position on the workspace. */ Blockly.navigation.focusWorkspace = function() { - var cursor = Blockly.navigation.cursor_; + Blockly.hideChaff(); + var cursor = Blockly.getMainWorkspace().getCursor(); var reset = Blockly.getMainWorkspace().getToolbox() ? true : false; var topBlocks = Blockly.getMainWorkspace().getTopBlocks(); @@ -738,7 +658,7 @@ Blockly.navigation.focusWorkspace = function() { if (topBlocks.length > 0) { cursor.setLocation(Blockly.navigation.getTopNode(topBlocks[0])); } else { - var ws = cursor.workspace_; + var ws = Blockly.getMainWorkspace(); // TODO: Find the center of the visible workspace. var wsCoord = new Blockly.utils.Coordinate(100, 100); var wsNode = Blockly.ASTNode.createWorkspaceNode(ws, wsCoord); @@ -750,7 +670,7 @@ Blockly.navigation.focusWorkspace = function() { * Handles hitting the enter key on the workspace. */ Blockly.navigation.handleEnterForWS = function() { - var cursor = Blockly.navigation.cursor_; + var cursor = Blockly.getMainWorkspace().getCursor(); var curNode = cursor.getCurNode(); var nodeType = curNode.getType(); if (nodeType === Blockly.ASTNode.types.FIELD) { @@ -798,7 +718,10 @@ Blockly.navigation.getSourceBlock_ = function(node) { * @package */ Blockly.navigation.moveCursorOnBlockDelete = function(deletedBlock) { - var cursor = Blockly.navigation.cursor_; + if (!Blockly.getMainWorkspace()) { + return; + } + var cursor = Blockly.getMainWorkspace().getCursor(); if (cursor) { var curNode = cursor.getCurNode(); var block = Blockly.navigation.getSourceBlock_(curNode); @@ -833,7 +756,7 @@ Blockly.navigation.moveCursorOnBlockDelete = function(deletedBlock) { * @package */ Blockly.navigation.moveCursorOnBlockMutation = function(mutatedBlock) { - var cursor = Blockly.navigation.cursor_; + var cursor = Blockly.getMainWorkspace().getCursor(); if (cursor) { var curNode = cursor.getCurNode(); var block = Blockly.navigation.getSourceBlock_(curNode); @@ -852,30 +775,11 @@ Blockly.navigation.moveCursorOnBlockMutation = function(mutatedBlock) { Blockly.navigation.onKeyPress = function(e) { var key = Blockly.user.keyMap.serializeKeyEvent(e); var action = Blockly.user.keyMap.getActionByKeyCode(key); - var curNode = Blockly.navigation.cursor_.getCurNode(); - var readOnly = Blockly.getMainWorkspace().options.readOnly; - var actionHandled = false; if (action) { - if (Blockly.keyboardAccessibilityMode) { - if (!readOnly) { - if (curNode && curNode.getType() === Blockly.ASTNode.types.FIELD) { - actionHandled = curNode.getLocation().onBlocklyAction(action); - } - if (!actionHandled) { - actionHandled = Blockly.navigation.onBlocklyAction(action); - } - // If in readonly mode only handle valid actions. - } else if (Blockly.navigation.READONLY_ACTION_LIST.indexOf(action) > -1) { - actionHandled = Blockly.navigation.onBlocklyAction(action); - } - // If not in accessibility mode only hanlde turning on keyboard navigation. - } else if (action.name === Blockly.navigation.actionNames.TOGGLE_KEYBOARD_NAV) { - Blockly.navigation.enableKeyboardAccessibility(); - actionHandled = true; - } + return Blockly.navigation.onBlocklyAction(action); } - return actionHandled; + return false; }; /** @@ -883,12 +787,49 @@ Blockly.navigation.onKeyPress = function(e) { * the given action. * @param {!Blockly.Action} action The current action. * @return {boolean} True if the action has been handled, false otherwise. - * @package */ Blockly.navigation.onBlocklyAction = function(action) { + var readOnly = Blockly.getMainWorkspace().options.readOnly; + var actionHandled = false; + + if (Blockly.keyboardAccessibilityMode) { + if (!readOnly) { + var curNode = Blockly.getMainWorkspace().getCursor().getCurNode(); + if (curNode && curNode.getType() === Blockly.ASTNode.types.FIELD) { + actionHandled = curNode.getLocation().onBlocklyAction(action); + } + if (!actionHandled) { + actionHandled = Blockly.navigation.handleActions_(action); + } + // If in readonly mode only handle valid actions. + } else if (Blockly.navigation.READONLY_ACTION_LIST.indexOf(action) > -1) { + actionHandled = Blockly.navigation.handleActions_(action); + } + // If not in accessibility mode only hanlde turning on keyboard navigation. + } else if (action.name === Blockly.navigation.actionNames.TOGGLE_KEYBOARD_NAV) { + Blockly.navigation.enableKeyboardAccessibility(); + actionHandled = true; + } + return actionHandled; +}; + +/** + * Handles the action or dispatches to the appropriate action handler. + * @param {!Blockly.Action} action The current action + * @return {boolean} True if the action has been handled, false otherwise. + * @private + */ +Blockly.navigation.handleActions_ = function(action) { if (action.name === Blockly.navigation.actionNames.TOGGLE_KEYBOARD_NAV) { Blockly.navigation.disableKeyboardAccessibility(); return true; + } else if (action.name === Blockly.navigation.actionNames.TOOLBOX) { + if (!Blockly.getMainWorkspace().getToolbox()) { + Blockly.navigation.focusFlyout(); + } else { + Blockly.navigation.focusToolbox(); + } + return true; } else if (Blockly.navigation.currentState_ === Blockly.navigation.STATE_WS) { return Blockly.navigation.workspaceOnAction_(action); } else if (Blockly.navigation.currentState_ === Blockly.navigation.STATE_FLYOUT) { @@ -908,16 +849,16 @@ Blockly.navigation.onBlocklyAction = function(action) { Blockly.navigation.workspaceOnAction_ = function(action) { switch (action.name) { case Blockly.navigation.actionNames.PREVIOUS: - Blockly.navigation.cursor_.prev(); + Blockly.getMainWorkspace().getCursor().prev(); return true; case Blockly.navigation.actionNames.OUT: - Blockly.navigation.cursor_.out(); + Blockly.getMainWorkspace().getCursor().out(); return true; case Blockly.navigation.actionNames.NEXT: - Blockly.navigation.cursor_.next(); + Blockly.getMainWorkspace().getCursor().next(); return true; case Blockly.navigation.actionNames.IN: - Blockly.navigation.cursor_.in(); + Blockly.getMainWorkspace().getCursor().in(); return true; case Blockly.navigation.actionNames.INSERT: Blockly.navigation.modify_(); @@ -928,13 +869,6 @@ Blockly.navigation.workspaceOnAction_ = function(action) { case Blockly.navigation.actionNames.DISCONNECT: Blockly.navigation.disconnectBlocks(); return true; - case Blockly.navigation.actionNames.TOOLBOX: - if (!Blockly.getMainWorkspace().getToolbox()) { - Blockly.navigation.focusFlyout(); - } else { - Blockly.navigation.focusToolbox(); - } - return true; default: return false; } @@ -949,13 +883,13 @@ Blockly.navigation.workspaceOnAction_ = function(action) { Blockly.navigation.flyoutOnAction_ = function(action) { switch (action.name) { case Blockly.navigation.actionNames.PREVIOUS: - Blockly.navigation.selectPreviousBlockInFlyout(); + Blockly.navigation.getFlyoutCursor_().prev(); return true; case Blockly.navigation.actionNames.OUT: Blockly.navigation.focusToolbox(); return true; case Blockly.navigation.actionNames.NEXT: - Blockly.navigation.selectNextBlockInFlyout(); + Blockly.navigation.getFlyoutCursor_().next(); return true; case Blockly.navigation.actionNames.MARK: Blockly.navigation.insertFromFlyout(); @@ -1012,7 +946,11 @@ Blockly.navigation.enableKeyboardAccessibility = function() { Blockly.navigation.disableKeyboardAccessibility = function() { if (Blockly.keyboardAccessibilityMode) { Blockly.keyboardAccessibilityMode = false; - Blockly.navigation.cursor_.hide(); + Blockly.getMainWorkspace().getCursor().hide(); + Blockly.getMainWorkspace().getMarker().hide(); + if (Blockly.navigation.getFlyoutCursor_()) { + Blockly.navigation.getFlyoutCursor_().hide(); + } } }; @@ -1138,5 +1076,6 @@ Blockly.navigation.READONLY_ACTION_LIST = [ Blockly.navigation.ACTION_PREVIOUS, Blockly.navigation.ACTION_OUT, Blockly.navigation.ACTION_IN, - Blockly.navigation.ACTION_NEXT + Blockly.navigation.ACTION_NEXT, + Blockly.navigation.ACTION_TOGGLE_KEYBOARD_NAV ]; diff --git a/core/workspace.js b/core/workspace.js index 3de71b896..8f35183d9 100644 --- a/core/workspace.js +++ b/core/workspace.js @@ -27,6 +27,7 @@ goog.provide('Blockly.Workspace'); goog.require('Blockly.Cursor'); +goog.require('Blockly.MarkerCursor'); goog.require('Blockly.Events'); goog.require('Blockly.Themes.Classic'); goog.require('Blockly.utils'); @@ -117,22 +118,28 @@ Blockly.Workspace = function(opt_options) { this.potentialVariableMap_ = null; /** - * The cursor for navigating blocks. - * @type {!Blockly.Cursor} + * The cursor used to navigate around the AST for keyboard navigation. + * @type {Blockly.Cursor} + * @private */ - this.cursor = this.createCursor(); + this.cursor_ = null; /** - * The marker that shows where a user has marked while navigating blocks. - * @type {!Blockly.Cursor} + * The marker used to mark a location for keyboard navigation. + * @type {Blockly.MarkerCursor} + * @private */ - this.marker = this.createMarker(); + this.marker_ = null; // Set the default theme. This is for headless workspaces. This will get // overwritten by the theme passed into the inject call for rendered workspaces. if (!Blockly.getTheme()) { Blockly.setTheme(Blockly.Themes.Classic); } + + this.setCursor(new Blockly.Cursor()); + + this.setMarker(new Blockly.MarkerCursor()); }; /** @@ -162,19 +169,37 @@ Blockly.Workspace.prototype.MAX_UNDO = 1024; Blockly.Workspace.prototype.connectionDBList = null; /** - * Adds cursor for keyboard navigation. - * @return {!Blockly.Cursor} Cursor for keyboard navigation. + * Sets the cursor for keyboard navigation. + * @param {Blockly.Cursor} cursor The cursor used to navigate around the Blockly + * AST for keyboard navigation. */ -Blockly.Workspace.prototype.createCursor = function() { - return new Blockly.Cursor(); +Blockly.Workspace.prototype.setCursor = function(cursor) { + this.cursor_ = cursor; }; /** - * Adds marker for keyboard navigation. - * @return {!Blockly.Cursor} Cursor for keyboard navigation. + * Sets the marker for keyboard navigation. + * @param {Blockly.MarkerCursor} marker The marker used to mark a location for + * keyboard navigation. */ -Blockly.Workspace.prototype.createMarker = function() { - return new Blockly.Cursor(true); +Blockly.Workspace.prototype.setMarker = function(marker) { + this.marker_ = marker; +}; + +/** + * Get the cursor used to navigate around the AST for keyboard navigation. + * @return {Blockly.Cursor} The cursor for this workspace. + */ +Blockly.Workspace.prototype.getCursor = function() { + return this.cursor_; +}; + +/** + * Get the marker used to mark a location for keyboard navigation. + * @return {Blockly.MarkerCursor} the marker for this workspace. + */ +Blockly.Workspace.prototype.getMarker = function() { + return this.marker_; }; diff --git a/core/workspace_svg.js b/core/workspace_svg.js index 38e24f990..2b708a5a0 100644 --- a/core/workspace_svg.js +++ b/core/workspace_svg.js @@ -396,19 +396,34 @@ Blockly.WorkspaceSvg.prototype.inverseScreenCTM_ = null; Blockly.WorkspaceSvg.prototype.inverseScreenCTMDirty_ = true; /** - * Adds cursor for keyboard navigation. - * @return {!Blockly.CursorSvg} Cursor for keyboard navigation. + * Sets the cursor for use with keyboard navigation. + * @param {!Blockly.Cursor} cursor The cursor used to move around this workspace. */ -Blockly.WorkspaceSvg.prototype.createCursor = function() { - return new Blockly.CursorSvg(this); +Blockly.WorkspaceSvg.prototype.setCursor = function(cursor) { + if (this.cursor_) { + this.cursor_.getDrawer().dispose(); + } + this.cursor_ = cursor; + this.cursor_.setDrawer(new Blockly.CursorSvg(this, false)); + if (this.svgGroup_) { + this.svgGroup_.appendChild(this.cursor_.getDrawer().createDom()); + } }; /** - * Adds marker for keyboard navigation. - * @return {!Blockly.CursorSvg} Marker for keyboard navigation. + * Sets the marker for use with keyboard navigation. + * @param {!Blockly.MarkerCursor} marker The immovable cursor used to mark a + * location on the workspace. */ -Blockly.WorkspaceSvg.prototype.createMarker = function() { - return new Blockly.CursorSvg(this, true); +Blockly.WorkspaceSvg.prototype.setMarker = function(marker) { + if (this.marker_) { + this.marker_.getDrawer().dispose(); + } + this.marker_ = marker; + this.marker_.setDrawer(new Blockly.CursorSvg(this, true)); + if (this.svgGroup_) { + this.svgGroup_.appendChild(this.marker_.getDrawer().createDom()); + } }; /** @@ -578,10 +593,10 @@ Blockly.WorkspaceSvg.prototype.createDom = function(opt_backgroundClass) { } this.recordDeleteAreas(); - var svgCursor = this.cursor.createDom(); + var svgCursor = this.cursor_.getDrawer().createDom(); this.svgGroup_.appendChild(svgCursor); - var svgMarker = this.marker.createDom(); + var svgMarker = this.marker_.getDrawer().createDom(); this.svgGroup_.appendChild(svgMarker); return this.svgGroup_; @@ -625,12 +640,12 @@ Blockly.WorkspaceSvg.prototype.dispose = function() { this.zoomControls_ = null; } - if (this.marker) { - this.marker.dispose(); + if (this.marker_) { + this.marker_.getDrawer().dispose(); } - if (this.cursor) { - this.cursor.dispose(); + if (this.cursor_) { + this.cursor_.getDrawer().dispose(); } if (this.audioManager_) { @@ -1081,7 +1096,7 @@ Blockly.WorkspaceSvg.prototype.pasteBlock_ = function(xmlBlock) { var block = Blockly.Xml.domToBlock(xmlBlock, this); // Handle paste for keyboard navigation - var markedNode = Blockly.navigation.marker_.getCurNode(); + var markedNode = this.getMarker().getCurNode(); if (Blockly.keyboardAccessibilityMode && markedNode) { Blockly.navigation.insertBlock(block, markedNode.getLocation()); return; diff --git a/tests/mocha/cursor_test.js b/tests/mocha/cursor_test.js index 501bfdadf..9ad8b0c12 100644 --- a/tests/mocha/cursor_test.js +++ b/tests/mocha/cursor_test.js @@ -46,7 +46,8 @@ suite('Cursor', function() { } ]); this.workspace = new Blockly.Workspace(); - this.cursor = this.workspace.cursor; + this.workspace.setCursor(new Blockly.Cursor()); + this.cursor = this.workspace.getCursor(); var blockA = this.workspace.newBlock('input_statement'); var blockB = this.workspace.newBlock('input_statement'); var blockC = this.workspace.newBlock('input_statement'); diff --git a/tests/mocha/navigation_modify_test.js b/tests/mocha/navigation_modify_test.js index 98f704e04..6603b82ca 100644 --- a/tests/mocha/navigation_modify_test.js +++ b/tests/mocha/navigation_modify_test.js @@ -37,25 +37,25 @@ suite('Insert/Modify', function() { // TODO: Marked connection or cursor connection is already connected. suite('Marker on next', function() { setup(function() { - Blockly.navigation.marker_.setLocation( + this.workspace.getMarker().setLocation( Blockly.ASTNode.createConnectionNode( this.stack_block_1.nextConnection)); }); test('Cursor on workspace', function() { - Blockly.navigation.cursor_.setLocation( + this.workspace.getCursor().setLocation( Blockly.ASTNode.createWorkspaceNode(this.workspace, new Blockly.utils.Coordinate(0, 0))); chai.assert.isFalse(Blockly.navigation.modify_()); }); test('Cursor on compatible connection', function() { - Blockly.navigation.cursor_.setLocation( + this.workspace.getCursor().setLocation( Blockly.ASTNode.createConnectionNode( this.stack_block_2.previousConnection)); chai.assert.isTrue(Blockly.navigation.modify_()); chai.assert.equal(this.stack_block_1.getNextBlock().id, 'stack_block_2'); }); test('Cursor on incompatible connection', function() { - Blockly.navigation.cursor_.setLocation( + this.workspace.getCursor().setLocation( Blockly.ASTNode.createConnectionNode( this.stack_block_2.nextConnection)); // Connect method will try to find a way to connect blocks with @@ -64,14 +64,14 @@ suite('Insert/Modify', function() { chai.assert.equal(this.stack_block_1.getNextBlock(), this.stack_block_2); }); test('Cursor on really incompatible connection', function() { - Blockly.navigation.cursor_.setLocation( + this.workspace.getCursor().setLocation( Blockly.ASTNode.createConnectionNode( this.row_block_1.outputConnection)); chai.assert.isFalse(Blockly.navigation.modify_()); chai.assert.isNull(this.stack_block_1.getNextBlock()); }); test('Cursor on block', function() { - Blockly.navigation.cursor_.setLocation( + this.workspace.getCursor().setLocation( Blockly.ASTNode.createBlockNode( this.stack_block_2)); chai.assert.isTrue(Blockly.navigation.modify_()); @@ -81,40 +81,40 @@ suite('Insert/Modify', function() { suite('Marker on previous', function() { setup(function() { - Blockly.navigation.marker_.setLocation( + this.workspace.getMarker().setLocation( Blockly.ASTNode.createConnectionNode( this.stack_block_1.previousConnection)); }); test('Cursor on compatible connection', function() { - Blockly.navigation.cursor_.setLocation( + this.workspace.getCursor().setLocation( Blockly.ASTNode.createConnectionNode( this.stack_block_2.nextConnection)); chai.assert.isTrue(Blockly.navigation.modify_()); chai.assert.equal(this.stack_block_1.getPreviousBlock().id, 'stack_block_2'); }); test('Cursor on incompatible connection', function() { - Blockly.navigation.cursor_.setLocation( + this.workspace.getCursor().setLocation( Blockly.ASTNode.createConnectionNode( this.stack_block_2.previousConnection)); chai.assert.isFalse(Blockly.navigation.modify_()); chai.assert.isNull(this.stack_block_1.getPreviousBlock()); }); test('Cursor on really incompatible connection', function() { - Blockly.navigation.cursor_.setLocation( + this.workspace.getCursor().setLocation( Blockly.ASTNode.createConnectionNode( this.row_block_1.outputConnection)); chai.assert.isFalse(Blockly.navigation.modify_()); chai.assert.isNull(this.stack_block_1.getNextBlock()); }); test('Cursor on block', function() { - Blockly.navigation.cursor_.setLocation( + this.workspace.getCursor().setLocation( Blockly.ASTNode.createBlockNode( this.stack_block_2)); chai.assert.isTrue(Blockly.navigation.modify_()); chai.assert.equal(this.stack_block_1.getPreviousBlock().id, 'stack_block_2'); }); test('Cursor on incompatible block', function() { - Blockly.navigation.cursor_.setLocation( + this.workspace.getCursor().setLocation( Blockly.ASTNode.createBlockNode( this.row_block_1)); chai.assert.isFalse(Blockly.navigation.modify_()); @@ -124,19 +124,19 @@ suite('Insert/Modify', function() { suite('Marker on value input', function() { setup(function() { - Blockly.navigation.marker_.setLocation( + this.workspace.getMarker().setLocation( Blockly.ASTNode.createConnectionNode( this.row_block_1.inputList[0].connection)); }); test('Cursor on compatible connection', function() { - Blockly.navigation.cursor_.setLocation( + this.workspace.getCursor().setLocation( Blockly.ASTNode.createConnectionNode( this.row_block_2.outputConnection)); chai.assert.isTrue(Blockly.navigation.modify_()); chai.assert.equal(this.row_block_2.getParent().id, 'row_block_1'); }); test('Cursor on incompatible connection', function() { - Blockly.navigation.cursor_.setLocation( + this.workspace.getCursor().setLocation( Blockly.ASTNode.createConnectionNode( this.row_block_2.inputList[0].connection)); // Connect method will try to find a way to connect blocks with @@ -146,13 +146,13 @@ suite('Insert/Modify', function() { this.row_block_2); }); test('Cursor on really incompatible connection', function() { - Blockly.navigation.cursor_.setLocation( + this.workspace.getCursor().setLocation( Blockly.ASTNode.createConnectionNode( this.stack_block_1.previousConnection)); chai.assert.isFalse(Blockly.navigation.modify_()); }); test('Cursor on block', function() { - Blockly.navigation.cursor_.setLocation( + this.workspace.getCursor().setLocation( Blockly.ASTNode.createBlockNode( this.row_block_2)); chai.assert.isTrue(Blockly.navigation.modify_()); @@ -165,12 +165,12 @@ suite('Insert/Modify', function() { this.statement_block_1.inputList[0].connection.connect( this.stack_block_1.previousConnection); this.stack_block_1.nextConnection.connect(this.stack_block_2.previousConnection); - Blockly.navigation.marker_.setLocation( + this.workspace.getMarker().setLocation( Blockly.ASTNode.createInputNode( this.statement_block_1.inputList[0])); }); test('Cursor on block inside statement', function() { - Blockly.navigation.cursor_.setLocation( + this.workspace.getCursor().setLocation( Blockly.ASTNode.createConnectionNode( this.stack_block_2.previousConnection)); chai.assert.isTrue(Blockly.navigation.modify_()); @@ -178,14 +178,14 @@ suite('Insert/Modify', function() { this.statement_block_1); }); test('Cursor on stack', function() { - Blockly.navigation.cursor_.setLocation( + this.workspace.getCursor().setLocation( Blockly.ASTNode.createStackNode( this.statement_block_2)); chai.assert.isTrue(Blockly.navigation.modify_()); chai.assert.equal(this.statement_block_2.getParent().id, 'statement_block_1'); }); test('Cursor on incompatible type', function() { - Blockly.navigation.cursor_.setLocation( + this.workspace.getCursor().setLocation( Blockly.ASTNode.createConnectionNode( this.row_block_1.outputConnection)); chai.assert.isFalse(Blockly.navigation.modify_()); @@ -196,31 +196,31 @@ suite('Insert/Modify', function() { suite('Marker on output', function() { setup(function() { - Blockly.navigation.marker_.setLocation( + this.workspace.getMarker().setLocation( Blockly.ASTNode.createConnectionNode( this.row_block_1.outputConnection)); }); test('Cursor on compatible connection', function() { - Blockly.navigation.cursor_.setLocation( + this.workspace.getCursor().setLocation( Blockly.ASTNode.createConnectionNode( this.row_block_2.inputList[0].connection)); chai.assert.isTrue(Blockly.navigation.modify_()); chai.assert.equal(this.row_block_1.getParent().id, 'row_block_2'); }); test('Cursor on incompatible connection', function() { - Blockly.navigation.cursor_.setLocation( + this.workspace.getCursor().setLocation( Blockly.ASTNode.createConnectionNode( this.row_block_2.outputConnection)); chai.assert.isFalse(Blockly.navigation.modify_()); }); test('Cursor on really incompatible connection', function() { - Blockly.navigation.cursor_.setLocation( + this.workspace.getCursor().setLocation( Blockly.ASTNode.createConnectionNode( this.stack_block_1.previousConnection)); chai.assert.isFalse(Blockly.navigation.modify_()); }); test('Cursor on block', function() { - Blockly.navigation.cursor_.setLocation( + this.workspace.getCursor().setLocation( Blockly.ASTNode.createBlockNode( this.row_block_2)); chai.assert.isTrue(Blockly.navigation.modify_()); @@ -232,12 +232,12 @@ suite('Insert/Modify', function() { suite('Marked Workspace', function() { setup(function() { - Blockly.navigation.marker_.setLocation( + this.workspace.getMarker().setLocation( Blockly.ASTNode.createWorkspaceNode( this.workspace, new Blockly.utils.Coordinate(100, 200))); }); test('Cursor on row block', function() { - Blockly.navigation.cursor_.setLocation( + this.workspace.getCursor().setLocation( Blockly.ASTNode.createBlockNode( this.row_block_1)); chai.assert.isTrue(Blockly.navigation.modify_()); @@ -247,7 +247,7 @@ suite('Insert/Modify', function() { }); test('Cursor on output connection', function() { - Blockly.navigation.cursor_.setLocation( + this.workspace.getCursor().setLocation( Blockly.ASTNode.createConnectionNode( this.row_block_1.outputConnection)); chai.assert.isTrue(Blockly.navigation.modify_()); @@ -257,7 +257,7 @@ suite('Insert/Modify', function() { }); test('Cursor on previous connection', function() { - Blockly.navigation.cursor_.setLocation( + this.workspace.getCursor().setLocation( Blockly.ASTNode.createConnectionNode( this.stack_block_1.previousConnection)); chai.assert.isTrue(Blockly.navigation.modify_()); @@ -267,7 +267,7 @@ suite('Insert/Modify', function() { }); test('Cursor on input connection', function() { - Blockly.navigation.cursor_.setLocation( + this.workspace.getCursor().setLocation( Blockly.ASTNode.createConnectionNode( this.row_block_1.inputList[0].connection)); // Move the source block to the marked location on the workspace. @@ -275,7 +275,7 @@ suite('Insert/Modify', function() { }); test('Cursor on next connection', function() { - Blockly.navigation.cursor_.setLocation( + this.workspace.getCursor().setLocation( Blockly.ASTNode.createConnectionNode( this.stack_block_1.nextConnection)); // Move the source block to the marked location on the workspace. @@ -286,7 +286,7 @@ suite('Insert/Modify', function() { this.row_block_1.inputList[0].connection.connect( this.row_block_2.outputConnection); - Blockly.navigation.cursor_.setLocation( + this.workspace.getCursor().setLocation( Blockly.ASTNode.createBlockNode( this.row_block_2)); @@ -301,7 +301,7 @@ suite('Insert/Modify', function() { this.stack_block_1.nextConnection.connect( this.stack_block_2.previousConnection); - Blockly.navigation.cursor_.setLocation( + this.workspace.getCursor().setLocation( Blockly.ASTNode.createBlockNode( this.stack_block_2)); @@ -313,7 +313,7 @@ suite('Insert/Modify', function() { }); test('Cursor on workspace', function() { - Blockly.navigation.cursor_.setLocation( + this.workspace.getCursor().setLocation( Blockly.ASTNode.createWorkspaceNode( this.workspace, new Blockly.utils.Coordinate(100, 100))); chai.assert.isFalse(Blockly.navigation.modify_()); @@ -327,12 +327,12 @@ suite('Insert/Modify', function() { // These tests are using a stack block, but do not depend on the type of // the block. setup(function() { - Blockly.navigation.marker_.setLocation( + this.workspace.getMarker().setLocation( Blockly.ASTNode.createBlockNode( this.stack_block_1)); }); test('Cursor on workspace', function() { - Blockly.navigation.cursor_.setLocation( + this.workspace.getCursor().setLocation( Blockly.ASTNode.createWorkspaceNode( this.workspace, new Blockly.utils.Coordinate(100, 100))); chai.assert.isFalse(Blockly.navigation.modify_()); @@ -341,30 +341,30 @@ suite('Insert/Modify', function() { }); suite('Marked stack block', function() { setup(function() { - Blockly.navigation.marker_.setLocation( + this.workspace.getMarker().setLocation( Blockly.ASTNode.createBlockNode( this.stack_block_1)); }); test('Cursor on row block', function() { - Blockly.navigation.cursor_.setLocation( + this.workspace.getCursor().setLocation( Blockly.ASTNode.createBlockNode( this.row_block_1)); chai.assert.isFalse(Blockly.navigation.modify_()); }); test('Cursor on stack block', function() { - Blockly.navigation.cursor_.setLocation( + this.workspace.getCursor().setLocation( Blockly.ASTNode.createBlockNode( this.stack_block_1)); chai.assert.isFalse(Blockly.navigation.modify_()); }); test('Cursor on next connection', function() { - Blockly.navigation.cursor_.setLocation( + this.workspace.getCursor().setLocation( Blockly.ASTNode.createConnectionNode( this.stack_block_2.nextConnection)); chai.assert.isFalse(Blockly.navigation.modify_()); }); test('Cursor on previous connection', function() { - Blockly.navigation.cursor_.setLocation( + this.workspace.getCursor().setLocation( Blockly.ASTNode.createConnectionNode( this.stack_block_2.previousConnection)); chai.assert.isFalse(Blockly.navigation.modify_()); @@ -372,30 +372,30 @@ suite('Insert/Modify', function() { }); suite('Marked row block', function() { setup(function() { - Blockly.navigation.marker_.setLocation( + this.workspace.getMarker().setLocation( Blockly.ASTNode.createBlockNode( this.row_block_1)); }); test('Cursor on stack block', function() { - Blockly.navigation.cursor_.setLocation( + this.workspace.getCursor().setLocation( Blockly.ASTNode.createBlockNode( this.stack_block_1)); chai.assert.isFalse(Blockly.navigation.modify_()); }); test('Cursor on row block', function() { - Blockly.navigation.cursor_.setLocation( + this.workspace.getCursor().setLocation( Blockly.ASTNode.createBlockNode( this.row_block_1)); chai.assert.isFalse(Blockly.navigation.modify_()); }); test('Cursor on value input connection', function() { - Blockly.navigation.cursor_.setLocation( + this.workspace.getCursor().setLocation( Blockly.ASTNode.createConnectionNode( this.row_block_2.inputList[0].connection)); chai.assert.isFalse(Blockly.navigation.modify_()); }); test('Cursor on output connection', function() { - Blockly.navigation.cursor_.setLocation( + this.workspace.getCursor().setLocation( Blockly.ASTNode.createConnectionNode( this.row_block_2.outputConnection)); chai.assert.isFalse(Blockly.navigation.modify_()); diff --git a/tests/mocha/navigation_test.js b/tests/mocha/navigation_test.js index e6b9f29cb..fc6a1fec1 100644 --- a/tests/mocha/navigation_test.js +++ b/tests/mocha/navigation_test.js @@ -93,8 +93,9 @@ suite('Navigation', function() { chai.assert.isTrue(Blockly.navigation.onKeyPress(this.mockEvent)); chai.assert.equal(Blockly.navigation.currentState_, Blockly.navigation.STATE_FLYOUT); + var flyoutCursor = Blockly.navigation.getFlyoutCursor_(); - chai.assert.equal(Blockly.navigation.flyoutBlock_.getFieldValue("TEXT"), + chai.assert.equal(flyoutCursor.getCurNode().getLocation().getFieldValue("TEXT"), "FirstCategory-FirstBlock"); }); @@ -154,19 +155,23 @@ suite('Navigation', function() { chai.assert.isTrue(Blockly.navigation.onKeyPress(this.mockEvent)); chai.assert.equal(Blockly.navigation.currentState_, Blockly.navigation.STATE_FLYOUT); - chai.assert.equal(Blockly.navigation.flyoutBlock_.getFieldValue("TEXT"), + chai.assert.equal(Blockly.navigation.getFlyoutCursor_().getCurNode().getLocation().getFieldValue("TEXT"), "FirstCategory-FirstBlock"); }); test('Previous', function() { - Blockly.navigation.selectNextBlockInFlyout(); - chai.assert.equal(Blockly.navigation.flyoutBlock_.getFieldValue("TEXT"), + var flyoutBlocks = this.workspace.getFlyout().getWorkspace().getTopBlocks(); + Blockly.navigation.getFlyoutCursor_().setLocation( + Blockly.ASTNode.createStackNode(flyoutBlocks[1])); + var flyoutBlock = Blockly.navigation.getFlyoutCursor_().getCurNode().getLocation(); + chai.assert.equal(flyoutBlock.getFieldValue("TEXT"), "FirstCategory-SecondBlock"); this.mockEvent.keyCode = Blockly.utils.KeyCodes.W; chai.assert.isTrue(Blockly.navigation.onKeyPress(this.mockEvent)); chai.assert.equal(Blockly.navigation.currentState_, Blockly.navigation.STATE_FLYOUT); - chai.assert.equal(Blockly.navigation.flyoutBlock_.getFieldValue("TEXT"), + flyoutBlock = Blockly.navigation.getFlyoutCursor_().getCurNode().getLocation(); + chai.assert.equal(flyoutBlock.getFieldValue("TEXT"), "FirstCategory-FirstBlock"); }); @@ -175,7 +180,8 @@ suite('Navigation', function() { chai.assert.isTrue(Blockly.navigation.onKeyPress(this.mockEvent)); chai.assert.equal(Blockly.navigation.currentState_, Blockly.navigation.STATE_FLYOUT); - chai.assert.equal(Blockly.navigation.flyoutBlock_.getFieldValue("TEXT"), + var flyoutBlock = Blockly.navigation.getFlyoutCursor_().getCurNode().getLocation(); + chai.assert.equal(flyoutBlock.getFieldValue("TEXT"), "FirstCategory-SecondBlock"); }); @@ -238,18 +244,17 @@ suite('Navigation', function() { }); test('Previous', function() { - var cursor = Blockly.navigation.cursor_; - sinon.spy(cursor, 'prev'); + sinon.spy(this.workspace.getCursor(), 'prev'); this.mockEvent.keyCode = Blockly.utils.KeyCodes.W; chai.assert.isTrue(Blockly.navigation.onKeyPress(this.mockEvent)); - chai.assert.isTrue(cursor.prev.calledOnce); + chai.assert.isTrue(this.workspace.getCursor().prev.calledOnce); chai.assert.equal(Blockly.navigation.currentState_, Blockly.navigation.STATE_WS); - cursor.prev.restore(); + this.workspace.getCursor().prev.restore(); }); test('Next', function() { - var cursor = Blockly.navigation.cursor_; + var cursor = this.workspace.getCursor(); sinon.spy(cursor, 'next'); this.mockEvent.keyCode = Blockly.utils.KeyCodes.S; chai.assert.isTrue(Blockly.navigation.onKeyPress(this.mockEvent)); @@ -260,7 +265,7 @@ suite('Navigation', function() { }); test('Out', function() { - var cursor = Blockly.navigation.cursor_; + var cursor = this.workspace.getCursor(); sinon.spy(cursor, 'out'); this.mockEvent.keyCode = Blockly.utils.KeyCodes.A; chai.assert.isTrue(Blockly.navigation.onKeyPress(this.mockEvent)); @@ -271,7 +276,7 @@ suite('Navigation', function() { }); test('In', function() { - var cursor = Blockly.navigation.cursor_; + var cursor = this.workspace.getCursor(); sinon.spy(cursor, 'in'); this.mockEvent.keyCode = Blockly.utils.KeyCodes.D; chai.assert.isTrue(Blockly.navigation.onKeyPress(this.mockEvent)); @@ -292,11 +297,11 @@ suite('Navigation', function() { }); test('Mark', function() { - Blockly.navigation.cursor_.setLocation( + this.workspace.getCursor().setLocation( Blockly.ASTNode.createConnectionNode(this.basicBlock.previousConnection)); this.mockEvent.keyCode = Blockly.utils.KeyCodes.ENTER; chai.assert.isTrue(Blockly.navigation.onKeyPress(this.mockEvent)); - var markedNode = Blockly.navigation.marker_.getCurNode(); + var markedNode = this.workspace.getMarker().getCurNode(); chai.assert.equal(markedNode.getLocation(), this.basicBlock.previousConnection); chai.assert.equal(Blockly.navigation.currentState_, Blockly.navigation.STATE_WS); @@ -313,6 +318,11 @@ suite('Navigation', function() { suite('Test key press', function() { setup(function() { + this.workspace = new Blockly.Workspace({readOnly: false}); + Blockly.user.keyMap.setKeyMap(Blockly.user.keyMap.createDefaultKeyMap()); + Blockly.mainWorkspace = this.workspace; + Blockly.keyboardAccessibilityMode = true; + this.mockEvent = { getModifierState: function() { return false; @@ -320,11 +330,9 @@ suite('Navigation', function() { }; }); test('Action does not exist', function() { - var cursor = new Blockly.Cursor(); - var field = new Blockly.Field('value'); - Blockly.navigation.setCursor(cursor); + var field = new Blockly.FieldDropdown([['a','b'], ['c','d']]); sinon.spy(field, 'onBlocklyAction'); - cursor.setLocation(Blockly.ASTNode.createFieldNode(field)); + this.workspace.getCursor().setLocation(Blockly.ASTNode.createFieldNode(field)); this.mockEvent.keyCode = Blockly.utils.KeyCodes.N; var isHandled = Blockly.navigation.onKeyPress(this.mockEvent); @@ -335,45 +343,33 @@ suite('Navigation', function() { }); test('Action exists - field handles action', function() { - var cursor = new Blockly.Cursor(); - var field = new Blockly.Field('value'); - Blockly.navigation.setCursor(cursor); - sinon.spy(Blockly.navigation, 'onBlocklyAction'); + var field = new Blockly.FieldDropdown([['a','b'], ['c','d']]); sinon.stub(field, 'onBlocklyAction').callsFake(function(){ return true; }); - cursor.setLocation(Blockly.ASTNode.createFieldNode(field)); + this.workspace.getCursor().setLocation(Blockly.ASTNode.createFieldNode(field)); - this.mockEvent.keyCode = Blockly.utils.KeyCodes.A; - var isHandled = Blockly.navigation.onKeyPress(this.mockEvent); + var isHandled = Blockly.navigation.onBlocklyAction(Blockly.navigation.ACTION_OUT); chai.assert.isTrue(isHandled); chai.assert.isTrue(field.onBlocklyAction.calledOnce); - chai.assert.isFalse(Blockly.navigation.onBlocklyAction.calledOnce); - Blockly.navigation.onBlocklyAction.restore(); field.onBlocklyAction.restore(); }); test('Action exists - field does not handle action', function() { - var cursor = new Blockly.Cursor(); - var field = new Blockly.Field('value'); - Blockly.navigation.setCursor(cursor); + var field = new Blockly.FieldDropdown([['a','b'], ['c','d']]); sinon.spy(field, 'onBlocklyAction'); - sinon.spy(Blockly.navigation, 'onBlocklyAction'); - cursor.setLocation(Blockly.ASTNode.createFieldNode(field)); + this.workspace.getCursor().setLocation(Blockly.ASTNode.createFieldNode(field)); this.mockEvent.keyCode = Blockly.utils.KeyCodes.A; - var isHandled = Blockly.navigation.onKeyPress(this.mockEvent); + var isHandled = Blockly.navigation.onBlocklyAction(Blockly.navigation.ACTION_OUT); chai.assert.isTrue(isHandled); chai.assert.isTrue(field.onBlocklyAction.calledOnce); field.onBlocklyAction.restore(); - Blockly.navigation.onBlocklyAction.restore(); }); test('Toggle Action Off', function() { - var cursor = new Blockly.Cursor(); - Blockly.navigation.setCursor(cursor); this.mockEvent.keyCode = 'Control75'; sinon.spy(Blockly.navigation, 'onBlocklyAction'); Blockly.keyboardAccessibilityMode = true; @@ -386,9 +382,6 @@ suite('Navigation', function() { }); test('Toggle Action On', function() { - var cursor = new Blockly.Cursor(); - Blockly.navigation.setCursor(cursor); - this.workspace = Blockly.inject('blocklyDiv', {readOnly: false}); this.mockEvent.keyCode = 'Control75'; sinon.stub(Blockly.navigation, 'focusWorkspace'); Blockly.keyboardAccessibilityMode = false; @@ -429,7 +422,7 @@ suite('Navigation', function() { "helpUrl": "" }]); this.workspace = new Blockly.Workspace({readOnly: true}); - Blockly.navigation.setCursor(this.workspace.cursor); + this.workspace.setCursor(new Blockly.Cursor()); Blockly.mainWorkspace = this.workspace; this.fieldBlock1 = this.workspace.newBlock('field_block'); Blockly.keyboardAccessibilityMode = true; @@ -448,14 +441,14 @@ suite('Navigation', function() { test('Perform valid action for read only', function() { var astNode = Blockly.ASTNode.createBlockNode(this.fieldBlock1); - this.workspace.cursor.setLocation(astNode); + this.workspace.getCursor().setLocation(astNode); this.mockEvent.keyCode = Blockly.utils.KeyCodes.S; chai.assert.isTrue(Blockly.navigation.onKeyPress(this.mockEvent)); }); test('Perform invalid action for read only', function() { var astNode = Blockly.ASTNode.createBlockNode(this.fieldBlock1); - this.workspace.cursor.setLocation(astNode); + this.workspace.getCursor().setLocation(astNode); this.mockEvent.keyCode = Blockly.utils.KeyCodes.I; chai.assert.isFalse(Blockly.navigation.onKeyPress(this.mockEvent)); }); @@ -463,7 +456,7 @@ suite('Navigation', function() { test('Try to perform action on a field', function() { var field = this.fieldBlock1.inputList[0].fieldRow[0]; var astNode = Blockly.ASTNode.createFieldNode(field); - this.workspace.cursor.setLocation(astNode); + this.workspace.getCursor().setLocation(astNode); this.mockEvent.keyCode = Blockly.utils.KeyCodes.ENTER; chai.assert.isFalse(Blockly.navigation.onKeyPress(this.mockEvent)); }); @@ -498,7 +491,7 @@ suite('Navigation', function() { test('Insert from flyout with a valid connection marked', function() { var previousConnection = this.basicBlock.previousConnection; var prevNode = Blockly.ASTNode.createConnectionNode(previousConnection); - Blockly.navigation.marker_.setLocation(prevNode); + this.workspace.getMarker().setLocation(prevNode); Blockly.navigation.focusToolbox(); Blockly.navigation.focusFlyout(); @@ -530,10 +523,10 @@ suite('Navigation', function() { test('Connect two blocks that are on the workspace', function() { var targetNode = Blockly.ASTNode.createConnectionNode(this.basicBlock.previousConnection); - Blockly.navigation.marker_.setLocation(targetNode); + this.workspace.getMarker().setLocation(targetNode); var sourceNode = Blockly.ASTNode.createConnectionNode(this.basicBlock2.nextConnection); - Blockly.navigation.cursor_.setLocation(sourceNode); + this.workspace.getCursor().setLocation(sourceNode); Blockly.navigation.modify_(); var insertedBlock = this.basicBlock.previousConnection.targetBlock(); @@ -678,18 +671,18 @@ suite('Navigation', function() { this.basicBlockA.nextConnection.connect(this.basicBlockB.previousConnection); var astNode = Blockly.ASTNode.createBlockNode(this.basicBlockB); // Set the cursor to be on the child block - this.workspace.cursor.setLocation(astNode); + this.workspace.getCursor().setLocation(astNode); // Remove the child block this.basicBlockB.dispose(); - chai.assert.equal(this.workspace.cursor.getCurNode().getType(), + chai.assert.equal(this.workspace.getCursor().getCurNode().getType(), Blockly.ASTNode.types.NEXT); }); test('Delete block - no parent ', function() { var astNode = Blockly.ASTNode.createBlockNode(this.basicBlockB); - this.workspace.cursor.setLocation(astNode); + this.workspace.getCursor().setLocation(astNode); this.basicBlockB.dispose(); - chai.assert.equal(this.workspace.cursor.getCurNode().getType(), + chai.assert.equal(this.workspace.getCursor().getCurNode().getType(), Blockly.ASTNode.types.WORKSPACE); }); @@ -697,10 +690,10 @@ suite('Navigation', function() { this.basicBlockA.nextConnection.connect(this.basicBlockB.previousConnection); var astNode = Blockly.ASTNode.createBlockNode(this.basicBlockB); // Set the cursor to be on the child block - this.workspace.cursor.setLocation(astNode); + this.workspace.getCursor().setLocation(astNode); // Remove the parent block this.basicBlockA.dispose(); - chai.assert.equal(this.workspace.cursor.getCurNode().getType(), + chai.assert.equal(this.workspace.getCursor().getCurNode().getType(), Blockly.ASTNode.types.WORKSPACE); }); @@ -708,10 +701,10 @@ suite('Navigation', function() { this.basicBlockA.nextConnection.connect(this.basicBlockB.previousConnection); var astNode = Blockly.ASTNode.createStackNode(this.basicBlockA); // Set the cursor to be on the stack - this.workspace.cursor.setLocation(astNode); + this.workspace.getCursor().setLocation(astNode); // Remove the top block in the stack this.basicBlockA.dispose(); - chai.assert.equal(this.workspace.cursor.getCurNode().getType(), + chai.assert.equal(this.workspace.getCursor().getCurNode().getType(), Blockly.ASTNode.types.WORKSPACE); }); });