SideBySide2: Add "or [x] entire file" context option

Make it easier for users to get whole file context setting by
adding a checkbox next to the input field. When set the field
is cleared, so now users see:

  Context Lines   [    ] or [x] entire file

There are focus problems when the user modifies the lines of
context input text box and then clicks the "entire file" check mark
to show the entire file. The browser first delivers the change
event for the text box, and then delivers the click event on the
checkmark. Debounce these by delaying update of the view by 200
milliseconds, allowing the second click event to cancel the first
update and take priority, as this reflects the user intent.

When entire file is checked and the user enters a number into the
context box, immediately uncheck the entire file check box. When
focus is lost from the context box its new value will be committed
and the view will be updated.

Change-Id: Ic8f8f480df3ef39115c5322516ec149c1df8f89e
This commit is contained in:
Shawn Pearce
2013-12-05 11:29:29 -08:00
parent ce04c0994b
commit b5ee585d5a
2 changed files with 64 additions and 14 deletions

View File

@@ -14,6 +14,7 @@
package com.google.gerrit.client.diff;
import static com.google.gerrit.reviewdb.client.AccountDiffPreference.DEFAULT_CONTEXT;
import static com.google.gerrit.reviewdb.client.AccountDiffPreference.WHOLE_FILE_CONTEXT;
import static com.google.gerrit.reviewdb.client.AccountDiffPreference.Whitespace.IGNORE_ALL_SPACE;
import static com.google.gerrit.reviewdb.client.AccountDiffPreference.Whitespace.IGNORE_NONE;
@@ -34,13 +35,16 @@ import com.google.gwt.event.dom.client.ChangeEvent;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.KeyDownEvent;
import com.google.gwt.event.dom.client.KeyDownHandler;
import com.google.gwt.event.dom.client.KeyPressEvent;
import com.google.gwt.event.logical.shared.ValueChangeEvent;
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.Timer;
import com.google.gwt.user.client.ui.Anchor;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.CheckBox;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.HTMLPanel;
import com.google.gwt.user.client.ui.ListBox;
@@ -62,12 +66,15 @@ class PreferencesBox extends Composite {
private final CodeMirror cmA;
private final CodeMirror cmB;
private DiffPreferences prefs;
private int contextLastValue;
private Timer updateContextTimer;
@UiField Style style;
@UiField Anchor close;
@UiField ListBox ignoreWhitespace;
@UiField NpIntTextBox tabWidth;
@UiField NpIntTextBox context;
@UiField CheckBox contextEntireFile;
@UiField ToggleButton intralineDifference;
@UiField ToggleButton syntaxHighlighting;
@UiField ToggleButton whitespaceErrors;
@@ -91,8 +98,7 @@ class PreferencesBox extends Composite {
super.onLoad();
save.setVisible(Gerrit.isSignedIn());
addDomHandler(
new KeyDownHandler() {
addDomHandler(new KeyDownHandler() {
@Override
public void onKeyDown(KeyDownEvent event) {
if (event.getNativeKeyCode() == KEY_ESCAPE
@@ -100,8 +106,16 @@ class PreferencesBox extends Composite {
close();
}
}
},
KeyDownEvent.getType());
}, KeyDownEvent.getType());
updateContextTimer = new Timer() {
@Override
public void run() {
if (prefs.context() == WHOLE_FILE_CONTEXT) {
contextEntireFile.setValue(true);
}
view.setContext(prefs.context());
}
};
}
void set(DiffPreferences prefs) {
@@ -129,9 +143,12 @@ class PreferencesBox extends Composite {
}
if (prefs.context() == WHOLE_FILE_CONTEXT) {
contextLastValue = DEFAULT_CONTEXT;
context.setText("");
contextEntireFile.setValue(true);
} else {
context.setIntValue(prefs.context());
contextEntireFile.setValue(false);
}
}
@@ -148,19 +165,48 @@ class PreferencesBox extends Composite {
view.setShowIntraline(prefs.intralineDifference());
}
@UiHandler("context")
void onContextKey(KeyPressEvent e) {
if (contextEntireFile.getValue()) {
char c = e.getCharCode();
if ('0' <= c && c <= '9') {
contextEntireFile.setValue(false);
}
}
}
@UiHandler("context")
void onContext(ValueChangeEvent<String> e) {
String v = e.getValue();
int c;
if (v != null && v.length() > 0) {
c = Math.min(Math.max(0, Integer.parseInt(v)), 32767);
contextEntireFile.setValue(false);
} else if (v == null || v.isEmpty()) {
c = WHOLE_FILE_CONTEXT;
} else {
return;
}
prefs.context(c);
view.setContext(prefs.context());
updateContextTimer.schedule(200);
}
@UiHandler("contextEntireFile")
void onContextEntireFile(ValueChangeEvent<Boolean> e) {
// If a click arrives too fast after onContext applied an update
// the user committed the context line update by clicking on the
// whole file checkmark. Drop this event, but transfer focus.
if (e.getValue()) {
contextLastValue = context.getIntValue();
context.setText("");
prefs.context(WHOLE_FILE_CONTEXT);
} else {
prefs.context(contextLastValue > 0 ? contextLastValue : DEFAULT_CONTEXT);
context.setIntValue(prefs.context());
context.setFocus(true);
context.setSelectionRange(0, context.getText().length());
}
updateContextTimer.schedule(200);
}
@UiHandler("tabWidth")

View File

@@ -41,8 +41,10 @@ limitations under the License.
-moz-border-radius: 10px;
}
.dialog .box { margin: 10px; }
.box { margin: 10px; }
.box .gwt-TextBox { padding: 0; }
.context { vertical-align: bottom; }
.table tr { min-height: 23px; }
.table th,
.table td {
@@ -160,9 +162,11 @@ limitations under the License.
</tr>
<tr>
<th><ui:msg>Lines of Context</ui:msg></th>
<td><x:NpIntTextBox ui:field='context'
<td><ui:msg><x:NpIntTextBox ui:field='context'
addStyleNames='{style.context}'
visibleLength='4'
alignment='RIGHT'/></td>
alignment='RIGHT'/>
or <g:CheckBox ui:field='contextEntireFile'>entire file</g:CheckBox></ui:msg></td>
</tr>
<tr>
<th><ui:msg>Intraline Difference</ui:msg></th>