Switch to CodeMirror simplescrollbars addon
Instead of implementing the scrollbar inside of Gerrit, use the simplescrollbars addon available with CodeMirror 4.9. The addon has handlers for scroll wheel support and other basic browser behavior that was broken or missing in Gerrit's own scrollbar replacement. Markers in the overview bar can now be simpler widgets that track the vertical scale ratio of the CM3 instance and position accordingly. Change-Id: Ib117668ee0f85955d1e47034e32676bc9796d0d2
This commit is contained in:
@@ -16,9 +16,6 @@ package com.google.gerrit.client.diff;
|
||||
|
||||
import static com.google.gerrit.client.diff.DisplaySide.A;
|
||||
import static com.google.gerrit.client.diff.DisplaySide.B;
|
||||
import static com.google.gerrit.client.diff.OverviewBar.MarkType.DELETE;
|
||||
import static com.google.gerrit.client.diff.OverviewBar.MarkType.EDIT;
|
||||
import static com.google.gerrit.client.diff.OverviewBar.MarkType.INSERT;
|
||||
|
||||
import com.google.gerrit.client.diff.DiffInfo.Region;
|
||||
import com.google.gerrit.client.diff.DiffInfo.Span;
|
||||
@@ -79,7 +76,7 @@ class ChunkManager {
|
||||
private final SideBySide2 host;
|
||||
private final CodeMirror cmA;
|
||||
private final CodeMirror cmB;
|
||||
private final OverviewBar sidePanel;
|
||||
private final Scrollbar scrollbar;
|
||||
private final LineMapper mapper;
|
||||
|
||||
private List<DiffChunkInfo> chunks;
|
||||
@@ -91,11 +88,11 @@ class ChunkManager {
|
||||
ChunkManager(SideBySide2 host,
|
||||
CodeMirror cmA,
|
||||
CodeMirror cmB,
|
||||
OverviewBar sidePanel) {
|
||||
Scrollbar scrollbar) {
|
||||
this.host = host;
|
||||
this.cmA = cmA;
|
||||
this.cmB = cmB;
|
||||
this.sidePanel = sidePanel;
|
||||
this.scrollbar = scrollbar;
|
||||
this.mapper = new LineMapper();
|
||||
}
|
||||
|
||||
@@ -197,11 +194,11 @@ class ChunkManager {
|
||||
|
||||
private void addGutterTag(Region region, int startA, int startB) {
|
||||
if (region.a() == null) {
|
||||
sidePanel.add(cmB, startB, region.b().length(), INSERT);
|
||||
scrollbar.insert(cmB, startB, region.b().length());
|
||||
} else if (region.b() == null) {
|
||||
sidePanel.add(cmA, startA, region.a().length(), DELETE);
|
||||
scrollbar.delete(cmA, cmB, startA, region.a().length());
|
||||
} else {
|
||||
sidePanel.add(cmB, startB, region.b().length(), EDIT);
|
||||
scrollbar.edit(cmB, startB, region.b().length());
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -19,6 +19,7 @@ import com.google.gwt.event.dom.client.MouseOutEvent;
|
||||
import com.google.gwt.event.dom.client.MouseOutHandler;
|
||||
import com.google.gwt.event.dom.client.MouseOverEvent;
|
||||
import com.google.gwt.event.dom.client.MouseOverHandler;
|
||||
import com.google.gwt.resources.client.CssResource;
|
||||
import com.google.gwt.user.client.ui.Composite;
|
||||
|
||||
import net.codemirror.lib.CodeMirror;
|
||||
@@ -32,8 +33,22 @@ abstract class CommentBox extends Composite {
|
||||
Resources.I.style().ensureInjected();
|
||||
}
|
||||
|
||||
interface Style extends CssResource {
|
||||
String commentWidgets();
|
||||
String commentBox();
|
||||
String contents();
|
||||
String message();
|
||||
String header();
|
||||
String summary();
|
||||
String date();
|
||||
|
||||
String goPrev();
|
||||
String goNext();
|
||||
String goUp();
|
||||
}
|
||||
|
||||
private final CommentGroup group;
|
||||
private OverviewBar.MarkHandle mark;
|
||||
private ScrollbarAnnotation annotation;
|
||||
private FromTo fromTo;
|
||||
private TextMarker rangeMarker;
|
||||
private TextMarker rangeHighlightMarker;
|
||||
@@ -79,12 +94,12 @@ abstract class CommentBox extends Composite {
|
||||
return group.getCommentManager();
|
||||
}
|
||||
|
||||
OverviewBar.MarkHandle getMark() {
|
||||
return mark;
|
||||
ScrollbarAnnotation getAnnotation() {
|
||||
return annotation;
|
||||
}
|
||||
|
||||
void setMark(OverviewBar.MarkHandle mh) {
|
||||
mark = mh;
|
||||
void setAnnotation(ScrollbarAnnotation mh) {
|
||||
annotation = mh;
|
||||
}
|
||||
|
||||
void setRangeHighlight(boolean highlight) {
|
||||
|
@@ -159,10 +159,9 @@ class CommentManager {
|
||||
getPatchSetIdFromSide(side),
|
||||
info);
|
||||
group.add(box);
|
||||
box.setMark(host.diffTable.overview.add(
|
||||
box.setAnnotation(host.diffTable.scrollbar.comment(
|
||||
host.getCmFromSide(side),
|
||||
Math.max(0, info.line() - 1), 1,
|
||||
OverviewBar.MarkType.COMMENT));
|
||||
Math.max(0, info.line() - 1)));
|
||||
published.put(info.id(), box);
|
||||
}
|
||||
}
|
||||
@@ -223,10 +222,9 @@ class CommentManager {
|
||||
}
|
||||
|
||||
group.add(box);
|
||||
box.setMark(host.diffTable.overview.add(
|
||||
box.setAnnotation(host.diffTable.scrollbar.draft(
|
||||
host.getCmFromSide(side),
|
||||
Math.max(0, info.line() - 1), 1,
|
||||
OverviewBar.MarkType.DRAFT));
|
||||
Math.max(0, info.line() - 1)));
|
||||
return box;
|
||||
}
|
||||
|
||||
|
@@ -61,7 +61,7 @@ class DiffTable extends Composite {
|
||||
|
||||
@UiField Element cmA;
|
||||
@UiField Element cmB;
|
||||
@UiField OverviewBar overview;
|
||||
Scrollbar scrollbar;
|
||||
@UiField Element patchSetNavRow;
|
||||
@UiField Element patchSetNavCellA;
|
||||
@UiField Element patchSetNavCellB;
|
||||
@@ -92,6 +92,7 @@ class DiffTable extends Composite {
|
||||
PatchSetSelectBox2.link(patchSetSelectBoxA, patchSetSelectBoxB);
|
||||
|
||||
initWidget(uiBinder.createAndBindUi(this));
|
||||
this.scrollbar = new Scrollbar(this);
|
||||
this.parent = parent;
|
||||
this.headerVisible = true;
|
||||
this.visibleA = true;
|
||||
@@ -211,7 +212,6 @@ class DiffTable extends Composite {
|
||||
}
|
||||
|
||||
void refresh() {
|
||||
overview.refresh();
|
||||
if (header) {
|
||||
CodeMirror cm = parent.getCmFromSide(DisplaySide.A);
|
||||
diffHeaderText.getStyle().setMarginLeft(
|
||||
|
@@ -19,7 +19,8 @@ limitations under the License.
|
||||
xmlns:d='urn:import:com.google.gerrit.client.diff'>
|
||||
<ui:style type='com.google.gerrit.client.diff.DiffTable.DiffTableStyle'>
|
||||
@external .CodeMirror, .CodeMirror-lines, .CodeMirror-selectedtext;
|
||||
@external .CodeMirror-linenumber, .CodeMirror-vscrollbar .CodeMirror-scroll;
|
||||
@external .CodeMirror-linenumber;
|
||||
@external .CodeMirror-overlayscroll-vertical, .CodeMirror-scroll;
|
||||
@external .CodeMirror-dialog-bottom;
|
||||
@external .cm-animate-fat-cursor, .CodeMirror-cursor;
|
||||
@external .cm-searching, .cm-trailingspace, .cm-tab;
|
||||
@@ -75,14 +76,9 @@ limitations under the License.
|
||||
.hideA .psNavB, .hideA .b { width: 100% }
|
||||
.hideB .psNavA, .hideB .a { width: 100% }
|
||||
|
||||
.overview {
|
||||
width: 10px;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
/* Hide scrollbars, OverviewBar controls both views. */
|
||||
.difftable .CodeMirror-scroll { padding-right: 0; }
|
||||
.difftable .CodeMirror-vscrollbar { display: none !important; }
|
||||
/* Hide scrollbars on A, B controls both views. */
|
||||
.a .CodeMirror-scroll { margin-right: -36px; }
|
||||
.a .CodeMirror-overlayscroll-vertical { display: none !important; }
|
||||
|
||||
.showLineNumbers .b { border-left: none; }
|
||||
.b { border-left: 1px solid #ddd; }
|
||||
@@ -195,16 +191,13 @@ limitations under the License.
|
||||
<td ui:field='patchSetNavCellB' class='{style.psNavB}'>
|
||||
<d:PatchSetSelectBox2 ui:field='patchSetSelectBoxB' />
|
||||
</td>
|
||||
<td class='{style.overview}' />
|
||||
</tr>
|
||||
<tr ui:field='diffHeaderRow' class='{style.diff_header}'>
|
||||
<td colspan='2'><pre ui:field='diffHeaderText' /></td>
|
||||
<td class='{style.overview}' />
|
||||
</tr>
|
||||
<tr>
|
||||
<td ui:field='cmA' class='{style.a}' />
|
||||
<td ui:field='cmB' class='{style.b}' />
|
||||
<td class='{style.overview}'><d:OverviewBar ui:field='overview'/></td>
|
||||
</tr>
|
||||
</table>
|
||||
<g:FlowPanel ui:field='widgets' visible='false'/>
|
||||
|
@@ -236,7 +236,7 @@ class DraftBox extends CommentBox {
|
||||
getCommentManager().setUnsaved(this, false);
|
||||
setRangeHighlight(false);
|
||||
clearRange();
|
||||
getMark().remove();
|
||||
getAnnotation().remove();
|
||||
getCommentGroup().remove(this);
|
||||
getCm().focus();
|
||||
}
|
||||
|
@@ -1,266 +0,0 @@
|
||||
//Copyright (C) 2013 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.core.client.GWT;
|
||||
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.event.dom.client.MouseDownEvent;
|
||||
import com.google.gwt.event.dom.client.MouseMoveEvent;
|
||||
import com.google.gwt.event.dom.client.MouseUpEvent;
|
||||
import com.google.gwt.resources.client.CssResource;
|
||||
import com.google.gwt.uibinder.client.UiBinder;
|
||||
import com.google.gwt.uibinder.client.UiField;
|
||||
import com.google.gwt.uibinder.client.UiHandler;
|
||||
import com.google.gwt.user.client.DOM;
|
||||
import com.google.gwt.user.client.ui.Composite;
|
||||
import com.google.gwt.user.client.ui.HTMLPanel;
|
||||
import com.google.gwt.user.client.ui.Label;
|
||||
import com.google.gwt.user.client.ui.Widget;
|
||||
|
||||
import net.codemirror.lib.CodeMirror;
|
||||
import net.codemirror.lib.LineCharacter;
|
||||
import net.codemirror.lib.ScrollInfo;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/** Displays overview of all edits and comments in this file. */
|
||||
class OverviewBar extends Composite implements ClickHandler {
|
||||
interface Binder extends UiBinder<HTMLPanel, OverviewBar> {}
|
||||
private static final Binder uiBinder = GWT.create(Binder.class);
|
||||
|
||||
interface Style extends CssResource {
|
||||
String gutter();
|
||||
String halfGutter();
|
||||
String comment();
|
||||
String draft();
|
||||
String insert();
|
||||
String delete();
|
||||
String viewportDrag();
|
||||
}
|
||||
|
||||
enum MarkType {
|
||||
COMMENT, DRAFT, INSERT, DELETE, EDIT
|
||||
}
|
||||
|
||||
@UiField Style style;
|
||||
@UiField Label viewport;
|
||||
|
||||
private final List<MarkHandle> diff;
|
||||
private final Set<MarkHandle> comments;
|
||||
private CodeMirror cmB;
|
||||
|
||||
private boolean dragging;
|
||||
private int startY;
|
||||
private double ratio;
|
||||
|
||||
OverviewBar() {
|
||||
initWidget(uiBinder.createAndBindUi(this));
|
||||
diff = new ArrayList<>();
|
||||
comments = new HashSet<>();
|
||||
addDomHandler(this, ClickEvent.getType());
|
||||
}
|
||||
|
||||
void init(CodeMirror cmB) {
|
||||
this.cmB = cmB;
|
||||
}
|
||||
|
||||
void refresh() {
|
||||
update(cmB.getScrollInfo());
|
||||
}
|
||||
|
||||
void update(ScrollInfo si) {
|
||||
double viewHeight = si.getClientHeight();
|
||||
double r = ratio(si);
|
||||
|
||||
com.google.gwt.dom.client.Style style = viewport.getElement().getStyle();
|
||||
style.setTop(si.getTop() * r, Unit.PX);
|
||||
style.setHeight(Math.max(10, viewHeight * r), Unit.PX);
|
||||
getElement().getStyle().setHeight(viewHeight, Unit.PX);
|
||||
for (MarkHandle info : diff) {
|
||||
info.position(r);
|
||||
}
|
||||
for (MarkHandle info : comments) {
|
||||
info.position(r);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onUnload() {
|
||||
super.onUnload();
|
||||
if (dragging) {
|
||||
DOM.releaseCapture(viewport.getElement());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(ClickEvent e) {
|
||||
if (e.getY() < viewport.getElement().getOffsetTop()) {
|
||||
CodeMirror.handleVimKey(cmB, "<PageUp>");
|
||||
} else {
|
||||
CodeMirror.handleVimKey(cmB, "<PageDown>");
|
||||
}
|
||||
cmB.focus();
|
||||
}
|
||||
|
||||
@UiHandler("viewport")
|
||||
void onMouseDown(MouseDownEvent e) {
|
||||
if (cmB != null) {
|
||||
dragging = true;
|
||||
ratio = ratio(cmB.getScrollInfo());
|
||||
startY = e.getY();
|
||||
viewport.addStyleName(style.viewportDrag());
|
||||
DOM.setCapture(viewport.getElement());
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
}
|
||||
}
|
||||
|
||||
@UiHandler("viewport")
|
||||
void onMouseMove(MouseMoveEvent e) {
|
||||
if (dragging) {
|
||||
int y = e.getRelativeY(getElement()) - startY;
|
||||
cmB.scrollToY(Math.max(0, y / ratio));
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
}
|
||||
}
|
||||
|
||||
@UiHandler("viewport")
|
||||
void onMouseUp(MouseUpEvent e) {
|
||||
if (dragging) {
|
||||
dragging = false;
|
||||
DOM.releaseCapture(viewport.getElement());
|
||||
viewport.removeStyleName(style.viewportDrag());
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
}
|
||||
}
|
||||
|
||||
private double ratio(ScrollInfo si) {
|
||||
double barHeight = si.getClientHeight();
|
||||
double contentHeight = si.getHeight();
|
||||
return barHeight / contentHeight;
|
||||
}
|
||||
|
||||
MarkHandle add(CodeMirror cm, int line, int height, MarkType type) {
|
||||
MarkHandle mark = new MarkHandle(cm, line, height);
|
||||
switch (type) {
|
||||
case COMMENT:
|
||||
mark.addStyleName(style.comment());
|
||||
comments.add(mark);
|
||||
break;
|
||||
case DRAFT:
|
||||
mark.addStyleName(style.draft());
|
||||
mark.getElement().setInnerText("*");
|
||||
comments.add(mark);
|
||||
break;
|
||||
case INSERT:
|
||||
mark.addStyleName(style.insert());
|
||||
diff.add(mark);
|
||||
break;
|
||||
case DELETE:
|
||||
mark.addStyleName(style.delete());
|
||||
diff.add(mark);
|
||||
break;
|
||||
case EDIT:
|
||||
mark.edit = DOM.createDiv();
|
||||
mark.edit.setClassName(style.halfGutter());
|
||||
mark.getElement().appendChild(mark.edit);
|
||||
mark.addStyleName(style.insert());
|
||||
diff.add(mark);
|
||||
break;
|
||||
}
|
||||
if (cmB != null) {
|
||||
mark.position(ratio(cmB.getScrollInfo()));
|
||||
}
|
||||
((HTMLPanel) getWidget()).add(mark);
|
||||
return mark;
|
||||
}
|
||||
|
||||
void clearDiffMarkers() {
|
||||
for (MarkHandle mark : diff) {
|
||||
mark.removeFromParent();
|
||||
}
|
||||
diff.clear();
|
||||
}
|
||||
|
||||
class MarkHandle extends Widget implements ClickHandler {
|
||||
private static final int MIN_HEIGHT = 3;
|
||||
|
||||
private final CodeMirror cm;
|
||||
private final int line;
|
||||
private final int height;
|
||||
private Element edit;
|
||||
|
||||
MarkHandle(CodeMirror cm, int line, int height) {
|
||||
this.cm = cm;
|
||||
this.line = line;
|
||||
this.height = height;
|
||||
|
||||
setElement((Element)(DOM.createDiv()));
|
||||
setStyleName(style.gutter());
|
||||
addDomHandler(this, ClickEvent.getType());
|
||||
}
|
||||
|
||||
void position(double ratio) {
|
||||
double y = cm.heightAtLine(line, "local");
|
||||
getElement().getStyle().setTop(y * ratio, Unit.PX);
|
||||
if (height > 1) {
|
||||
double e = cm.heightAtLine(line + height, "local");
|
||||
double h = Math.max(MIN_HEIGHT, (e - y) * ratio);
|
||||
getElement().getStyle().setHeight(h, Unit.PX);
|
||||
if (edit != null) {
|
||||
edit.getStyle().setHeight(h, Unit.PX);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(ClickEvent e) {
|
||||
if (height == 1 || !visible()) {
|
||||
e.stopPropagation();
|
||||
|
||||
double y = cm.heightAtLine(line, "local");
|
||||
double viewport = cm.getScrollInfo().getClientHeight();
|
||||
cm.setCursor(LineCharacter.create(line));
|
||||
cm.scrollToY(y - 0.5 * viewport);
|
||||
cm.focus();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean visible() {
|
||||
int markT = getElement().getOffsetTop();
|
||||
int markE = markT + getElement().getOffsetHeight();
|
||||
|
||||
int viewT = viewport.getElement().getOffsetTop();
|
||||
int viewE = viewT + viewport.getElement().getOffsetHeight();
|
||||
|
||||
return (viewT <= markT && markT < viewE) // mark top within viewport
|
||||
|| (viewT <= markE && markE < viewE) // mark end within viewport
|
||||
|| (markT <= viewT && viewE <= markE); // mark contains viewport
|
||||
}
|
||||
|
||||
void remove() {
|
||||
removeFromParent();
|
||||
comments.remove(this);
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,71 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
Copyright (C) 2013 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.
|
||||
-->
|
||||
<ui:UiBinder xmlns:ui='urn:ui:com.google.gwt.uibinder'
|
||||
xmlns:g='urn:import:com.google.gwt.user.client.ui'>
|
||||
<ui:style type='com.google.gerrit.client.diff.OverviewBar.Style'>
|
||||
.overview {
|
||||
position: relative;
|
||||
}
|
||||
.gutter {
|
||||
cursor: pointer;
|
||||
position: absolute;
|
||||
height: 3px;
|
||||
width: 6px;
|
||||
border: 1px solid grey;
|
||||
margin-left: 1px;
|
||||
}
|
||||
.halfGutter {
|
||||
cursor: pointer;
|
||||
position: absolute;
|
||||
height: 3px;
|
||||
width: 3px;
|
||||
background-color: #faa;
|
||||
}
|
||||
.comment, .draft {
|
||||
background-color: #fcfa96;
|
||||
z-index: 2;
|
||||
}
|
||||
.draft {
|
||||
text-align: center;
|
||||
font-size: small;
|
||||
line-height: 0.5;
|
||||
color: inherit !important;
|
||||
text-decoration: none !important;
|
||||
}
|
||||
.delete {
|
||||
background-color: #faa;
|
||||
}
|
||||
.insert {
|
||||
background-color: #9f9;
|
||||
}
|
||||
.viewport {
|
||||
position: absolute;
|
||||
background-color: rgba(128, 128, 128, 0.50);
|
||||
border: 1px solid #444;
|
||||
width: 8px;
|
||||
cursor: default;
|
||||
z-index: 3;
|
||||
border-radius: 4px;
|
||||
}
|
||||
.viewportDrag {
|
||||
cursor: move;
|
||||
}
|
||||
</ui:style>
|
||||
<g:HTMLPanel styleName='{style.overview}'>
|
||||
<g:Label ui:field='viewport' styleName='{style.viewport}'/>
|
||||
</g:HTMLPanel>
|
||||
</ui:UiBinder>
|
@@ -16,29 +16,16 @@ package com.google.gerrit.client.diff;
|
||||
|
||||
import com.google.gwt.core.client.GWT;
|
||||
import com.google.gwt.resources.client.ClientBundle;
|
||||
import com.google.gwt.resources.client.CssResource;
|
||||
import com.google.gwt.resources.client.ImageResource;
|
||||
|
||||
/** Resources used by diff. */
|
||||
interface Resources extends ClientBundle {
|
||||
static final Resources I = GWT.create(Resources.class);
|
||||
|
||||
@Source("CommentBoxUi.css") Style style();
|
||||
@Source("CommentBox.css") CommentBox.Style style();
|
||||
@Source("Scrollbar.css") Scrollbar.Style scrollbarStyle();
|
||||
|
||||
@Source("goPrev.png") ImageResource goPrev();
|
||||
@Source("goNext.png") ImageResource goNext();
|
||||
@Source("goUp.png") ImageResource goUp();
|
||||
|
||||
interface Style extends CssResource {
|
||||
String commentWidgets();
|
||||
String commentBox();
|
||||
String contents();
|
||||
String message();
|
||||
String header();
|
||||
String summary();
|
||||
String date();
|
||||
|
||||
String goPrev();
|
||||
String goNext();
|
||||
String goUp();
|
||||
}
|
||||
}
|
||||
|
@@ -22,7 +22,6 @@ import net.codemirror.lib.ScrollInfo;
|
||||
class ScrollSynchronizer {
|
||||
private DiffTable diffTable;
|
||||
private LineMapper mapper;
|
||||
private OverviewBar overview;
|
||||
private ScrollCallback active;
|
||||
private ScrollCallback callbackA;
|
||||
private ScrollCallback callbackB;
|
||||
@@ -32,7 +31,6 @@ class ScrollSynchronizer {
|
||||
LineMapper mapper) {
|
||||
this.diffTable = diffTable;
|
||||
this.mapper = mapper;
|
||||
this.overview = diffTable.overview;
|
||||
|
||||
callbackA = new ScrollCallback(cmA, cmB, DisplaySide.A);
|
||||
callbackB = new ScrollCallback(cmB, cmA, DisplaySide.B);
|
||||
@@ -87,7 +85,6 @@ class ScrollSynchronizer {
|
||||
if (active == this) {
|
||||
ScrollInfo si = src.getScrollInfo();
|
||||
updateScreenHeader(si);
|
||||
overview.update(si);
|
||||
dst.scrollTo(si.getLeft(), align(si.getTop()));
|
||||
state = 0;
|
||||
}
|
||||
|
@@ -0,0 +1,58 @@
|
||||
/* 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.
|
||||
*/
|
||||
|
||||
@external .CodeMirror;
|
||||
@external .CodeMirror-overlayscroll-horizontal;
|
||||
@external .CodeMirror-overlayscroll-vertical;
|
||||
|
||||
.CodeMirror-overlayscroll-horizontal div {
|
||||
min-width: 25px;
|
||||
}
|
||||
.CodeMirror-overlayscroll-vertical div {
|
||||
min-height: 25px;
|
||||
}
|
||||
|
||||
.CodeMirror .CodeMirror-overlayscroll-vertical {
|
||||
z-index: inherit;
|
||||
}
|
||||
.CodeMirror .CodeMirror-overlayscroll-horizontal div,
|
||||
.CodeMirror .CodeMirror-overlayscroll-vertical div {
|
||||
background-color: rgba(128, 128, 128, 0.50);
|
||||
z-index: 8;
|
||||
}
|
||||
|
||||
.comment, .draft, .insert, .delete, .edit {
|
||||
min-height: 5px;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
z-index: 7;
|
||||
}
|
||||
|
||||
.comment, .draft {
|
||||
color: #0d0d0d;
|
||||
font-size: 9px;
|
||||
}
|
||||
|
||||
.delete {
|
||||
background-color: #faa;
|
||||
}
|
||||
.insert {
|
||||
background-color: #9f9;
|
||||
}
|
||||
.edit {
|
||||
border-left: 3px solid #faa;
|
||||
width: 2px !important;
|
||||
background-color: #9f9;
|
||||
}
|
@@ -0,0 +1,98 @@
|
||||
// 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.resources.client.CssResource;
|
||||
|
||||
import net.codemirror.lib.CodeMirror;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/** Displays overview of all edits and comments in this file. */
|
||||
class Scrollbar {
|
||||
static {
|
||||
Resources.I.scrollbarStyle().ensureInjected();
|
||||
}
|
||||
|
||||
interface Style extends CssResource {
|
||||
String comment();
|
||||
String draft();
|
||||
String insert();
|
||||
String delete();
|
||||
String edit();
|
||||
}
|
||||
|
||||
private final List<ScrollbarAnnotation> diff = new ArrayList<>();
|
||||
private final DiffTable parent;
|
||||
|
||||
Scrollbar(DiffTable d) {
|
||||
parent = d;
|
||||
}
|
||||
|
||||
ScrollbarAnnotation comment(CodeMirror cm, int line) {
|
||||
ScrollbarAnnotation a = new ScrollbarAnnotation(cm);
|
||||
a.setStyleName(Resources.I.scrollbarStyle().comment());
|
||||
a.at(line);
|
||||
a.getElement().setInnerText("\u2736"); // Six pointed black star
|
||||
parent.add(a);
|
||||
return a;
|
||||
}
|
||||
|
||||
ScrollbarAnnotation draft(CodeMirror cm, int line) {
|
||||
ScrollbarAnnotation a = new ScrollbarAnnotation(cm);
|
||||
a.setStyleName(Resources.I.scrollbarStyle().draft());
|
||||
a.at(line);
|
||||
a.getElement().setInnerText("\u270D"); // Writing hand
|
||||
parent.add(a);
|
||||
return a;
|
||||
}
|
||||
|
||||
ScrollbarAnnotation insert(CodeMirror cm, int line, int len) {
|
||||
ScrollbarAnnotation a = diff(cm, line, len);
|
||||
a.setStyleName(Resources.I.scrollbarStyle().insert());
|
||||
parent.add(a);
|
||||
return a;
|
||||
}
|
||||
|
||||
ScrollbarAnnotation delete(CodeMirror cmA, CodeMirror cmB, int line, int len) {
|
||||
ScrollbarAnnotation a = diff(cmA, line, len);
|
||||
a.setStyleName(Resources.I.scrollbarStyle().delete());
|
||||
a.renderOn(cmB);
|
||||
parent.add(a);
|
||||
return a;
|
||||
}
|
||||
|
||||
ScrollbarAnnotation edit(CodeMirror cm, int line, int len) {
|
||||
ScrollbarAnnotation a = diff(cm, line, len);
|
||||
a.setStyleName(Resources.I.scrollbarStyle().edit());
|
||||
parent.add(a);
|
||||
return a;
|
||||
}
|
||||
|
||||
private ScrollbarAnnotation diff(CodeMirror cm, int s, int n) {
|
||||
ScrollbarAnnotation a = new ScrollbarAnnotation(cm);
|
||||
a.at(CodeMirror.pos(s), CodeMirror.pos(s + n));
|
||||
diff.add(a);
|
||||
return a;
|
||||
}
|
||||
|
||||
void removeDiffAnnotations() {
|
||||
for (ScrollbarAnnotation a : diff) {
|
||||
a.remove();
|
||||
}
|
||||
diff.clear();
|
||||
}
|
||||
}
|
@@ -0,0 +1,119 @@
|
||||
// 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.LineCharacter;
|
||||
|
||||
/** 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 LineCharacter from;
|
||||
private LineCharacter 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(CodeMirror.pos(line), CodeMirror.pos(line + 1));
|
||||
}
|
||||
|
||||
void at(LineCharacter from, LineCharacter 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", new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
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.getLine();
|
||||
int h = to.getLine() - 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().getClientHeight();
|
||||
cm.setCursor(from);
|
||||
cm.scrollTo(0, y - 0.5 * viewport);
|
||||
cm.focus();
|
||||
}
|
||||
}
|
@@ -553,8 +553,7 @@ public class SideBySide2 extends Screen {
|
||||
|
||||
cmA = newCM(diff.meta_a(), diff.text_a(), DisplaySide.A, diffTable.cmA);
|
||||
cmB = newCM(diff.meta_b(), diff.text_b(), DisplaySide.B, diffTable.cmB);
|
||||
diffTable.overview.init(cmB);
|
||||
chunkManager = new ChunkManager(this, cmA, cmB, diffTable.overview);
|
||||
chunkManager = new ChunkManager(this, cmA, cmB, diffTable.scrollbar);
|
||||
skipManager = new SkipManager(this, commentManager);
|
||||
|
||||
columnMarginA = DOM.createDiv();
|
||||
@@ -650,6 +649,7 @@ public class SideBySide2 extends Screen {
|
||||
.set("tabSize", prefs.tabSize())
|
||||
.set("mode", mode)
|
||||
.set("lineWrapping", false)
|
||||
.set("scrollbarStyle", "overlay")
|
||||
.set("styleSelectedText", true)
|
||||
.set("showTrailingSpace", prefs.showWhitespaceErrors())
|
||||
.set("keyMap", "vim_ro")
|
||||
@@ -790,7 +790,6 @@ public class SideBySide2 extends Screen {
|
||||
public void run() {
|
||||
skipManager.removeAll();
|
||||
skipManager.render(context, diff);
|
||||
diffTable.overview.refresh();
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -981,7 +980,6 @@ public class SideBySide2 extends Screen {
|
||||
int height = getCodeMirrorHeight();
|
||||
cmA.setHeight(height);
|
||||
cmB.setHeight(height);
|
||||
diffTable.overview.refresh();
|
||||
}
|
||||
|
||||
private int getCodeMirrorHeight() {
|
||||
@@ -1084,7 +1082,7 @@ public class SideBySide2 extends Screen {
|
||||
public void run() {
|
||||
skipManager.removeAll();
|
||||
chunkManager.reset();
|
||||
diffTable.overview.clearDiffMarkers();
|
||||
diffTable.scrollbar.removeDiffAnnotations();
|
||||
setShowIntraline(prefs.intralineDifference());
|
||||
render(diff);
|
||||
chunkManager.adjustPadding();
|
||||
|
@@ -133,7 +133,6 @@ class SkipBar extends Composite {
|
||||
void expandBefore(int cnt) {
|
||||
expandSideBefore(cnt);
|
||||
otherBar.expandSideBefore(cnt);
|
||||
manager.getOverviewBar().refresh();
|
||||
}
|
||||
|
||||
private void expandSideBefore(int cnt) {
|
||||
@@ -185,7 +184,6 @@ class SkipBar extends Composite {
|
||||
expandSideAll();
|
||||
otherBar.expandSideAll();
|
||||
manager.remove(this, otherBar);
|
||||
manager.getOverviewBar().refresh();
|
||||
}
|
||||
|
||||
@UiHandler("upArrow")
|
||||
@@ -198,7 +196,6 @@ class SkipBar extends Composite {
|
||||
void onExpandAfter(@SuppressWarnings("unused") ClickEvent e) {
|
||||
expandAfter();
|
||||
otherBar.expandAfter();
|
||||
manager.getOverviewBar().refresh();
|
||||
cm.focus();
|
||||
}
|
||||
}
|
||||
|
@@ -38,10 +38,6 @@ class SkipManager {
|
||||
this.commentManager = commentManager;
|
||||
}
|
||||
|
||||
OverviewBar getOverviewBar() {
|
||||
return host.diffTable.overview;
|
||||
}
|
||||
|
||||
void render(int context, DiffInfo diff) {
|
||||
if (context == AccountDiffPreference.WHOLE_FILE_CONTEXT) {
|
||||
return;
|
||||
@@ -111,7 +107,6 @@ class SkipManager {
|
||||
for (SkipBar bar : skipBars) {
|
||||
bar.expandSideAll();
|
||||
}
|
||||
getOverviewBar().refresh();
|
||||
skipBars = null;
|
||||
line0 = null;
|
||||
}
|
||||
|
@@ -69,6 +69,9 @@ public class CodeMirror extends JavaScriptObject {
|
||||
public final native void setHeight(double h) /*-{ this.setSize(null, h); }-*/;
|
||||
public final native void setHeight(String h) /*-{ this.setSize(null, h); }-*/;
|
||||
public final native String getLine(int n) /*-{ return this.getLine(n) }-*/;
|
||||
public final native double barHeight() /*-{ return this.display.barHeight }-*/;
|
||||
public final native double barWidth() /*-{ return this.display.barWidth }-*/;
|
||||
public final native int lastLine() /*-{ return this.lastLine() }-*/;
|
||||
|
||||
public final native void refresh() /*-{ this.refresh(); }-*/;
|
||||
public final native Element getWrapperElement() /*-{ return this.getWrapperElement(); }-*/;
|
||||
@@ -178,10 +181,14 @@ public class CodeMirror extends JavaScriptObject {
|
||||
});
|
||||
}-*/;
|
||||
|
||||
public final native void on(String event, Runnable thunk) /*-{
|
||||
this.on(event, $entry(function() {
|
||||
thunk.@java.lang.Runnable::run()();
|
||||
}));
|
||||
public final native void off(String event, RegisteredHandler h) /*-{
|
||||
this.off(event, h)
|
||||
}-*/;
|
||||
|
||||
public final native RegisteredHandler on(String event, Runnable thunk) /*-{
|
||||
var h = $entry(function() { thunk.@java.lang.Runnable::run()() });
|
||||
this.on(event, h);
|
||||
return h;
|
||||
}-*/;
|
||||
|
||||
public final native void on(String event, EventHandler handler) /*-{
|
||||
@@ -354,6 +361,11 @@ public class CodeMirror extends JavaScriptObject {
|
||||
}
|
||||
}
|
||||
|
||||
public static class RegisteredHandler extends JavaScriptObject {
|
||||
protected RegisteredHandler() {
|
||||
}
|
||||
}
|
||||
|
||||
public interface EventHandler {
|
||||
public void handle(CodeMirror instance, NativeEvent event);
|
||||
}
|
||||
|
@@ -1,6 +1,7 @@
|
||||
CM_CSS = [
|
||||
'lib/codemirror.css',
|
||||
'addon/dialog/dialog.css',
|
||||
'addon/scroll/simplescrollbars.css',
|
||||
]
|
||||
|
||||
CM_THEMES = [
|
||||
@@ -16,6 +17,7 @@ CM_JS = [
|
||||
'lib/codemirror.js',
|
||||
'keymap/vim.js',
|
||||
'addon/dialog/dialog.js',
|
||||
'addon/scroll/simplescrollbars.js',
|
||||
'addon/search/searchcursor.js',
|
||||
'addon/search/search.js',
|
||||
'addon/selection/mark-selection.js',
|
||||
|
Reference in New Issue
Block a user