Browse Source

[menu] Abstract out the generic concept of a jump scroller

Signed-off-by: Michael Brown <mcb30@ipxe.org>
tags/v1.20.1
Michael Brown 9 years ago
parent
commit
58752cc10d
3 changed files with 221 additions and 65 deletions
  1. 140
    0
      src/hci/jumpscroll.c
  2. 31
    65
      src/hci/tui/menu_ui.c
  3. 50
    0
      src/include/ipxe/jumpscroll.h

+ 140
- 0
src/hci/jumpscroll.c View File

1
+/*
2
+ * Copyright (C) 2015 Michael Brown <mbrown@fensystems.co.uk>.
3
+ *
4
+ * This program is free software; you can redistribute it and/or
5
+ * modify it under the terms of the GNU General Public License as
6
+ * published by the Free Software Foundation; either version 2 of the
7
+ * License, or (at your option) any later version.
8
+ *
9
+ * This program is distributed in the hope that it will be useful, but
10
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12
+ * General Public License for more details.
13
+ *
14
+ * You should have received a copy of the GNU General Public License
15
+ * along with this program; if not, write to the Free Software
16
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17
+ * 02110-1301, USA.
18
+ *
19
+ * You can also choose to distribute this program under the terms of
20
+ * the Unmodified Binary Distribution Licence (as given in the file
21
+ * COPYING.UBDL), provided that you have satisfied its requirements.
22
+ */
23
+
24
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
25
+
26
+/**
27
+ * Jump scrolling
28
+ *
29
+ */
30
+
31
+#include <assert.h>
32
+#include <ipxe/keys.h>
33
+#include <ipxe/jumpscroll.h>
34
+
35
+/**
36
+ * Handle keypress
37
+ *
38
+ * @v scroll		Jump scroller
39
+ * @v key		Key pressed by user
40
+ * @ret move		Scroller movement, or zero
41
+ */
42
+int jump_scroll_key ( struct jump_scroller *scroll, int key ) {
43
+
44
+	/* Sanity checks */
45
+	assert ( scroll->rows != 0 );
46
+	assert ( scroll->count != 0 );
47
+	assert ( scroll->current < scroll->count );
48
+	assert ( scroll->first < scroll->count );
49
+	assert ( scroll->first <= scroll->current );
50
+	assert ( scroll->current < ( scroll->first + scroll->rows ) );
51
+
52
+	/* Handle key, if applicable */
53
+	switch ( key ) {
54
+	case KEY_UP:
55
+		return -1;
56
+	case KEY_DOWN:
57
+		return +1;
58
+	case KEY_PPAGE:
59
+		return ( scroll->first - scroll->current - 1 );
60
+	case KEY_NPAGE:
61
+		return ( scroll->first - scroll->current + scroll->rows );
62
+	case KEY_HOME:
63
+		return -( scroll->count );
64
+	case KEY_END:
65
+		return +( scroll->count );
66
+	default:
67
+		return 0;
68
+	}
69
+}
70
+
71
+/**
72
+ * Move scroller
73
+ *
74
+ * @v scroll		Jump scroller
75
+ * @v move		Scroller movement
76
+ * @ret move		Continuing scroller movement (if applicable)
77
+ */
78
+int jump_scroll_move ( struct jump_scroller *scroll, int move ) {
79
+	int current = scroll->current;
80
+	int last = ( scroll->count - 1 );
81
+
82
+	/* Sanity checks */
83
+	assert ( move != 0 );
84
+	assert ( scroll->count != 0 );
85
+
86
+	/* Move to the new current item */
87
+	current += move;
88
+
89
+	/* Check for start/end of list */
90
+	if ( current < 0 ) {
91
+		/* We have attempted to move before the start of the
92
+		 * list.  Move to the start of the list and continue
93
+		 * moving forwards (if applicable).
94
+		 */
95
+		scroll->current = 0;
96
+		return +1;
97
+	} else if ( current > last ) {
98
+		/* We have attempted to move after the end of the
99
+		 * list.  Move to the end of the list and continue
100
+		 * moving backwards (if applicable).
101
+		 */
102
+		scroll->current = last;
103
+		return -1;
104
+	} else {
105
+		/* Update the current item and continue moving in the
106
+		 * same direction (if applicable).
107
+		 */
108
+		scroll->current = current;
109
+		return ( ( move > 0 ) ? +1 : -1 );
110
+	}
111
+}
112
+
113
+/**
114
+ * Jump scroll to new page (if applicable)
115
+ *
116
+ * @v scroll		Jump scroller
117
+ * @ret jumped		Jumped to a new page
118
+ */
119
+int jump_scroll ( struct jump_scroller *scroll ) {
120
+	unsigned int index;
121
+
122
+	/* Sanity checks */
123
+	assert ( scroll->rows != 0 );
124
+	assert ( scroll->count != 0 );
125
+	assert ( scroll->current < scroll->count );
126
+	assert ( scroll->first < scroll->count );
127
+
128
+	/* Do nothing if we are already on the correct page */
129
+	index = ( scroll->current - scroll->first );
130
+	if ( index < scroll->rows )
131
+		return 0;
132
+
133
+	/* Move to required page */
134
+	while ( scroll->first < scroll->current )
135
+		scroll->first += scroll->rows;
136
+	while ( scroll->first > scroll->current )
137
+		scroll->first -= scroll->rows;
138
+
139
+	return 1;
140
+}

