Exemple #1
0
 /// <summary>
 /// Destroy the widget and hide the div if it is being used by the specified
 /// object.
 /// </summary>
 /// <param name="oldOwner">The object that was using this container.</param>
 public static void hideIfOwner(object oldOwner)
 {
     if (WidgetDiv.owner_ == oldOwner)
     {
         WidgetDiv.hide();
     }
 }
        /// <summary>
        /// Show the inline free-text editor on top of the text.
        /// </summary>
        /// <param name="opt_quietInput">True if editor should be created without
        /// focus.Defaults to false.</param>
        public override void showEditor_(bool opt_quietInput)
        {
            this.workspace_ = (WorkspaceSvg)this.sourceBlock_.workspace;
            var quietInput = opt_quietInput || false;

            if (!quietInput && (goog.userAgent.MOBILE || goog.userAgent.ANDROID ||
                                goog.userAgent.IPAD))
            {
                // Mobile browsers have issues with in-line textareas (focus & keyboards).
                var newValue = Window.Prompt(Msg.CHANGE_VALUE_TITLE, this.text_);
                if (this.sourceBlock_ != null)
                {
                    newValue = this.callValidator(newValue);
                }
                this.setValue(newValue);
                return;
            }

            WidgetDiv.show(this, this.sourceBlock_.RTL, this.widgetDispose_());
            var div = WidgetDiv.DIV;
            // Create the input.
            var htmlInput = (HTMLInputElement)
                            goog.dom.createDom(goog.dom.TagName.INPUT, "blocklyHtmlInput");

            htmlInput.SetAttribute("spellcheck", this.spellcheck_.ToString());
            var fontSize =
                (FieldTextInput.FONTSIZE * this.workspace_.scale) + "pt";

            div.Style.FontSize       = fontSize;
            htmlInput.Style.FontSize = fontSize;
            /** @type {!HTMLInputElement} */
            FieldTextInput.htmlInput_ = htmlInput;
            div.AppendChild(htmlInput);

            htmlInput.Value        = htmlInput.DefaultValue = this.text_;
            htmlInput["oldValue_"] = null;
            this.validate_();
            this.resizeEditor_();
            if (!quietInput)
            {
                htmlInput.Focus();
                htmlInput.Select();
            }

            // Bind to keydown -- trap Enter without IME and Esc to hide.
            htmlInput["onKeyDownWrapper_"] =
                Core.bindEventWithChecks_(htmlInput, "keydown", this,
                                          new Action <KeyboardEvent>(this.onHtmlInputKeyDown_));
            // Bind to keyup -- trap Enter; resize after every keystroke.
            htmlInput["onKeyUpWrapper_"] =
                Core.bindEventWithChecks_(htmlInput, "keyup", this,
                                          new Action <Event>(this.onHtmlInputChange_));
            // Bind to keyPress -- repeatedly resize when holding down a key.
            htmlInput["onKeyPressWrapper_"] =
                Core.bindEventWithChecks_(htmlInput, "keypress", this,
                                          new Action <Event>(this.onHtmlInputChange_));
            htmlInput["onWorkspaceChangeWrapper_"] = new Action <Events.Abstract>(this.resizeEditor_);
            this.workspace_.addChangeListener((Action <Events.Abstract>)htmlInput["onWorkspaceChangeWrapper_"]);
        }
Exemple #3
0
        /// <summary>
        /// Initialize and display the widget div.  Close the old one if needed.
        /// </summary>
        /// <param name="newOwner">The object that will be using this container.</param>
        /// <param name="rtl">Right-to-left (true) or left-to-right (false).</param>
        /// <param name="dispose">Optional cleanup function to be run when the widget
        /// is closed.</param>
        public static void show(object newOwner, bool rtl, Action dispose)
        {
            WidgetDiv.hide();
            WidgetDiv.owner_   = newOwner;
            WidgetDiv.dispose_ = dispose;
            // Temporarily move the widget to the top of the screen so that it does not
            // cause a scrollbar jump in Firefox when displayed.
            var xy = goog.style.getViewportPageOffset(Document.Instance);

            WidgetDiv.DIV.Style.Top       = xy.y + "px";
            WidgetDiv.DIV.Style.Direction = rtl ? Direction.Rtl : Direction.Ltr;
            WidgetDiv.DIV.Style.Display   = Display.Block;
        }
