parent
989699c620
commit
83700b4e14
@ -7,6 +7,7 @@ import { FieldSpeed } from "./field_speed";
|
|||||||
import { FieldBrickButtons } from "./field_brickbuttons";
|
import { FieldBrickButtons } from "./field_brickbuttons";
|
||||||
import { FieldTurnRatio } from "./field_turnratio";
|
import { FieldTurnRatio } from "./field_turnratio";
|
||||||
import { FieldColorEnum } from "./field_color";
|
import { FieldColorEnum } from "./field_color";
|
||||||
|
import { FieldMusic } from "./field_music";
|
||||||
|
|
||||||
pxt.editor.initFieldExtensionsAsync = function (opts: pxt.editor.FieldExtensionOptions): Promise<pxt.editor.FieldExtensionResult> {
|
pxt.editor.initFieldExtensionsAsync = function (opts: pxt.editor.FieldExtensionOptions): Promise<pxt.editor.FieldExtensionResult> {
|
||||||
pxt.debug('loading pxt-ev3 target extensions...')
|
pxt.debug('loading pxt-ev3 target extensions...')
|
||||||
@ -30,6 +31,9 @@ pxt.editor.initFieldExtensionsAsync = function (opts: pxt.editor.FieldExtensionO
|
|||||||
}, {
|
}, {
|
||||||
selector: "colorenum",
|
selector: "colorenum",
|
||||||
editor: FieldColorEnum
|
editor: FieldColorEnum
|
||||||
|
}, {
|
||||||
|
selector: "music",
|
||||||
|
editor: FieldMusic
|
||||||
}]
|
}]
|
||||||
};
|
};
|
||||||
return Promise.resolve<pxt.editor.FieldExtensionResult>(res);
|
return Promise.resolve<pxt.editor.FieldExtensionResult>(res);
|
||||||
|
@ -544,7 +544,7 @@ export class FieldMotors extends Blockly.FieldDropdown implements Blockly.FieldC
|
|||||||
/**
|
/**
|
||||||
* Callback for when the drop-down is hidden.
|
* Callback for when the drop-down is hidden.
|
||||||
*/
|
*/
|
||||||
protected onHide_ = function () {
|
protected onHide_() {
|
||||||
Blockly.DropDownDiv.content_.removeAttribute('role');
|
Blockly.DropDownDiv.content_.removeAttribute('role');
|
||||||
Blockly.DropDownDiv.content_.removeAttribute('aria-haspopup');
|
Blockly.DropDownDiv.content_.removeAttribute('aria-haspopup');
|
||||||
Blockly.DropDownDiv.content_.removeAttribute('aria-activedescendant');
|
Blockly.DropDownDiv.content_.removeAttribute('aria-activedescendant');
|
||||||
|
311
fieldeditors/field_music.ts
Normal file
311
fieldeditors/field_music.ts
Normal file
@ -0,0 +1,311 @@
|
|||||||
|
/// <reference path="../node_modules/pxt-core/localtypings/blockly.d.ts"/>
|
||||||
|
/// <reference path="../node_modules/pxt-core/built/pxtblocks.d.ts"/>
|
||||||
|
/// <reference path="../node_modules/pxt-core/built/pxtsim.d.ts"/>
|
||||||
|
|
||||||
|
export interface FieldMusicOptions extends pxtblockly.FieldImagesOptions {
|
||||||
|
columns?: string;
|
||||||
|
width?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare const pxtTargetBundle: any;
|
||||||
|
|
||||||
|
let soundCache: any;
|
||||||
|
|
||||||
|
export class FieldMusic extends pxtblockly.FieldImages implements Blockly.FieldCustom {
|
||||||
|
public isFieldCustom_ = true;
|
||||||
|
|
||||||
|
private selectedCategory_: string;
|
||||||
|
|
||||||
|
private categoriesCache_: string[];
|
||||||
|
|
||||||
|
private static MUSIC_DATA_URI = `data:image/svg+xml;base64,PHN2ZyBpZD0ic3ZnNDEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgdmlld0JveD0iMCAwIDIzIDIzIj48cGF0aCBpZD0ibWVudV9pY25fbXVzaWMiIGQ9Ik0xMy45IDEyLjhjMS43LjMgMy4zIDEuMiA0LjMgMi42aDFzMS41LTQuNC0xLjgtNy41LTkuNy0zLjEtMTIuMSAwYy0xLjcgMi4xLTIuMyA1LTEuNCA3LjVoLjhzMS43LTIuNSA0LjQtMi42QzkgMTcuMiA5IDIxIDkgMjFjLTEuOS0uNC0zLjUtMS42LTQuNC0zLjQtMi0uNC0zLjYtMi4yLTMuNi00LjRDMSA2LjcgNS45IDMgMTEuNSAzczEwLjggNC4zIDEwLjQgMTAuMmMtLjIgNC4xLTMuNiA0LjQtMy42IDQuNC0uOCAxLjgtMi40IDMuMS00LjMgMy40LS4xLTQuNS0uMS04LjItLjEtOC4yeiIgZmlsbD0iI2ZmZiIvPjwvc3ZnPg==`;
|
||||||
|
|
||||||
|
constructor(text: string, options: FieldMusicOptions, validator?: Function) {
|
||||||
|
super(text, { sort: true, data: options.data }, validator);
|
||||||
|
|
||||||
|
this.columns_ = parseInt(options.columns) || 4;
|
||||||
|
this.width_ = parseInt(options.width) || 380;
|
||||||
|
|
||||||
|
this.setText = Blockly.FieldDropdown.prototype.setText;
|
||||||
|
this.updateWidth = (Blockly.Field as any).prototype.updateWidth;
|
||||||
|
this.updateTextNode_ = Blockly.Field.prototype.updateTextNode_;
|
||||||
|
|
||||||
|
if (!pxt.BrowserUtils.isIE() && !soundCache) {
|
||||||
|
soundCache = JSON.parse(pxtTargetBundle.bundledpkgs['music']['sounds.jres']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a dropdown menu under the text.
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
public showEditor_() {
|
||||||
|
// If there is an existing drop-down we own, this is a request to hide the drop-down.
|
||||||
|
if (Blockly.DropDownDiv.hideIfOwner(this)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// If there is an existing drop-down someone else owns, hide it immediately and clear it.
|
||||||
|
Blockly.DropDownDiv.hideWithoutAnimation();
|
||||||
|
Blockly.DropDownDiv.clearContent();
|
||||||
|
// Populate the drop-down with the icons for this field.
|
||||||
|
let dropdownDiv = Blockly.DropDownDiv.getContentDiv();
|
||||||
|
let contentDiv = document.createElement('div');
|
||||||
|
// Accessibility properties
|
||||||
|
contentDiv.setAttribute('role', 'menu');
|
||||||
|
contentDiv.setAttribute('aria-haspopup', 'true');
|
||||||
|
contentDiv.className = 'blocklyMusicFieldOptions';
|
||||||
|
const options = this.getOptions();
|
||||||
|
options.sort();
|
||||||
|
|
||||||
|
// Create categoies
|
||||||
|
const categories = this.getCategories(options);
|
||||||
|
const selectedCategory = this.parseCategory(this.getText());
|
||||||
|
this.selectedCategory_ = selectedCategory || categories[0];
|
||||||
|
|
||||||
|
let categoriesDiv = document.createElement('div');
|
||||||
|
// Accessibility properties
|
||||||
|
categoriesDiv.setAttribute('role', 'menu');
|
||||||
|
categoriesDiv.setAttribute('aria-haspopup', 'true');
|
||||||
|
categoriesDiv.style.backgroundColor = this.sourceBlock_.getColourTertiary();
|
||||||
|
categoriesDiv.className = 'blocklyMusicFieldCategories';
|
||||||
|
|
||||||
|
this.refreshCategories(categoriesDiv, categories);
|
||||||
|
|
||||||
|
this.refreshOptions(contentDiv, options);
|
||||||
|
|
||||||
|
contentDiv.style.width = (this as any).width_ + 'px';
|
||||||
|
contentDiv.style.cssFloat = 'left';
|
||||||
|
|
||||||
|
dropdownDiv.style.maxHeight = `410px`;
|
||||||
|
dropdownDiv.appendChild(categoriesDiv);
|
||||||
|
dropdownDiv.appendChild(contentDiv);
|
||||||
|
|
||||||
|
Blockly.DropDownDiv.setColour(this.sourceBlock_.getColour(), this.sourceBlock_.getColourTertiary());
|
||||||
|
|
||||||
|
// Calculate positioning based on the field position.
|
||||||
|
let scale = this.sourceBlock_.workspace.scale;
|
||||||
|
let bBox = { width: this.size_.width, height: this.size_.height };
|
||||||
|
bBox.width *= scale;
|
||||||
|
bBox.height *= scale;
|
||||||
|
let position = this.fieldGroup_.getBoundingClientRect();
|
||||||
|
let primaryX = position.left + bBox.width / 2;
|
||||||
|
let primaryY = position.top + bBox.height;
|
||||||
|
let secondaryX = primaryX;
|
||||||
|
let secondaryY = position.top;
|
||||||
|
// Set bounds to workspace; show the drop-down.
|
||||||
|
(Blockly.DropDownDiv as any).setBoundsElement(this.sourceBlock_.workspace.getParentSvg().parentNode);
|
||||||
|
(Blockly.DropDownDiv as any).show(this, primaryX, primaryY, secondaryX, secondaryY,
|
||||||
|
this.onHide_.bind(this));
|
||||||
|
|
||||||
|
// Update colour to look selected.
|
||||||
|
if (this.sourceBlock_.isShadow()) {
|
||||||
|
this.savedPrimary_ = this.sourceBlock_.getColour();
|
||||||
|
this.sourceBlock_.setColour(this.sourceBlock_.getColourTertiary(),
|
||||||
|
this.sourceBlock_.getColourSecondary(), this.sourceBlock_.getColourTertiary());
|
||||||
|
} else if (this.box_) {
|
||||||
|
this.box_.setAttribute('fill', this.sourceBlock_.getColourTertiary());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getCategories(options: any) {
|
||||||
|
if (this.categoriesCache_) return this.categoriesCache_;
|
||||||
|
let categoryMap = {};
|
||||||
|
for (let i = 0, option: any; option = options[i]; i++) {
|
||||||
|
const content = (options[i] as any)[0]; // Human-readable text or image.
|
||||||
|
const category = this.parseCategory(content);
|
||||||
|
categoryMap[category] = true;
|
||||||
|
}
|
||||||
|
this.categoriesCache_ = Object.keys(categoryMap);
|
||||||
|
return this.categoriesCache_;
|
||||||
|
}
|
||||||
|
|
||||||
|
refreshCategories(categoriesDiv: Element, categories: string[]) {
|
||||||
|
// Show category dropdown.
|
||||||
|
for (let i = 0; i < categories.length; i++) {
|
||||||
|
const category = categories[i];
|
||||||
|
|
||||||
|
let button = document.createElement('button');
|
||||||
|
button.setAttribute('id', ':' + i); // For aria-activedescendant
|
||||||
|
button.setAttribute('role', 'menuitem');
|
||||||
|
button.setAttribute('class', 'blocklyDropdownTag');
|
||||||
|
button.setAttribute('data-value', category);
|
||||||
|
|
||||||
|
let backgroundColor = '#1A9DBC';
|
||||||
|
if (category == this.selectedCategory_) {
|
||||||
|
// This icon is selected, show it in a different colour
|
||||||
|
backgroundColor = '#0c4e5e';
|
||||||
|
button.setAttribute('aria-selected', 'true');
|
||||||
|
}
|
||||||
|
button.style.backgroundColor = backgroundColor;
|
||||||
|
button.style.borderColor = backgroundColor;
|
||||||
|
Blockly.bindEvent_(button, 'click', this, this.categoryClick_);
|
||||||
|
Blockly.bindEvent_(button, 'mouseup', this, this.categoryClick_);
|
||||||
|
|
||||||
|
const textNode = this.createTextNode_(category);
|
||||||
|
textNode.setAttribute('data-value', category);
|
||||||
|
button.appendChild(textNode);
|
||||||
|
categoriesDiv.appendChild(button);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
refreshOptions(contentDiv: Element, options: any) {
|
||||||
|
|
||||||
|
// Show options
|
||||||
|
for (let i = 0, option: any; option = options[i]; i++) {
|
||||||
|
let content = (options[i] as any)[0]; // Human-readable text or image.
|
||||||
|
const value = (options[i] as any)[1]; // Language-neutral value.
|
||||||
|
|
||||||
|
// Filter for options in selected category
|
||||||
|
const category = this.parseCategory(content);
|
||||||
|
if (this.selectedCategory_ != category) continue;
|
||||||
|
|
||||||
|
// Icons with the type property placeholder take up space but don't have any functionality
|
||||||
|
// Use for special-case layouts
|
||||||
|
if (content.type == 'placeholder') {
|
||||||
|
let placeholder = document.createElement('span');
|
||||||
|
placeholder.setAttribute('class', 'blocklyDropDownPlaceholder');
|
||||||
|
placeholder.style.width = content.width + 'px';
|
||||||
|
placeholder.style.height = content.height + 'px';
|
||||||
|
contentDiv.appendChild(placeholder);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let button = document.createElement('button');
|
||||||
|
button.setAttribute('id', ':' + i); // For aria-activedescendant
|
||||||
|
button.setAttribute('role', 'menuitem');
|
||||||
|
button.setAttribute('class', 'blocklyDropDownButton');
|
||||||
|
button.title = content;
|
||||||
|
if ((this as any).columns_) {
|
||||||
|
button.style.width = (((this as any).width_ / (this as any).columns_) - 8) + 'px';
|
||||||
|
//button.style.height = ((this.width_ / this.columns_) - 8) + 'px';
|
||||||
|
} else {
|
||||||
|
button.style.width = content.width + 'px';
|
||||||
|
button.style.height = content.height + 'px';
|
||||||
|
}
|
||||||
|
let backgroundColor = this.savedPrimary_ || this.sourceBlock_.getColour();
|
||||||
|
if (value == this.getValue()) {
|
||||||
|
// This icon is selected, show it in a different colour
|
||||||
|
backgroundColor = this.sourceBlock_.getColourTertiary();
|
||||||
|
button.setAttribute('aria-selected', 'true');
|
||||||
|
}
|
||||||
|
button.style.backgroundColor = backgroundColor;
|
||||||
|
button.style.borderColor = this.sourceBlock_.getColourTertiary();
|
||||||
|
Blockly.bindEvent_(button, 'click', this, this.buttonClick_);
|
||||||
|
Blockly.bindEvent_(button, 'mouseup', this, this.buttonClick_);
|
||||||
|
// These are applied manually instead of using the :hover pseudoclass
|
||||||
|
// because Android has a bad long press "helper" menu and green highlight
|
||||||
|
// that we must prevent with ontouchstart preventDefault
|
||||||
|
let that = this;
|
||||||
|
Blockly.bindEvent_(button, 'mousedown', button, function (e) {
|
||||||
|
this.setAttribute('class', 'blocklyDropDownButton blocklyDropDownButtonHover');
|
||||||
|
e.preventDefault();
|
||||||
|
});
|
||||||
|
Blockly.bindEvent_(button, 'mouseenter', button, function () {
|
||||||
|
that.buttonEnter_(value);
|
||||||
|
});
|
||||||
|
Blockly.bindEvent_(button, 'mouseleave', button, function () {
|
||||||
|
that.buttonLeave_();
|
||||||
|
});
|
||||||
|
Blockly.bindEvent_(button, 'mouseover', button, function () {
|
||||||
|
this.setAttribute('class', 'blocklyDropDownButton blocklyDropDownButtonHover');
|
||||||
|
contentDiv.setAttribute('aria-activedescendant', this.id);
|
||||||
|
});
|
||||||
|
Blockly.bindEvent_(button, 'mouseout', button, function () {
|
||||||
|
this.setAttribute('class', 'blocklyDropDownButton');
|
||||||
|
contentDiv.removeAttribute('aria-activedescendant');
|
||||||
|
});
|
||||||
|
let buttonImg = document.createElement('img');
|
||||||
|
buttonImg.src = FieldMusic.MUSIC_DATA_URI;
|
||||||
|
//buttonImg.alt = icon.alt;
|
||||||
|
// Upon click/touch, we will be able to get the clicked element as e.target
|
||||||
|
// Store a data attribute on all possible click targets so we can match it to the icon.
|
||||||
|
const textNode = this.createTextNode_(content);
|
||||||
|
button.setAttribute('data-value', value);
|
||||||
|
buttonImg.setAttribute('data-value', value);
|
||||||
|
textNode.setAttribute('data-value', value);
|
||||||
|
|
||||||
|
button.appendChild(buttonImg);
|
||||||
|
button.appendChild(textNode);
|
||||||
|
contentDiv.appendChild(button);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
trimOptions_() {
|
||||||
|
}
|
||||||
|
|
||||||
|
protected onHide_() {
|
||||||
|
super.onHide_();
|
||||||
|
Blockly.DropDownDiv.getContentDiv().style.maxHeight = '';
|
||||||
|
this.stopSounds();
|
||||||
|
}
|
||||||
|
|
||||||
|
private createTextNode_(content: string) {
|
||||||
|
const category = this.parseCategory(content);
|
||||||
|
let text = content.substr(content.indexOf(' ') + 1);
|
||||||
|
text = text.length > 15 ? text.substr(0, 12) + "..." : text;
|
||||||
|
const textSpan = document.createElement('span');
|
||||||
|
textSpan.setAttribute('class', 'blocklyDropdownText');
|
||||||
|
textSpan.textContent = text;
|
||||||
|
return textSpan;
|
||||||
|
}
|
||||||
|
|
||||||
|
private parseCategory(content: string) {
|
||||||
|
return content.substr(0, content.indexOf(' '));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected buttonClick_ = function (e: any) {
|
||||||
|
let value = e.target.getAttribute('data-value');
|
||||||
|
this.setValue(value);
|
||||||
|
Blockly.DropDownDiv.hide();
|
||||||
|
};
|
||||||
|
|
||||||
|
private setSelectedCategory(value: string) {
|
||||||
|
this.selectedCategory_ = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected categoryClick_ = function (e: any) {
|
||||||
|
let value = e.target.getAttribute('data-value');
|
||||||
|
this.setSelectedCategory(value);
|
||||||
|
|
||||||
|
const options = this.getOptions();
|
||||||
|
options.sort();
|
||||||
|
const categories = this.getCategories(options);
|
||||||
|
|
||||||
|
const dropdownDiv = Blockly.DropDownDiv.getContentDiv();
|
||||||
|
const categoriesDiv = dropdownDiv.childNodes[0] as HTMLElement;
|
||||||
|
const contentDiv = dropdownDiv.childNodes[1] as HTMLDivElement;
|
||||||
|
categoriesDiv.innerHTML = '';
|
||||||
|
contentDiv.innerHTML = '';
|
||||||
|
|
||||||
|
this.refreshCategories(categoriesDiv, categories);
|
||||||
|
this.refreshOptions(contentDiv, options);
|
||||||
|
|
||||||
|
this.stopSounds();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback for when a button is hovered over inside the drop-down.
|
||||||
|
* Should be bound to the FieldIconMenu.
|
||||||
|
* @param {Event} e DOM event for the mouseover
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
protected buttonEnter_ = function (value: any) {
|
||||||
|
if (soundCache) {
|
||||||
|
const jresValue = value.substring(value.lastIndexOf('.') + 1);
|
||||||
|
const buf = soundCache[jresValue];
|
||||||
|
if (buf) {
|
||||||
|
const refBuf = {
|
||||||
|
data: pxt.U.stringToUint8Array(atob(buf))
|
||||||
|
}
|
||||||
|
pxsim.AudioContextManager.playBufferAsync(refBuf as any);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
protected buttonLeave_ = function () {
|
||||||
|
this.stopSounds();
|
||||||
|
};
|
||||||
|
|
||||||
|
private stopSounds() {
|
||||||
|
pxsim.AudioContextManager.stop();
|
||||||
|
}
|
||||||
|
}
|
1
fieldeditors/field_music_icons/music.svg
Normal file
1
fieldeditors/field_music_icons/music.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<svg id="svg41" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 23 23"><path id="menu_icn_music" d="M13.9 12.8c1.7.3 3.3 1.2 4.3 2.6h1s1.5-4.4-1.8-7.5-9.7-3.1-12.1 0c-1.7 2.1-2.3 5-1.4 7.5h.8s1.7-2.5 4.4-2.6C9 17.2 9 21 9 21c-1.9-.4-3.5-1.6-4.4-3.4-2-.4-3.6-2.2-3.6-4.4C1 6.7 5.9 3 11.5 3s10.8 4.3 10.4 10.2c-.2 4.1-3.6 4.4-3.6 4.4-.8 1.8-2.4 3.1-4.3 3.4-.1-4.5-.1-8.2-.1-8.2z" fill="#fff"/></svg>
|
After Width: | Height: | Size: 397 B |
@ -49,7 +49,7 @@ namespace sounds {
|
|||||||
export const communicationGameOver = music.fromWAV(hex``);
|
export const communicationGameOver = music.fromWAV(hex``);
|
||||||
//% fixedInstance jres block="communication go"
|
//% fixedInstance jres block="communication go"
|
||||||
export const communicationGo = music.fromWAV(hex``);
|
export const communicationGo = music.fromWAV(hex``);
|
||||||
//% fixedInstance jres block="communicationGoodJob"
|
//% fixedInstance jres block="communication good job"
|
||||||
export const communicationGoodJob = music.fromWAV(hex``);
|
export const communicationGoodJob = music.fromWAV(hex``);
|
||||||
//% fixedInstance jres block="communication good"
|
//% fixedInstance jres block="communication good"
|
||||||
export const communicationGood = music.fromWAV(hex``);
|
export const communicationGood = music.fromWAV(hex``);
|
||||||
@ -263,7 +263,7 @@ namespace music {
|
|||||||
* Play a sound and wait until it finishes
|
* Play a sound and wait until it finishes
|
||||||
* @param sound the sound to play
|
* @param sound the sound to play
|
||||||
*/
|
*/
|
||||||
//% blockId=music_play_sound_effect_until_done block="play sound effect %sound|until done"
|
//% blockId=music_play_sound_effect_until_done block="play sound effect %sound=music_sound_picker|until done"
|
||||||
//% weight=98 blockGap=8
|
//% weight=98 blockGap=8
|
||||||
//% help=music/play-sound-effect-until-done
|
//% help=music/play-sound-effect-until-done
|
||||||
export function playSoundEffectUntilDone(sound: Sound) {
|
export function playSoundEffectUntilDone(sound: Sound) {
|
||||||
@ -278,6 +278,7 @@ namespace music {
|
|||||||
* @param sound the sound
|
* @param sound the sound
|
||||||
*/
|
*/
|
||||||
//% blockId=music_sound_picker block="%sound" shim=TD_ID
|
//% blockId=music_sound_picker block="%sound" shim=TD_ID
|
||||||
|
//% sound.fieldEditor="music"
|
||||||
//% weight=0 blockHidden=1
|
//% weight=0 blockHidden=1
|
||||||
export function __soundPicker(sound: Sound): Sound {
|
export function __soundPicker(sound: Sound): Sound {
|
||||||
return sound;
|
return sound;
|
||||||
@ -287,7 +288,7 @@ namespace music {
|
|||||||
* Start playing a sound and don't wait for it to finish.
|
* Start playing a sound and don't wait for it to finish.
|
||||||
* @param sound the sound to play
|
* @param sound the sound to play
|
||||||
*/
|
*/
|
||||||
//% blockId=music_play_sound_effect block="play sound effect %sound"
|
//% blockId=music_play_sound_effect block="play sound effect %sound=music_sound_picker"
|
||||||
//% weight=99 blockGap=8
|
//% weight=99 blockGap=8
|
||||||
//% help=music/play-sound-effect
|
//% help=music/play-sound-effect
|
||||||
export function playSoundEffect(sound: Sound) {
|
export function playSoundEffect(sound: Sound) {
|
||||||
|
@ -21,12 +21,7 @@ namespace pxsim.SoundMethods {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function stop() {
|
export function stop() {
|
||||||
return new Promise<void>(resolve => {
|
pxsim.AudioContextManager.stop();
|
||||||
if (audio) {
|
|
||||||
audio.pause();
|
|
||||||
}
|
|
||||||
resolve();
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -30,3 +30,35 @@
|
|||||||
.blocklyDropDownButton {
|
.blocklyDropDownButton {
|
||||||
border-radius: 0px !important;
|
border-radius: 0px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Music field editor */
|
||||||
|
|
||||||
|
.blocklyDropdownText {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
line-height: 1.5rem;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.blocklyMusicFieldCategories {
|
||||||
|
position: absolute;
|
||||||
|
text-align: center;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
top: 0;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
.blocklyMusicFieldOptions {
|
||||||
|
margin-top: 80px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.blocklyDropdownTag {
|
||||||
|
padding: 2px;
|
||||||
|
font-weight: bold;
|
||||||
|
margin: 4px;
|
||||||
|
border-radius: 0;
|
||||||
|
outline: none;
|
||||||
|
border: 1px solid;
|
||||||
|
transition: box-shadow .1s;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user