+ 31
- 65
src/hci/tui/menu_ui.c View File

36
 #include <ipxe/timer.h>
36
 #include <ipxe/timer.h>
37
 #include <ipxe/console.h>
37
 #include <ipxe/console.h>
38
 #include <ipxe/ansicol.h>
38
 #include <ipxe/ansicol.h>
39
+#include <ipxe/jumpscroll.h>
39
 #include <ipxe/menu.h>
40
 #include <ipxe/menu.h>
40
 
41
 
41
 /* Screen layout */
42
 /* Screen layout */
50
 struct menu_ui {
51
 struct menu_ui {
51
 	/** Menu */
52
 	/** Menu */
52
 	struct menu *menu;
53
 	struct menu *menu;
53
-	/** Number of menu items */
54
-	unsigned int count;
55
-	/** Currently selected item */
56
-	int selected;
57
-	/** First visible item */
58
-	int first_visible;
54
+	/** Jump scroller */
55
+	struct jump_scroller scroll;
59
 	/** Timeout (0=indefinite) */
56
 	/** Timeout (0=indefinite) */
60
 	unsigned long timeout;
57
 	unsigned long timeout;
61
 };
58
 };
84
  * @v ui		Menu user interface
81
  * @v ui		Menu user interface
85
  * @v index		Index
82
  * @v index		Index
86
  */
83
  */