Exemple #4
0
 /// <summary>
 /// Close tooltips, context menus, dropdown selections, etc.
 /// </summary>
 /// <param name="opt_allowToolbox">If true, don't close the toolbox.</param>
 public static void hideChaff(bool opt_allowToolbox = false)
 {
     Tooltip.hide();
     WidgetDiv.hide();
     if (!opt_allowToolbox)
     {
         var workspace = Core.getMainWorkspace();
         if (workspace.toolbox_ != null &&
             workspace.toolbox_.flyout_ != null &&
             workspace.toolbox_.flyout_.autoClose)
         {
             workspace.toolbox_.clearSelection();
         }
     }
 }
Exemple #5
0
 /// <summary>
 /// Destroy the widget and hide the div.
 /// </summary>
 public static void hide(Event e = null)
 {
     if (WidgetDiv.owner_ != null)
     {
         WidgetDiv.owner_            = null;
         WidgetDiv.DIV.Style.Display = Display.None;
         WidgetDiv.DIV.Style.Left    = "";
         WidgetDiv.DIV.Style.Top     = "";
         if (WidgetDiv.dispose_ != null)
         {
             WidgetDiv.dispose_();
         }
         WidgetDiv.dispose_ = null;
         goog.dom.removeChildren(WidgetDiv.DIV);
     }
 }
        /// <summary>
        /// Handle key down to the editor.
        /// </summary>
        /// <param name="e">Keyboard event.</param>
        private void onHtmlInputKeyDown_(KeyboardEvent e)
        {
            var htmlInput = FieldTextInput.htmlInput_;
            int tabKey = 9, enterKey = 13, escKey = 27;

            if (e.KeyCode == enterKey)
            {
                WidgetDiv.hide();
            }
            else if (e.KeyCode == escKey)
            {
                htmlInput.Value = htmlInput.DefaultValue;
                WidgetDiv.hide();
            }
            else if (e.KeyCode == tabKey)
            {
                WidgetDiv.hide();
                this.sourceBlock_.tab(this, !e.ShiftKey);
                e.PreventDefault();
            }
        }
Exemple #7
0
 /// <summary>
 /// When hovering over an element, schedule a tooltip to be shown.  If a tooltip
 /// is already visible, hide it if the mouse strays out of a certain radius.
 /// </summary>
 /// <param name="e">Mouse event.</param>
 private static void onMouseMove_(MouseEvent e)
 {
     if (Tooltip.element_ == null || Tooltip.tooltip_ == null)
     {
         // No tooltip here to show.
         return;
     }
     else if (Core.dragMode_ != Core.DRAG_NONE)
     {
         // Don't display a tooltip during a drag.
         return;
     }
     else if (WidgetDiv.isVisible())
     {
         // Don't display a tooltip if a widget is open (tooltip would be under it).
         return;
     }
     if (Tooltip.visible)
     {
         // Compute the distance between the mouse position when the tooltip was
         // shown and the current mouse position.  Pythagorean theorem.
         var dx = Tooltip.lastX_ - e.PageX;
         var dy = Tooltip.lastY_ - e.PageY;
         if (System.Math.Sqrt(dx * dx + dy * dy) > Tooltip.RADIUS_OK)
         {
             Tooltip.hide();
         }
     }
     else if (Tooltip.poisonedElement_ != Tooltip.element_)
     {
         // The mouse moved, clear any previously scheduled tooltip.
         Window.ClearTimeout(Tooltip.showPid_);
         // Maybe this time the mouse will stay put.  Schedule showing of tooltip.
         Tooltip.lastX_   = e.PageX;
         Tooltip.lastY_   = e.PageY;
         Tooltip.showPid_ =
             Window.SetTimeout(Tooltip.show_, Tooltip.HOVER_MS);
     }
 }
