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();