mirror of
https://github.com/google/blockly.git
synced 2026-01-07 17:10:11 +01:00
Merge pull request #2313 from rachel-fenichel/feature/dropdowndiv
Add dropdowndiv and use it for the colour picker and angle fields
This commit is contained in:
@@ -52,19 +52,20 @@ goog.addDependency("../../../" + dir + "/core/constants.js", ['Blockly.constants
|
||||
goog.addDependency("../../../" + dir + "/core/contextmenu.js", ['Blockly.ContextMenu'], ['Blockly.Events.BlockCreate', 'Blockly.utils', 'Blockly.utils.uiMenu', 'Blockly.Xml', 'goog.events', 'goog.math.Coordinate', 'goog.ui.Menu', 'goog.ui.MenuItem', 'goog.userAgent']);
|
||||
goog.addDependency("../../../" + dir + "/core/css.js", ['Blockly.Css'], []);
|
||||
goog.addDependency("../../../" + dir + "/core/dragged_connection_manager.js", ['Blockly.DraggedConnectionManager'], ['Blockly.BlockAnimations', 'Blockly.RenderedConnection', 'goog.math.Coordinate']);
|
||||
goog.addDependency("../../../" + dir + "/core/dropdowndiv.js", ['Blockly.DropDownDiv'], ['Blockly.utils', 'goog.dom', 'goog.style']);
|
||||
goog.addDependency("../../../" + dir + "/core/events.js", ['Blockly.Events'], ['Blockly.utils']);
|
||||
goog.addDependency("../../../" + dir + "/core/events_abstract.js", ['Blockly.Events.Abstract'], ['Blockly.Events']);
|
||||
goog.addDependency("../../../" + dir + "/core/extensions.js", ['Blockly.Extensions'], ['Blockly.Mutator', 'Blockly.utils']);
|
||||
goog.addDependency("../../../" + dir + "/core/field.js", ['Blockly.Field'], ['Blockly.Events.BlockChange', 'Blockly.Gesture', 'Blockly.utils', 'goog.math.Size', 'goog.style', 'goog.userAgent']);
|
||||
goog.addDependency("../../../" + dir + "/core/field_angle.js", ['Blockly.FieldAngle'], ['Blockly.FieldTextInput', 'Blockly.utils', 'goog.userAgent']);
|
||||
goog.addDependency("../../../" + dir + "/core/field_angle.js", ['Blockly.FieldAngle'], ['Blockly.DropDownDiv', 'Blockly.FieldTextInput', 'Blockly.utils', 'goog.userAgent']);
|
||||
goog.addDependency("../../../" + dir + "/core/field_checkbox.js", ['Blockly.FieldCheckbox'], ['Blockly.Field', 'Blockly.utils']);
|
||||
goog.addDependency("../../../" + dir + "/core/field_colour.js", ['Blockly.FieldColour'], ['Blockly.Field', 'Blockly.utils', 'goog.style']);
|
||||
goog.addDependency("../../../" + dir + "/core/field_colour.js", ['Blockly.FieldColour'], ['Blockly.DropDownDiv', 'Blockly.Field', 'Blockly.utils', 'goog.style']);
|
||||
goog.addDependency("../../../" + dir + "/core/field_date.js", ['Blockly.FieldDate'], ['Blockly.Field', 'Blockly.utils', 'goog.date', 'goog.date.DateTime', 'goog.events', 'goog.i18n.DateTimeSymbols', 'goog.i18n.DateTimeSymbols_he', 'goog.style', 'goog.ui.DatePicker']);
|
||||
goog.addDependency("../../../" + dir + "/core/field_dropdown.js", ['Blockly.FieldDropdown'], ['Blockly.Field', 'Blockly.utils', 'Blockly.utils.uiMenu', 'goog.events', 'goog.ui.Menu', 'goog.ui.MenuItem', 'goog.userAgent']);
|
||||
goog.addDependency("../../../" + dir + "/core/field_image.js", ['Blockly.FieldImage'], ['Blockly.Field', 'Blockly.utils', 'goog.math.Size']);
|
||||
goog.addDependency("../../../" + dir + "/core/field_label.js", ['Blockly.FieldLabel'], ['Blockly.Field', 'Blockly.Tooltip', 'Blockly.utils', 'goog.math.Size']);
|
||||
goog.addDependency("../../../" + dir + "/core/field_number.js", ['Blockly.FieldNumber'], ['Blockly.FieldTextInput']);
|
||||
goog.addDependency("../../../" + dir + "/core/field_textinput.js", ['Blockly.FieldTextInput'], ['Blockly.Field', 'Blockly.Msg', 'Blockly.utils', 'goog.math.Coordinate', 'goog.userAgent']);
|
||||
goog.addDependency("../../../" + dir + "/core/field_textinput.js", ['Blockly.FieldTextInput'], ['Blockly.DropDownDiv', 'Blockly.Field', 'Blockly.Msg', 'Blockly.utils', 'goog.math.Coordinate', 'goog.userAgent']);
|
||||
goog.addDependency("../../../" + dir + "/core/field_variable.js", ['Blockly.FieldVariable'], ['Blockly.FieldDropdown', 'Blockly.Msg', 'Blockly.utils', 'Blockly.VariableModel', 'Blockly.Variables', 'goog.math.Size']);
|
||||
goog.addDependency("../../../" + dir + "/core/flyout_base.js", ['Blockly.Flyout'], ['Blockly.Block', 'Blockly.Events', 'Blockly.Events.BlockCreate', 'Blockly.Events.VarCreate', 'Blockly.FlyoutButton', 'Blockly.Gesture', 'Blockly.Touch', 'Blockly.utils', 'Blockly.WorkspaceSvg', 'Blockly.Xml', 'goog.math.Rect']);
|
||||
goog.addDependency("../../../" + dir + "/core/flyout_button.js", ['Blockly.FlyoutButton'], ['Blockly.utils', 'goog.math.Coordinate']);
|
||||
@@ -75,7 +76,7 @@ goog.addDependency("../../../" + dir + "/core/generator.js", ['Blockly.Generator
|
||||
goog.addDependency("../../../" + dir + "/core/gesture.js", ['Blockly.Gesture'], ['Blockly.BlockAnimations', 'Blockly.BlockDragger', 'Blockly.BubbleDragger', 'Blockly.constants', 'Blockly.Events.Ui', 'Blockly.FlyoutDragger', 'Blockly.Tooltip', 'Blockly.Touch', 'Blockly.utils', 'Blockly.WorkspaceDragger', 'goog.math.Coordinate']);
|
||||
goog.addDependency("../../../" + dir + "/core/grid.js", ['Blockly.Grid'], ['Blockly.utils', 'goog.userAgent']);
|
||||
goog.addDependency("../../../" + dir + "/core/icon.js", ['Blockly.Icon'], ['Blockly.utils', 'goog.math.Coordinate']);
|
||||
goog.addDependency("../../../" + dir + "/core/inject.js", ['Blockly.inject'], ['Blockly.BlockDragSurfaceSvg', 'Blockly.Css', 'Blockly.Grid', 'Blockly.Options', 'Blockly.utils', 'Blockly.WorkspaceSvg', 'Blockly.WorkspaceDragSurfaceSvg', 'goog.ui.Component', 'goog.userAgent']);
|
||||
goog.addDependency("../../../" + dir + "/core/inject.js", ['Blockly.inject'], ['Blockly.BlockDragSurfaceSvg', 'Blockly.Css', 'Blockly.DropDownDiv', 'Blockly.Grid', 'Blockly.Options', 'Blockly.utils', 'Blockly.WorkspaceSvg', 'Blockly.WorkspaceDragSurfaceSvg', 'goog.ui.Component', 'goog.userAgent']);
|
||||
goog.addDependency("../../../" + dir + "/core/input.js", ['Blockly.Input'], ['Blockly.Connection', 'Blockly.FieldLabel']);
|
||||
goog.addDependency("../../../" + dir + "/core/insertion_marker_manager.js", ['Blockly.InsertionMarkerManager'], ['Blockly.BlockAnimations', 'Blockly.Events.BlockMove', 'Blockly.RenderedConnection', 'goog.math.Coordinate']);
|
||||
goog.addDependency("../../../" + dir + "/core/msg.js", ['Blockly.Msg'], []);
|
||||
@@ -1759,6 +1760,7 @@ goog.require('Blockly.ConnectionDB');
|
||||
goog.require('Blockly.ContextMenu');
|
||||
goog.require('Blockly.Css');
|
||||
goog.require('Blockly.DraggedConnectionManager');
|
||||
goog.require('Blockly.DropDownDiv');
|
||||
goog.require('Blockly.Events');
|
||||
goog.require('Blockly.Events.Abstract');
|
||||
goog.require('Blockly.Events.BlockBase');
|
||||
|
||||
@@ -333,6 +333,7 @@ Blockly.onContextMenu_ = function(e) {
|
||||
Blockly.hideChaff = function(opt_allowToolbox) {
|
||||
Blockly.Tooltip.hide();
|
||||
Blockly.WidgetDiv.hide();
|
||||
Blockly.DropDownDiv.hideWithoutAnimation();
|
||||
// For now the trashcan flyout always autocloses because it overlays the
|
||||
// trashcan UI (no trashcan to click to close it)
|
||||
var workspace = Blockly.getMainWorkspace();
|
||||
|
||||
135
core/css.js
135
core/css.js
@@ -183,6 +183,61 @@ Blockly.Css.CONTENT = [
|
||||
'z-index: 100000;', /* big value for bootstrap3 compatibility */
|
||||
'}',
|
||||
|
||||
'.blocklyDropDownDiv {',
|
||||
'position: fixed;',
|
||||
'left: 0;',
|
||||
'top: 0;',
|
||||
'z-index: 1000;',
|
||||
'display: none;',
|
||||
'border: 1px solid;',
|
||||
'border-radius: 2px;',
|
||||
'padding: 4px;',
|
||||
'-webkit-user-select: none;',
|
||||
'}',
|
||||
|
||||
'.blocklyDropDownContent {',
|
||||
'max-height: 300px;', // @todo: spec for maximum height.
|
||||
'overflow: auto;',
|
||||
'overflow-x: hidden;',
|
||||
'}',
|
||||
|
||||
'.blocklyDropDownArrow {',
|
||||
'position: absolute;',
|
||||
'left: 0;',
|
||||
'top: 0;',
|
||||
'width: 16px;',
|
||||
'height: 16px;',
|
||||
'z-index: -1;',
|
||||
'background-color: inherit;',
|
||||
'border-color: inherit;',
|
||||
'}',
|
||||
|
||||
'.blocklyDropDownButton {',
|
||||
'display: inline-block;',
|
||||
'float: left;',
|
||||
'padding: 0;',
|
||||
'margin: 4px;',
|
||||
'border-radius: 4px;',
|
||||
'outline: none;',
|
||||
'border: 1px solid;',
|
||||
'transition: box-shadow .1s;',
|
||||
'cursor: pointer;',
|
||||
'}',
|
||||
|
||||
'.arrowTop {',
|
||||
'border-top: 1px solid;',
|
||||
'border-left: 1px solid;',
|
||||
'border-top-left-radius: 4px;',
|
||||
'border-color: inherit;',
|
||||
'}',
|
||||
|
||||
'.arrowBottom {',
|
||||
'border-bottom: 1px solid;',
|
||||
'border-right: 1px solid;',
|
||||
'border-bottom-right-radius: 4px;',
|
||||
'border-color: inherit;',
|
||||
'}',
|
||||
|
||||
'.blocklyResizeSE {',
|
||||
'cursor: se-resize;',
|
||||
'fill: #aaa;',
|
||||
@@ -766,6 +821,13 @@ Blockly.Css.CONTENT = [
|
||||
'z-index: 20000;', /* Arbitrary, but some apps depend on it... */
|
||||
'}',
|
||||
|
||||
'.blocklyDropDownDiv .goog-menu {',
|
||||
'cursor: default;',
|
||||
'font: normal 13px "Helvetica Neue", Helvetica, sans-serif;',
|
||||
'outline: none;',
|
||||
'z-index: 20000;', /* Arbitrary, but some apps depend on it... */
|
||||
'}',
|
||||
|
||||
/* Copied from: goog/css/menuitem.css */
|
||||
/*
|
||||
* Copyright 2009 The Closure Library Authors. All Rights Reserved.
|
||||
@@ -795,19 +857,22 @@ Blockly.Css.CONTENT = [
|
||||
* rely solely on the BiDi flipping by the CSS compiler. That's why we're
|
||||
* not adding the #noflip to .goog-menuitem.
|
||||
*/
|
||||
'.blocklyWidgetDiv .goog-menuitem {',
|
||||
'.blocklyWidgetDiv .goog-menuitem, ',
|
||||
'.blocklyDropDownDiv .goog-menuitem {',
|
||||
'color: #000;',
|
||||
'font: normal 13px Arial, sans-serif;',
|
||||
'list-style: none;',
|
||||
'margin: 0;',
|
||||
/* 28px on the left for icon or checkbox; 7em on the right for shortcut. */
|
||||
'padding: 4px 7em 4px 28px;',
|
||||
'min-width: 7em;',
|
||||
'padding: 5px 5px 5px 28px;',
|
||||
'white-space: nowrap;',
|
||||
'}',
|
||||
|
||||
/* BiDi override for the resting state. */
|
||||
/* #noflip */
|
||||
'.blocklyWidgetDiv .goog-menuitem.goog-menuitem-rtl {',
|
||||
'.blocklyWidgetDiv .goog-menuitem.goog-menuitem-rtl, ',
|
||||
'.blocklyDropDownDiv .goog-menuitem.goog-menuitem-rtl {',
|
||||
/* Flip left/right padding for BiDi. */
|
||||
'padding-left: 7em;',
|
||||
'padding-right: 28px;',
|
||||
@@ -816,36 +881,43 @@ Blockly.Css.CONTENT = [
|
||||
/* If a menu doesn't have checkable items or items with icons,
|
||||
* remove padding.
|
||||
*/
|
||||
'.blocklyWidgetDiv .goog-menu-nocheckbox .goog-menuitem,',
|
||||
'.blocklyWidgetDiv .goog-menu-noicon .goog-menuitem {',
|
||||
'.blocklyWidgetDiv .goog-menu-nocheckbox .goog-menuitem, ',
|
||||
'.blocklyWidgetDiv .goog-menu-noicon .goog-menuitem, ',
|
||||
'.blocklyDropDownDiv .goog-menu-nocheckbox .goog-menuitem, ',
|
||||
'.blocklyDropDownDiv .goog-menu-noicon .goog-menuitem { ',
|
||||
'padding-left: 12px;',
|
||||
'}',
|
||||
|
||||
/* If a menu doesn't have items with shortcuts, leave just enough room for
|
||||
* submenu arrows, if they are rendered.
|
||||
*/
|
||||
'.blocklyWidgetDiv .goog-menu-noaccel .goog-menuitem {',
|
||||
'.blocklyWidgetDiv .goog-menu-noaccel .goog-menuitem, ',
|
||||
'.blocklyDropDownDiv .goog-menu-noaccel .goog-menuitem {',
|
||||
'padding-right: 20px;',
|
||||
'}',
|
||||
|
||||
'.blocklyWidgetDiv .goog-menuitem-content {',
|
||||
'.blocklyWidgetDiv .goog-menuitem-content, ',
|
||||
'.blocklyDropDownDiv .goog-menuitem-content {',
|
||||
'color: #000;',
|
||||
'font: normal 13px Arial, sans-serif;',
|
||||
'}',
|
||||
|
||||
/* State: disabled. */
|
||||
'.blocklyWidgetDiv .goog-menuitem-disabled .goog-menuitem-accel,',
|
||||
'.blocklyWidgetDiv .goog-menuitem-disabled .goog-menuitem-content {',
|
||||
'.blocklyWidgetDiv .goog-menuitem-disabled .goog-menuitem-accel, ',
|
||||
'.blocklyWidgetDiv .goog-menuitem-disabled .goog-menuitem-content, ',
|
||||
'.blocklyDropDownDiv .goog-menuitem-disabled .goog-menuitem-accel, ',
|
||||
'.blocklyDropDownDiv .goog-menuitem-disabled .goog-menuitem-content {',
|
||||
'color: #ccc !important;',
|
||||
'}',
|
||||
|
||||
'.blocklyWidgetDiv .goog-menuitem-disabled .goog-menuitem-icon {',
|
||||
'.blocklyWidgetDiv .goog-menuitem-disabled .goog-menuitem-icon, ',
|
||||
'.blocklyDropDownDiv .goog-menuitem-disabled .goog-menuitem-icon {',
|
||||
'opacity: 0.3;',
|
||||
'filter: alpha(opacity=30);',
|
||||
'}',
|
||||
|
||||
/* State: hover. */
|
||||
'.blocklyWidgetDiv .goog-menuitem-highlight,',
|
||||
'.blocklyWidgetDiv .goog-menuitem-highlight, ',
|
||||
'.blocklyWidgetDiv .goog-menuitem-hover {',
|
||||
'background-color: #d6e9f8;',
|
||||
/* Use an explicit top and bottom border so that the selection is visible',
|
||||
@@ -857,9 +929,16 @@ Blockly.Css.CONTENT = [
|
||||
'padding-top: 3px;',
|
||||
'}',
|
||||
|
||||
'.blocklyDropDownDiv .goog-menuitem-highlight, ',
|
||||
'.blocklyDropDownDiv .goog-menuitem-hover {',
|
||||
'background-color: rgba(0, 0, 0, 0.2);',
|
||||
'}',
|
||||
|
||||
/* State: selected/checked. */
|
||||
'.blocklyWidgetDiv .goog-menuitem-checkbox,',
|
||||
'.blocklyWidgetDiv .goog-menuitem-icon {',
|
||||
'.blocklyWidgetDiv .goog-menuitem-checkbox, ',
|
||||
'.blocklyWidgetDiv .goog-menuitem-icon, ',
|
||||
'.blocklyDropDownDiv .goog-menuitem-checkbox, ',
|
||||
'.blocklyDropDownDiv .goog-menuitem-icon {',
|
||||
'background-repeat: no-repeat;',
|
||||
'height: 16px;',
|
||||
'left: 6px;',
|
||||
@@ -871,21 +950,29 @@ Blockly.Css.CONTENT = [
|
||||
|
||||
/* BiDi override for the selected/checked state. */
|
||||
/* #noflip */
|
||||
'.blocklyWidgetDiv .goog-menuitem-rtl .goog-menuitem-checkbox,',
|
||||
'.blocklyWidgetDiv .goog-menuitem-rtl .goog-menuitem-icon {',
|
||||
'.blocklyWidgetDiv .goog-menuitem-rtl .goog-menuitem-checkbox, ',
|
||||
'.blocklyWidgetDiv .goog-menuitem-rtl .goog-menuitem-icon, ',
|
||||
'.blocklyDropDownDiv .goog-menuitem-rtl .goog-menuitem-checkbox, ',
|
||||
'.blocklyDropDownDiv .goog-menuitem-rtl .goog-menuitem-icon {',
|
||||
/* Flip left/right positioning. */
|
||||
'left: auto;',
|
||||
'right: 6px;',
|
||||
'}',
|
||||
|
||||
'.blocklyWidgetDiv .goog-option-selected .goog-menuitem-checkbox,',
|
||||
'.blocklyWidgetDiv .goog-option-selected .goog-menuitem-icon {',
|
||||
'.blocklyWidgetDiv .goog-option-selected .goog-menuitem-checkbox, ',
|
||||
'.blocklyWidgetDiv .goog-option-selected .goog-menuitem-icon, ',
|
||||
'.blocklyDropDownDiv .goog-option-selected .goog-menuitem-checkbox, ',
|
||||
'.blocklyDropDownDiv .goog-option-selected .goog-menuitem-icon {',
|
||||
/* Client apps may override the URL at which they serve the sprite. */
|
||||
'background: url(//ssl.gstatic.com/editor/editortoolbar.png) no-repeat -512px 0;',
|
||||
'position: static;', /* Scroll with the menu. */
|
||||
'float: left;',
|
||||
'margin-left: -24px;',
|
||||
'}',
|
||||
|
||||
/* Keyboard shortcut ("accelerator") style. */
|
||||
'.blocklyWidgetDiv .goog-menuitem-accel {',
|
||||
'.blocklyWidgetDiv .goog-menuitem-accel, ',
|
||||
'.blocklyDropDownDiv .goog-menuitem-accel {',
|
||||
'color: #999;',
|
||||
/* Keyboard shortcuts are untranslated; always left-to-right. */
|
||||
/* #noflip */
|
||||
@@ -899,7 +986,8 @@ Blockly.Css.CONTENT = [
|
||||
|
||||
/* BiDi override for shortcut style. */
|
||||
/* #noflip */
|
||||
'.blocklyWidgetDiv .goog-menuitem-rtl .goog-menuitem-accel {',
|
||||
'.blocklyWidgetDiv .goog-menuitem-rtl .goog-menuitem-accel, ',
|
||||
'.blocklyDropDownDiv .goog-menuitem-rtl .goog-menuitem-accel {',
|
||||
/* Flip left/right positioning and text alignment. */
|
||||
'left: 0;',
|
||||
'right: auto;',
|
||||
@@ -907,11 +995,13 @@ Blockly.Css.CONTENT = [
|
||||
'}',
|
||||
|
||||
/* Mnemonic styles. */
|
||||
'.blocklyWidgetDiv .goog-menuitem-mnemonic-hint {',
|
||||
'.blocklyWidgetDiv .goog-menuitem-mnemonic-hint, ',
|
||||
'.blocklyDropDownDiv .goog-menuitem-mnemonic-hint {',
|
||||
'text-decoration: underline;',
|
||||
'}',
|
||||
|
||||
'.blocklyWidgetDiv .goog-menuitem-mnemonic-separator {',
|
||||
'.blocklyWidgetDiv .goog-menuitem-mnemonic-separator, ',
|
||||
'.blocklyDropDownDiv .goog-menuitem-mnemonic-separator {',
|
||||
'color: #999;',
|
||||
'font-size: 12px;',
|
||||
'padding-left: 4px;',
|
||||
@@ -931,7 +1021,8 @@ Blockly.Css.CONTENT = [
|
||||
* @author attila@google.com (Attila Bodis)
|
||||
*/
|
||||
|
||||
'.blocklyWidgetDiv .goog-menuseparator {',
|
||||
'.blocklyWidgetDiv .goog-menuseparator, ',
|
||||
'.blocklyDropDownDiv .goog-menuseparator {',
|
||||
'border-top: 1px solid #ccc;',
|
||||
'margin: 4px 0;',
|
||||
'padding: 0;',
|
||||
|
||||
538
core/dropdowndiv.js
Normal file
538
core/dropdowndiv.js
Normal file
@@ -0,0 +1,538 @@
|
||||
/**
|
||||
* @license
|
||||
* Visual Blocks Editor
|
||||
*
|
||||
* Copyright 2016 Massachusetts Institute of Technology
|
||||
* All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview A div that floats on top of the workspace, for drop-down menus.
|
||||
* The drop-down can be kept inside the workspace, animate in/out, etc.
|
||||
* @author tmickel@mit.edu (Tim Mickel)
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
goog.provide('Blockly.DropDownDiv');
|
||||
|
||||
goog.require('Blockly.utils');
|
||||
|
||||
goog.require('goog.dom');
|
||||
goog.require('goog.style');
|
||||
|
||||
/**
|
||||
* Class for drop-down div.
|
||||
* @constructor
|
||||
*/
|
||||
Blockly.DropDownDiv = function() {
|
||||
};
|
||||
|
||||
/**
|
||||
* The div element. Set once by Blockly.DropDownDiv.createDom.
|
||||
* @type {Element}
|
||||
* @private
|
||||
*/
|
||||
Blockly.DropDownDiv.DIV_ = null;
|
||||
|
||||
/**
|
||||
* Drop-downs will appear within the bounds of this element if possible.
|
||||
* Set in Blockly.DropDownDiv.setBoundsElement.
|
||||
* @type {Element}
|
||||
* @private
|
||||
*/
|
||||
Blockly.DropDownDiv.boundsElement_ = null;
|
||||
|
||||
/**
|
||||
* The object currently using the drop-down.
|
||||
* @type {Object}
|
||||
* @private
|
||||
*/
|
||||
Blockly.DropDownDiv.owner_ = null;
|
||||
|
||||
/**
|
||||
* Whether the dropdown was positioned to a field or the source block.
|
||||
* @type {boolean}
|
||||
* @private
|
||||
*/
|
||||
Blockly.DropDownDiv.positionToField_ = null;
|
||||
|
||||
/**
|
||||
* Arrow size in px. Should match the value in CSS (need to position pre-render).
|
||||
* @type {number}
|
||||
* @const
|
||||
*/
|
||||
Blockly.DropDownDiv.ARROW_SIZE = 16;
|
||||
|
||||
/**
|
||||
* Drop-down border size in px. Should match the value in CSS (need to position the arrow).
|
||||
* @type {number}
|
||||
* @const
|
||||
*/
|
||||
Blockly.DropDownDiv.BORDER_SIZE = 1;
|
||||
|
||||
/**
|
||||
* Amount the arrow must be kept away from the edges of the main drop-down div, in px.
|
||||
* @type {number}
|
||||
* @const
|
||||
*/
|
||||
Blockly.DropDownDiv.ARROW_HORIZONTAL_PADDING = 12;
|
||||
|
||||
/**
|
||||
* Amount drop-downs should be padded away from the source, in px.
|
||||
* @type {number}
|
||||
* @const
|
||||
*/
|
||||
Blockly.DropDownDiv.PADDING_Y = 16;
|
||||
|
||||
/**
|
||||
* Length of animations in seconds.
|
||||
* @type {number}
|
||||
* @const
|
||||
*/
|
||||
Blockly.DropDownDiv.ANIMATION_TIME = 0.25;
|
||||
|
||||
/**
|
||||
* Timer for animation out, to be cleared if we need to immediately hide
|
||||
* without disrupting new shows.
|
||||
* @type {number}
|
||||
*/
|
||||
Blockly.DropDownDiv.animateOutTimer_ = null;
|
||||
|
||||
/**
|
||||
* Callback for when the drop-down is hidden.
|
||||
* @type {Function}
|
||||
*/
|
||||
Blockly.DropDownDiv.onHide_ = 0;
|
||||
|
||||
/**
|
||||
* Create and insert the DOM element for this div.
|
||||
*/
|
||||
Blockly.DropDownDiv.createDom = function() {
|
||||
if (Blockly.DropDownDiv.DIV_) {
|
||||
return; // Already created.
|
||||
}
|
||||
var div = document.createElement('div');
|
||||
div.className = 'blocklyDropDownDiv';
|
||||
document.body.appendChild(div);
|
||||
Blockly.DropDownDiv.DIV_ = div;
|
||||
|
||||
var content = document.createElement('div');
|
||||
content.className = 'blocklyDropDownContent';
|
||||
div.appendChild(content);
|
||||
Blockly.DropDownDiv.content_ = content;
|
||||
|
||||
var arrow = document.createElement('div');
|
||||
arrow.className = 'blocklyDropDownArrow';
|
||||
div.appendChild(arrow);
|
||||
Blockly.DropDownDiv.arrow_ = arrow;
|
||||
|
||||
// Transition animation for transform: translate() and opacity.
|
||||
Blockly.DropDownDiv.DIV_.style.transition = 'transform ' +
|
||||
Blockly.DropDownDiv.ANIMATION_TIME + 's, ' +
|
||||
'opacity ' + Blockly.DropDownDiv.ANIMATION_TIME + 's';
|
||||
};
|
||||
|
||||
/**
|
||||
* Set an element to maintain bounds within. Drop-downs will appear
|
||||
* within the box of this element if possible.
|
||||
* @param {Element} boundsElement Element to bind drop-down to.
|
||||
*/
|
||||
Blockly.DropDownDiv.setBoundsElement = function(boundsElement) {
|
||||
Blockly.DropDownDiv.boundsElement_ = boundsElement;
|
||||
};
|
||||
|
||||
/**
|
||||
* Provide the div for inserting content into the drop-down.
|
||||
* @return {Element} Div to populate with content
|
||||
*/
|
||||
Blockly.DropDownDiv.getContentDiv = function() {
|
||||
return Blockly.DropDownDiv.content_;
|
||||
};
|
||||
|
||||
/**
|
||||
* Clear the content of the drop-down.
|
||||
*/
|
||||
Blockly.DropDownDiv.clearContent = function() {
|
||||
Blockly.DropDownDiv.content_.innerHTML = '';
|
||||
Blockly.DropDownDiv.content_.style.width = '';
|
||||
};
|
||||
|
||||
/**
|
||||
* Set the colour for the drop-down.
|
||||
* @param {string} backgroundColour Any CSS color for the background
|
||||
* @param {string} borderColour Any CSS color for the border
|
||||
*/
|
||||
Blockly.DropDownDiv.setColour = function(backgroundColour, borderColour) {
|
||||
Blockly.DropDownDiv.DIV_.style.backgroundColor = backgroundColour;
|
||||
Blockly.DropDownDiv.DIV_.style.borderColor = borderColour;
|
||||
};
|
||||
|
||||
/**
|
||||
* Set the category for the drop-down.
|
||||
* @param {string} category The new category for the drop-down.
|
||||
*/
|
||||
Blockly.DropDownDiv.setCategory = function(category) {
|
||||
Blockly.DropDownDiv.DIV_.setAttribute('data-category', category);
|
||||
};
|
||||
|
||||
/**
|
||||
* Shortcut to show and place the drop-down with positioning determined
|
||||
* by a particular block. The primary position will be below the block,
|
||||
* and the secondary position above the block. Drop-down will be
|
||||
* constrained to the block's workspace.
|
||||
* @param {!Blockly.Field} field The field showing the drop-down
|
||||
* @param {!Blockly.Block} block Block to position the drop-down around.
|
||||
* @param {Function=} opt_onHide Optional callback for when the drop-down is hidden.
|
||||
* @param {Number} opt_secondaryYOffset Optional Y offset for above-block positioning.
|
||||
* @return {boolean} True if the menu rendered below block; false if above.
|
||||
*/
|
||||
Blockly.DropDownDiv.showPositionedByBlock = function(field, block,
|
||||
opt_onHide, opt_secondaryYOffset) {
|
||||
var scale = block.workspace.scale;
|
||||
var bBox = {width: block.width, height: block.height};
|
||||
bBox.width *= scale;
|
||||
bBox.height *= scale;
|
||||
var position = block.getSvgRoot().getBoundingClientRect();
|
||||
// If we can fit it, render below the block.
|
||||
var primaryX = position.left + bBox.width / 2;
|
||||
var primaryY = position.top + bBox.height;
|
||||
// If we can't fit it, render above the entire parent block.
|
||||
var secondaryX = primaryX;
|
||||
var secondaryY = position.top;
|
||||
if (opt_secondaryYOffset) {
|
||||
secondaryY += opt_secondaryYOffset;
|
||||
}
|
||||
// Set bounds to workspace; show the drop-down.
|
||||
Blockly.DropDownDiv.setBoundsElement(block.workspace.getParentSvg().parentNode);
|
||||
return Blockly.DropDownDiv.show(field, primaryX, primaryY, secondaryX, secondaryY, opt_onHide);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Shortcut to show and place the drop-down with positioning determined
|
||||
* by a particular field. The primary position will be below the field,
|
||||
* and the secondary position above the field. Drop-down will be
|
||||
* constrained to the block's workspace.
|
||||
* @param {Object} owner The object showing the drop-down
|
||||
* @param {Function=} opt_onHide Optional callback for when the drop-down is hidden.
|
||||
* @param {Number} opt_secondaryYOffset Optional Y offset for above-block positioning.
|
||||
* @return {boolean} True if the menu rendered below block; false if above.
|
||||
*/
|
||||
Blockly.DropDownDiv.showPositionedByField = function(owner,
|
||||
opt_onHide, opt_secondaryYOffset) {
|
||||
var position = owner.fieldGroup_.getBoundingClientRect();
|
||||
// If we can fit it, render below the block.
|
||||
var primaryX = position.left + position.width / 2;
|
||||
var primaryY = position.bottom;
|
||||
// If we can't fit it, render above the entire parent block.
|
||||
var secondaryX = primaryX;
|
||||
var secondaryY = position.top;
|
||||
if (opt_secondaryYOffset) {
|
||||
secondaryY += opt_secondaryYOffset;
|
||||
}
|
||||
// Set bounds to workspace; show the drop-down.
|
||||
Blockly.DropDownDiv.positionToField_ = true;
|
||||
Blockly.DropDownDiv.setBoundsElement(
|
||||
owner.sourceBlock_.workspace.getParentSvg().parentNode);
|
||||
return Blockly.DropDownDiv.show(
|
||||
owner, primaryX, primaryY, secondaryX, secondaryY, opt_onHide);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Show and place the drop-down.
|
||||
* The drop-down is placed with an absolute "origin point" (x, y) - i.e.,
|
||||
* the arrow will point at this origin and box will positioned below or above it.
|
||||
* If we can maintain the container bounds at the primary point, the arrow will
|
||||
* point there, and the container will be positioned below it.
|
||||
* If we can't maintain the container bounds at the primary point, fall-back to the
|
||||
* secondary point and position above.
|
||||
* @param {Object} owner The object showing the drop-down
|
||||
* @param {number} primaryX Desired origin point x, in absolute px
|
||||
* @param {number} primaryY Desired origin point y, in absolute px
|
||||
* @param {number} secondaryX Secondary/alternative origin point x, in absolute px
|
||||
* @param {number} secondaryY Secondary/alternative origin point y, in absolute px
|
||||
* @param {Function=} opt_onHide Optional callback for when the drop-down is hidden
|
||||
* @return {boolean} True if the menu rendered at the primary origin point.
|
||||
*/
|
||||
Blockly.DropDownDiv.show = function(owner, primaryX, primaryY, secondaryX, secondaryY, opt_onHide) {
|
||||
Blockly.DropDownDiv.owner_ = owner;
|
||||
Blockly.DropDownDiv.onHide_ = opt_onHide;
|
||||
var metrics = Blockly.DropDownDiv.getPositionMetrics(primaryX, primaryY, secondaryX, secondaryY);
|
||||
// Update arrow CSS
|
||||
Blockly.DropDownDiv.arrow_.style.transform = 'translate(' +
|
||||
metrics.arrowX + 'px,' + metrics.arrowY + 'px) rotate(45deg)';
|
||||
Blockly.DropDownDiv.arrow_.setAttribute('class',
|
||||
metrics.arrowAtTop ? 'blocklyDropDownArrow arrowTop' : 'blocklyDropDownArrow arrowBottom');
|
||||
Blockly.DropDownDiv.arrow_.style.display =
|
||||
metrics.arrowVisible ? '' : 'none';
|
||||
|
||||
// When we change `translate` multiple times in close succession,
|
||||
// Chrome may choose to wait and apply them all at once.
|
||||
// Since we want the translation to initial X, Y to be immediate,
|
||||
// and the translation to final X, Y to be animated,
|
||||
// we saw problems where both would be applied after animation was turned on,
|
||||
// making the dropdown appear to fly in from (0, 0).
|
||||
// Using both `left`, `top` for the initial translation and then `translate`
|
||||
// for the animated transition to final X, Y is a workaround.
|
||||
|
||||
Blockly.DropDownDiv.positionInternal_(
|
||||
metrics.initialX, metrics.initialY,
|
||||
metrics.finalX, metrics.finalY);
|
||||
return metrics.arrowAtTop;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get sizing info about the bounding element.
|
||||
* @return {!Object} an object containing size information about the bounding
|
||||
* element (bounding box and width/height).
|
||||
* @private
|
||||
*/
|
||||
Blockly.DropDownDiv.getBoundsInfo_ = function() {
|
||||
var boundPosition = Blockly.DropDownDiv.boundsElement_.getBoundingClientRect();
|
||||
var boundSize = goog.style.getSize(Blockly.DropDownDiv.boundsElement_);
|
||||
|
||||
return {
|
||||
left: boundPosition.left,
|
||||
right: boundPosition.left + boundSize.width,
|
||||
top: boundPosition.top,
|
||||
bottom: boundPosition.top + boundSize.height,
|
||||
width: boundSize.width,
|
||||
height: boundSize.height
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Helper to position the drop-down and the arrow, maintaining bounds.
|
||||
* See explanation of origin points in Blockly.DropDownDiv.show.
|
||||
* @param {number} primaryX Desired origin point x, in absolute px
|
||||
* @param {number} primaryY Desired origin point y, in absolute px
|
||||
* @param {number} secondaryX Secondary/alternative origin point x, in absolute px
|
||||
* @param {number} secondaryY Secondary/alternative origin point y, in absolute px
|
||||
* @returns {Object} Various final metrics, including rendered positions for drop-down and arrow.
|
||||
*/
|
||||
Blockly.DropDownDiv.getPositionMetrics = function(primaryX, primaryY, secondaryX, secondaryY) {
|
||||
var boundsInfo = Blockly.DropDownDiv.getBoundsInfo_();
|
||||
var div = Blockly.DropDownDiv.DIV_;
|
||||
var divSize = goog.style.getSize(div);
|
||||
|
||||
// First decide if we will render at primary or secondary position
|
||||
// i.e., above or below
|
||||
// renderX, renderY will eventually be the final rendered position of the box.
|
||||
var renderX, renderY, renderedSecondary, renderedTertiary;
|
||||
// Can the div fit inside the bounds if we render below the primary point?
|
||||
if (primaryY + divSize.height > boundsInfo.bottom) {
|
||||
// We can't fit below in terms of y. Can we fit above?
|
||||
if (secondaryY - divSize.height < boundsInfo.top) {
|
||||
// We also can't fit above, so just render at the top of the screen.
|
||||
renderX = primaryX;
|
||||
renderY = 0;
|
||||
renderedSecondary = false;
|
||||
renderedTertiary = true;
|
||||
} else {
|
||||
// We can fit above, render secondary
|
||||
renderX = secondaryX;
|
||||
renderY = secondaryY - divSize.height - Blockly.DropDownDiv.PADDING_Y;
|
||||
renderedSecondary = true;
|
||||
}
|
||||
} else {
|
||||
// We can fit below, render primary
|
||||
renderX = primaryX;
|
||||
renderY = primaryY + Blockly.DropDownDiv.PADDING_Y;
|
||||
renderedSecondary = false;
|
||||
}
|
||||
|
||||
var centerX = renderX;
|
||||
// The dropdown's X position is at the top-left of the dropdown rect, but the
|
||||
// dropdown should appear centered relative to the desired origin point.
|
||||
renderX -= divSize.width / 2;
|
||||
// Fit horizontally in the bounds.
|
||||
renderX = Blockly.utils.clampNumber(
|
||||
boundsInfo.left, renderX, boundsInfo.right - divSize.width);
|
||||
|
||||
// Calculate the absolute arrow X. The arrow wants to be as close to the
|
||||
// origin point as possible. The arrow may not be centered in the dropdown div.
|
||||
var absoluteArrowX = centerX - Blockly.DropDownDiv.ARROW_SIZE / 2;
|
||||
// Keep in overall bounds
|
||||
absoluteArrowX = Blockly.utils.clampNumber(
|
||||
boundsInfo.left, absoluteArrowX, boundsInfo.right);
|
||||
|
||||
// Convert the arrow position to be relative to the top left corner of the div.
|
||||
var relativeArrowX = absoluteArrowX - renderX;
|
||||
|
||||
// Pad the arrow by some pixels, primarily so that it doesn't render on top
|
||||
// of a rounded border.
|
||||
relativeArrowX = Blockly.utils.clampNumber(
|
||||
Blockly.DropDownDiv.ARROW_HORIZONTAL_PADDING,
|
||||
relativeArrowX,
|
||||
divSize.width - Blockly.DropDownDiv.ARROW_HORIZONTAL_PADDING -
|
||||
Blockly.DropDownDiv.ARROW_SIZE);
|
||||
|
||||
var arrowY = (renderedSecondary) ?
|
||||
divSize.height - Blockly.DropDownDiv.BORDER_SIZE : 0;
|
||||
arrowY -= (Blockly.DropDownDiv.ARROW_SIZE / 2) +
|
||||
Blockly.DropDownDiv.BORDER_SIZE;
|
||||
|
||||
var initialY;
|
||||
if (renderedSecondary) {
|
||||
initialY = secondaryY - divSize.height; // No padding on Y
|
||||
} else {
|
||||
initialY = primaryY; // No padding on Y
|
||||
}
|
||||
|
||||
return {
|
||||
initialX: renderX, // X position remains constant during animation.
|
||||
initialY : initialY,
|
||||
finalX: renderX,
|
||||
finalY: renderY,
|
||||
arrowX: relativeArrowX,
|
||||
arrowY: arrowY,
|
||||
arrowAtTop: !renderedSecondary,
|
||||
arrowVisible: !renderedTertiary
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Is the container visible?
|
||||
* @return {boolean} True if visible.
|
||||
*/
|
||||
Blockly.DropDownDiv.isVisible = function() {
|
||||
return !!Blockly.DropDownDiv.owner_;
|
||||
};
|
||||
|
||||
/**
|
||||
* Hide the menu only if it is owned by the provided object.
|
||||
* @param {Object} owner Object which must be owning the drop-down to hide
|
||||
* @return {Boolean} True if hidden
|
||||
*/
|
||||
Blockly.DropDownDiv.hideIfOwner = function(owner) {
|
||||
if (Blockly.DropDownDiv.owner_ === owner) {
|
||||
Blockly.DropDownDiv.hide();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Hide the menu, triggering animation.
|
||||
*/
|
||||
Blockly.DropDownDiv.hide = function() {
|
||||
// Start the animation by setting the translation and fading out.
|
||||
var div = Blockly.DropDownDiv.DIV_;
|
||||
// Reset to (initialX, initialY) - i.e., no translation.
|
||||
div.style.transform = 'translate(0px, 0px)';
|
||||
div.style.opacity = 0;
|
||||
Blockly.DropDownDiv.animateOutTimer_ = setTimeout(function() {
|
||||
// Finish animation - reset all values to default.
|
||||
Blockly.DropDownDiv.hideWithoutAnimation();
|
||||
}, Blockly.DropDownDiv.ANIMATION_TIME * 1000);
|
||||
if (Blockly.DropDownDiv.onHide_) {
|
||||
Blockly.DropDownDiv.onHide_();
|
||||
Blockly.DropDownDiv.onHide_ = null;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Hide the menu, without animation.
|
||||
*/
|
||||
Blockly.DropDownDiv.hideWithoutAnimation = function() {
|
||||
if (!Blockly.DropDownDiv.isVisible()) {
|
||||
return;
|
||||
}
|
||||
Blockly.DropDownDiv.animateOutTimer_ && window.clearTimeout(Blockly.DropDownDiv.animateOutTimer_);
|
||||
Blockly.DropDownDiv.positionInternal_();
|
||||
Blockly.DropDownDiv.clearContent();
|
||||
Blockly.DropDownDiv.owner_ = null;
|
||||
if (Blockly.DropDownDiv.onHide_) {
|
||||
Blockly.DropDownDiv.onHide_();
|
||||
Blockly.DropDownDiv.onHide_ = null;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Set the dropdown div's position.
|
||||
* @param {number} initialX Initial Horizontal location (window coordinates, not body).
|
||||
* @param {number} initialY Initial Vertical location (window coordinates, not body).
|
||||
* @param {number} finalX Final Horizontal location (window coordinates, not body).
|
||||
* @param {number} finalY Final Vertical location (window coordinates, not body).
|
||||
* @private
|
||||
*/
|
||||
Blockly.DropDownDiv.positionInternal_ = function(initialX, initialY, finalX, finalY) {
|
||||
initialX = initialX == null ? initialX : Math.floor(initialX);
|
||||
initialY = initialY == null ? initialY : Math.floor(initialY);
|
||||
finalX = finalX == null ? finalX : Math.floor(finalX);
|
||||
finalY = finalY == null ? finalY : Math.floor(finalY);
|
||||
|
||||
var div = Blockly.DropDownDiv.DIV_;
|
||||
// First apply initial translation.
|
||||
div.style.left = initialX != null ? initialX + 'px' : '';
|
||||
div.style.top = initialY != null ? initialY + 'px' : '';
|
||||
if (finalX != null) {
|
||||
// Show the div.
|
||||
div.style.display = 'block';
|
||||
div.style.opacity = 1;
|
||||
// Add final translate, animated through `transition`.
|
||||
// Coordinates are relative to (initialX, initialY),
|
||||
// where the drop-down is absolutely positioned.
|
||||
var dx = (finalX - initialX);
|
||||
var dy = (finalY - initialY);
|
||||
div.style.transform = 'translate(' + dx + 'px,' + dy + 'px)';
|
||||
} else {
|
||||
// Hide the div.
|
||||
div.style.display = 'none';
|
||||
div.style.transform = '';
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Repositions the dropdownDiv on window resize. If it doesn't know how to
|
||||
* calculate the new position, it wll just hide it instead.
|
||||
*/
|
||||
Blockly.DropDownDiv.repositionForWindowResize = function() {
|
||||
// This condition mainly catches the dropdown div when it is being used as a
|
||||
// dropdown. It is important not to close it in this case because on Android,
|
||||
// when a field is focused, the soft keyboard opens triggering a window resize
|
||||
// event and we want the dropdown div to stick around so users can type into it.
|
||||
if (Blockly.DropDownDiv.owner_) {
|
||||
var block = Blockly.DropDownDiv.owner_.sourceBlock_;
|
||||
var scale = block.workspace.scale;
|
||||
var bBox = {
|
||||
width: Blockly.DropDownDiv.positionToField_ ?
|
||||
Blockly.DropDownDiv.owner_.size_.width : block.width,
|
||||
height: Blockly.DropDownDiv.positionToField_ ?
|
||||
Blockly.DropDownDiv.owner_.size_.height : block.height
|
||||
};
|
||||
bBox.width *= scale;
|
||||
bBox.height *= scale;
|
||||
var position = Blockly.DropDownDiv.positionToField_ ?
|
||||
Blockly.DropDownDiv.owner_.fieldGroup_.getBoundingClientRect() :
|
||||
block.getSvgRoot().getBoundingClientRect();
|
||||
// If we can fit it, render below the block.
|
||||
var primaryX = position.left + bBox.width / 2;
|
||||
var primaryY = position.top + bBox.height;
|
||||
// If we can't fit it, render above the entire parent block.
|
||||
var secondaryX = primaryX;
|
||||
var secondaryY = position.top;
|
||||
var metrics = Blockly.DropDownDiv.getPositionMetrics(
|
||||
primaryX, primaryY, secondaryX, secondaryY);
|
||||
Blockly.DropDownDiv.positionInternal_(
|
||||
metrics.initialX, metrics.initialY,
|
||||
metrics.finalX, metrics.finalY);
|
||||
} else {
|
||||
Blockly.DropDownDiv.hide();
|
||||
}
|
||||
};
|
||||
@@ -26,6 +26,7 @@
|
||||
|
||||
goog.provide('Blockly.FieldAngle');
|
||||
|
||||
goog.require('Blockly.DropDownDiv');
|
||||
goog.require('Blockly.FieldTextInput');
|
||||
goog.require('Blockly.utils');
|
||||
|
||||
@@ -165,11 +166,12 @@ Blockly.FieldAngle.prototype.showEditor_ = function() {
|
||||
goog.userAgent.MOBILE || goog.userAgent.ANDROID || goog.userAgent.IPAD;
|
||||
// Mobile browsers have issues with in-line textareas (focus & keyboards).
|
||||
Blockly.FieldAngle.superClass_.showEditor_.call(this, noFocus);
|
||||
var div = Blockly.WidgetDiv.DIV;
|
||||
if (!div.firstChild) {
|
||||
// Mobile interface uses Blockly.prompt.
|
||||
return;
|
||||
}
|
||||
|
||||
// If there is an existing drop-down someone else owns, hide it immediately and clear it.
|
||||
Blockly.DropDownDiv.hideWithoutAnimation();
|
||||
Blockly.DropDownDiv.clearContent();
|
||||
var div = Blockly.DropDownDiv.getContentDiv();
|
||||
|
||||
// Build the SVG DOM.
|
||||
var svg = Blockly.utils.createSvgElement('svg', {
|
||||
'xmlns': 'http://www.w3.org/2000/svg',
|
||||
@@ -204,14 +206,17 @@ Blockly.FieldAngle.prototype.showEditor_ = function() {
|
||||
Blockly.FieldAngle.HALF + ',' + Blockly.FieldAngle.HALF + ')'
|
||||
}, svg);
|
||||
}
|
||||
svg.style.marginLeft = (15 - Blockly.FieldAngle.RADIUS) + 'px';
|
||||
|
||||
|
||||
Blockly.DropDownDiv.setColour(this.sourceBlock_.getColour(),
|
||||
this.sourceBlock_.getColour());
|
||||
Blockly.DropDownDiv.showPositionedByField(this);
|
||||
// The angle picker is different from other fields in that it updates on
|
||||
// mousemove even if it's not in the middle of a drag. In future we may
|
||||
// change this behavior. For now, using bindEvent_ instead of
|
||||
// bindEventWithChecks_ allows it to work without a mousedown/touchstart.
|
||||
this.clickWrapper_ =
|
||||
Blockly.bindEvent_(svg, 'click', this, Blockly.WidgetDiv.hide);
|
||||
Blockly.bindEvent_(svg, 'click', this, this.hide_.bind(this));
|
||||
this.moveWrapper1_ =
|
||||
Blockly.bindEvent_(circle, 'mousemove', this, this.onMouseMove);
|
||||
this.moveWrapper2_ =
|
||||
@@ -219,6 +224,18 @@ Blockly.FieldAngle.prototype.showEditor_ = function() {
|
||||
this.updateGraph_();
|
||||
};
|
||||
|
||||
/**
|
||||
* Hide the editor and unbind event listeners.
|
||||
* @private
|
||||
*/
|
||||
Blockly.FieldAngle.prototype.hide_ = function() {
|
||||
Blockly.unbindEvent_(this.moveWrapper1_);
|
||||
Blockly.unbindEvent_(this.moveWrapper2_);
|
||||
Blockly.unbindEvent_(this.clickWrapper_);
|
||||
Blockly.DropDownDiv.hideIfOwner(this);
|
||||
Blockly.WidgetDiv.hide();
|
||||
};
|
||||
|
||||
/**
|
||||
* Set the angle to match the mouse's position.
|
||||
* @param {!Event} e Mouse move event.
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
|
||||
goog.provide('Blockly.FieldColour');
|
||||
|
||||
goog.require('Blockly.DropDownDiv');
|
||||
goog.require('Blockly.Field');
|
||||
goog.require('Blockly.utils');
|
||||
|
||||
@@ -60,6 +61,22 @@ Blockly.FieldColour.fromJson = function(options) {
|
||||
return new Blockly.FieldColour(options['colour']);
|
||||
};
|
||||
|
||||
/**
|
||||
* Default width of a colour field.
|
||||
* @type {number}
|
||||
* @private
|
||||
* @const
|
||||
*/
|
||||
Blockly.FieldColour.DEFAULT_WIDTH = 16;
|
||||
|
||||
/**
|
||||
* Default height of a colour field.
|
||||
* @type {number}
|
||||
* @private
|
||||
* @const
|
||||
*/
|
||||
Blockly.FieldColour.DEFAULT_HEIGHT = 12;
|
||||
|
||||
/**
|
||||
* Array of colours used by this field. If null, use the global list.
|
||||
* @type {Array.<string>}
|
||||
@@ -82,12 +99,30 @@ Blockly.FieldColour.prototype.titles_ = null;
|
||||
*/
|
||||
Blockly.FieldColour.prototype.columns_ = 0;
|
||||
|
||||
/**
|
||||
* Border colour for the dropdown div showing the colour picker. Must be a CSS
|
||||
* string.
|
||||
* @type {string}
|
||||
* @private
|
||||
*/
|
||||
Blockly.FieldColour.prototype.DROPDOWN_BORDER_COLOUR = 'silver';
|
||||
|
||||
/**
|
||||
* Background colour for the dropdown div showing the colour picker. Must be a
|
||||
* CSS string.
|
||||
* @type {string}
|
||||
* @private
|
||||
*/
|
||||
Blockly.FieldColour.prototype.DROPDOWN_BACKGROUND_COLOUR = 'white';
|
||||
|
||||
/**
|
||||
* Install this field on a block.
|
||||
*/
|
||||
Blockly.FieldColour.prototype.init = function() {
|
||||
Blockly.FieldColour.superClass_.init.call(this);
|
||||
this.borderRect_.style['fillOpacity'] = 1;
|
||||
this.size_ = new goog.math.Size(Blockly.FieldColour.DEFAULT_WIDTH,
|
||||
Blockly.FieldColour.DEFAULT_HEIGHT);
|
||||
this.setValue(this.getValue());
|
||||
};
|
||||
|
||||
@@ -112,6 +147,31 @@ Blockly.FieldColour.prototype.getValue = function() {
|
||||
return this.colour_;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the size, and rerender if necessary.
|
||||
* @return {!goog.math.Size} Height and width.
|
||||
*/
|
||||
Blockly.FieldColour.prototype.getSize = function() {
|
||||
if (!this.size_.width) {
|
||||
this.render_();
|
||||
}
|
||||
|
||||
return this.size_;
|
||||
};
|
||||
|
||||
/**
|
||||
* Updates the width of the field. Colour fields have a constant width, but
|
||||
* the width is sometimes reset to force a rerender.
|
||||
*/
|
||||
Blockly.FieldColour.prototype.updateWidth = function() {
|
||||
var width = Blockly.FieldColour.DEFAULT_WIDTH;
|
||||
if (this.borderRect_) {
|
||||
this.borderRect_.setAttribute('width',
|
||||
width + Blockly.BlockSvg.SEP_SPACE_X);
|
||||
}
|
||||
this.size_.width = width;
|
||||
};
|
||||
|
||||
/**
|
||||
* Set the colour.
|
||||
* @param {string} colour The new colour in '#rrggbb' format.
|
||||
@@ -217,21 +277,16 @@ Blockly.FieldColour.prototype.setColumns = function(columns) {
|
||||
* @private
|
||||
*/
|
||||
Blockly.FieldColour.prototype.showEditor_ = function() {
|
||||
Blockly.WidgetDiv.show(this, this.sourceBlock_.RTL,
|
||||
Blockly.FieldColour.widgetDispose_);
|
||||
|
||||
// Record viewport dimensions before adding the widget.
|
||||
var viewportBBox = Blockly.utils.getViewportBBox();
|
||||
var anchorBBox = this.getScaledBBox_();
|
||||
Blockly.DropDownDiv.hideWithoutAnimation();
|
||||
Blockly.DropDownDiv.clearContent();
|
||||
|
||||
// Create and add the colour picker, then record the size.
|
||||
var picker = this.createWidget_();
|
||||
Blockly.WidgetDiv.DIV.appendChild(picker);
|
||||
var paletteSize = goog.style.getSize(picker);
|
||||
Blockly.DropDownDiv.getContentDiv().appendChild(picker);
|
||||
Blockly.DropDownDiv.setColour(
|
||||
this.DROPDOWN_BACKGROUND_COLOUR, this.DROPDOWN_BORDER_COLOUR);
|
||||
|
||||
// Position the picker to line up with the field.
|
||||
Blockly.WidgetDiv.positionWithAnchor(viewportBBox, anchorBBox, paletteSize,
|
||||
this.sourceBlock_.RTL);
|
||||
Blockly.DropDownDiv.showPositionedByField(this);
|
||||
|
||||
// Configure event handler on the table to listen for any event in a cell.
|
||||
Blockly.FieldColour.onUpWrapper_ = Blockly.bindEvent_(picker,
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
|
||||
goog.provide('Blockly.FieldTextInput');
|
||||
|
||||
goog.require('Blockly.DropDownDiv');
|
||||
goog.require('Blockly.Field');
|
||||
goog.require('Blockly.Msg');
|
||||
goog.require('Blockly.utils');
|
||||
@@ -260,11 +261,14 @@ Blockly.FieldTextInput.prototype.onHtmlInputKeyDown_ = function(e) {
|
||||
var tabKey = 9, enterKey = 13, escKey = 27;
|
||||
if (e.keyCode == enterKey) {
|
||||
Blockly.WidgetDiv.hide();
|
||||
Blockly.DropDownDiv.hideIfOwner(this);
|
||||
} else if (e.keyCode == escKey) {
|
||||
htmlInput.value = htmlInput.defaultValue;
|
||||
Blockly.WidgetDiv.hide();
|
||||
Blockly.DropDownDiv.hideIfOwner(this);
|
||||
} else if (e.keyCode == tabKey) {
|
||||
Blockly.WidgetDiv.hide();
|
||||
Blockly.DropDownDiv.hideIfOwner(this);
|
||||
this.sourceBlock_.tab(this, !e.shiftKey);
|
||||
e.preventDefault();
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ goog.provide('Blockly.inject');
|
||||
|
||||
goog.require('Blockly.BlockDragSurfaceSvg');
|
||||
goog.require('Blockly.Css');
|
||||
goog.require('Blockly.DropDownDiv');
|
||||
goog.require('Blockly.Grid');
|
||||
goog.require('Blockly.Options');
|
||||
goog.require('Blockly.utils');
|
||||
@@ -342,6 +343,7 @@ Blockly.createMainWorkspace_ = function(svg, options, blockDragSurface,
|
||||
// The SVG is now fully assembled.
|
||||
Blockly.svgResize(mainWorkspace);
|
||||
Blockly.WidgetDiv.createDom();
|
||||
Blockly.DropDownDiv.createDom();
|
||||
Blockly.Tooltip.createDom();
|
||||
return mainWorkspace;
|
||||
};
|
||||
|
||||
@@ -1029,3 +1029,20 @@ Blockly.utils.getBlockTypeCounts = function(block, opt_stripFollowing) {
|
||||
}
|
||||
return typeCountsMap;
|
||||
};
|
||||
|
||||
/**
|
||||
* Clamp the provided number between the lower bound and the upper bound.
|
||||
* @param {Number} lowerBound The desired lower bound.
|
||||
* @param {Number} number The number to clamp.
|
||||
* @param {Number} upperBound The desired upper bound.
|
||||
* @return {Number} the clamped number
|
||||
* @package
|
||||
*/
|
||||
Blockly.utils.clampNumber = function(lowerBound, number, upperBound) {
|
||||
if (upperBound < lowerBound) {
|
||||
var temp = upperBound;
|
||||
upperBound = lowerBound;
|
||||
lowerBound = temp;
|
||||
}
|
||||
return Math.max(lowerBound, Math.min(number, upperBound));
|
||||
};
|
||||
|
||||
@@ -173,7 +173,8 @@ Blockly.defineBlocksWithJsonArray([ // BEGIN JSON EXTRACT
|
||||
"text": "NO ANGLE FIELD"
|
||||
}
|
||||
}
|
||||
]
|
||||
],
|
||||
"colour": "230"
|
||||
},
|
||||
{
|
||||
"type": "example_date",
|
||||
|
||||
Reference in New Issue
Block a user