private void manageChunkIcons(AceEditorNative editor) { Element container = editor.getContainer(); if (container == null) return; Element[] icons = DomUtils.getElementsByClassName(container, ThemeStyles.INSTANCE.inlineChunkToolbar()); for (Element icon : icons) icon.removeFromParent(); if (!uiPrefs_.showInlineToolbarForRCodeChunks().getValue()) return; if (!shouldDisplayIcons(editor)) return; Element[] chunkStarts = DomUtils.getElementsByClassName("rstudio_chunk_start ace_start"); for (int i = 0; i < chunkStarts.length; i++) { Element el = chunkStarts[i]; if (isPseudoMarker(el)) continue; if (!isRunnableChunk(el, editor)) continue; if (!DomUtils.isVisibleVert(container, el)) continue; if (el.getChildCount() > 0) el.removeAllChildren(); addToolbar(el, isSetupChunk(el, editor), editor); } }
public void scrollToCursor(ScrollPanel scrollPanel, int paddingVert, int paddingHoriz) { DomUtils.ensureVisibleVert( scrollPanel.getElement(), widget_.getEditor().getRenderer().getCursorElement(), paddingVert); DomUtils.ensureVisibleHoriz( scrollPanel.getElement(), widget_.getEditor().getRenderer().getCursorElement(), paddingHoriz, paddingHoriz, false); }
public void showModal() { if (mainWidget_ == null) { mainWidget_ = createMainWidget(); // get the main widget to line up with the right edge of the buttons. mainWidget_.getElement().getStyle().setMarginRight(3, Unit.PX); mainPanel_.insert(mainWidget_, 0); } originallyActiveElement_ = DomUtils.getActiveElement(); if (originallyActiveElement_ != null) originallyActiveElement_.blur(); // position the dialog positionAndShowDialog(); // defer shown notification to allow all elements to render // before attempting to interact w/ them programatically (e.g. setFocus) Timer timer = new Timer() { public void run() { onDialogShown(); } }; timer.schedule(100); }
@Override public void ensureSelectedRowIsVisible() { ArrayList<TableRowElement> rows = table_.getSelectedRows(); if (rows.size() > 0) { DomUtils.ensureVisibleVert(scrollPanel_.getElement(), rows.get(0), 20); } }
public void onClick(ClickEvent event) { // If clicking on the input panel already, stop propagation. if (event.getSource() == input_) { event.stopPropagation(); return; } // Don't drive focus to the input unless there is no selection. // Otherwise it would interfere with the ability to select stuff // from the output buffer for copying to the clipboard. // (BUG: DomUtils.selectionExists() doesn't work in a timely // fashion on IE8.) if (!DomUtils.selectionExists() && isInputOnscreen()) { input_.setFocus(true); DomUtils.collapseSelection(false); } }
private boolean trimExcess() { if (maxLines_ <= 0) return false; // No limit in effect int linesToTrim = lines_ - maxLines_; if (linesToTrim > 0) { lines_ -= DomUtils.trimLines(output_.getElement(), lines_ - maxLines_); return true; } return false; }
private Element getTabBarElement() { return (Element) DomUtils.findNode( getElement(), true, false, new NodePredicate() { public boolean test(Node n) { if (n.getNodeType() != Node.ELEMENT_NODE) return false; return ((Element) n).getClassName().contains("gwt-TabLayoutPanelTabs"); } }); }
private void commitPosition(int pos) { lastElementX_ = pos; // check to see if we're overlapping with another tab for (int i = 0; i < dragTabsHost_.getChildCount(); i++) { // skip non-element DOM nodes Node node = dragTabsHost_.getChild(i); if (node.getNodeType() != Node.ELEMENT_NODE) { continue; } // skip the current candidate (no point in testing it for swap) if (i == candidatePos_) { continue; } // skip the element we're dragging and elements that are not tabs Element ele = (Element) node; if (ele == dragElement_ || ele.getClassName().indexOf("gwt-TabLayoutPanelTab") < 0) { continue; } int left = DomUtils.leftRelativeTo(dragTabsHost_, ele); int right = left + ele.getClientWidth(); int minOverlap = Math.min(initDragWidth_ / 2, ele.getClientWidth() / 2); // a little complicated: compute the number of overlapping pixels // with this element; if the overlap is more than half of our width // (or the width of the candidate), it's swapping time if (Math.min(lastElementX_ + initDragWidth_, right) - Math.max(lastElementX_, left) >= minOverlap) { dragTabsHost_.removeChild(dragPlaceholder_); if (candidatePos_ > i) { dragTabsHost_.insertBefore(dragPlaceholder_, ele); } else { dragTabsHost_.insertAfter(dragPlaceholder_, ele); } candidatePos_ = i; // account for the extra element when moving to the right of the // original location if (dragElement_ != null && startPos_ != null) { destPos_ = startPos_ <= candidatePos_ ? candidatePos_ - 1 : candidatePos_; } else { destPos_ = candidatePos_; } } } }
private void display(Widget panel, Element underlyingMarker) { // Bail if the underlying marker isn't wide enough if (underlyingMarker.getOffsetWidth() < 250) return; // Get the 'virtual' parent -- this is the Ace scroller that houses all // of the Ace content, where we want our icons to live. We need them // to live here so that they properly hide when the user scrolls and // e.g. markers are only partially visible. Element virtualParent = DomUtils.getParent(underlyingMarker, 3); // We'd prefer to use 'getOffsetTop()' here, but that seems to give // some janky dimensions due to how the Ace layers are ... layered, // so we manually compute it. int top = underlyingMarker.getAbsoluteTop() - virtualParent.getAbsoluteTop(); panel.getElement().getStyle().setTop(top, Unit.PX); virtualParent.appendChild(panel.getElement()); }
private boolean isInputOnscreen() { return DomUtils.isVisibleVert(scrollPanel_.getElement(), inputLine_.getElement()); }
private boolean output(String text, String className, boolean addToTop) { if (text.indexOf('\f') >= 0) clearOutput(); Node node; boolean isOutput = StringUtil.isNullOrEmpty(className) || className.equals(styles_.output()); if (isOutput && !addToTop && trailingOutput_ != null) { // Short-circuit the case where we're appending output to the // bottom, and there's already some output there. We need to // treat this differently in case the new output uses control // characters to pound over parts of the previous output. int oldLineCount = DomUtils.countLines(trailingOutput_, true); trailingOutputConsole_.submit(text); trailingOutput_.setNodeValue(ensureNewLine(trailingOutputConsole_.toString())); int newLineCount = DomUtils.countLines(trailingOutput_, true); lines_ += newLineCount - oldLineCount; } else { Element outEl = output_.getElement(); text = VirtualConsole.consolify(text); if (isOutput) { VirtualConsole console = new VirtualConsole(); console.submit(text); String consoleSnapshot = console.toString(); // We use ensureNewLine to make sure that even if output // doesn't end with \n, a prompt will appear on its own line. // However, if we call ensureNewLine indiscriminantly (i.e. // on an output that's going to be followed by another output) // we can end up inserting newlines where they don't belong. // // It's safe to add a newline when we're appending output to // the end of the console, because if the next append is also // output, we'll use the contents of VirtualConsole and the // newline we add here will be plowed over. // // If we're prepending output to the top of the console, then // it's safe to add a newline if the next chunk (which is already // there) is something besides output. if (!addToTop || (!outEl.hasChildNodes() || outEl.getFirstChild().getNodeType() != Node.TEXT_NODE)) { consoleSnapshot = ensureNewLine(consoleSnapshot); } node = Document.get().createTextNode(consoleSnapshot); if (!addToTop) { trailingOutput_ = (Text) node; trailingOutputConsole_ = console; } } else { SpanElement span = Document.get().createSpanElement(); span.setClassName(className); span.setInnerText(text); node = span; if (!addToTop) { trailingOutput_ = null; trailingOutputConsole_ = null; } } if (addToTop) outEl.insertFirst(node); else outEl.appendChild(node); lines_ += DomUtils.countLines(node, true); } return !trimExcess(); }
public void endDrag(final Event evt, int action) { if (curState_ == STATE_NONE) return; // remove the properties used to position for dragging if (dragElement_ != null) { dragElement_.getStyle().clearLeft(); dragElement_.getStyle().clearPosition(); dragElement_.getStyle().clearZIndex(); dragElement_.getStyle().clearDisplay(); dragElement_.getStyle().clearOpacity(); // insert this tab where the placeholder landed if we're not // cancelling if (action == ACTION_COMMIT) { dragTabsHost_.removeChild(dragElement_); dragTabsHost_.insertAfter(dragElement_, dragPlaceholder_); } } // remove the placeholder if (dragPlaceholder_ != null) { dragTabsHost_.removeChild(dragPlaceholder_); dragPlaceholder_ = null; } if (dragElement_ != null && action == ACTION_EXTERNAL) { // if we own the dragged tab, change to external drag state dragElement_.getStyle().setOpacity(0.4); curState_ = STATE_EXTERNAL; } else { // otherwise, we're back to pristine curState_ = STATE_NONE; events_.fireEvent(new DocTabDragStateChangedEvent(DocTabDragStateChangedEvent.STATE_NONE)); } if (dragElement_ != null && action == ACTION_COMMIT) { // let observer know we moved; adjust the destination position one to // the left if we're right of the start position to account for the // position of the tab prior to movement if (startPos_ != null && startPos_ != destPos_) { TabReorderEvent event = new TabReorderEvent(startPos_, destPos_); fireEvent(event); } } // this is the case when we adopt someone else's doc if (dragElement_ == null && evt != null && action == ACTION_COMMIT) { // pull the document ID and source window out String data = evt.getDataTransfer().getData(getDataTransferFormat()); if (StringUtil.isNullOrEmpty(data)) return; // the data format is docID|windowID; windowID can be omitted if // the main window is the origin String pieces[] = data.split("\\|"); if (pieces.length < 1) return; events_.fireEvent( new DocWindowChangedEvent( pieces[0], pieces.length > 1 ? pieces[1] : "", initDragParams_, destPos_)); } // this is the case when our own drag ends; if it ended outside our // window and outside all satellites, treat it as a tab tear-off if (dragElement_ != null && evt != null && action == ACTION_CANCEL) { // if this is the last tab in satellite, we don't want to tear // it out boolean isLastSatelliteTab = docTabs_.size() == 1 && Satellite.isCurrentWindowSatellite(); // did the user drag the tab outside this doc? if (!isLastSatelliteTab && DomUtils.elementFromPoint(evt.getClientX(), evt.getClientY()) == null) { // did it end in any RStudio satellite window? String targetWindowName; Satellite satellite = RStudioGinjector.INSTANCE.getSatellite(); if (Satellite.isCurrentWindowSatellite()) { // this is a satellite, ask the main window targetWindowName = satellite.getWindowAtPoint(evt.getScreenX(), evt.getScreenY()); } else { // this is the main window, query our own satellites targetWindowName = RStudioGinjector.INSTANCE .getSatelliteManager() .getWindowAtPoint(evt.getScreenX(), evt.getScreenY()); } if (targetWindowName == null) { // it was dragged over nothing RStudio owns--pop it out events_.fireEvent( new PopoutDocInitiatedEvent( initDragParams_.getDocId(), new Point(evt.getScreenX(), evt.getScreenY()))); } } } if (curState_ != STATE_EXTERNAL) { // if we're in an end state, clear the drag element dragElement_ = null; } }
private void beginDrag(Event evt) { String docId = initDragParams_.getDocId(); int dragTabWidth = initDragWidth_; // set drag element state dragTabsHost_ = getTabBarElement(); dragScrollHost_ = dragTabsHost_.getParentElement(); outOfBounds_ = 0; candidatePos_ = null; startPos_ = null; // attempt to determine which tab the cursor is over Point hostPos = DomUtils.getRelativePosition(Document.get().getBody(), dragTabsHost_); int dragX = evt.getClientX() - hostPos.getX(); for (int i = 0; i < dragTabsHost_.getChildCount(); i++) { Node node = dragTabsHost_.getChild(i); if (node.getNodeType() == Node.ELEMENT_NODE) { int left = DomUtils.leftRelativeTo(dragTabsHost_, Element.as(node)) - dragScrollHost_.getScrollLeft(); int right = left + Element.as(node).getOffsetWidth(); if (left <= dragX && dragX <= right) { candidatePos_ = i; break; } } } // let the rest of the IDE know we're dragging (this will enable us to // disable drag targets that might otherwise be happy to accept the // data) curState_ = STATE_DRAGGING; events_.fireEvent( new DocTabDragStateChangedEvent(DocTabDragStateChangedEvent.STATE_DRAGGING)); // the relative position of the last node determines how far we // can drag dragMax_ = DomUtils.leftRelativeTo(dragTabsHost_, getLastChildElement(dragTabsHost_)) + getLastChildElement(dragTabsHost_).getClientWidth(); lastCursorX_ = evt.getClientX(); // account for cursor starting out of bounds (e.g. dragging into // empty space on the right of the panel) if (lastCursorX_ > dragMax_ + (initDragParams_.getCursorOffset())) outOfBounds_ = (lastCursorX_ - dragMax_) - initDragParams_.getCursorOffset(); // attempt to ascertain whether the element being dragged is one of // our own documents for (DocTab tab : docTabs_) { if (tab.getDocId() == docId) { dragElement_ = tab.getElement().getParentElement().getParentElement(); break; } } // if we couldn't find the horizontal drag position in any tab, append // to the end if (candidatePos_ == null) { candidatePos_ = dragTabsHost_.getChildCount(); } destPos_ = candidatePos_; // if we're dragging one of our own documents, figure out its physical // position if (dragElement_ != null) { for (int i = 0; i < dragTabsHost_.getChildCount(); i++) { if (dragTabsHost_.getChild(i) == dragElement_) { startPos_ = i; break; } } } // compute the start location for the drag if (candidatePos_ >= dragTabsHost_.getChildCount()) { Element lastTab = getLastChildElement(dragTabsHost_); lastElementX_ = DomUtils.leftRelativeTo(dragTabsHost_, lastTab) + lastTab.getOffsetWidth(); } else { lastElementX_ = DomUtils.leftRelativeTo( dragTabsHost_, Element.as(dragTabsHost_.getChild(candidatePos_))); } // if we're dragging one of our own tabs, snap it out of the // tabset if (dragElement_ != null) { dragElement_.getStyle().setPosition(Position.ABSOLUTE); dragElement_.getStyle().setLeft(lastElementX_, Unit.PX); dragElement_.getStyle().setZIndex(100); Scheduler.get() .scheduleDeferred( new ScheduledCommand() { @Override public void execute() { dragElement_.getStyle().setDisplay(Display.NONE); } }); } // create the placeholder that shows where this tab will go when the // mouse is released dragPlaceholder_ = Document.get().createDivElement(); dragPlaceholder_.getStyle().setWidth(dragTabWidth - 4, Unit.PX); dragPlaceholder_.getStyle().setHeight(dragTabsHost_.getClientHeight() - 3, Unit.PX); dragPlaceholder_.getStyle().setDisplay(Display.INLINE_BLOCK); dragPlaceholder_.getStyle().setPosition(Position.RELATIVE); dragPlaceholder_.getStyle().setFloat(Float.LEFT); dragPlaceholder_.getStyle().setBorderStyle(BorderStyle.DOTTED); dragPlaceholder_.getStyle().setBorderColor("#A1A2A3"); dragPlaceholder_.getStyle().setBorderWidth(1, Unit.PX); dragPlaceholder_.getStyle().setMarginLeft(1, Unit.PX); dragPlaceholder_.getStyle().setMarginRight(1, Unit.PX); dragPlaceholder_.getStyle().setProperty("borderTopLeftRadius", "4px"); dragPlaceholder_.getStyle().setProperty("borderTopRightRadius", "4px"); dragPlaceholder_.getStyle().setProperty("borderBottom", "0px"); if (candidatePos_ < dragTabsHost_.getChildCount()) { dragTabsHost_.insertBefore(dragPlaceholder_, dragTabsHost_.getChild(candidatePos_)); } else { dragTabsHost_.appendChild(dragPlaceholder_); } }
@Override public void onBrowserEvent(Event event) { if (event.getType() == "dragenter") { if (dropPoint_ != null && event.getClientX() == dropPoint_.getX() && event.getClientY() == dropPoint_.getY()) { // Very occasionally (~5%?), dropping a tab will generate a // superfluous "dragenter" event immediately after the drop event // at exactly the same coordinates. We want to ignore this since // it will send us into dragging state; to do so, we cache the // coordinates when a tab is dropped and suppress entering drag // mode from those coordinates very briefly (note that this won't // keep the user from immediately dragging the tab around since // you need to move the cursor in some way to initiate a drag, // invalidating the coordinates.) dropPoint_ = null; return; } if (curState_ == STATE_EXTERNAL) { // element that was being dragged around outside boundaries // has come back in; clear it and treat like a new drag dragElement_.getStyle().clearOpacity(); dragElement_ = null; curState_ = STATE_NONE; } if (curState_ == STATE_NONE) { beginDrag(event); } event.preventDefault(); } else if (event.getType() == "dragover") { if (curState_ == STATE_DRAGGING) drag(event); event.preventDefault(); } else if (event.getType() == "drop") { endDrag(event, ACTION_COMMIT); event.preventDefault(); // cache the drop point for 250ms (see comments in event handler for // dragenter) dropPoint_ = new Point(event.getClientX(), event.getClientY()); Scheduler.get() .scheduleFixedDelay( new Scheduler.RepeatingCommand() { @Override public boolean execute() { dropPoint_ = null; return false; } }, 250); } else if (event.getType() == "dragend") { if (curState_ != STATE_NONE) { endDrag(event, ACTION_CANCEL); } event.preventDefault(); } else if (event.getType() == "dragleave") { if (curState_ == STATE_NONE) return; // when a drag leaves the window entirely, we get a dragleave event // at 0, 0 (which we always want to treat as a cancel) if (!(event.getClientX() == 0 && event.getClientY() == 0)) { // look at where the cursor is now--if it's inside the tab panel, // do nothing, but if it's outside the tab panel, treat that as // a cancel Element ele = DomUtils.elementFromPoint(event.getClientX(), event.getClientY()); while (ele != null && ele != Document.get().getBody()) { if (ele.getClassName().contains("gwt-TabLayoutPanelTabs")) { return; } ele = ele.getParentElement(); } } if (dragElement_ != null) { // dim the element being drag to hint that it'll be moved endDrag(event, ACTION_EXTERNAL); } else { // if this wasn't our element, we can cancel the drag entirely endDrag(event, ACTION_CANCEL); } } }
public void ensureSelectedTabIsVisible(boolean animate) { if (currentAnimation_ != null) { currentAnimation_.cancel(); currentAnimation_ = null; } Element selectedTab = (Element) DomUtils.findNode( getElement(), true, false, new NodePredicate() { public boolean test(Node n) { if (n.getNodeType() != Node.ELEMENT_NODE) return false; return ((Element) n).getClassName().contains("gwt-TabLayoutPanelTab-selected"); } }); if (selectedTab == null) { return; } selectedTab = selectedTab.getFirstChildElement().getFirstChildElement(); Element tabBar = getTabBarElement(); if (!isVisible() || !isAttached() || tabBar.getOffsetWidth() == 0) return; // not yet loaded final Element tabBarParent = tabBar.getParentElement(); final int start = tabBarParent.getScrollLeft(); int end = DomUtils.ensureVisibleHoriz( tabBarParent, selectedTab, padding_, padding_ + rightMargin_, true); // When tabs are closed, the overall width shrinks, and this can lead // to cases where there's too much empty space on the screen Node lastTab = getLastChildElement(tabBar); if (lastTab == null || lastTab.getNodeType() != Node.ELEMENT_NODE) return; int edge = DomUtils.getRelativePosition(tabBarParent, Element.as(lastTab)).x + Element.as(lastTab).getOffsetWidth(); end = Math.min(end, Math.max(0, edge - (tabBarParent.getOffsetWidth() - rightMargin_))); if (edge <= tabBarParent.getOffsetWidth() - rightMargin_) end = 0; if (start != end) { if (!animate) { tabBarParent.setScrollLeft(end); } else { final int finalEnd = end; currentAnimation_ = new Animation() { @Override protected void onUpdate(double progress) { double delta = (finalEnd - start) * progress; tabBarParent.setScrollLeft((int) (start + delta)); } @Override protected void onComplete() { if (this == currentAnimation_) { tabBarParent.setScrollLeft(finalEnd); currentAnimation_ = null; } } }; currentAnimation_.run(Math.max(200, Math.min(1500, Math.abs(end - start) * 2))); } } }
private void ensureRowVisible(final int row) { if (scrollPanel_ != null) DomUtils.ensureVisibleVert(scrollPanel_.getElement(), getRow(row), 0); }