Exemple #8
0
        /// <summary>
        /// Create a date picker under the date field.
        /// </summary>
        public override void showEditor_(bool opt_quietInput)
        {
            WidgetDiv.show(this, this.sourceBlock_.RTL,
                           new Action(FieldDate.widgetDispose_));
            // Create the date picker using Closure.
            FieldDate.loadLanguage_();
            var picker = new goog.ui.DatePicker();

            picker.setAllowNone(false);
            picker.setShowWeekNum(false);

            // Position the picker to line up with the field.
            // Record windowSize and scrollOffset before adding the picker.
            var windowSize   = goog.dom.getViewportSize();
            var scrollOffset = goog.style.getViewportPageOffset(Document.Instance);
            var xy           = this.getAbsoluteXY_();
            var borderBBox   = this.getScaledBBox_();
            var div          = WidgetDiv.DIV;

            picker.render(div);
            picker.setDate(new Date(this.getValue()));
            // Record pickerSize after adding the date picker.
            var pickerSize = goog.style.getSize(picker.getElement());

            // Flip the picker vertically if off the bottom.
            if (xy.y + pickerSize.height + borderBBox.height >=
                windowSize.height + scrollOffset.y)
            {
                xy.y -= pickerSize.height - 1;
            }
            else
            {
                xy.y += borderBBox.height - 1;
            }
            if (this.sourceBlock_.RTL)
            {
                xy.x += borderBBox.width;
                xy.x -= pickerSize.width;
                // Don't go offscreen left.
                if (xy.x < scrollOffset.x)
                {
                    xy.x = scrollOffset.x;
                }
            }
            else
            {
                // Don't go offscreen right.
                if (xy.x > windowSize.width + scrollOffset.x - pickerSize.width)
                {
                    xy.x = windowSize.width + scrollOffset.x - pickerSize.width;
                }
            }
            WidgetDiv.position(xy.x, xy.y, windowSize, scrollOffset,
                               this.sourceBlock_.RTL);

            // Configure event handler.
            var thisField = this;

            FieldDate.changeEventKey_ = goog.events.listen(picker,
                                                           goog.ui.DatePicker.Events.CHANGE,
                                                           new Action <Bridge.Html5.Event>((e) => {
                var date = e.Date != null ? e.Date.ToIsoString(true) : "";
                WidgetDiv.hide();
                if (thisField.sourceBlock_ != null)
                {
                    // Call any validation function, and allow it to override.
                    date = thisField.callValidator(date);
                }
                thisField.setValue(date);
            }));
        }
Exemple #9
0
 /// <summary>
 /// Close the colour picker if this input is being deleted.
 /// </summary>
 public override void dispose()
 {
     WidgetDiv.hideIfOwner(this);
     base.dispose();
 }
