Skip to content

Commit

Permalink
Add preferences to extension API (#1341)
Browse files Browse the repository at this point in the history
* Add options menu

* Default to empty list

* Expect test to be function

* Rename to settings and add helpers

* Add test for menu item and settings

* Fix other failing tests
  • Loading branch information
gsteinLTU authored Dec 14, 2022
1 parent aa905a1 commit de91a58
Show file tree
Hide file tree
Showing 3 changed files with 111 additions and 6 deletions.
27 changes: 27 additions & 0 deletions src/extensions.js
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,10 @@
return null;
};

Extension.prototype.getSettings = function() {
return [];
};

Extension.prototype.getCategories = function() {
return [];
};
Expand All @@ -182,6 +186,29 @@
Extension.prototype.onOpenRole = function() {
};

class ExtensionSetting {
constructor(label, toggle, test, onHint = '', offHint = '', hide = false) {
this.label = label;
this.toggle = toggle,
this.test = test,
this.onHint = onHint,
this.offHint = offHint,
this.hide = hide
}
}

ExtensionSetting.createFromLocalStorage = function(label, id, defaultValue = false, onHint = '', offHint = '', hide = false){
return new ExtensionSetting(
label,
() => {
window.localStorage.setItem(id, !(window.localStorage.getItem(id) ?? defaultValue));
},
() => window.localStorage.getItem(id) ?? defaultValue,
onHint, offHint, hide);
}

Extension.ExtensionSetting = ExtensionSetting;

class LabelPart {
constructor(spec, fn) {
if (spec[0] !== '%') {
Expand Down
51 changes: 50 additions & 1 deletion src/gui-ext.js
Original file line number Diff line number Diff line change
Expand Up @@ -482,7 +482,56 @@ IDE_Morph.prototype.extensionsMenu = function() {
return menu;
};

return menuFromDict(dict);
let menu = menuFromDict(dict);

const on = new SymbolMorph(
'checkedBox',
MorphicPreferences.menuFontSize * 0.75
),
off = new SymbolMorph(
'rectangle',
MorphicPreferences.menuFontSize * 0.75
);

// Add preferences
this.extensions.registry
.filter(ext => ext.getSettings())
.forEach(ext => {
const name = ext.name || ext.constructor.name;
let thisExtMenu = menu.items.find(item => item[0] == name);

let prefs = ext.getSettings();

if(thisExtMenu){
thisExtMenu = thisExtMenu[1];

// Only show menu if there is a non-hidden option available
if(prefs.find(pref => !pref.hide || world.currentKey == 16) !== undefined){
let newOptionsMenu = new MenuMorph(this);
thisExtMenu.addMenu('Options', newOptionsMenu);

// Add each setting as a toggle
prefs.forEach(pref => {

let test = pref.test;

if (!pref.hide || world.currentKey == 16) {
newOptionsMenu.addItem(
[
(test() ? on : off),
pref.label
],
pref.toggle,
test() ? pref.onHint : pref.offHint,
pref.hide ? new Color(100, 0, 0) : null
);
}
});
}
}
});

return menu;
};

IDE_Morph.prototype.requestProjectReload = async function (reason) {
Expand Down
39 changes: 34 additions & 5 deletions test/extensions.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,16 @@ describe('extensions', function() {
TestExtension.prototype = new Extension('TestExt');
TestExtension.prototype.getMenu = () => {
return {
'hello!': function() {},
'TestMenuItem': function() {},
};
};
TestExtension.prototype.getSettings = () => {
return [new Extension.ExtensionSetting(
'Test Setting',
() => {},
() => false
)];
};
TestExtension.prototype.getCategories = () => [
new Extension.Category(
'TEST!',
Expand Down Expand Up @@ -107,6 +114,27 @@ describe('extensions', function() {
driver.dialog().destroy();
});

it('should create menu item', function() {
driver.click(driver.ide().controlBar.extensionsButton);
driver.click(driver.dialog().children[1]);
const subMenuItems = driver.dialog().children[2].children.map(c => c.labelString);
assert(subMenuItems.includes('TestMenuItem'));
driver.dialog().destroy();
});

it('should create settings', function() {
driver.click(driver.ide().controlBar.extensionsButton);
driver.click(driver.dialog().children[1]);
const subMenuItems = driver.dialog().children[2].children.map(c => c.labelString);
assert(subMenuItems.includes('Options'));
driver.click(driver.dialog().children[2].children[2]);

const optionMenuItems = driver.dialog().children[2].children[3].children.map(c => c.labelString);
assert(optionMenuItems.find(item => item && item[1] == 'Test Setting'));

driver.dialog().destroy();
});

it('should not load an extension twice', function() {
const {NetsBloxExtensions} = driver.globals();
const extCount = NetsBloxExtensions.registry.length;
Expand Down Expand Up @@ -143,15 +171,16 @@ describe('extensions', function() {
driver.selectCategory('TEST!');
assert.equal(
driver.palette().contents.children.length,
2
5
);
});

it('should show new blocks on the stage', function() {
driver.selectStage();
driver.selectCategory('TEST!');
assert(
driver.palette().contents.children.length > 1
assert.equal(
driver.palette().contents.children.length,
4
);
});

Expand Down Expand Up @@ -183,7 +212,7 @@ describe('extensions', function() {
driver.selectCategory('TEST!');
const block = driver.palette().contents.children.find(child => child.selector === 'spriteBlock');
const [inputSlot] = block.inputs();
assert.equal(inputSlot.evaluate(), 'this is a second test');
assert(Object.keys(inputSlot.choices).includes('this is a second test'));
});

it('should hide sprite block on stage', function() {
Expand Down

0 comments on commit de91a58

Please sign in to comment.