Skip to content

Commit 355b56f

Browse files
Mark Lentcznermzero
Mark Lentczner
authored andcommitted
many tweaks to the UI of history search
moved scrollIntoView into a new util.js module -- now it's perfect!
1 parent ba441b3 commit 355b56f

File tree

6 files changed

+131
-59
lines changed

6 files changed

+131
-59
lines changed

history notes

+16-14
Original file line numberDiff line numberDiff line change
@@ -126,21 +126,23 @@ kill command
126126

127127
[x] entering history search should focus on command line if needed
128128
[x] return should enter current focus into command line
129-
[] ensure that focused entry is always scrolled into view
129+
[x] ensure that focused entry is always scrolled into view
130+
[x] better css
131+
[-] for when in history mode
132+
[x] for the focused item
133+
[x] corner cases better
134+
[x] when the selection goes empty shouldn't loose focus place
135+
[x] when focus at bottom with dir 1 goes out of match, then back in
136+
[x] bindings
137+
[x] should ESC key enter or cancel - cancel
138+
[x] should ALT+/ and ALT+\ enter history search - yes
139+
[x] what key should be bound to clear the search field - C-u
140+
130141
[] scroll list so that it is at bottom, not top of screen
131-
[] better css
132-
[] for when in history mode
133-
[] for the focused item
134-
[] corner cases better
135-
[] when the selection goes empty shouldn't loose focus place
136-
[] when focus at bottom with dir 1 goes out of match, then back in
137-
[] animage hiding/showing of output ?
138-
[] bindings
139-
[] should ESC key enter or cancel ?
140-
[] should ALT+/ and ALT+\ enter history search ?
141-
[] what key should be bound to clear the search field?
142-
[] display focused entry overlaid onto command line ala completion ?
143-
[] restore commandline on cancel search?
142+
143+
[-] animate hiding/showing of output ?
144+
[-] display focused entry overlaid onto command line ala completion ?
145+
[-] restore commandline on cancel search?
144146

145147

146148
From readline's History UI

static/css/plush.css