Exemple #10
0
        /// <summary>
        /// Create a main workspace and add it to the SVG.
        /// </summary>
        /// <param name="svg">SVG element with pattern defined.</param>
        /// <param name="options">Dictionary of options.</param>
        /// <returns>Newly created main workspace.</returns>
        private static WorkspaceSvg createMainWorkspace_(SVGElement svg, Options options)
        {
            options.parentWorkspace = null;
            var mainWorkspace = new WorkspaceSvg(options);

            mainWorkspace.scale = options.zoomOptions.startScale;
            svg.AppendChild(mainWorkspace.createDom("blocklyMainBackground"));
            // A null translation will also apply the correct initial scale.
            mainWorkspace.translate(0, 0);
            mainWorkspace.markFocused(null);

            if (!options.readOnly && !options.hasScrollbars)
            {
                var workspaceChanged = new Action <Events.Abstract>((e) => {
                    if (Core.dragMode_ == Core.DRAG_NONE)
                    {
                        var metrics  = mainWorkspace.getMetrics();
                        var edgeLeft = metrics.viewLeft + metrics.absoluteLeft;
                        var edgeTop  = metrics.viewTop + metrics.absoluteTop;
                        if (metrics.contentTop < edgeTop ||
                            metrics.contentTop + metrics.contentHeight >
                            metrics.viewHeight + edgeTop ||
                            metrics.contentLeft <
                            (options.RTL ? metrics.viewLeft : edgeLeft) ||
                            metrics.contentLeft + metrics.contentWidth > (options.RTL ?
                                                                          metrics.viewWidth : metrics.viewWidth + edgeLeft))
                        {
                            // One or more blocks may be out of bounds.  Bump them back in.
                            var MARGIN = 25;
                            var blocks = mainWorkspace.getTopBlocks(false);
                            foreach (var block in blocks)
                            {
                                var blockXY = block.getRelativeToSurfaceXY();
                                var blockHW = ((BlockSvg)block).getHeightWidth();
                                // Bump any block that's above the top back inside.
                                var overflowTop = edgeTop + MARGIN - blockHW.height - blockXY.y;
                                if (overflowTop > 0)
                                {
                                    block.moveBy(0, overflowTop);
                                }
                                // Bump any block that's below the bottom back inside.
                                var overflowBottom =
                                    edgeTop + metrics.viewHeight - MARGIN - blockXY.y;
                                if (overflowBottom < 0)
                                {
                                    block.moveBy(0, overflowBottom);
                                }
                                // Bump any block that's off the left back inside.
                                var overflowLeft = MARGIN + edgeLeft -
                                                   blockXY.x - (options.RTL ? 0 : blockHW.width);
                                if (overflowLeft > 0)
                                {
                                    block.moveBy(overflowLeft, 0);
                                }
                                // Bump any block that's off the right back inside.
                                var overflowRight = edgeLeft + metrics.viewWidth - MARGIN -
                                                    blockXY.x + (options.RTL ? blockHW.width : 0);
                                if (overflowRight < 0)
                                {
                                    block.moveBy(overflowRight, 0);
                                }
                            }
                        }
                    }
                });
                mainWorkspace.addChangeListener(workspaceChanged);
            }
            // The SVG is now fully assembled.
            Core.svgResize(mainWorkspace);
            WidgetDiv.createDom();
            Tooltip.createDom();
            return(mainWorkspace);
        }
