// Copyright (C) 2014 The Android Open Source Project // // 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. package com.google.gerrit.client.diff; import com.google.gwt.dom.client.Element; import com.google.gwt.dom.client.Style.Unit; import com.google.gwt.event.dom.client.ClickEvent; import com.google.gwt.event.dom.client.ClickHandler; import com.google.gwt.user.client.DOM; import com.google.gwt.user.client.ui.Widget; import net.codemirror.lib.CodeMirror; import net.codemirror.lib.CodeMirror.RegisteredHandler; import net.codemirror.lib.Pos; /** Displayed on the vertical scrollbar to place a chunk or comment. */ class ScrollbarAnnotation extends Widget implements ClickHandler { private final CodeMirror cm; private CodeMirror cmB; private RegisteredHandler refresh; private Pos from; private Pos to; private double scale; ScrollbarAnnotation(CodeMirror cm) { setElement((Element) DOM.createDiv()); getElement().setAttribute("not-content", "true"); addDomHandler(this, ClickEvent.getType()); this.cm = cm; this.cmB = cm; } void remove() { removeFromParent(); } void at(int line) { at(Pos.create(line), Pos.create(line + 1)); } void at(Pos from, Pos to) { this.from = from; this.to = to; } void renderOn(CodeMirror cm) { this.cmB = cm; } @Override protected void onLoad() { cmB.getWrapperElement().appendChild(getElement()); refresh = cmB.on( "refresh", () -> { if (updateScale()) { updatePosition(); } }); updateScale(); updatePosition(); } @Override protected void onUnload() { cmB.off("refresh", refresh); } private boolean updateScale() { double old = scale; double docHeight = cmB.getWrapperElement().getClientHeight(); double lineHeight = cmB.heightAtLine(cmB.lastLine() + 1, "local"); scale = (docHeight - cmB.barHeight()) / lineHeight; return old != scale; } private void updatePosition() { double top = cm.charCoords(from, "local").top() * scale; double bottom = cm.charCoords(to, "local").bottom() * scale; Element e = getElement(); e.getStyle().setTop(top, Unit.PX); e.getStyle().setWidth(Math.max(2, cm.barWidth() - 1), Unit.PX); e.getStyle().setHeight(Math.max(3, bottom - top), Unit.PX); } @Override public void onClick(ClickEvent event) { event.stopPropagation(); int line = from.line(); int h = to.line() - line; if (h > 5) { // Map click inside of the annotation to the relative position // within the region covered by the annotation. double s = ((double) event.getY()) / getElement().getOffsetHeight(); line += (int) (s * h); } double y = cm.heightAtLine(line, "local"); double viewport = cm.getScrollInfo().clientHeight(); cm.setCursor(from); cm.scrollTo(0, y - 0.5 * viewport); cm.focus(); } }