+5
Original file line numberDiff line numberDiff line change
@@ -439,6 +439,7 @@ h1,h2,h3,h4,blockquote,q,.maia-nav ul {
439439

440440
.job {
441441
border-left: 2px solid #fff;
442+
margin: 0.5em 0;
442443
}
443444
.job.topic {
444445
border-left-color: #ddd;
@@ -586,6 +587,10 @@ h1,h2,h3,h4,blockquote,q,.maia-nav ul {
586587
display: none;
587588
}
588589

590+
.history-search .job.history-focus {
591+
background-color: #ffe;
592+
}
593+
589594
.history-search .job.history-focus .command {
590595
font-weight: bold;
591596
padding-left: 0.5em;

static/js/history.js

+60-16
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
1414

15-
define(['jobs'], function(jobs) {
15+
define(['util', 'jobs'], function(util, jobs) {
1616
"use strict";
1717

1818
var api;
@@ -102,34 +102,38 @@ define(['jobs'], function(jobs) {
102102

103103
var searchMode = false;
104104
var searchFocus = null;
105+
var searchPriorFocus = null;
105106
var searchLastDir = -1;
107+
var scrollback;
106108
var commandline;
107109

108110
function startSearch(dir) {
109-
$('#scrollback').addClass('history-search');
110111
searchMode = true;
111112
searchFocus = null;
112113
searchLastDir = dir;
114+
scrollback = $('#scrollback');
115+
scrollback.addClass('history-search');
113116
commandline = $('#commandline');
114-
search(commandline.val());
115117
commandline.focus();
118+
search();
116119
}
117120

118121
function cancelSearch() {
119-
$('#scrollback').removeClass('history-search');
120-
var j = $('#scrollback .job');
122+
scrollback.removeClass('history-search');
123+
var j = scrollback.find('.job');
121124
j.show('fast')
122125
j.removeClass('history-match history-focus');
123126
searchMode = false;
124127
searchFocus = null;
128+
searchPriorFocus = null;
125129
commandline.focus();
126130
}
127131

128132
var ANIM_SPEED = 100;
129133

130-
function search(s) {
131-
s = s || '';
132-
$('#scrollback .job')
134+
function search() {
135+
var s = commandline.val() || '';
136+
scrollback.find('.job')
133137
.each(function(idx) {
134138
var n = $(this);
135139
if (s.length === 0 || n.find('.command').text().indexOf(s) >= 0) {
@@ -143,6 +147,18 @@ define(['jobs'], function(jobs) {
143147
if (!searchFocus || !(searchFocus.hasClass('history-match'))) {
144148
nextFocus(searchLastDir);
145149
}
150+
setTimeout(rescrollMatch, ANIM_SPEED);
151+
}
152+
153+
function rescrollMatch() {
154+
if (searchFocus) {
155+
util.scrollIntoView(scrollback, searchFocus);
156+
}
157+
}
158+
159+
function clearSearch() {
160+
commandline.val('');
161+
search();
146162
}
147163

148164
function endSearch() {
@@ -156,24 +172,38 @@ define(['jobs'], function(jobs) {
156172
cancelSearch();
157173
}
158174

175+
function focusEdge(dir) {
176+
var js = scrollback.find('.history-match');
177+
switch (dir) {
178+
case -1: return js.first();
179+
case 1: return js.last();
180+
}
181+
}
159182
function nextFocus(dir) {
160183
searchLastDir = dir;
161184
var next = null;
162185
if (searchFocus) {
186+
searchPriorFocus = searchFocus;
163187
switch (dir) {
164188
case -1: next = searchFocus.prevAll('.history-match').first(); break;
165189
case 1: next = searchFocus.nextAll('.history-match').first(); break;
166190
}
167191
searchFocus.removeClass('history-focus');
192+
if (next === null || next.length === 0) {
193+
next = focusEdge(dir);
194+
}
168195
} else {
169-
var js = $('#scrollback .history-match');
170-
switch (dir) {
171-
case -1: next = js.last(); break;
172-
case 1: next = js.first(); break;
196+
if (searchPriorFocus && searchPriorFocus.hasClass('history-match')) {
197+
next = searchPriorFocus;
198+
} else {
199+
next = focusEdge(-dir);
200+
// -dir because if there is no focus, and the user goes UP,
201+
// they want to start with the last item.
173202
}
174203
}
175204
if (next && next.length > 0) {
176205
next.addClass('history-focus');
206+
util.scrollIntoView(scrollback, next);
177207
searchFocus = next;
178208
} else {
179209
searchFocus = null;
@@ -187,6 +217,11 @@ define(['jobs'], function(jobs) {
187217
case 82: startSearch(-1); return false; // CTRL+R
188218
case 83: startSearch(1); return false; // CTRL+S
189219
}
220+
} else if (e.altKey && !(e.ctrlKey || e.shiftKey || e.metaKey)) {
221+
switch (e.which) {
222+
case 191: startSearch(-1); return false; // ALT+/
223+
case 220: startSearch(1); return false; // ALT+\
224+
}
190225
}
191226
} else {
192227
if (!(e.ctrlKey || e.shiftKey || e.metaKey)) { // ALT+ is optional
@@ -198,21 +233,30 @@ define(['jobs'], function(jobs) {
198233
if (!(e.ctrlKey || e.altKey || e.shiftKey || e.metaKey)) {
199234
switch (e.which) {
200235
case 13: endSearch(); return false; // RETURN
201-
case 27: endSearch(); return false; // ESC
236+
case 27: cancelSearch(); return false; // ESC
237+
// Standard readline bindings would be endSearch, but this
238+
// seems to be what most people expect in this context
202239
}
203240
} else if (e.ctrlKey && !(e.altKey || e.shiftKey || e.metaKey)) {
204241
switch (e.which) {
205242
case 71: cancelSearch(); return false; // CTRL+G
206243
case 82: nextFocus(-1); return false; // CTRL+R
207244
case 83: nextFocus(1); return false; // CTRL+S
245+
case 85: clearSearch(); return false; // CTRL+U
208246
}
209-
}
247+
} else if (e.altKey && !(e.ctrlKey || e.shiftKey || e.metaKey)) {
248+
switch (e.which) {
249+
case 191: nextFocus(-1); return false; // ALT+/
250+
case 220: nextFocus(1); return false; // ALT+\
251+
}
252+
}
253+
210254
}
211255
}
212256

213-
function commandChange(s) {
257+
function commandChange() {
214258
if (searchMode) {
215-
search(s);
259+
search();
216260
return false;
217261
}
218262
}

static/js/jobs.js

+4-28
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
1414

15-
define(['jquery', 'hterm'], function($){
15+
define(['jquery', 'util', 'hterm'], function($, util){
1616
'use strict';
1717

1818
var LINES_IN_TINY = 3;
@@ -39,30 +39,6 @@ define(['jquery', 'hterm'], function($){
3939
return $(elem).closest('.job').data('jobPrivate');
4040
}
4141

42-
function scrollIntoView(scroller, inner, offset) {
43-
var sTop = scroller.scrollTop();
44-
var sBottom = sTop + scroller.height();
45-
var sTop0 = sTop;
46-
47-
var iTop = inner.offset().top - scroller.children().offset().top;
48-
// TODO(mzero): this doesn't work if the scroller has non-scrolled content
49-
var iBottom = iTop + inner.outerHeight(true);
50-
51-
if (offset) {
52-
iTop += offset;
53-
}
54-
55-
if (iBottom > sBottom) {
56-
sTop += iBottom - sBottom;
57-
}
58-
if (iTop < sTop) {
59-
sTop -= sTop - iTop;
60-
}
61-
if (sTop != sTop0) {
62-
scroller.scrollTop(sTop);
63-
}
64-
}
65-
6642
var currentTopic = null;
6743

6844
function blurAll() {
@@ -77,7 +53,7 @@ define(['jquery', 'hterm'], function($){
7753
currentTopic = nextTopic;
7854
if (currentTopic) {
7955
currentTopic.addClass('topic focus');
80-
scrollIntoView(scrollback, currentTopic);
56+
util.scrollIntoView(scrollback, currentTopic);
8157
}
8258
}
8359

@@ -245,7 +221,7 @@ define(['jquery', 'hterm'], function($){
245221
} else {
246222
output.removeClass('output-hide output-tiny output-page output-full');
247223
output.addClass('output-' + m);
248-
setTimeout(function() { scrollIntoView(scrollback, node); }, 100);
224+
setTimeout(function() { util.scrollIntoView(scrollback, node); }, 100);
249225
// have to wait until layout has been recomputed for new size
250226
}
251227
};
@@ -387,7 +363,7 @@ define(['jquery', 'hterm'], function($){
387363
adjustOutput();
388364

389365
if (wasAtEnd && lastOutputTime >= scrollIfAfter) {
390-
scrollIntoView(output, outputArea, spanOffset);
366+
util.scrollIntoView(output, outputArea, spanOffset);
391367
}
392368
}
393369

static/js/shell.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -315,7 +315,7 @@ define(['history', 'cwd', 'jobs', 'jquery'], function(history, cwd, jobs, $){
315315
});
316316

317317
commandline.keyup(function(e) {
318-
var r = history.commandChange($(this).val());
318+
var r = history.commandChange();
319319
if (r !== undefined) return r;
320320

321321
requestRunComplete();

static/js/util.js

+45
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
// Copyright 2012 Google Inc. All Rights Reserved.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
define(['jquery'], function($){
16+
'use strict';
17+
18+
function scrollIntoView(scroller, inner, offset) {
19+
var sTop = scroller.scrollTop();
20+
var sBottom = sTop + scroller.height();
21+
var sTop0 = sTop;
22+
23+
var iTop = inner.offset().top - scroller.children().offset().top;
24+
// TODO(mzero): this doesn't work if the scroller has non-scrolled content
25+
var iBottom = iTop + inner.outerHeight(true);
26+
27+
if (offset) {
28+
iTop += offset;
29+
}
30+
31+
if (iBottom > sBottom) {
32+
sTop += iBottom - sBottom;
33+
}
34+
if (iTop < sTop) {
35+
sTop -= sTop - iTop;
36+
}
37+
if (sTop != sTop0) {
38+
scroller.scrollTop(sTop);
39+
}
40+
}
41+
42+
return {
43+
scrollIntoView: scrollIntoView
44+
};
45+
});

0 commit comments

Comments
 (0)