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

@@ -0,0 +1,140 @@
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,6 +36,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
36 36
 #include <ipxe/timer.h>
37 37
 #include <ipxe/console.h>
38 38
 #include <ipxe/ansicol.h>
39
+#include <ipxe/jumpscroll.h>
39 40
 #include <ipxe/menu.h>
40 41
 
41 42
 /* Screen layout */
@@ -50,12 +51,8 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
50 51
 struct menu_ui {
51 52
 	/** Menu */
52 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 56
 	/** Timeout (0=indefinite) */
60 57
 	unsigned long timeout;
61 58
 };
@@ -84,7 +81,7 @@ static struct menu_item * menu_item ( struct menu *menu, unsigned int index ) {
84 81
  * @v ui		Menu user interface
85 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 85
 	struct menu_item *item;
89 86
 	unsigned int row_offset;
90 87
 	char buf[ MENU_COLS + 1 /* NUL */ ];
@@ -94,7 +91,7 @@ static void draw_menu_item ( struct menu_ui *ui, int index ) {
94 91
 	size_t len;
95 92
 
96 93
 	/* Move to start of row */
97
-	row_offset = ( index - ui->first_visible );
94
+	row_offset = ( index - ui->scroll.first );
98 95
 	move ( ( MENU_ROW + row_offset ), MENU_COL );
99 96
 
100 97
 	/* Get menu item */
@@ -106,7 +103,7 @@ static void draw_menu_item ( struct menu_ui *ui, int index ) {
106 103
 			color_set ( CPAIR_SEPARATOR, NULL );
107 104
 
108 105
 		/* Highlight if this is the selected item */
109
-		if ( index == ui->selected ) {
106
+		if ( index == ui->scroll.current ) {
110 107
 			color_set ( CPAIR_SELECT, NULL );
111 108
 			attron ( A_BOLD );
112 109
 		}
@@ -125,7 +122,7 @@ static void draw_menu_item ( struct menu_ui *ui, int index ) {
125 122
 			snprintf ( timeout_buf, sizeof ( timeout_buf ), "(%ld)",
126 123
 				   ( ( ui->timeout + TICKS_PER_SEC - 1 ) /
127 124
 				     TICKS_PER_SEC ) );
128
-		if ( ( index == ui->selected ) && ( ui->timeout != 0 ) ) {
125
+		if ( ( index == ui->scroll.current ) && ( ui->timeout != 0 ) ) {
129 126
 			memcpy ( ( buf + MENU_COLS - MENU_PAD - timeout_len ),
130 127
 				 timeout_buf, timeout_len );
131 128
 		}
@@ -154,24 +151,17 @@ static void draw_menu_item ( struct menu_ui *ui, int index ) {
154 151
 static void draw_menu_items ( struct menu_ui *ui ) {
155 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 154
 	/* Draw ellipses before and/or after the list as necessary */
164 155
 	color_set ( CPAIR_SEPARATOR, NULL );
165 156
 	mvaddstr ( ( MENU_ROW - 1 ), ( MENU_COL + MENU_PAD ),
166
-		   ( ( ui->first_visible > 0 ) ? "..." : "   " ) );
157
+		   ( jump_scroll_is_first ( &ui->scroll ) ? "   " : "..." ) );
167 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 160
 	color_set ( CPAIR_NORMAL, NULL );
171 161
 
172 162
 	/* Draw visible items */
173 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,8 +174,7 @@ static void draw_menu_items ( struct menu_ui *ui ) {
184 174
 static int menu_loop ( struct menu_ui *ui, struct menu_item **selected ) {
185 175
 	struct menu_item *item;
186 176
 	unsigned long timeout;
187
-	unsigned int delta;
188
-	int current;
177
+	unsigned int previous;
189 178
 	int key;
190 179
 	int i;
191 180
 	int move;
@@ -194,7 +183,7 @@ static int menu_loop ( struct menu_ui *ui, struct menu_item **selected ) {
194 183
 
195 184
 	do {
196 185
 		/* Record current selection */
197
-		current = ui->selected;
186
+		previous = ui->scroll.current;
198 187
 
199 188
 		/* Calculate timeout as remainder of current second */
200 189
 		timeout = ( ui->timeout % TICKS_PER_SEC );
@@ -213,27 +202,11 @@ static int menu_loop ( struct menu_ui *ui, struct menu_item **selected ) {
213 202
 			/* Cancel any timeout */
214 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 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 210
 			case ESC:
238 211
 			case CTRL_C:
239 212
 				rc = -ECANCELED;
@@ -251,7 +224,7 @@ static int menu_loop ( struct menu_ui *ui, struct menu_item **selected ) {
251 224
 						i++;
252 225
 						continue;
253 226
 					}
254
-					ui->selected = i;
227
+					ui->scroll.current = i;
255 228
 					if ( item->label ) {
256 229
 						chosen = 1;
257 230
 					} else {
@@ -264,31 +237,22 @@ static int menu_loop ( struct menu_ui *ui, struct menu_item **selected ) {
264 237
 
265 238
 		/* Move selection, if applicable */
266 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 242
 			if ( item->label )
277 243
 				break;
278
-			move = ( ( move > 0 ) ? +1 : -1 );
279 244
 		}
280 245
 
281 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 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 254
 		/* Record selection */
291
-		item = menu_item ( ui->menu, ui->selected );
255
+		item = menu_item ( ui->menu, ui->scroll.current );
292 256
 		assert ( item != NULL );
293 257
 		assert ( item->label != NULL );
294 258
 		*selected = item;
@@ -317,21 +281,22 @@ int show_menu ( struct menu *menu, unsigned long timeout,
317 281
 	/* Initialise UI */
318 282
 	memset ( &ui, 0, sizeof ( ui ) );
319 283
 	ui.menu = menu;
284
+	ui.scroll.rows = MENU_ROWS;
320 285
 	ui.timeout = timeout;
321 286
 	list_for_each_entry ( item, &menu->items, list ) {
322 287
 		if ( item->label ) {
323 288
 			if ( ! labelled_count )
324
-				ui.selected = ui.count;
289
+				ui.scroll.current = ui.scroll.count;
325 290
 			labelled_count++;
326 291
 			if ( select ) {
327 292
 				if ( strcmp ( select, item->label ) == 0 )
328
-					ui.selected = ui.count;
293
+					ui.scroll.current = ui.scroll.count;
329 294
 			} else {
330 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 301
 	if ( ! labelled_count ) {
337 302
 		/* Menus with no labelled items cannot be selected
@@ -353,8 +318,9 @@ int show_menu ( struct menu *menu, unsigned long timeout,
353 318
 	snprintf ( buf, sizeof ( buf ), "%s", ui.menu->title );
354 319
 	mvprintw ( TITLE_ROW, ( ( COLS - strlen ( buf ) ) / 2 ), "%s", buf );
355 320
 	attroff ( A_BOLD );
321
+	jump_scroll ( &ui.scroll );
356 322
 	draw_menu_items ( &ui );
357
-	draw_menu_item ( &ui, ui.selected );
323
+	draw_menu_item ( &ui, ui.scroll.current );
358 324
 
359 325
 	/* Enter main loop */
360 326
 	rc = menu_loop ( &ui, selected );

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

@@ -0,0 +1,50 @@
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