/// <summary> /// Return the coordinates of the top-left corner of this element relative to /// its parent. Only for SVG elements and children (e.g. rect, g, path). /// </summary> /// <param name="element">SVG element to find the coordinates of.</param> /// <returns>Object with .x and .y properties.</returns> internal static goog.math.Coordinate getRelativeXY_(Element element) { var xy = new goog.math.Coordinate(0, 0); // First, check for x and y attributes. var x = element.GetAttribute("x"); if (!String.IsNullOrEmpty(x)) { xy.x = Script.ParseFloat(x); } var y = element.GetAttribute("y"); if (!String.IsNullOrEmpty(y)) { xy.y = Script.ParseFloat(y); } // Second, check for transform="translate(...)" attribute. var transform = element.GetAttribute("transform"); var r = transform?.Match(Core.getRelativeXY__XY_REGEXP_); if (r?.Length > 0) { xy.x += Script.ParseFloat(r[1]); if (r[3] != null) { xy.y += Script.ParseFloat(r[3]); } } return(xy); }
/// <summary> /// Position the widget at a given location. Prevent the widget from going /// offscreen top or left (right in RTL). /// </summary> /// <param name="anchorX">Horizontal location (window coorditates, not body).</param> /// <param name="anchorY">Vertical location (window coorditates, not body).</param> /// <param name="windowSize">Height/width of window.</param> /// <param name="scrollOffset">X/y of window scrollbars.</param> /// <param name="rtl">True if RTL, false if LTR.</param> public static void position(double anchorX, double anchorY, goog.math.Size windowSize, goog.math.Coordinate scrollOffset, bool rtl) { // Don't let the widget go above the top edge of the window. if (anchorY < scrollOffset.y) { anchorY = scrollOffset.y; } if (rtl) { // Don't let the widget go right of the right edge of the window. if (anchorX > windowSize.width + scrollOffset.x) { anchorX = windowSize.width + scrollOffset.x; } } else { // Don't let the widget go left of the left edge of the window. if (anchorX < scrollOffset.x) { anchorX = scrollOffset.x; } } WidgetDiv.DIV.Style.Left = anchorX + "px"; WidgetDiv.DIV.Style.Top = anchorY + "px"; WidgetDiv.DIV.Style.Height = windowSize.height + "px"; }
/** * Record the block's new location. Called after the move. */ public void recordNew() { var location = this.currentLocation_(); this.newParentId = location.parentId; this.newInputName = location.inputName; this.newCoordinate = location.coordinate; }
/// <summary> /// Notification that the anchor has moved. /// Update the arrow and bubble accordingly. /// </summary> /// <param name="xy">Absolute location.</param> public void setAnchorLocation(goog.math.Coordinate xy) { this.anchorXY_ = xy; if (this.rendered_) { this.positionBubble_(); } }
/// <summary> /// Notification that the icon has moved. Update the arrow accordingly. /// </summary> /// <param name="xy">Absolute location.</param> internal void setIconLocation(goog.math.Coordinate xy) { this.iconXY_ = xy; if (this.isVisible()) { this.bubble_.setAnchorLocation(xy); } }
/// <summary> /// Class for a button in the flyout. /// </summary> /// <param name="workspace">The workspace in which to place this /// button.</param> /// <param name="targetWorkspace">The flyout's target workspace.</param> /// <param name="text">The text to display on the button.</param> public FlyoutButton(WorkspaceSvg workspace, WorkspaceSvg targetWorkspace, string text, string d = null, bool label = false) { this.workspace_ = workspace; this.targetWorkspace_ = targetWorkspace; this.text_ = text; position_ = new goog.math.Coordinate(0.0, 0.0); this.callback_ = Core.flyoutButtonCallbacks_[d]; this.isLabel_ = label; }
/// <summary> /// Decode the JSON event. /// </summary> /// <param name="json">JSON representation.</param> public override void fromJson(EventJsonData json) { base.fromJson(json); this.newParentId = json.newParentId.ToString(); this.newInputName = json.newInputName.ToString(); if (json.newCoordinate != null) { var xy = (json.newCoordinate.ToString()).Split(","); this.newCoordinate = new goog.math.Coordinate(Script.ParseFloat(xy[0]), Script.ParseFloat(xy[1])); } }
/// <summary> /// Class for a block move ev. Created before the move. /// </summary> /// <param name="block">The moved block. Null for a blank ev.</param> public Move(Block block) : base(block, Events.MOVE) { if (block == null) { return; // Blank ev to be populated by fromJson. } var location = this.currentLocation_(); this.oldParentId = location.parentId; this.oldInputName = location.inputName; this.oldCoordinate = location.coordinate; }
/// <summary> /// Notification that the icon has moved, but we don't really know where. /// Recompute the icon's location from scratch. /// </summary> internal void computeIconLocation() { // Find coordinates for the centre of the icon and update the arrow. var blockXY = this.block_.getRelativeToSurfaceXY(); var iconXY = Core.getRelativeXY_(this.iconGroup_); var newXY = new goog.math.Coordinate( blockXY.x + iconXY.x + this.SIZE / 2, blockXY.y + iconXY.y + this.SIZE / 2); if (!goog.math.Coordinate.equals(this.getIconLocation(), newXY)) { this.setIconLocation(newXY); } }
/// <summary> /// Class for UI bubble. /// </summary> /// <param name="workspace">The workspace on which to draw the /// bubble.</param> /// <param name="content">SVG content for the bubble.</param> /// <param name="shape">SVG element to avoid eclipsing.</param> /// <param name="anchorXY">Absolute position of bubble's anchor /// point.</param> /// <param name="bubbleWidth">Width of bubble, or null if not resizable.</param> /// <param name="bubbleHeight">Height of bubble, or null if not resizable.</param> public Bubble(WorkspaceSvg workspace, SVGElement content, SVGElement shape, goog.math.Coordinate anchorXY, double bubbleWidth, double bubbleHeight) { this.workspace_ = workspace; this.content_ = content; this.shape_ = shape; var angle = Bubble.ARROW_ANGLE; if (this.workspace_.RTL) { angle = -angle; } this.arrow_radians_ = goog.math.toRadians(angle); var canvas = workspace.getBubbleCanvas(); canvas.AppendChild(this.createDom_(content, !(bubbleWidth != 0.0 && bubbleHeight != 0.0))); this.setAnchorLocation(anchorXY); if (bubbleWidth == 0.0 || bubbleHeight == 0.0) { var bBox = /** @type {SVGLocatable} */ (this.content_).getBBox(); bubbleWidth = bBox.width + 2 * Bubble.BORDER_WIDTH; bubbleHeight = bBox.height + 2 * Bubble.BORDER_WIDTH; } this.setBubbleSize(bubbleWidth, bubbleHeight); // Render the bubble. this.positionBubble_(); this.renderArrow_(); this.rendered_ = true; if (!workspace.options.readOnly) { Core.bindEventWithChecks_(this.bubbleBack_, "mousedown", this, new Action <MouseEvent>(this.bubbleMouseDown_)); if (this.resizeGroup_ != null) { Core.bindEventWithChecks_(this.resizeGroup_, "mousedown", this, new Action <MouseEvent>(this.resizeMouseDown_)); } } }
public Scrollbar(WorkspaceSvg workspace, bool horizontal, bool opt_pair = false) { this.workspace_ = workspace; this.pair_ = opt_pair; this.horizontal_ = horizontal; this.oldHostMetrics_ = null; this.createDom_(); this.position_ = new goog.math.Coordinate(0, 0); if (horizontal) { this.svgBackground_.SetAttribute("height", Scrollbar.scrollbarThickness.ToString()); this.svgHandle_.SetAttribute("height", (Scrollbar.scrollbarThickness - 5).ToString()); this.svgHandle_.SetAttribute("y", 2.5.ToString()); this.lengthAttribute_ = "width"; this.positionAttribute_ = "x"; } else { this.svgBackground_.SetAttribute("width", Scrollbar.scrollbarThickness.ToString()); this.svgHandle_.SetAttribute("width", (Scrollbar.scrollbarThickness - 5).ToString()); this.svgHandle_.SetAttribute("x", 2.5.ToString()); this.lengthAttribute_ = "height"; this.positionAttribute_ = "y"; } var scrollbar = this; this.onMouseDownBarWrapper_ = Core.bindEventWithChecks_( this.svgBackground_, "mousedown", scrollbar, new Action <MouseEvent>(scrollbar.onMouseDownBar_)); this.onMouseDownHandleWrapper_ = Core.bindEventWithChecks_(this.svgHandle_, "mousedown", scrollbar, new Action <MouseEvent>(scrollbar.onMouseDownHandle_)); }
public override void handleMouseUp(events.BrowserEvent e) { var parentMenu = (Menu)this.getParent(); if (parentMenu != null) { var oldCoords = parentMenu.openingCoords; // Clear out the saved opening coords immediately so they"re not used twice. parentMenu.openingCoords = null; if (oldCoords != null /*&& goog.isNumber(e.clientX)*/) { var newCoords = new goog.math.Coordinate(e.clientX, e.clientY); if (goog.math.Coordinate.equals(oldCoords, newCoords)) { // This menu was opened by a mousedown and we"re handling the consequent // mouseup. The coords haven't changed, meaning this was a simple click, // not a click and drag. Don't do the usual behavior because the menu // just popped up under the mouse and the user didn't mean to activate // this item. return; } } } base.handleMouseUp(e); }
/// <summary> /// Returns whether the provided element is to be considered inside the menu for /// purposes such as dismissing the menu on an event. This is so submenus can /// make use of elements outside their own DOM. /// </summary> /// <param name="element">The element to test for.</param> /// <returns>Whether the provided element is to be considered inside /// the menu.</returns> public bool containsElement(HTMLElement element) { if (((MenuRenderer)this.getRenderer()).containsElement(this, element)) { return(true); } for (int i = 0, count = this.getChildCount(); i < count; i++) { var child = this.getChildAt(i); if ( /*typeof child.containsElement == "function"*/ child is Menu menu /**/ && /*child*/ menu /**/.containsElement(element)) { return(true); } } return(false); } /// <summary> /// Adds a new menu item at the end of the menu. /// </summary> /// <param name="item"> Menu</param> /// item to add to the menu. public void addItem(Union <MenuHeader, MenuItem, MenuSeparator> item) { this.addChild((Component)item.Value, true); } /// <summary> /// Adds a new menu item at a specific index in the menu. /// </summary> /// <param name="item">Menu /// item to add to the menu.</param> /// <param name="n"> Index at which to insert the menu item.</param> public void addItemAt(Union <MenuHeader, MenuItem, MenuSeparator> item, int n) { this.addChildAt((Component)item.Value, n, true); } /// <summary> /// Removes an item from the menu and disposes of it. /// </summary> /// <param name="item"> The /// menu item to remove.</param> public void removeItem(Union <MenuHeader, MenuItem, MenuSeparator> item) { var removedChild = this.removeChild((Component)item.Value, true); if (removedChild != null) { removedChild.dispose(); } } /// <summary> /// Removes a menu item at a given index in the menu and disposes of it. /// </summary> /// <param name="n">Index of item.</param> public void removeItemAt(int n) { var removedChild = this.removeChildAt(n, true); if (removedChild != null) { removedChild.dispose(); } } /// <summary> /// Returns a reference to the menu item at a given index. /// </summary> /// <param name="n"> Index of menu item.</param> /// <returns>Reference to the menu item.</returns> public Union <MenuHeader, MenuItem, MenuSeparator> getItemAt(int n) { return(new Union <MenuHeader, MenuItem, MenuSeparator>(this.getChildAt(n))); } /// <summary> /// Returns the number of items in the menu (including separators). /// </summary> /// <returns>The number of items in the menu.</returns> public int getItemCount() { return(this.getChildCount()); } /// <summary> /// Returns an array containing the menu items contained in the menu. /// </summary> /// <returns>An array of menu items.</returns> public JsArray <MenuItem> getItems() { // TODO(user): Remove reference to getItems and instead use getChildAt, // forEachChild, and getChildCount var children = new JsArray <MenuItem>(); this.forEachChild((child) => { children.Push((MenuItem)child); }); return(children); } /// <summary> /// Sets the position of the menu relative to the view port. /// </summary> /// <param name="x">Left position or coordinate obj.</param> /// <param name="opt_y"> Top position.</param> public void setPosition(Union <double, goog.math.Coordinate> x, double opt_y = 0.0) { // NOTE(user): It is necessary to temporarily set the display from none, so // that the position gets set correctly. var visible = this.isVisible(); if (!visible) { goog.style.setElementShown(this.getElement(), true); } goog.style.setPageOffset(this.getElement(), x, opt_y); if (!visible) { goog.style.setElementShown(this.getElement(), false); } } /// <summary> /// Gets the page offset of the menu, or null if the menu isn't visible /// </summary> /// <returns>Object holding the x-y coordinates of the /// menu or null if the menu is not visible.</returns> public goog.math.Coordinate getPosition() { return(this.isVisible() ? goog.style.getPageOffset(this.getElement()) : null); } /// <summary> /// Sets whether the menu can automatically move focus to its key event target /// when it is set to visible. /// </summary> /// <param name="allow">Whether the menu can automatically move focus to its</param> public void setAllowAutoFocus(bool allow) { this.allowAutoFocus_ = allow; if (allow) { this.setFocusable(true); } } /// <summary> /// </summary> /// <returns>Whether the menu can automatically move focus to its key /// event target when it is set to visible.</returns> public bool getAllowAutoFocus() { return(this.allowAutoFocus_); } /// <summary> /// Sets whether the menu will highlight disabled menu items or skip to the next /// active item. /// </summary> /// <param name="allow">Whether the menu will highlight disabled menu items or /// skip to the next active item.</param> public void setAllowHighlightDisabled(bool allow) { this.allowHighlightDisabled_ = allow; } /// <summary> /// </summary> /// <returns>Whether the menu will highlight disabled menu items or skip /// to the next active item.</returns> public bool getAllowHighlightDisabled() { return(this.allowHighlightDisabled_); } public override bool setVisible(bool visible, bool opt_force = false) { return(setVisible(visible, opt_force)); } /// <summary> /// </summary> /// <param name="show">Whether to show or hide the menu.</param> /// <param name="opt_force">If true, doesn't check whether the menu /// already has the requested visibility, and doesn't dispatch any events.</param> /// <param name="opt_e">Mousedown event that caused this menu to /// be made visible (ignored if show is false).</param> /// <returns></returns> public bool setVisible(bool show, bool opt_force = false, goog.events.BrowserEvent opt_e = null) { var visibilityChanged = base.setVisible(show, opt_force); if (visibilityChanged && show && this.isInDocument() && this.allowAutoFocus_) { this.getKeyEventTarget().Focus(); } if (show && opt_e != null /*&& goog.isNumber(opt_e.clientX)*/) { this.openingCoords = new goog.math.Coordinate(opt_e.clientX, opt_e.clientY); }
/// <summary> /// Find the closest compatible connection to this connection. /// </summary> /// <param name="conn">The connection searching for a compatible /// mate.</param> /// <param name="maxRadius">The maximum radius to another connection.</param> /// <param name="dxy">Offset between this connection's location /// in the database and the current location (as a result of dragging).</param> /// <returns>Contains two properties:' connection' which is either another connection or null, /// and 'radius' which is the distance.</returns> public RenderedConnection.Closest searchForClosest(RenderedConnection conn, double maxRadius, goog.math.Coordinate dxy) { // Don't bother. if (this.array_.Length == 0) { return(new RenderedConnection.Closest { connection = null, radius = maxRadius }); } // Stash the values of x and y from before the drag. var baseY = conn.y_; var baseX = conn.x_; conn.x_ = baseX + dxy.x; conn.y_ = baseY + dxy.y; // findPositionForConnection finds an index for insertion, which is always // after any block with the same y index. We want to search both forward // and back, so search on both sides of the index. var closestIndex = this.findPositionForConnection_(conn); Connection bestConnection = null; var bestRadius = maxRadius; RenderedConnection temp; // Walk forward and back on the y axis looking for the closest x,y point. var pointerMin = closestIndex - 1; while (pointerMin >= 0 && this.isInYRange_(pointerMin, conn.y_, maxRadius)) { temp = this.array_[pointerMin]; if (conn.isConnectionAllowed(temp, bestRadius)) { bestConnection = temp; bestRadius = temp.distanceFrom(conn); } pointerMin--; } var pointerMax = closestIndex; while (pointerMax < this.array_.Length && this.isInYRange_(pointerMax, conn.y_, maxRadius)) { temp = this.array_[pointerMax]; if (conn.isConnectionAllowed(temp, bestRadius)) { bestConnection = temp; bestRadius = temp.distanceFrom(conn); } pointerMax++; } // Reset the values of x and y. conn.x_ = baseX; conn.y_ = baseY; // If there were no valid connections, bestConnection will be null. return(new RenderedConnection.Closest { connection = bestConnection, radius = bestRadius }); }
/// <summary> /// Class for a connection between blocks that may be rendered on screen. /// </summary> /// <param name="source">The block establishing this connection.</param> /// <param name="type">The type of the connection.</param> public RenderedConnection(Block source, int type) : base(source, type) { this.offsetInBlock_ = new goog.math.Coordinate(0, 0); }
/// <summary> /// Move this connection to the location given by its offset within the block and /// the coordinate of the block's top left corner. /// </summary> /// <param name="blockTL">The coordinate of the top left corner /// of the block.</param> public void moveToOffset(goog.math.Coordinate blockTL) { this.moveTo(blockTL.x + this.offsetInBlock_.x, blockTL.y + this.offsetInBlock_.y); }
public Location(string parentId, string inputName, goog.math.Coordinate coordinate) { this.parentId = parentId; this.inputName = inputName; this.coordinate = coordinate; }