Merge pull request #169 from Microsoft/screenimgeditor
Initial screen field editor.
@ -3,6 +3,7 @@
|
||||
|
||||
import { deployCoreAsync, initAsync } from "./deploy";
|
||||
import { FieldPorts } from "./field_ports";
|
||||
import { FieldImages } from "./field_images";
|
||||
|
||||
pxt.editor.initExtensionsAsync = function(opts: pxt.editor.ExtensionOptions): Promise<pxt.editor.ExtensionResult> {
|
||||
pxt.debug('loading pxt-ev3 target extensions...')
|
||||
@ -11,6 +12,9 @@ pxt.editor.initExtensionsAsync = function(opts: pxt.editor.ExtensionOptions): Pr
|
||||
fieldEditors: [{
|
||||
selector: "ports",
|
||||
editor: FieldPorts
|
||||
}, {
|
||||
selector: "images",
|
||||
editor: FieldImages
|
||||
}],
|
||||
deployCoreAsync
|
||||
};
|
||||
|
114
editor/field_images.ts
Normal file
@ -0,0 +1,114 @@
|
||||
/// <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 FieldImagesOptions extends pxtblockly.FieldImageDropdownOptions {
|
||||
}
|
||||
|
||||
export class FieldImages extends pxtblockly.FieldImageDropdown implements Blockly.FieldCustom {
|
||||
public isFieldCustom_ = true;
|
||||
|
||||
constructor(text: string, options: FieldImagesOptions, validator?: Function) {
|
||||
super(text, options, validator);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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');
|
||||
const options = this.getOptions();
|
||||
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.
|
||||
// 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.alt;
|
||||
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.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 as any).buttonClick_);
|
||||
Blockly.bindEvent_(button, 'mouseup', this, (this as any).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
|
||||
Blockly.bindEvent_(button, 'mousedown', button, function (e) {
|
||||
this.setAttribute('class', 'blocklyDropDownButton blocklyDropDownButtonHover');
|
||||
e.preventDefault();
|
||||
});
|
||||
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 = content.src;
|
||||
//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.
|
||||
button.setAttribute('data-value', value);
|
||||
buttonImg.setAttribute('data-value', value);
|
||||
button.appendChild(buttonImg);
|
||||
contentDiv.appendChild(button);
|
||||
}
|
||||
contentDiv.style.width = (this as any).width_ + 'px';
|
||||
dropdownDiv.appendChild(contentDiv);
|
||||
|
||||
Blockly.DropDownDiv.setColour(this.sourceBlock_.getColour(), this.sourceBlock_.getColourTertiary());
|
||||
|
||||
// Calculate positioning based on the field position.
|
||||
var scale = this.sourceBlock_.workspace.scale;
|
||||
var bBox = { width: this.size_.width, height: this.size_.height };
|
||||
bBox.width *= scale;
|
||||
bBox.height *= scale;
|
||||
var position = this.fieldGroup_.getBoundingClientRect();
|
||||
var primaryX = position.left + bBox.width / 2;
|
||||
var primaryY = position.top + bBox.height;
|
||||
var secondaryX = primaryX;
|
||||
var 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 as any).onHide_.bind(this));
|
||||
}
|
||||
}
|
After Width: | Height: | Size: 375 KiB |
BIN
legoresources/MC22122017/01b - MakeCode - basicdesign@2x.png
Normal file
After Width: | Height: | Size: 384 KiB |
After Width: | Height: | Size: 383 KiB |
After Width: | Height: | Size: 418 KiB |
BIN
legoresources/MC22122017/02a - MakeCode - portselector@2x.png
Normal file
After Width: | Height: | Size: 386 KiB |
BIN
legoresources/MC22122017/03a - MakeCode - all elements@2x.png
Normal file
After Width: | Height: | Size: 442 KiB |
BIN
legoresources/MC22122017/03b - MakeCode - motorsync@2x.png
Normal file
After Width: | Height: | Size: 404 KiB |
BIN
legoresources/MC22122017/04a - MakeCode - medmotor@2x.png
Normal file
After Width: | Height: | Size: 430 KiB |
BIN
legoresources/MC22122017/04b - MakeCode - medmotor@2x.png
Normal file
After Width: | Height: | Size: 428 KiB |
BIN
legoresources/MC22122017/05a - MakeCode - large motor@2x.png
Normal file
After Width: | Height: | Size: 426 KiB |
BIN
legoresources/MC22122017/05b - MakeCode - large motor@2x.png
Normal file
After Width: | Height: | Size: 425 KiB |
BIN
legoresources/MC22122017/06 - MakeCode - color - 1@2x.png
Normal file
After Width: | Height: | Size: 428 KiB |
BIN
legoresources/MC22122017/06 - MakeCode - color - 2@2x.png
Normal file
After Width: | Height: | Size: 422 KiB |
BIN
legoresources/MC22122017/06 - MakeCode - color - 3@2x.png
Normal file
After Width: | Height: | Size: 429 KiB |
BIN
legoresources/MC22122017/07a - MakeCode - Gyro@2x.png
Normal file
After Width: | Height: | Size: 423 KiB |
BIN
legoresources/MC22122017/07b - MakeCode - Gyro@2x.png
Normal file
After Width: | Height: | Size: 413 KiB |
BIN
legoresources/MC22122017/08 - MakeCode - UltraSound@2x.png
Normal file
After Width: | Height: | Size: 408 KiB |
BIN
legoresources/MC22122017/09a - MakeCode - display image@2x.png
Normal file
After Width: | Height: | Size: 478 KiB |
BIN
legoresources/MC22122017/10a - MakeCode – mouseover@2x.png
Normal file
After Width: | Height: | Size: 407 KiB |
BIN
legoresources/MC22122017/10b - MakeCode – mouseover@2x.png
Normal file
After Width: | Height: | Size: 408 KiB |
BIN
legoresources/MC22122017/10c - MakeCode - popup w-overlay@2x.png
Normal file
After Width: | Height: | Size: 351 KiB |
BIN
legoresources/MC22122017/11a - MakeCode - delete block@2x.png
Normal file
After Width: | Height: | Size: 346 KiB |
BIN
legoresources/MC22122017/11b - MakeCode - delete block@2x.png
Normal file
After Width: | Height: | Size: 366 KiB |
BIN
legoresources/MC22122017/12a - MakeCode - fullscreen@2x.png
Normal file
After Width: | Height: | Size: 382 KiB |
BIN
legoresources/MC22122017/12b - MakeCode - fullscreen@2x.png
Normal file
After Width: | Height: | Size: 424 KiB |
BIN
legoresources/MC22122017/Loader - 1@2x.png
Normal file
After Width: | Height: | Size: 132 KiB |
BIN
legoresources/MC22122017/Loader – 2@2x.png
Normal file
After Width: | Height: | Size: 132 KiB |
BIN
legoresources/MC22122017/XX-blocks@2x.png
Normal file
After Width: | Height: | Size: 270 KiB |
@ -151,9 +151,9 @@ namespace brick {
|
||||
* @param image the image
|
||||
*/
|
||||
//% blockId=screen_image_picker block="%image" shim=TD_ID
|
||||
//% image.fieldEditor="imagedropdown"
|
||||
//% image.fieldEditor="images"
|
||||
//% image.fieldOptions.columns=6
|
||||
//% image.fieldOptions.hasSearchBar=true
|
||||
//% image.fieldOptions.width=600
|
||||
//% group="Screen" weight=0 blockHidden=1
|
||||
export function __imagePicker(image: Image): Image {
|
||||
return image;
|
||||
|