diff --git a/content/src/controller.js b/content/src/controller.js index 9fd6b6f8..b832898d 100644 --- a/content/src/controller.js +++ b/content/src/controller.js @@ -172,8 +172,7 @@ function updateTopControls(addHistory) { { id: 'saveas', label: 'Copy and Save As...' } ], disabled: !cansave(), - }, - { + }, { id: 'screenshot', title: 'Take screenshot', label: '' @@ -191,6 +190,7 @@ function updateTopControls(addHistory) { // if (!specialowner()) { + // Applies to both files and dirs: a simple "new file" button. buttons.push({ id: 'new', title: 'Make a new program', label: 'New'}); @@ -234,6 +234,20 @@ function updateTopControls(addHistory) { id: 'guide', label: 'Guide', title: 'Open online guide'}); } + + // + // If this directory has an owner (i.e., not the root owner), + // enable splitscreen toggle. + // + + if (model.ownername) { + buttons.push({ + id: 'splitscreen', + title: 'Toggle split screen', + label: '' + }); + } + } // buttons.push({id: 'done', label: 'Done', title: 'tooltip text'}); view.showButtons(buttons); @@ -456,6 +470,9 @@ function runAction() { cancelAndClearPosition('back'); rotateModelLeft(true); } + if (!view.getPreviewMode()) { + view.setPreviewMode(true, true /* no animation */); + } // Hide the guide, if any if (guide.isVisible()) { guide.show(false); @@ -743,6 +760,10 @@ view.on('toggleblocks', function(p, useblocks) { view.getPaneEditorLanguage(p)); }); +view.on('splitscreen', function() { + view.setPreviewMode(!view.getPreviewMode()); +}); + function saveAction(forceOverwrite, loginPrompt, doneCallback) { if (nosaveowner()) { return; diff --git a/content/src/editor.less b/content/src/editor.less index e0bd58e8..503c91d4 100644 --- a/content/src/editor.less +++ b/content/src/editor.less @@ -269,6 +269,13 @@ body.modal #overlay { position: relative; } +#splitscreen > .splitscreenicon { + display: block; + height: 16px; + width: 19px; + background-image: url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTkiIGhlaWdodD0iMTYiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHJlY3QgeD0iMTAiIHk9IjEiIHdpZHRoPSI4IiBoZWlnaHQ9IjE0IiBzdHJva2U9IiMzMzMiIGZpbGw9Im5vbmUiIHN0cm9rZS13aWR0aD0iMiIvPjxwYXRoIGQ9Ik0wLDIgTDYsMiBNMCw2IEw4LDYgTTAsMTAgTDYsMTAgTTAsMTQgTDcsMTQiIGZpbGw9Im5vbmUiIHN0cm9rZT0iIzMzMyIgc3Ryb2tlLXdpZHRoPSIyIi8+PC9zdmc+"); +} + #topright button .droparrow { position: absolute; right: 0; @@ -509,6 +516,10 @@ body.modal #middle { margin-right: -28px; } +#middle.rightedge { + right: 38px; +} + .framed #middle, .framed .vcenter { margin-top: -28px; } diff --git a/content/src/view.js b/content/src/view.js index cbeb138f..32061977 100644 --- a/content/src/view.js +++ b/content/src/view.js @@ -1087,19 +1087,27 @@ function showLoginDialog(opts) { // PANE MANAGEMENT /////////////////////////////////////////////////////////////////////////// -function setPreviewMode(shown, instant) { - var duration = instant ? 0 : 400; +function setPreviewMode(shown, noanimation) { + var change = (shown != state.previewMode); + var delay = (noanimation || !change) ? 0 : 400; if (shown) { - $('#middle').show(); - $('.right').css({left: '50%', width: '50%'}); - $('.left').css({width: '50%'}); + $('#middle').removeClass('rightedge'); + $('.right').animate({left: '50%', width: '50%'}, delay); + $('.left').animate({width: '50%'}, delay, finished); $('.back').css({left: '-50%', width: '50%'}); } else { - $('#middle').hide(); - $('.right').css({left: '100%', width: '100%'}); - $('.left').css({width: '100%'}); + $('#middle').addClass('rightedge'); + $('.right').animate({left: '100%', width: '100%'}, delay); + $('.left').animate({width: '100%'}, delay, finished); $('.back').css({left: '-100%', width: '100%'}); - clearPane(paneid('right')); + // clearPane(paneid('right')); + } + function finished() { + if (change) { + // Tell all editors and directory listings to resize. + $(window).trigger('resize.editor'); + $(window).trigger('resize.listing'); + } } state.previewMode = shown; } @@ -1115,7 +1123,10 @@ function rotateLeft() { $('.back').finish().css({left:'100%'}); $('.left').finish().animate({left: '-50%'}); $('.right').finish().animate({left: 0}); - $('.back').animate({left: '50%'}); + $('.back').animate({left: '50%'}, function() { + // Pin this div - chrome can sometimes scroll it even with overflow:hidden + $('#overflow').scrollLeft(0); + }); $(panelParts(idb)).removeClass('back').addClass('right'); $(panelParts(idr)).removeClass('right').addClass('left'); $(panelParts(idl)).removeClass('left').addClass('back'); @@ -1129,7 +1140,10 @@ function rotateRight() { $('.back').finish().css({left:'-50%'}); $('.right').finish().animate({left: '100%'}); $('.left').finish().animate({left: '50%'}); - $('.back').animate({left: 0}); + $('.back').animate({left: 0}, function() { + // Pin this div - chrome can sometimes scroll it even with overflow:hidden + $('#overflow').scrollLeft(0); + }); $(panelParts(idb)).removeClass('back').addClass('left'); $(panelParts(idr)).removeClass('right').addClass('back'); $(panelParts(idl)).removeClass('left').addClass('right'); @@ -2393,6 +2407,10 @@ function setPaneEditorData(pane, doc, filename, useblocks) { paneState.settingUp = null; updatePaneTitle(pane); + + // Work around undesired scrolling bug - + // repro: turn off split pane view, and linger over a file to force preload. + $('#overflow').scrollLeft(0); } function setupSubEditor(box, pane, paneState, text, htmlorcss, tearDown) { @@ -2776,6 +2794,9 @@ function setPaneEditorReadOnly(pane, ro) { var containers = []; if (!paneState.editor) { return; } paneState.editor.setReadOnly(ro); + if (paneState.dropletEditor) { + paneState.dropletEditor.setReadOnly(ro); + } containers.push(paneState.editor.container); if (paneState.htmlEditor) { paneState.htmlEditor.setReadOnly(ro); diff --git a/test/edit_code.js b/test/edit_code.js index 05df24f0..76fe7e76 100644 --- a/test/edit_code.js +++ b/test/edit_code.js @@ -476,6 +476,31 @@ describe('code editor', function() { done(); }); }); + it('should be able to switch to single-pane mode', function(done) { + asyncTest(_page, one_step_timeout, null, function() { + // Click on the triangle run button. + $('#splitscreen').click(); + }, function() { + try { + // Wait for the preview frame to show + if (!/100/.test($('#charliepanebox').prop('style').left)) return; + return { + bravowidth: $('#bravopanebox').prop('style').width, + middleclass: $('#middle').prop('class') + }; + } + catch(e) { + return {poll: true, error: e}; + } + }, function(err, result) { + assert.ifError(err); + // The main pane should be 100% wide. + assert.equal(result.bravowidth, '100%'); + // The middle button should be pulled right. + assert.equal(result.middleclass, 'rightedge'); + done(); + }); + }); it('should be able to run the program', function(done) { asyncTest(_page, one_step_timeout, null, function() { // Click on the triangle run button. @@ -496,6 +521,7 @@ describe('code editor', function() { getxy: seval('getxy()'), touchesred: seval('touches red'), touchesblue: seval('touches blue'), + bravowidth: $('#bravopanebox').prop('style').width, queuelen: seval('turtle.queue().length') }; } @@ -513,6 +539,8 @@ describe('code editor', function() { assert.equal(result.touchesred, false); // The turtle should be touching blue pixels that it drew. assert.equal(result.touchesblue, true); + // The main pane should be 50% wide again. + assert.equal(result.bravowidth, '50%'); // There should be no further animations on the turtle queue. assert.equal(result.queuelen, 0); done();