87
-static void draw_menu_item ( struct menu_ui *ui, int index ) {
84
+static void draw_menu_item ( struct menu_ui *ui, unsigned int index ) {
88
 	struct menu_item *item;
85
 	struct menu_item *item;
89
 	unsigned int row_offset;
86
 	unsigned int row_offset;
90
 	char buf[ MENU_COLS + 1 /* NUL */ ];
87
 	char buf[ MENU_COLS + 1 /* NUL */ ];
94
 	size_t len;
91
 	size_t len;
95
 
92
 
96
 	/* Move to start of row */
93
 	/* Move to start of row */
97
-	row_offset = ( index - ui->first_visible );
94
+	row_offset = ( index - ui->scroll.first );
98
 	move ( ( MENU_ROW + row_offset ), MENU_COL );
95
 	move ( ( MENU_ROW + row_offset ), MENU_COL );
99
 
96
 
100
 	/* Get menu item */
97
 	/* Get menu item */
106
 			color_set ( CPAIR_SEPARATOR, NULL );
103
 			color_set ( CPAIR_SEPARATOR, NULL );
107
 
104
 
108
 		/* Highlight if this is the selected item */
105
 		/* Highlight if this is the selected item */
109
-		if ( index == ui->selected ) {
106
+		if ( index == ui->scroll.current ) {
110
 			color_set ( CPAIR_SELECT, NULL );
107
 			color_set ( CPAIR_SELECT, NULL );
111
 			attron ( A_BOLD );
108
 			attron ( A_BOLD );
112
 		}
109
 		}
125
 			snprintf ( timeout_buf, sizeof ( timeout_buf ), "(%ld)",
122
 			snprintf ( timeout_buf, sizeof ( timeout_buf ), "(%ld)",
126
 				   ( ( ui->timeout + TICKS_PER_SEC - 1 ) /
123
 				   ( ( ui->timeout + TICKS_PER_SEC - 1 ) /
127
 				     TICKS_PER_SEC ) );
124
 				     TICKS_PER_SEC ) );
128
-		if ( ( index == ui->selected ) && ( ui->timeout != 0 ) ) {
125
+		if ( ( index == ui->scroll.current ) && ( ui->timeout != 0 ) ) {
129
 			memcpy ( ( buf + MENU_COLS - MENU_PAD - timeout_len ),
126
 			memcpy ( ( buf + MENU_COLS - MENU_PAD - timeout_len ),
130
 				 timeout_buf, timeout_len );
127
 				 timeout_buf, timeout_len );
131
 		}
128
 		}
154
 static void draw_menu_items ( struct menu_ui *ui ) {
151
 static void draw_menu_items ( struct menu_ui *ui ) {
155
 	unsigned int i;
152
 	unsigned int i;
156
 
153
 
157
-	/* Jump scroll to correct point in list */
158
-	while ( ui->first_visible < ui->selected )
159
-		ui->first_visible += MENU_ROWS;
160
-	while ( ui->first_visible > ui->selected )
161
-		ui->first_visible -= MENU_ROWS;
162
-
163
 	/* Draw ellipses before and/or after the list as necessary */
154
 	/* Draw ellipses before and/or after the list as necessary */
164
 	color_set ( CPAIR_SEPARATOR, NULL );
155
 	color_set ( CPAIR_SEPARATOR, NULL );
165
 	mvaddstr ( ( MENU_ROW - 1 ), ( MENU_COL + MENU_PAD ),
156
 	mvaddstr ( ( MENU_ROW - 1 ), ( MENU_COL + MENU_PAD ),
166
-		   ( ( ui->first_visible > 0 ) ? "..." : "   " ) );
157
+		   ( jump_scroll_is_first ( &ui->scroll ) ? "   " : "..." ) );
167
 	mvaddstr ( ( MENU_ROW + MENU_ROWS ), ( MENU_COL + MENU_PAD ),
158
 	mvaddstr ( ( MENU_ROW + MENU_ROWS ), ( MENU_COL + MENU_PAD ),
168
-		   ( ( ( ui->first_visible + MENU_ROWS ) < ui->count ) ?
169
-		     "..." : "   " ) );
159
+		   ( jump_scroll_is_last ( &ui->scroll ) ? "   " : "..." ) );
170
 	color_set ( CPAIR_NORMAL, NULL );
160
 	color_set ( CPAIR_NORMAL, NULL );
171
 
161
 
172
 	/* Draw visible items */
162
 	/* Draw visible items */
173
 	for ( i = 0 ; i < MENU_ROWS ; i++ )
163
 	for ( i = 0 ; i < MENU_ROWS ; i++ )
174
-		draw_menu_item ( ui, ( ui->first_visible + i ) );
164
+		draw_menu_item ( ui, ( ui->scroll.first + i ) );
175
 }
165
 }
176
 
166
 
177
 /**
167
 /**
184
 static int menu_loop ( struct menu_ui *ui, struct menu_item **selected ) {
174
 static int menu_loop ( struct menu_ui *ui, struct menu_item **selected ) {
185
 	struct menu_item *item;
175
 	struct menu_item *item;
186
 	unsigned long timeout;
176
 	unsigned long timeout;
187
-	unsigned int delta;
188
-	int current;
177
+	unsigned int previous;
189
 	int key;
178
 	int key;
190
 	int i;
179
 	int i;
191
 	int move;
180
 	int move;
194
 
183
 
195
 	do {
184
 	do {
196
 		/* Record current selection */
185
 		/* Record current selection */
197
-		current = ui->selected;
186
+		previous = ui->scroll.current;
198
 
187
 
199
 		/* Calculate timeout as remainder of current second */
188
 		/* Calculate timeout as remainder of current second */
200
 		timeout = ( ui->timeout % TICKS_PER_SEC );
189
 		timeout = ( ui->timeout % TICKS_PER_SEC );
213
 			/* Cancel any timeout */
202
 			/* Cancel any timeout */
214
 			ui->timeout = 0;
203
 			ui->timeout = 0;
215
 
204
 
216
-			/* Handle key */
205
+			/* Handle scroll keys */
206
+			move = jump_scroll_key ( &ui->scroll, key );
207
+
208
+			/* Handle other keys */
217
 			switch ( key ) {
209
 			switch ( key ) {
218
-			case KEY_UP:
219
-				move = -1;
220
-				break;
221
-			case KEY_DOWN:
222
-				move = +1;
223
-				break;
224
-			case KEY_PPAGE:
225
-				move = ( ui->first_visible - ui->selected - 1 );
226
-				break;
227
-			case KEY_NPAGE:
228
-				move = ( ui->first_visible - ui->selected
229
-					 + MENU_ROWS );
230
-				break;
231
-			case KEY_HOME:
232
-				move = -ui->count;
233
-				break;
234
-			case KEY_END:
235
-				move = +ui->count;
236
-				break;
237
 			case ESC:
210
 			case ESC:
238
 			case CTRL_C:
211
 			case CTRL_C:
239
 				rc = -ECANCELED;
212
 				rc = -ECANCELED;
251
 						i++;
224
 						i++;
252
 						continue;
225
 						continue;
253
 					}
226
 					}
254
-					ui->selected = i;
227
+					ui->scroll.current = i;
255
 					if ( item->label ) {
228
 					if ( item->label ) {
256
 						chosen = 1;
229
 						chosen = 1;
257
 					} else {
230
 					} else {
264
 
237
 
265
 		/* Move selection, if applicable */
238
 		/* Move selection, if applicable */
266
 		while ( move ) {
239
 		while ( move ) {
267
-			ui->selected += move;
268
-			if ( ui->selected < 0 ) {
269
-				ui->selected = 0;
270
-				move = +1;
271
-			} else if ( ui->selected >= ( int ) ui->count ) {
272
-				ui->selected = ( ui->count - 1 );
273
-				move = -1;
274
-			}
275
-			item = menu_item ( ui->menu, ui->selected );
240
+			move = jump_scroll_move ( &ui->scroll, move );
241
+			item = menu_item ( ui->menu, ui->scroll.current );
276
 			if ( item->label )
242
 			if ( item->label )
277
 				break;
243
 				break;
278
-			move = ( ( move > 0 ) ? +1 : -1 );
279
 		}
244
 		}
280
 
245
 
281
 		/* Redraw selection if necessary */
246
 		/* Redraw selection if necessary */
282
-		if ( ( ui->selected != current ) || ( timeout != 0 ) ) {
283
-			draw_menu_item ( ui, current );
284
-			delta = ( ui->selected - ui->first_visible );
285
-			if ( delta >= MENU_ROWS )
247
+		if ( ( ui->scroll.current != previous ) || ( timeout != 0 ) ) {
248
+			draw_menu_item ( ui, previous );
249
+			if ( jump_scroll ( &ui->scroll ) )
286
 				draw_menu_items ( ui );
250
 				draw_menu_items ( ui );
287
-			draw_menu_item ( ui, ui->selected );
251
+			draw_menu_item ( ui, ui->scroll.current );
288
 		}
252
 		}
289
 
253
 
290
 		/* Record selection */
254
 		/* Record selection */
291
-		item = menu_item ( ui->menu, ui->selected );
255
+		item = menu_item ( ui->menu, ui->scroll.current );
292
 		assert ( item != NULL );
256
 		assert ( item != NULL );
293
 		assert ( item->label != NULL );
257
 		assert ( item->label != NULL );
294
 		*selected = item;
258
 		*selected = item;
317
 	/* Initialise UI */
281
 	/* Initialise UI */
318
 	memset ( &ui, 0, sizeof ( ui ) );
282
 	memset ( &ui, 0, sizeof ( ui ) );
319
 	ui.menu = menu;
283
 	ui.menu = menu;
284
+	ui.scroll.rows = MENU_ROWS;
320
 	ui.timeout = timeout;
285
 	ui.timeout = timeout;
321
 	list_for_each_entry ( item, &menu->items, list ) {
286
 	list_for_each_entry ( item, &menu->items, list ) {
322
 		if ( item->label ) {
287
 		if ( item->label ) {
323
 			if ( ! labelled_count )
288
 			if ( ! labelled_count )
324
-				ui.selected = ui.count;
289
+				ui.scroll.current = ui.scroll.count;
325
 			labelled_count++;
290
 			labelled_count++;
326
 			if ( select ) {
291
 			if ( select ) {
327
 				if ( strcmp ( select, item->label ) == 0 )
292
 				if ( strcmp ( select, item->label ) == 0 )
328
-					ui.selected = ui.count;
293
+					ui.scroll.current = ui.scroll.count;
329
 			} else {
294
 			} else {
330
 				if ( item->is_default )
295
 				if ( item->is_default )
331
-					ui.selected = ui.count;
296
+					ui.scroll.current = ui.scroll.count;
332
 			}
297
 			}
333
 		}
298
 		}
334
-		ui.count++;
299
+		ui.scroll.count++;
335
 	}
300
 	}
336
 	if ( ! labelled_count ) {
301
 	if ( ! labelled_count ) {
337
 		/* Menus with no labelled items cannot be selected
302
 		/* Menus with no labelled items cannot be selected
353
 	snprintf ( buf, sizeof ( buf ), "%s", ui.menu->title );
318
 	snprintf ( buf, sizeof ( buf ), "%s", ui.menu->title );
354
 	mvprintw ( TITLE_ROW, ( ( COLS - strlen ( buf ) ) / 2 ), "%s", buf );
319
 	mvprintw ( TITLE_ROW, ( ( COLS - strlen ( buf ) ) / 2 ), "%s", buf );
355
 	attroff ( A_BOLD );
320
 	attroff ( A_BOLD );
321
+	jump_scroll ( &ui.scroll );
356
 	draw_menu_items ( &ui );
322
 	draw_menu_items ( &ui );
357
-	draw_menu_item ( &ui, ui.selected );
323
+	draw_menu_item ( &ui, ui.scroll.current );
358
 
324
 
359
 	/* Enter main loop */
325
 	/* Enter main loop */
360
 	rc = menu_loop ( &ui, selected );
326
 	rc = menu_loop ( &ui, selected );

+ 50
- 0
src/include/ipxe/jumpscroll.h View File

1
+#ifndef _IPXE_JUMPSCROLL_H
2
+#define _IPXE_JUMPSCROLL_H
3
+
4
+/** @file
5
+ *
6
+ * Jump scrolling
7
+ *
8
+ */
9
+
10
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
11
+
12
+/** A jump scroller */
13
+struct jump_scroller {
14
+	/** Maximum number of visible rows */
15
+	unsigned int rows;
16
+	/** Total number of items */
17
+	unsigned int count;
18
+	/** Currently selected item */
19
+	unsigned int current;
20
+	/** First visible item */
21
+	unsigned int first;
22
+};
23
+
24
+/**
25
+ * Check if jump scroller is currently on first page
26
+ *
27
+ * @v scroll		Jump scroller
28
+ * @ret is_first	Scroller is currently on first page
29
+ */
30
+static inline int jump_scroll_is_first ( struct jump_scroller *scroll ) {
31
+
32
+	return ( scroll->first == 0 );
33
+}
34
+
35
+/**
36
+ * Check if jump scroller is currently on last page
37
+ *
38
+ * @v scroll		Jump scroller
39
+ * @ret is_last		Scroller is currently on last page
40
+ */
41
+static inline int jump_scroll_is_last ( struct jump_scroller *scroll ) {
42
+
43
+	return ( ( scroll->first + scroll->rows ) >= scroll->count );
44
+}
45
+
46
+extern int jump_scroll_key ( struct jump_scroller *scroll, int key );
47
+extern int jump_scroll_move ( struct jump_scroller *scroll, int move );
48
+extern int jump_scroll ( struct jump_scroller *scroll );
49
+
50
+#endif /* _IPXE_JUMPSCROLL_H */

Loading…
Cancel
Save