Exemple #11
0
        /// <summary>
        /// Create a dropdown menu under the text.
        /// </summary>
        public override void showEditor_(bool opt_quietInput)
        {
            WidgetDiv.show(this, this.sourceBlock_.RTL, null);
            var thisField = this;

            var callback = new Action <goog.events.Event>((e) => {
                var menuItem = (goog.ui.MenuItem)e.target;
                if (menuItem != null)
                {
                    var value = menuItem.getValue();
                    if (thisField.sourceBlock_ != null)
                    {
                        // Call any validation function, and allow it to override.
                        value = thisField.callValidator(value);
                    }
                    if (value != null)
                    {
                        thisField.setValue(value);
                    }
                }
                WidgetDiv.hideIfOwner(thisField);
            });

            var menu = new goog.ui.Menu();

            menu.setRightToLeft(this.sourceBlock_.RTL);
            var options = this.getOptions_();

            for (var i = 0; i < options.Length; i++)
            {
                var text     = options[i].text;              // Human-readable text.
                var value    = options[i].value;             // Language-neutral value.
                var menuItem = new goog.ui.MenuItem(text);
                menuItem.setRightToLeft(this.sourceBlock_.RTL);
                menuItem.setValue(value);
                menuItem.setCheckable(true);
                menu.addChild(menuItem, true);
                menuItem.setChecked(value == this.value_);
            }
            // Listen for mouse/keyboard events.
            goog.events.listen(menu, goog.ui.Component.EventType.ACTION, callback);
            // Listen for touch events (why doesn't Closure handle this already?).
            var callbackTouchStart = new Action <goog.events.BrowserEvent>((e) => {
                var control = menu.getOwnerControl((Node)e.target);
                // Highlight the menu item.
                control.handleMouseDown(e);
            });
            var callbackTouchEnd = new Action <goog.events.Event>((e) => {
                var control = menu.getOwnerControl((Node)e.target);
                // Activate the menu item.
                control.performActionInternal(e);
            });

            menu.getHandler().listen(menu.getElement(), goog.events.EventType.TOUCHSTART,
                                     callbackTouchStart);
            menu.getHandler().listen(menu.getElement(), goog.events.EventType.TOUCHEND,
                                     callbackTouchEnd);

            // Record windowSize and scrollOffset before adding menu.
            var windowSize   = goog.dom.getViewportSize();
            var scrollOffset = goog.style.getViewportPageOffset(Document.Instance);
            var xy           = this.getAbsoluteXY_();
            var borderBBox   = this.getScaledBBox_();
            var div          = WidgetDiv.DIV;

            menu.render(div);
            var menuDom = menu.getElement();

            Core.addClass_(menuDom, "blocklyDropdownMenu");
            // Record menuSize after adding menu.
            var menuSize = goog.style.getSize(menuDom);

            // Recalculate height for the total content, not only box height.
            menuSize.height = menuDom.ScrollHeight;

            // Position the menu.
            // Flip menu vertically if off the bottom.
            if (xy.y + menuSize.height + borderBBox.height >=
                windowSize.height + scrollOffset.y)
            {
                xy.y -= menuSize.height + 2;
            }
            else
            {
                xy.y += borderBBox.height;
            }
            if (this.sourceBlock_.RTL)
            {
                xy.x += borderBBox.width;
                xy.x += FieldDropdown.CHECKMARK_OVERHANG;
                // Don't go offscreen left.
                if (xy.x < scrollOffset.x + menuSize.width)
                {
                    xy.x = scrollOffset.x + menuSize.width;
                }
            }
            else
            {
                xy.x -= FieldDropdown.CHECKMARK_OVERHANG;
                // Don't go offscreen right.
                if (xy.x > windowSize.width + scrollOffset.x - menuSize.width)
                {
                    xy.x = windowSize.width + scrollOffset.x - menuSize.width;
                }
            }
            WidgetDiv.position(xy.x, xy.y, windowSize, scrollOffset,
                               this.sourceBlock_.RTL);
            menu.setAllowAutoFocus(true);
            menuDom.Focus();
        }
Exemple #12
0
        /// <summary>
        /// Create a palette under the colour field.
        /// </summary>
        public override void showEditor_(bool opt_quietInput)
        {
            WidgetDiv.show(this, this.sourceBlock_.RTL,
                           new Action(FieldColour.widgetDispose_));
            // Create the palette using Closure.
            var picker = new goog.ui.ColorPicker();

            picker.setSize(this.columns_ ?? FieldColour.COLUMNS);
            picker.setColors(new JsArray <string>(this.colours_ ?? FieldColour.COLOURS));

            // Position the palette to line up with the field.
            // Record windowSize and scrollOffset before adding the palette.
            var windowSize   = goog.dom.getViewportSize();
            var scrollOffset = goog.style.getViewportPageOffset(Document.Instance);
            var xy           = this.getAbsoluteXY_();
            var borderBBox   = this.getScaledBBox_();
            var div          = WidgetDiv.DIV;

            picker.render(div);
            picker.setSelectedColor(this.getValue());
            // Record paletteSize after adding the palette.
            var paletteSize = goog.style.getSize(picker.getElement());

            // Flip the palette vertically if off the bottom.
            if (xy.y + paletteSize.height + borderBBox.height >=
                windowSize.height + scrollOffset.y)
            {
                xy.y -= paletteSize.height - 1;
            }
            else
            {
                xy.y += borderBBox.height - 1;
            }
            if (this.sourceBlock_.RTL)
            {
                xy.x += borderBBox.width;
                xy.x -= paletteSize.width;
                // Don't go offscreen left.
                if (xy.x < scrollOffset.x)
                {
                    xy.x = scrollOffset.x;
                }
            }
            else
            {
                // Don't go offscreen right.
                if (xy.x > windowSize.width + scrollOffset.x - paletteSize.width)
                {
                    xy.x = windowSize.width + scrollOffset.x - paletteSize.width;
                }
            }
            WidgetDiv.position(xy.x, xy.y, windowSize, scrollOffset,
                               this.sourceBlock_.RTL);

            // Configure event handler.
            var thisField = this;

            FieldColour.changeEventKey_ = goog.events.listen(picker,
                                                             goog.ui.ColorPicker.EventType.CHANGE,
                                                             new Action <goog.events.Event>((e) => {
                string colour = ((goog.ui.ColorPicker)e.target).getSelectedColor() ?? "#000000";
                WidgetDiv.hide();
                if (thisField.sourceBlock_ != null)
                {
                    // Call any validation function, and allow it to override.
                    colour = thisField.callValidator(colour);
                }
                if (colour != null)
                {
                    thisField.setValue(colour);
                }
            }));
        }
Exemple #13
0
        /// <summary>
        /// Construct the menu based on the list of options and show the menu.
        /// </summary>
        /// <param name="e">Mouse event.</param>
        /// <param name="options">Array of menu options.</param>
        /// <param name="rtl">True if RTL, false if LTR.</param>
        public static void show(MouseEvent e, ContextMenuOption[] options, bool rtl)
        {
            WidgetDiv.show(Core.ContextMenu, rtl, null);
            if (options.Length == 0)
            {
                ContextMenu.hide();
                return;
            }

            /* Here's what one option object looks like:
             * {text: 'Make It So',
             * enabled: true,
             * callback: Blockly.MakeItSo}
             */
            var menu = new goog.ui.Menu();

            menu.setRightToLeft(rtl);
            foreach (var option in options)
            {
                var menuItem = new goog.ui.MenuItem(option.text);
                menuItem.setRightToLeft(rtl);
                menu.addChild(menuItem, true);
                menuItem.setEnabled(option.enabled);
                if (option.enabled)
                {
                    goog.events.listen(menuItem, goog.ui.Component.EventType.ACTION,
                                       option.callback);
                }
            }
            goog.events.listen(menu, goog.ui.Component.EventType.ACTION,
                               new Action <goog.events.Event>((ev) => ContextMenu.hide()));
            // Record windowSize and scrollOffset before adding menu.
            var windowSize   = goog.dom.getViewportSize();
            var scrollOffset = goog.style.getViewportPageOffset(Document.Instance);
            var div          = WidgetDiv.DIV;

            menu.render(div);
            var menuDom = menu.getElement();

            Core.addClass_(menuDom, "blocklyContextMenu");
            // Prevent system context menu when right-clicking a Blockly context menu.
            Core.bindEventWithChecks_(menuDom, "contextmenu", null, new Action <Event>(Core.noEvent));
            // Record menuSize after adding menu.
            var menuSize = goog.style.getSize(menuDom);

            // Position the menu.
            var x = e.ClientX + scrollOffset.x;
            var y = e.ClientY + scrollOffset.y;

            // Flip menu vertically if off the bottom.
            if (e.ClientY + menuSize.height >= windowSize.height)
            {
                y -= menuSize.height;
            }
            // Flip menu horizontally if off the edge.
            if (rtl)
            {
                if (menuSize.width >= e.ClientX)
                {
                    x += menuSize.width;
                }
            }
            else
            {
                if (e.ClientX + menuSize.width >= windowSize.width)
                {
                    x -= menuSize.width;
                }
            }
            WidgetDiv.position(x, y, windowSize, scrollOffset, rtl);

            menu.setAllowAutoFocus(true);
            // 1ms delay is required for focusing on context menus because some other
            // mouse event is still waiting in the queue and clears focus.
            Window.SetTimeout(() => { menuDom.Focus(); }, 1);
            ContextMenu.currentBlock = null;              // May be set by Blockly.Block.
        }
Exemple #14
0
 /// <summary>
 /// Hide the context menu.
 /// </summary>
 public static void hide()
 {
     WidgetDiv.hideIfOwner(Core.ContextMenu);
     ContextMenu.currentBlock = null;
 }