Index: uspace/app/nav/nav.c
===================================================================
--- uspace/app/nav/nav.c	(revision 453f96458238442bcb1a283b9281342c84c76a9d)
+++ uspace/app/nav/nav.c	(revision 54ddb59be14baa078affd298a0ddbf53724d7b29)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2021 Jiri Svoboda
+ * Copyright (c) 2022 Jiri Svoboda
  * All rights reserved.
  *
@@ -40,4 +40,5 @@
 #include <str.h>
 #include <ui/fixed.h>
+#include <ui/filelist.h>
 #include <ui/resource.h>
 #include <ui/ui.h>
@@ -314,5 +315,5 @@
 
 	panel = navigator_get_active_panel(navigator);
-	panel_open(panel, panel->cursor);
+	ui_file_list_open(panel->flist, ui_file_list_get_cursor(panel->flist));
 }
 
Index: uspace/app/nav/panel.c
===================================================================
--- uspace/app/nav/panel.c	(revision 453f96458238442bcb1a283b9281342c84c76a9d)
+++ uspace/app/nav/panel.c	(revision 54ddb59be14baa078affd298a0ddbf53724d7b29)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2021 Jiri Svoboda
+ * Copyright (c) 2022 Jiri Svoboda
  * All rights reserved.
  *
@@ -35,16 +35,13 @@
  */
 
-#include <dirent.h>
 #include <errno.h>
 #include <gfx/render.h>
 #include <gfx/text.h>
 #include <stdlib.h>
-#include <str.h>
 #include <task.h>
 #include <ui/control.h>
+#include <ui/filelist.h>
 #include <ui/paint.h>
 #include <ui/resource.h>
-#include <vfs/vfs.h>
-#include <qsort.h>
 #include "panel.h"
 #include "nav.h"
@@ -56,5 +53,5 @@
 
 /** Panel control ops */
-ui_control_ops_t panel_ctl_ops = {
+static ui_control_ops_t panel_ctl_ops = {
 	.destroy = panel_ctl_destroy,
 	.paint = panel_ctl_paint,
@@ -63,4 +60,11 @@
 };
 
+static void panel_flist_selected(ui_file_list_t *, void *, const char *);
+
+/** Panel file list callbacks */
+static ui_file_list_cb_t panel_flist_cb = {
+	.selected = panel_flist_selected
+};
+
 /** Create panel.
  *
@@ -90,23 +94,15 @@
 		goto error;
 
-	rc = gfx_color_new_ega(0x30, &panel->curs_color);
+	rc = gfx_color_new_ega(0x0f, &panel->act_border_color);
 	if (rc != EOK)
 		goto error;
 
-	rc = gfx_color_new_ega(0x0f, &panel->act_border_color);
+	rc = ui_file_list_create(window, active, &panel->flist);
 	if (rc != EOK)
 		goto error;
 
-	rc = gfx_color_new_ega(0x0f, &panel->dir_color);
-	if (rc != EOK)
-		goto error;
-
-	rc = gfx_color_new_ega(0x0a, &panel->svc_color);
-	if (rc != EOK)
-		goto error;
+	ui_file_list_set_cb(panel->flist, &panel_flist_cb, (void *)panel);
 
 	panel->window = window;
-	list_initialize(&panel->entries);
-	panel->entries_cnt = 0;
 	panel->active = active;
 	*rpanel = panel;
@@ -115,12 +111,8 @@
 	if (panel->color != NULL)
 		gfx_color_delete(panel->color);
-	if (panel->curs_color != NULL)
-		gfx_color_delete(panel->curs_color);
 	if (panel->act_border_color != NULL)
 		gfx_color_delete(panel->act_border_color);
-	if (panel->dir_color != NULL)
-		gfx_color_delete(panel->dir_color);
-	if (panel->svc_color != NULL)
-		gfx_color_delete(panel->svc_color);
+	if (panel->flist != NULL)
+		ui_file_list_destroy(panel->flist);
 	ui_control_delete(panel->control);
 	free(panel);
@@ -135,9 +127,5 @@
 {
 	gfx_color_delete(panel->color);
-	gfx_color_delete(panel->curs_color);
 	gfx_color_delete(panel->act_border_color);
-	gfx_color_delete(panel->dir_color);
-	gfx_color_delete(panel->svc_color);
-	panel_clear_entries(panel);
 	ui_control_delete(panel->control);
 	free(panel);
@@ -156,89 +144,15 @@
 }
 
-/** Paint panel entry.
- *
- * @param entry Panel entry
- * @param entry_idx Entry index (within list of entries)
- * @return EOK on success or an error code
- */
-errno_t panel_entry_paint(panel_entry_t *entry, size_t entry_idx)
-{
-	panel_t *panel = entry->panel;
+/** Paint panel.
+ *
+ * @param panel Panel
+ */
+errno_t panel_paint(panel_t *panel)
+{
 	gfx_context_t *gc = ui_window_get_gc(panel->window);
 	ui_resource_t *res = ui_window_get_res(panel->window);
-	gfx_font_t *font = ui_resource_get_font(res);
-	gfx_text_fmt_t fmt;
-	gfx_coord2_t pos;
-	gfx_rect_t rect;
-	size_t rows;
-	errno_t rc;
-
-	gfx_text_fmt_init(&fmt);
-	fmt.font = font;
-	rows = panel_page_size(panel);
-
-	/* Do not display entry outside of current page */
-	if (entry_idx < panel->page_idx ||
-	    entry_idx >= panel->page_idx + rows)
-		return EOK;
-
-	pos.x = panel->rect.p0.x + 1;
-	pos.y = panel->rect.p0.y + 1 + entry_idx - panel->page_idx;
-
-	if (entry == panel->cursor && panel->active)
-		fmt.color = panel->curs_color;
-	else if (entry->isdir)
-		fmt.color = panel->dir_color;
-	else if (entry->svc != 0)
-		fmt.color = panel->svc_color;
-	else
-		fmt.color = panel->color;
-
-	/* Draw entry background */
-	rect.p0 = pos;
-	rect.p1.x = panel->rect.p1.x - 1;
-	rect.p1.y = rect.p0.y + 1;
-
-	rc = gfx_set_color(gc, fmt.color);
-	if (rc != EOK)
-		return rc;
-
-	rc = gfx_fill_rect(gc, &rect);
-	if (rc != EOK)
-		return rc;
-
-	/*
-	 * Make sure name does not overflow the entry rectangle.
-	 *
-	 * XXX We probably want to measure the text width, and,
-	 * if it's too long, use gfx_text_find_pos() to find where
-	 * it should be cut off (and append some sort of overflow
-	 * marker.
-	 */
-	rc = gfx_set_clip_rect(gc, &rect);
-	if (rc != EOK)
-		return rc;
-
-	rc = gfx_puttext(&pos, &fmt, entry->name);
-	if (rc != EOK) {
-		(void) gfx_set_clip_rect(gc, NULL);
-		return rc;
-	}
-
-	return gfx_set_clip_rect(gc, NULL);
-}
-
-/** Paint panel.
- *
- * @param panel Panel
- */
-errno_t panel_paint(panel_t *panel)
-{
-	gfx_context_t *gc = ui_window_get_gc(panel->window);
-	ui_resource_t *res = ui_window_get_res(panel->window);
-	panel_entry_t *entry;
 	ui_box_style_t bstyle;
 	gfx_color_t *bcolor;
-	int i, lines;
+	ui_control_t *ctl;
 	errno_t rc;
 
@@ -263,16 +177,8 @@
 		return rc;
 
-	lines = panel_page_size(panel);
-	i = 0;
-
-	entry = panel->page;
-	while (entry != NULL && i < lines) {
-		rc = panel_entry_paint(entry, panel->page_idx + i);
-		if (rc != EOK)
-			return rc;
-
-		++i;
-		entry = panel_next(entry);
-	}
+	ctl = ui_file_list_ctl(panel->flist);
+	rc = ui_control_paint(ctl);
+	if (rc != EOK)
+		return rc;
 
 	rc = gfx_update(gc);
@@ -291,38 +197,11 @@
 ui_evclaim_t panel_kbd_event(panel_t *panel, kbd_event_t *event)
 {
+	ui_control_t *ctl;
+
 	if (!panel->active)
 		return ui_unclaimed;
 
-	if (event->type == KEY_PRESS) {
-		if ((event->mods & (KM_CTRL | KM_ALT | KM_SHIFT)) == 0) {
-			switch (event->key) {
-			case KC_UP:
-				panel_cursor_up(panel);
-				break;
-			case KC_DOWN:
-				panel_cursor_down(panel);
-				break;
-			case KC_HOME:
-				panel_cursor_top(panel);
-				break;
-			case KC_END:
-				panel_cursor_bottom(panel);
-				break;
-			case KC_PAGE_UP:
-				panel_page_up(panel);
-				break;
-			case KC_PAGE_DOWN:
-				panel_page_down(panel);
-				break;
-			case KC_ENTER:
-				panel_open(panel, panel->cursor);
-				break;
-			default:
-				break;
-			}
-		}
-	}
-
-	return ui_claimed;
+	ctl = ui_file_list_ctl(panel->flist);
+	return ui_control_kbd_event(ctl, event);
 }
 
@@ -336,8 +215,5 @@
 {
 	gfx_coord2_t pos;
-	gfx_rect_t irect;
-	panel_entry_t *entry;
-	size_t entry_idx;
-	int n;
+	ui_control_t *ctl;
 
 	pos.x = event->hpos;
@@ -349,68 +225,35 @@
 		panel_activate_req(panel);
 
+	ctl = ui_file_list_ctl(panel->flist);
+	return ui_control_pos_event(ctl, event);
+}
+
+/** Get base control for panel.
+ *
+ * @param panel Panel
+ * @return Base UI control
+ */
+ui_control_t *panel_ctl(panel_t *panel)
+{
+	return panel->control;
+}
+
+/** Set panel rectangle.
+ *
+ * @param panel Panel
+ * @param rect Rectangle
+ */
+void panel_set_rect(panel_t *panel, gfx_rect_t *rect)
+{
+	gfx_rect_t irect;
+
+	panel->rect = *rect;
+
 	irect.p0.x = panel->rect.p0.x + 1;
 	irect.p0.y = panel->rect.p0.y + 1;
-	irect.p1.x = panel->rect.p1.x - 1;
+	irect.p1.x = panel->rect.p1.x;
 	irect.p1.y = panel->rect.p1.y - 1;
 
-	if (event->type == POS_PRESS || event->type == POS_DCLICK) {
-		/* Did we click on one of the entries? */
-		if (gfx_pix_inside_rect(&pos, &irect)) {
-			/* Index within page */
-			n = pos.y - irect.p0.y;
-
-			/* Entry and its index within entire listing */
-			entry = panel_page_nth_entry(panel, n, &entry_idx);
-
-			if (event->type == POS_PRESS) {
-				/* Move to the entry found */
-				panel_cursor_move(panel, entry, entry_idx);
-			} else {
-				/* event->type == POS_DCLICK */
-				panel_open(panel, entry);
-			}
-		} else {
-			/* It's in the border. */
-			if (event->type == POS_PRESS) {
-				/* Top or bottom half? */
-				if (pos.y >= (irect.p0.y + irect.p1.y) / 2)
-					panel_page_down(panel);
-				else
-					panel_page_up(panel);
-			}
-		}
-	}
-
-	return ui_claimed;
-}
-
-/** Get base control for panel.
- *
- * @param panel Panel
- * @return Base UI control
- */
-ui_control_t *panel_ctl(panel_t *panel)
-{
-	return panel->control;
-}
-
-/** Set panel rectangle.
- *
- * @param panel Panel
- * @param rect Rectangle
- */
-void panel_set_rect(panel_t *panel, gfx_rect_t *rect)
-{
-	panel->rect = *rect;
-}
-
-/** Get panel page size.
- *
- * @param panel Panel
- * @return Number of entries that fit in panel at the same time.
- */
-unsigned panel_page_size(panel_t *panel)
-{
-	return panel->rect.p1.y - panel->rect.p0.y - 2;
+	ui_file_list_set_rect(panel->flist, &irect);
 }
 
@@ -435,9 +278,7 @@
 	errno_t rc;
 
-	if (panel->dir != NULL) {
-		rc = vfs_cwd_set(panel->dir);
-		if (rc != EOK)
-			return rc;
-	}
+	rc = ui_file_list_activate(panel->flist);
+	if (rc != EOK)
+		return rc;
 
 	panel->active = true;
@@ -452,15 +293,7 @@
 void panel_deactivate(panel_t *panel)
 {
+	ui_file_list_deactivate(panel->flist);
 	panel->active = false;
 	(void) panel_paint(panel);
-}
-
-/** Initialize panel entry attributes.
- *
- * @param attr Attributes
- */
-void panel_entry_attr_init(panel_entry_attr_t *attr)
-{
-	memset(attr, 0, sizeof(*attr));
 }
 
@@ -514,66 +347,4 @@
 }
 
-/** Append new panel entry.
- *
- * @param panel Panel
- * @param attr Entry attributes
- * @return EOK on success or an error code
- */
-errno_t panel_entry_append(panel_t *panel, panel_entry_attr_t *attr)
-{
-	panel_entry_t *entry;
-
-	entry = calloc(1, sizeof(panel_entry_t));
-	if (entry == NULL)
-		return ENOMEM;
-
-	entry->panel = panel;
-	entry->name = str_dup(attr->name);
-	if (entry->name == NULL) {
-		free(entry);
-		return ENOMEM;
-	}
-
-	entry->size = attr->size;
-	entry->isdir = attr->isdir;
-	entry->svc = attr->svc;
-	link_initialize(&entry->lentries);
-	list_append(&entry->lentries, &panel->entries);
-	++panel->entries_cnt;
-	return EOK;
-}
-
-/** Delete panel entry.
- *
- * @param entry Panel entry
- */
-void panel_entry_delete(panel_entry_t *entry)
-{
-	if (entry->panel->cursor == entry)
-		entry->panel->cursor = NULL;
-	if (entry->panel->page == entry)
-		entry->panel->page = NULL;
-
-	list_remove(&entry->lentries);
-	--entry->panel->entries_cnt;
-	free((char *) entry->name);
-	free(entry);
-}
-
-/** Clear panel entry list.
- *
- * @param panel Panel
- */
-void panel_clear_entries(panel_t *panel)
-{
-	panel_entry_t *entry;
-
-	entry = panel_first(panel);
-	while (entry != NULL) {
-		panel_entry_delete(entry);
-		entry = panel_first(panel);
-	}
-}
-
 /** Read directory into panel entry list.
  *
@@ -584,597 +355,29 @@
 errno_t panel_read_dir(panel_t *panel, const char *dirname)
 {
-	DIR *dir;
-	struct dirent *dirent;
-	vfs_stat_t finfo;
-	char newdir[256];
-	char *ndir = NULL;
-	panel_entry_attr_t attr;
-	panel_entry_t *next;
-	panel_entry_t *prev;
-	char *olddn;
-	size_t pg_size;
-	size_t max_idx;
-	size_t i;
-	errno_t rc;
-
-	rc = vfs_cwd_set(dirname);
-	if (rc != EOK)
-		return rc;
-
-	rc = vfs_cwd_get(newdir, sizeof(newdir));
-	if (rc != EOK)
-		return rc;
-
-	ndir = str_dup(newdir);
-	if (ndir == NULL)
-		return ENOMEM;
-
-	dir = opendir(".");
-	if (dir == NULL) {
-		rc = errno;
-		goto error;
-	}
-
-	if (str_cmp(ndir, "/") != 0) {
-		/* Need to add a synthetic up-dir entry */
-		panel_entry_attr_init(&attr);
-		attr.name = "..";
-		attr.isdir = true;
-
-		rc = panel_entry_append(panel, &attr);
-		if (rc != EOK)
-			goto error;
-	}
-
-	dirent = readdir(dir);
-	while (dirent != NULL) {
-		rc = vfs_stat_path(dirent->d_name, &finfo);
-		if (rc != EOK) {
-			/* Possibly a stale entry */
-			dirent = readdir(dir);
-			continue;
-		}
-
-		panel_entry_attr_init(&attr);
-		attr.name = dirent->d_name;
-		attr.size = finfo.size;
-		attr.isdir = finfo.is_directory;
-		attr.svc = finfo.service;
-
-		rc = panel_entry_append(panel, &attr);
-		if (rc != EOK)
-			goto error;
-
-		dirent = readdir(dir);
-	}
-
-	closedir(dir);
-
-	rc = panel_sort(panel);
-	if (rc != EOK)
-		goto error;
-
-	panel->cursor = panel_first(panel);
-	panel->cursor_idx = 0;
-	panel->page = panel_first(panel);
-	panel->page_idx = 0;
-
-	/* Moving up? */
-	if (str_cmp(dirname, "..") == 0) {
-		/* Get the last component of old path */
-		olddn = str_rchr(panel->dir, '/');
-		if (olddn != NULL && *olddn != '\0') {
-			/* Find corresponding entry */
-			++olddn;
-			next = panel_next(panel->cursor);
-			while (next != NULL && str_cmp(next->name, olddn) <= 0 &&
-			    next->isdir) {
-				panel->cursor = next;
-				++panel->cursor_idx;
-				next = panel_next(panel->cursor);
-			}
-
-			/* Move page so that cursor is in the center */
-			panel->page = panel->cursor;
-			panel->page_idx = panel->cursor_idx;
-
-			pg_size = panel_page_size(panel);
-
-			for (i = 0; i < pg_size / 2; i++) {
-				prev = panel_prev(panel->page);
-				if (prev == NULL)
-					break;
-
-				panel->page = prev;
-				--panel->page_idx;
-			}
-
-			/* Make sure page is not beyond the end if possible */
-			if (panel->entries_cnt > pg_size)
-				max_idx = panel->entries_cnt - pg_size;
-			else
-				max_idx = 0;
-
-			while (panel->page_idx > 0 && panel->page_idx > max_idx) {
-				prev = panel_prev(panel->page);
-				if (prev == NULL)
-					break;
-
-				panel->page = prev;
-				--panel->page_idx;
-			}
-		}
-	}
-
-	free(panel->dir);
-	panel->dir = ndir;
-
-	return EOK;
-error:
-	(void) vfs_cwd_set(panel->dir);
-	if (ndir != NULL)
-		free(ndir);
-	if (dir != NULL)
-		closedir(dir);
-	return rc;
-}
-
-/** Sort panel entries.
- *
- * @param panel Panel
- * @return EOK on success, ENOMEM if out of memory
- */
-errno_t panel_sort(panel_t *panel)
-{
-	panel_entry_t **emap;
-	panel_entry_t *entry;
-	size_t i;
-
-	/* Create an array to hold pointer to each entry */
-	emap = calloc(panel->entries_cnt, sizeof(panel_entry_t *));
-	if (emap == NULL)
-		return ENOMEM;
-
-	/* Write entry pointers to array */
-	entry = panel_first(panel);
-	i = 0;
-	while (entry != NULL) {
-		assert(i < panel->entries_cnt);
-		emap[i++] = entry;
-		entry = panel_next(entry);
-	}
-
-	/* Sort the array of pointers */
-	qsort(emap, panel->entries_cnt, sizeof(panel_entry_t *),
-	    panel_entry_ptr_cmp);
-
-	/* Unlink entries from entry list */
-	entry = panel_first(panel);
-	while (entry != NULL) {
-		list_remove(&entry->lentries);
-		entry = panel_first(panel);
-	}
-
-	/* Add entries back to entry list sorted */
-	for (i = 0; i < panel->entries_cnt; i++)
-		list_append(&emap[i]->lentries, &panel->entries);
-
-	free(emap);
-	return EOK;
-}
-
-/** Compare two panel entries indirectly referenced by pointers.
- *
- * @param pa Pointer to pointer to first entry
- * @param pb Pointer to pointer to second entry
- * @return <0, =0, >=0 if pa < b, pa == pb, pa > pb, resp.
- */
-int panel_entry_ptr_cmp(const void *pa, const void *pb)
-{
-	panel_entry_t *a = *(panel_entry_t **)pa;
-	panel_entry_t *b = *(panel_entry_t **)pb;
-	int dcmp;
-
-	/* Sort directories first */
-	dcmp = b->isdir - a->isdir;
-	if (dcmp != 0)
-		return dcmp;
-
-	return str_cmp(a->name, b->name);
-}
-
-/** Return first panel entry.
- *
- * @panel Panel
- * @return First panel entry or @c NULL if there are no entries
- */
-panel_entry_t *panel_first(panel_t *panel)
-{
-	link_t *link;
-
-	link = list_first(&panel->entries);
-	if (link == NULL)
-		return NULL;
-
-	return list_get_instance(link, panel_entry_t, lentries);
-}
-
-/** Return last panel entry.
- *
- * @panel Panel
- * @return Last panel entry or @c NULL if there are no entries
- */
-panel_entry_t *panel_last(panel_t *panel)
-{
-	link_t *link;
-
-	link = list_last(&panel->entries);
-	if (link == NULL)
-		return NULL;
-
-	return list_get_instance(link, panel_entry_t, lentries);
-}
-
-/** Return next panel entry.
- *
- * @param cur Current entry
- * @return Next entry or @c NULL if @a cur is the last entry
- */
-panel_entry_t *panel_next(panel_entry_t *cur)
-{
-	link_t *link;
-
-	link = list_next(&cur->lentries, &cur->panel->entries);
-	if (link == NULL)
-		return NULL;
-
-	return list_get_instance(link, panel_entry_t, lentries);
-}
-
-/** Return previous panel entry.
- *
- * @param cur Current entry
- * @return Previous entry or @c NULL if @a cur is the first entry
- */
-panel_entry_t *panel_prev(panel_entry_t *cur)
-{
-	link_t *link;
-
-	link = list_prev(&cur->lentries, &cur->panel->entries);
-	if (link == NULL)
-		return NULL;
-
-	return list_get_instance(link, panel_entry_t, lentries);
-}
-
-/** Find the n-th entry of the current panel page.
- *
- * If the page is short and has less than n+1 entries, return the last entry.
- *
- * @param panel Panel
- * @param n Which entry to get (starting from 0)
- * @param ridx Place to store index (within listing) of the entry
- * @return n-th entry of the page
- */
-panel_entry_t *panel_page_nth_entry(panel_t *panel, size_t n, size_t *ridx)
-{
-	panel_entry_t *entry;
-	panel_entry_t *next;
-	size_t i;
-	size_t idx;
-
-	assert(n < panel_page_size(panel));
-
-	entry = panel->page;
-	idx = panel->page_idx;
-	for (i = 0; i < n; i++) {
-		next = panel_next(entry);
-		if (next == NULL)
-			break;
-
-		entry = next;
-		++idx;
-	}
-
-	*ridx = idx;
-	return entry;
-}
-
-/** Move cursor to a new position, possibly scrolling.
- *
- * @param panel Panel
- * @param entry New entry under cursor
- * @param entry_idx Index of new entry under cursor
- */
-void panel_cursor_move(panel_t *panel, panel_entry_t *entry, size_t entry_idx)
-{
-	gfx_context_t *gc = ui_window_get_gc(panel->window);
-	panel_entry_t *old_cursor;
-	size_t old_idx;
-	size_t rows;
-	panel_entry_t *e;
-	size_t i;
-
-	rows = panel_page_size(panel);
-
-	old_cursor = panel->cursor;
-	old_idx = panel->cursor_idx;
-
-	panel->cursor = entry;
-	panel->cursor_idx = entry_idx;
-
-	if (entry_idx >= panel->page_idx &&
-	    entry_idx < panel->page_idx + rows) {
-		/*
-		 * If cursor is still on the current page, we're not
-		 * scrolling. Just unpaint old cursor and paint new
-		 * cursor.
-		 */
-		panel_entry_paint(old_cursor, old_idx);
-		panel_entry_paint(panel->cursor, panel->cursor_idx);
-
-		(void) gfx_update(gc);
-	} else {
-		/*
-		 * Need to scroll and update all rows.
-		 */
-
-		/* Scrolling up */
-		if (entry_idx < panel->page_idx) {
-			panel->page = entry;
-			panel->page_idx = entry_idx;
-		}
-
-		/* Scrolling down */
-		if (entry_idx >= panel->page_idx + rows) {
-			if (entry_idx >= rows) {
-				panel->page_idx = entry_idx - rows + 1;
-				/* Find first page entry (go back rows - 1) */
-				e = entry;
-				for (i = 0; i < rows - 1; i++) {
-					e = panel_prev(e);
-				}
-
-				/* Should be valid */
-				assert(e != NULL);
-				panel->page = e;
-			} else {
-				panel->page = panel_first(panel);
-				panel->page_idx = 0;
-			}
-		}
-
-		(void) panel_paint(panel);
-	}
-}
-
-/** Move cursor one entry up.
- *
- * @param panel Panel
- */
-void panel_cursor_up(panel_t *panel)
-{
-	panel_entry_t *prev;
-	size_t prev_idx;
-
-	prev = panel_prev(panel->cursor);
-	prev_idx = panel->cursor_idx - 1;
-	if (prev != NULL)
-		panel_cursor_move(panel, prev, prev_idx);
-}
-
-/** Move cursor one entry down.
- *
- * @param panel Panel
- */
-void panel_cursor_down(panel_t *panel)
-{
-	panel_entry_t *next;
-	size_t next_idx;
-
-	next = panel_next(panel->cursor);
-	next_idx = panel->cursor_idx + 1;
-	if (next != NULL)
-		panel_cursor_move(panel, next, next_idx);
-}
-
-/** Move cursor to top.
- *
- * @param panel Panel
- */
-void panel_cursor_top(panel_t *panel)
-{
-	panel_cursor_move(panel, panel_first(panel), 0);
-}
-
-/** Move cursor to bottom.
- *
- * @param panel Panel
- */
-void panel_cursor_bottom(panel_t *panel)
-{
-	panel_cursor_move(panel, panel_last(panel), panel->entries_cnt - 1);
-}
-
-/** Move one page up.
- *
- * @param panel Panel
- */
-void panel_page_up(panel_t *panel)
-{
-	gfx_context_t *gc = ui_window_get_gc(panel->window);
-	panel_entry_t *old_page;
-	panel_entry_t *old_cursor;
-	size_t old_idx;
-	size_t rows;
-	panel_entry_t *entry;
-	size_t i;
-
-	rows = panel_page_size(panel);
-
-	old_page = panel->page;
-	old_cursor = panel->cursor;
-	old_idx = panel->cursor_idx;
-
-	/* Move page by rows entries up (if possible) */
-	for (i = 0; i < rows; i++) {
-		entry = panel_prev(panel->page);
-		if (entry != NULL) {
-			panel->page = entry;
-			--panel->page_idx;
-		}
-	}
-
-	/* Move cursor by rows entries up (if possible) */
-
-	for (i = 0; i < rows; i++) {
-		entry = panel_prev(panel->cursor);
-		if (entry != NULL) {
-			panel->cursor = entry;
-			--panel->cursor_idx;
-		}
-	}
-
-	if (panel->page != old_page) {
-		/* We have scrolled. Need to repaint all entries */
-		(void) panel_paint(panel);
-	} else if (panel->cursor != old_cursor) {
-		/* No scrolling, but cursor has moved */
-		panel_entry_paint(old_cursor, old_idx);
-		panel_entry_paint(panel->cursor, panel->cursor_idx);
-
-		(void) gfx_update(gc);
-	}
-}
-
-/** Move one page down.
- *
- * @param panel Panel
- */
-void panel_page_down(panel_t *panel)
-{
-	gfx_context_t *gc = ui_window_get_gc(panel->window);
-	panel_entry_t *old_page;
-	panel_entry_t *old_cursor;
-	size_t old_idx;
-	size_t max_idx;
-	size_t rows;
-	panel_entry_t *entry;
-	size_t i;
-
-	rows = panel_page_size(panel);
-
-	old_page = panel->page;
-	old_cursor = panel->cursor;
-	old_idx = panel->cursor_idx;
-
-	if (panel->entries_cnt > rows)
-		max_idx = panel->entries_cnt - rows;
-	else
-		max_idx = 0;
-
-	/* Move page by rows entries down (if possible) */
-	for (i = 0; i < rows; i++) {
-		entry = panel_next(panel->page);
-		/* Do not scroll that results in a short page */
-		if (entry != NULL && panel->page_idx < max_idx) {
-			panel->page = entry;
-			++panel->page_idx;
-		}
-	}
-
-	/* Move cursor by rows entries down (if possible) */
-
-	for (i = 0; i < rows; i++) {
-		entry = panel_next(panel->cursor);
-		if (entry != NULL) {
-			panel->cursor = entry;
-			++panel->cursor_idx;
-		}
-	}
-
-	if (panel->page != old_page) {
-		/* We have scrolled. Need to repaint all entries */
-		(void) panel_paint(panel);
-	} else if (panel->cursor != old_cursor) {
-		/* No scrolling, but cursor has moved */
-		panel_entry_paint(old_cursor, old_idx);
-		panel_entry_paint(panel->cursor, panel->cursor_idx);
-
-		(void) gfx_update(gc);
-	}
-}
-
-/** Open panel entry.
- *
- * Perform Open action on a panel entry (e.g. switch to a subdirectory).
- *
- * @param panel Panel
- * @param entry Panel entry
+	return ui_file_list_read_dir(panel->flist, dirname);
+}
+
+/** Request panel activation.
+ *
+ * Call back to request panel activation.
+ *
+ * @param panel Panel
+ */
+void panel_activate_req(panel_t *panel)
+{
+	if (panel->cb != NULL && panel->cb->activate_req != NULL)
+		panel->cb->activate_req(panel->cb_arg, panel);
+}
+
+/** Open panel file entry.
+ *
+ * Perform Open action on a file entry (i.e. try running it).
+ *
+ * @param panel Panel
+ * @param fname File name
  *
  * @return EOK on success or an error code
  */
-errno_t panel_open(panel_t *panel, panel_entry_t *entry)
-{
-	if (entry->isdir)
-		return panel_open_dir(panel, entry);
-	else if (entry->svc == 0)
-		return panel_open_file(panel, entry);
-	else
-		return EOK;
-}
-
-/** Open panel directory entry.
- *
- * Perform Open action on a directory entry (i.e. switch to the directory).
- *
- * @param panel Panel
- * @param entry Panel entry (which is a directory)
- *
- * @return EOK on success or an error code
- */
-errno_t panel_open_dir(panel_t *panel, panel_entry_t *entry)
-{
-	gfx_context_t *gc = ui_window_get_gc(panel->window);
-	char *dirname;
-	errno_t rc;
-
-	assert(entry->isdir);
-
-	/*
-	 * Need to copy out name before we free the entry below
-	 * via panel_clear_entries().
-	 */
-	dirname = str_dup(entry->name);
-	if (dirname == NULL)
-		return ENOMEM;
-
-	panel_clear_entries(panel);
-
-	rc = panel_read_dir(panel, dirname);
-	if (rc != EOK) {
-		free(dirname);
-		return rc;
-	}
-
-	free(dirname);
-
-	rc = panel_paint(panel);
-	if (rc != EOK)
-		return rc;
-
-	return gfx_update(gc);
-}
-
-/** Open panel file entry.
- *
- * Perform Open action on a file entry (i.e. try running it).
- *
- * @param panel Panel
- * @param entry Panel entry (which is a file)
- *
- * @return EOK on success or an error code
- */
-errno_t panel_open_file(panel_t *panel, panel_entry_t *entry)
+static errno_t panel_open_file(panel_t *panel, const char *fname)
 {
 	task_id_t id;
@@ -1185,9 +388,4 @@
 	ui_t *ui;
 
-	/* It's not a directory */
-	assert(!entry->isdir);
-	/* It's not a service-special file */
-	assert(entry->svc == 0);
-
 	ui = ui_window_get_ui(panel->window);
 
@@ -1197,5 +395,5 @@
 		return rc;
 
-	rc = task_spawnl(&id, &wait, entry->name, entry->name, NULL);
+	rc = task_spawnl(&id, &wait, fname, fname, NULL);
 	if (rc != EOK)
 		goto error;
@@ -1218,14 +416,16 @@
 }
 
-/** Request panel activation.
- *
- * Call back to request panel activation.
- *
- * @param panel Panel
- */
-void panel_activate_req(panel_t *panel)
-{
-	if (panel->cb != NULL && panel->cb->activate_req != NULL)
-		panel->cb->activate_req(panel->cb_arg, panel);
+/** File in panel file list was selected.
+ *
+ * @param flist File list
+ * @param arg Argument (panel_t *)
+ * @param fname File name
+ */
+static void panel_flist_selected(ui_file_list_t *flist, void *arg,
+    const char *fname)
+{
+	panel_t *panel = (panel_t *)arg;
+
+	(void) panel_open_file(panel, fname);
 }
 
Index: uspace/app/nav/panel.h
===================================================================
--- uspace/app/nav/panel.h	(revision 453f96458238442bcb1a283b9281342c84c76a9d)
+++ uspace/app/nav/panel.h	(revision 54ddb59be14baa078affd298a0ddbf53724d7b29)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2021 Jiri Svoboda
+ * Copyright (c) 2022 Jiri Svoboda
  * All rights reserved.
  *
@@ -49,5 +49,4 @@
 extern void panel_destroy(panel_t *);
 extern void panel_set_cb(panel_t *, panel_cb_t *, void *);
-extern errno_t panel_entry_paint(panel_entry_t *, size_t);
 extern errno_t panel_paint(panel_t *);
 extern ui_evclaim_t panel_kbd_event(panel_t *, kbd_event_t *);
@@ -55,30 +54,8 @@
 extern ui_control_t *panel_ctl(panel_t *);
 extern void panel_set_rect(panel_t *, gfx_rect_t *);
-extern unsigned panel_page_size(panel_t *);
 extern bool panel_is_active(panel_t *);
 extern errno_t panel_activate(panel_t *);
 extern void panel_deactivate(panel_t *);
-extern void panel_entry_attr_init(panel_entry_attr_t *);
-extern errno_t panel_entry_append(panel_t *, panel_entry_attr_t *);
-extern void panel_entry_delete(panel_entry_t *);
-extern void panel_clear_entries(panel_t *);
 extern errno_t panel_read_dir(panel_t *, const char *);
-extern errno_t panel_sort(panel_t *);
-extern int panel_entry_ptr_cmp(const void *, const void *);
-extern panel_entry_t *panel_first(panel_t *);
-extern panel_entry_t *panel_last(panel_t *);
-extern panel_entry_t *panel_next(panel_entry_t *);
-extern panel_entry_t *panel_prev(panel_entry_t *);
-extern panel_entry_t *panel_page_nth_entry(panel_t *, size_t, size_t *);
-extern void panel_cursor_move(panel_t *, panel_entry_t *, size_t);
-extern void panel_cursor_up(panel_t *);
-extern void panel_cursor_down(panel_t *);
-extern void panel_cursor_top(panel_t *);
-extern void panel_cursor_bottom(panel_t *);
-extern void panel_page_up(panel_t *);
-extern void panel_page_down(panel_t *);
-extern errno_t panel_open(panel_t *, panel_entry_t *);
-extern errno_t panel_open_dir(panel_t *, panel_entry_t *);
-extern errno_t panel_open_file(panel_t *, panel_entry_t *);
 extern void panel_activate_req(panel_t *);
 
Index: uspace/app/nav/test/panel.c
===================================================================
--- uspace/app/nav/test/panel.c	(revision 453f96458238442bcb1a283b9281342c84c76a9d)
+++ uspace/app/nav/test/panel.c	(revision 54ddb59be14baa078affd298a0ddbf53724d7b29)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2021 Jiri Svoboda
+ * Copyright (c) 2022 Jiri Svoboda
  * All rights reserved.
  *
@@ -81,41 +81,4 @@
 }
 
-/** Test panel_entry_paint() */
-PCUT_TEST(entry_paint)
-{
-	ui_t *ui;
-	ui_window_t *window;
-	ui_wnd_params_t params;
-	panel_t *panel;
-	panel_entry_attr_t attr;
-	errno_t rc;
-
-	rc = ui_create_disp(NULL, &ui);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-
-	ui_wnd_params_init(&params);
-	params.caption = "Test";
-
-	rc = ui_window_create(ui, &params, &window);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-
-	rc = panel_create(window, true, &panel);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-
-	panel_entry_attr_init(&attr);
-	attr.name = "a";
-	attr.size = 1;
-
-	rc = panel_entry_append(panel, &attr);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-
-	rc = panel_entry_paint(panel_first(panel), 0);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-
-	panel_destroy(panel);
-	ui_window_destroy(window);
-	ui_destroy(ui);
-}
-
 /** Test panel_paint() */
 PCUT_TEST(paint)
@@ -205,91 +168,4 @@
 PCUT_TEST(pos_event)
 {
-	ui_t *ui;
-	ui_window_t *window;
-	ui_wnd_params_t params;
-	panel_t *panel;
-	ui_evclaim_t claimed;
-	pos_event_t event;
-	gfx_rect_t rect;
-	panel_entry_attr_t attr;
-	errno_t rc;
-
-	rc = ui_create_disp(NULL, &ui);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-
-	ui_wnd_params_init(&params);
-	params.caption = "Test";
-
-	rc = ui_window_create(ui, &params, &window);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-
-	rc = panel_create(window, true, &panel);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-
-	rect.p0.x = 0;
-	rect.p0.y = 0;
-	rect.p1.x = 10;
-	rect.p1.y = 10;
-	panel_set_rect(panel, &rect);
-
-	panel_entry_attr_init(&attr);
-	attr.name = "a";
-	attr.size = 1;
-	rc = panel_entry_append(panel, &attr);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-
-	attr.name = "b";
-	attr.size = 2;
-	rc = panel_entry_append(panel, &attr);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-
-	attr.name = "c";
-	attr.size = 3;
-	rc = panel_entry_append(panel, &attr);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-
-	panel->cursor = panel_first(panel);
-	panel->cursor_idx = 0;
-	panel->page = panel_first(panel);
-	panel->page_idx = 0;
-
-	event.pos_id = 0;
-	event.type = POS_PRESS;
-	event.btn_num = 1;
-
-	/* Clicking on the middle entry should select it */
-	event.hpos = 1;
-	event.vpos = 2;
-
-	claimed = panel_pos_event(panel, &event);
-	PCUT_ASSERT_EQUALS(ui_claimed, claimed);
-
-	PCUT_ASSERT_NOT_NULL(panel->cursor);
-	PCUT_ASSERT_STR_EQUALS("b", panel->cursor->name);
-	PCUT_ASSERT_INT_EQUALS(2, panel->cursor->size);
-
-	/* Clicking below the last entry should select it */
-	event.hpos = 1;
-	event.vpos = 4;
-	claimed = panel_pos_event(panel, &event);
-	PCUT_ASSERT_EQUALS(ui_claimed, claimed);
-
-	PCUT_ASSERT_NOT_NULL(panel->cursor);
-	PCUT_ASSERT_STR_EQUALS("c", panel->cursor->name);
-	PCUT_ASSERT_INT_EQUALS(3, panel->cursor->size);
-
-	/* Clicking on the top edge should do a page-up */
-	event.hpos = 1;
-	event.vpos = 0;
-	claimed = panel_pos_event(panel, &event);
-	PCUT_ASSERT_EQUALS(ui_claimed, claimed);
-
-	PCUT_ASSERT_NOT_NULL(panel->cursor);
-	PCUT_ASSERT_STR_EQUALS("a", panel->cursor->name);
-	PCUT_ASSERT_INT_EQUALS(1, panel->cursor->size);
-
-	panel_destroy(panel);
-	ui_window_destroy(window);
-	ui_destroy(ui);
 }
 
@@ -318,27 +194,4 @@
 }
 
-/** panel_page_size() returns correct size */
-PCUT_TEST(page_size)
-{
-	panel_t *panel;
-	gfx_rect_t rect;
-	errno_t rc;
-
-	rc = panel_create(NULL, true, &panel);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-
-	rect.p0.x = 10;
-	rect.p0.y = 20;
-	rect.p1.x = 30;
-	rect.p1.y = 40;
-
-	panel_set_rect(panel, &rect);
-
-	/* NOTE If page size changes, we have problems elsewhere in the tests */
-	PCUT_ASSERT_INT_EQUALS(18, panel_page_size(panel));
-
-	panel_destroy(panel);
-}
-
 /** panel_is_active() returns panel activity state */
 PCUT_TEST(is_active)
@@ -419,1161 +272,4 @@
 }
 
-/** panel_entry_append() appends new entry */
-PCUT_TEST(entry_append)
-{
-	panel_t *panel;
-	panel_entry_attr_t attr;
-	errno_t rc;
-
-	rc = panel_create(NULL, true, &panel);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-
-	panel_entry_attr_init(&attr);
-
-	attr.name = "a";
-	attr.size = 1;
-	rc = panel_entry_append(panel, &attr);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-
-	PCUT_ASSERT_INT_EQUALS(1, list_count(&panel->entries));
-
-	attr.name = "b";
-	attr.size = 2;
-	rc = panel_entry_append(panel, &attr);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-
-	PCUT_ASSERT_INT_EQUALS(2, list_count(&panel->entries));
-
-	panel_destroy(panel);
-}
-
-/** panel_entry_delete() deletes entry */
-PCUT_TEST(entry_delete)
-{
-	panel_t *panel;
-	panel_entry_t *entry;
-	panel_entry_attr_t attr;
-	errno_t rc;
-
-	rc = panel_create(NULL, true, &panel);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-
-	attr.name = "a";
-	attr.size = 1;
-	rc = panel_entry_append(panel, &attr);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-
-	attr.name = "b";
-	attr.size = 2;
-	rc = panel_entry_append(panel, &attr);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-
-	PCUT_ASSERT_INT_EQUALS(2, list_count(&panel->entries));
-
-	entry = panel_first(panel);
-	panel_entry_delete(entry);
-
-	PCUT_ASSERT_INT_EQUALS(1, list_count(&panel->entries));
-
-	entry = panel_first(panel);
-	panel_entry_delete(entry);
-
-	PCUT_ASSERT_INT_EQUALS(0, list_count(&panel->entries));
-
-	panel_destroy(panel);
-}
-
-/** panel_clear_entries() removes all entries from panel */
-PCUT_TEST(clear_entries)
-{
-	panel_t *panel;
-	panel_entry_attr_t attr;
-	errno_t rc;
-
-	rc = panel_create(NULL, true, &panel);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-
-	panel_entry_attr_init(&attr);
-	attr.name = "a";
-	attr.size = 1;
-	rc = panel_entry_append(panel, &attr);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-
-	attr.name = "a";
-	attr.size = 2;
-	rc = panel_entry_append(panel, &attr);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-
-	PCUT_ASSERT_INT_EQUALS(2, list_count(&panel->entries));
-
-	panel_clear_entries(panel);
-	PCUT_ASSERT_INT_EQUALS(0, list_count(&panel->entries));
-
-	panel_destroy(panel);
-}
-
-/** panel_read_dir() reads the contents of a directory */
-PCUT_TEST(read_dir)
-{
-	panel_t *panel;
-	panel_entry_t *entry;
-	char buf[L_tmpnam];
-	char *fname;
-	char *p;
-	errno_t rc;
-	FILE *f;
-	int rv;
-
-	/* Create name for temporary directory */
-	p = tmpnam(buf);
-	PCUT_ASSERT_NOT_NULL(p);
-
-	/* Create temporary directory */
-	rc = vfs_link_path(p, KIND_DIRECTORY, NULL);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-
-	rv = asprintf(&fname, "%s/%s", p, "a");
-	PCUT_ASSERT_TRUE(rv >= 0);
-
-	f = fopen(fname, "wb");
-	PCUT_ASSERT_NOT_NULL(f);
-
-	rv = fprintf(f, "X");
-	PCUT_ASSERT_TRUE(rv >= 0);
-
-	rv = fclose(f);
-	PCUT_ASSERT_INT_EQUALS(0, rv);
-
-	rc = panel_create(NULL, true, &panel);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-
-	rc = panel_read_dir(panel, p);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-
-	PCUT_ASSERT_INT_EQUALS(2, list_count(&panel->entries));
-
-	entry = panel_first(panel);
-	PCUT_ASSERT_NOT_NULL(entry);
-	PCUT_ASSERT_STR_EQUALS("..", entry->name);
-
-	entry = panel_next(entry);
-	PCUT_ASSERT_NOT_NULL(entry);
-	PCUT_ASSERT_STR_EQUALS("a", entry->name);
-	PCUT_ASSERT_INT_EQUALS(1, entry->size);
-
-	panel_destroy(panel);
-
-	rv = remove(fname);
-	PCUT_ASSERT_INT_EQUALS(0, rv);
-
-	rv = remove(p);
-	PCUT_ASSERT_INT_EQUALS(0, rv);
-
-	free(fname);
-}
-
-/** When moving to parent directory from a subdir, we seek to the
- * coresponding entry
- */
-PCUT_TEST(read_dir_up)
-{
-	panel_t *panel;
-	char buf[L_tmpnam];
-	char *subdir_a;
-	char *subdir_b;
-	char *subdir_c;
-	char *p;
-	errno_t rc;
-	int rv;
-
-	/* Create name for temporary directory */
-	p = tmpnam(buf);
-	PCUT_ASSERT_NOT_NULL(p);
-
-	/* Create temporary directory */
-	rc = vfs_link_path(p, KIND_DIRECTORY, NULL);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-
-	/* Create some subdirectories */
-
-	rv = asprintf(&subdir_a, "%s/%s", p, "a");
-	PCUT_ASSERT_TRUE(rv >= 0);
-	rc = vfs_link_path(subdir_a, KIND_DIRECTORY, NULL);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-
-	rv = asprintf(&subdir_b, "%s/%s", p, "b");
-	PCUT_ASSERT_TRUE(rv >= 0);
-	rc = vfs_link_path(subdir_b, KIND_DIRECTORY, NULL);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-
-	rv = asprintf(&subdir_c, "%s/%s", p, "c");
-	PCUT_ASSERT_TRUE(rv >= 0);
-	rc = vfs_link_path(subdir_c, KIND_DIRECTORY, NULL);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-
-	rc = panel_create(NULL, true, &panel);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-
-	/* Start in subdirectory "b" */
-	rc = panel_read_dir(panel, subdir_b);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-
-	/* Now go up (into p) */
-
-	rc = panel_read_dir(panel, "..");
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-
-	PCUT_ASSERT_NOT_NULL(panel->cursor);
-	PCUT_ASSERT_STR_EQUALS("b", panel->cursor->name);
-
-	panel_destroy(panel);
-
-	rv = remove(subdir_a);
-	PCUT_ASSERT_INT_EQUALS(0, rv);
-
-	rv = remove(subdir_b);
-	PCUT_ASSERT_INT_EQUALS(0, rv);
-
-	rv = remove(subdir_c);
-	PCUT_ASSERT_INT_EQUALS(0, rv);
-
-	rv = remove(p);
-	PCUT_ASSERT_INT_EQUALS(0, rv);
-
-	free(subdir_a);
-	free(subdir_b);
-	free(subdir_c);
-}
-
-/** panel_sort() sorts panel entries */
-PCUT_TEST(sort)
-{
-	panel_t *panel;
-	panel_entry_t *entry;
-	panel_entry_attr_t attr;
-	errno_t rc;
-
-	rc = panel_create(NULL, true, &panel);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-
-	panel_entry_attr_init(&attr);
-
-	attr.name = "b";
-	attr.size = 1;
-	rc = panel_entry_append(panel, &attr);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-
-	attr.name = "c";
-	attr.size = 3;
-	rc = panel_entry_append(panel, &attr);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-
-	attr.name = "a";
-	attr.size = 2;
-	rc = panel_entry_append(panel, &attr);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-
-	rc = panel_sort(panel);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-
-	entry = panel_first(panel);
-	PCUT_ASSERT_STR_EQUALS("a", entry->name);
-	PCUT_ASSERT_INT_EQUALS(2, entry->size);
-
-	entry = panel_next(entry);
-	PCUT_ASSERT_STR_EQUALS("b", entry->name);
-	PCUT_ASSERT_INT_EQUALS(1, entry->size);
-
-	entry = panel_next(entry);
-	PCUT_ASSERT_STR_EQUALS("c", entry->name);
-	PCUT_ASSERT_INT_EQUALS(3, entry->size);
-
-	panel_destroy(panel);
-}
-
-/** panel_entry_ptr_cmp compares two indirectly referenced entries */
-PCUT_TEST(entry_ptr_cmp)
-{
-	panel_t *panel;
-	panel_entry_t *a, *b;
-	panel_entry_attr_t attr;
-	int rel;
-	errno_t rc;
-
-	rc = panel_create(NULL, true, &panel);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-
-	panel_entry_attr_init(&attr);
-
-	attr.name = "a";
-	attr.size = 2;
-	rc = panel_entry_append(panel, &attr);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-
-	attr.name = "b";
-	attr.size = 1;
-	rc = panel_entry_append(panel, &attr);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-
-	a = panel_first(panel);
-	PCUT_ASSERT_NOT_NULL(a);
-	b = panel_next(a);
-	PCUT_ASSERT_NOT_NULL(b);
-
-	/* a < b */
-	rel = panel_entry_ptr_cmp(&a, &b);
-	PCUT_ASSERT_TRUE(rel < 0);
-
-	/* b > a */
-	rel = panel_entry_ptr_cmp(&b, &a);
-	PCUT_ASSERT_TRUE(rel > 0);
-
-	/* a == a */
-	rel = panel_entry_ptr_cmp(&a, &a);
-	PCUT_ASSERT_INT_EQUALS(0, rel);
-
-	panel_destroy(panel);
-}
-
-/** panel_first() returns valid entry or @c NULL as appropriate */
-PCUT_TEST(first)
-{
-	panel_t *panel;
-	panel_entry_t *entry;
-	panel_entry_attr_t attr;
-	errno_t rc;
-
-	rc = panel_create(NULL, true, &panel);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-
-	panel_entry_attr_init(&attr);
-
-	entry = panel_first(panel);
-	PCUT_ASSERT_NULL(entry);
-
-	/* Add one entry */
-	attr.name = "a";
-	attr.size = 1;
-	rc = panel_entry_append(panel, &attr);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-
-	/* Now try getting it */
-	entry = panel_first(panel);
-	PCUT_ASSERT_NOT_NULL(entry);
-	PCUT_ASSERT_STR_EQUALS("a", entry->name);
-	PCUT_ASSERT_INT_EQUALS(1, entry->size);
-
-	/* Add another entry */
-	attr.name = "b";
-	attr.size = 2;
-	rc = panel_entry_append(panel, &attr);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-
-	/* We should still get the first entry */
-	entry = panel_first(panel);
-	PCUT_ASSERT_NOT_NULL(entry);
-	PCUT_ASSERT_STR_EQUALS("a", entry->name);
-	PCUT_ASSERT_INT_EQUALS(1, entry->size);
-
-	panel_destroy(panel);
-}
-
-/** panel_last() returns valid entry or @c NULL as appropriate */
-PCUT_TEST(last)
-{
-	panel_t *panel;
-	panel_entry_t *entry;
-	panel_entry_attr_t attr;
-	errno_t rc;
-
-	rc = panel_create(NULL, true, &panel);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-
-	panel_entry_attr_init(&attr);
-
-	entry = panel_last(panel);
-	PCUT_ASSERT_NULL(entry);
-
-	/* Add one entry */
-	attr.name = "a";
-	attr.size = 1;
-	rc = panel_entry_append(panel, &attr);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-
-	/* Now try getting it */
-	entry = panel_last(panel);
-	PCUT_ASSERT_NOT_NULL(entry);
-	PCUT_ASSERT_STR_EQUALS("a", entry->name);
-	PCUT_ASSERT_INT_EQUALS(1, entry->size);
-
-	/* Add another entry */
-	attr.name = "b";
-	attr.size = 2;
-	rc = panel_entry_append(panel, &attr);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-
-	/* We should get new entry now */
-	entry = panel_last(panel);
-	PCUT_ASSERT_NOT_NULL(entry);
-	attr.name = "b";
-	attr.size = 2;
-	PCUT_ASSERT_STR_EQUALS("b", entry->name);
-	PCUT_ASSERT_INT_EQUALS(2, entry->size);
-
-	panel_destroy(panel);
-}
-
-/** panel_next() returns the next entry or @c NULL as appropriate */
-PCUT_TEST(next)
-{
-	panel_t *panel;
-	panel_entry_t *entry;
-	panel_entry_attr_t attr;
-	errno_t rc;
-
-	rc = panel_create(NULL, true, &panel);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-
-	panel_entry_attr_init(&attr);
-
-	/* Add one entry */
-	attr.name = "a";
-	attr.size = 1;
-	rc = panel_entry_append(panel, &attr);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-
-	/* Now try getting its successor */
-	entry = panel_first(panel);
-	PCUT_ASSERT_NOT_NULL(entry);
-
-	entry = panel_next(entry);
-	PCUT_ASSERT_NULL(entry);
-
-	/* Add another entry */
-	attr.name = "b";
-	attr.size = 2;
-	rc = panel_entry_append(panel, &attr);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-
-	/* Try getting the successor of first entry again */
-	entry = panel_first(panel);
-	PCUT_ASSERT_NOT_NULL(entry);
-
-	entry = panel_next(entry);
-	PCUT_ASSERT_NOT_NULL(entry);
-	PCUT_ASSERT_STR_EQUALS("b", entry->name);
-	PCUT_ASSERT_INT_EQUALS(2, entry->size);
-
-	panel_destroy(panel);
-}
-
-/** panel_prev() returns the previous entry or @c NULL as appropriate */
-PCUT_TEST(prev)
-{
-	panel_t *panel;
-	panel_entry_t *entry;
-	panel_entry_attr_t attr;
-	errno_t rc;
-
-	rc = panel_create(NULL, true, &panel);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-
-	panel_entry_attr_init(&attr);
-
-	/* Add one entry */
-	attr.name = "a";
-	attr.size = 1;
-	rc = panel_entry_append(panel, &attr);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-
-	/* Now try getting its predecessor */
-	entry = panel_last(panel);
-	PCUT_ASSERT_NOT_NULL(entry);
-
-	entry = panel_prev(entry);
-	PCUT_ASSERT_NULL(entry);
-
-	/* Add another entry */
-	attr.name = "b";
-	attr.size = 2;
-	rc = panel_entry_append(panel, &attr);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-
-	/* Try getting the predecessor of the new entry */
-	entry = panel_last(panel);
-	PCUT_ASSERT_NOT_NULL(entry);
-
-	entry = panel_prev(entry);
-	PCUT_ASSERT_NOT_NULL(entry);
-	PCUT_ASSERT_STR_EQUALS("a", entry->name);
-	PCUT_ASSERT_INT_EQUALS(1, entry->size);
-
-	panel_destroy(panel);
-}
-
-/** panel_page_nth_entry() .. */
-PCUT_TEST(page_nth_entry)
-{
-	panel_t *panel;
-	panel_entry_t *entry;
-	panel_entry_attr_t attr;
-	size_t idx;
-	errno_t rc;
-
-	rc = panel_create(NULL, true, &panel);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-
-	panel_entry_attr_init(&attr);
-
-	/* Add some entries */
-	attr.name = "a";
-	attr.size = 1;
-	rc = panel_entry_append(panel, &attr);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-
-	attr.name = "b";
-	attr.size = 2;
-	rc = panel_entry_append(panel, &attr);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-
-	attr.name = "c";
-	attr.size = 3;
-	rc = panel_entry_append(panel, &attr);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-
-	panel->page = panel_next(panel_first(panel));
-	panel->page_idx = 1;
-
-	entry = panel_page_nth_entry(panel, 0, &idx);
-	PCUT_ASSERT_STR_EQUALS("b", entry->name);
-	PCUT_ASSERT_INT_EQUALS(1, idx);
-
-	entry = panel_page_nth_entry(panel, 1, &idx);
-	PCUT_ASSERT_STR_EQUALS("c", entry->name);
-	PCUT_ASSERT_INT_EQUALS(2, idx);
-
-	entry = panel_page_nth_entry(panel, 2, &idx);
-	PCUT_ASSERT_STR_EQUALS("c", entry->name);
-	PCUT_ASSERT_INT_EQUALS(2, idx);
-
-	entry = panel_page_nth_entry(panel, 3, &idx);
-	PCUT_ASSERT_STR_EQUALS("c", entry->name);
-	PCUT_ASSERT_INT_EQUALS(2, idx);
-
-	panel_destroy(panel);
-}
-
-/** panel_cursor_move() ... */
-PCUT_TEST(cursor_move)
-{
-}
-
-/** panel_cursor_up() moves cursor one entry up */
-PCUT_TEST(cursor_up)
-{
-	ui_t *ui;
-	ui_window_t *window;
-	ui_wnd_params_t params;
-	panel_t *panel;
-	panel_entry_attr_t attr;
-	gfx_rect_t rect;
-	errno_t rc;
-
-	rc = ui_create_disp(NULL, &ui);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-
-	ui_wnd_params_init(&params);
-	params.caption = "Test";
-
-	rc = ui_window_create(ui, &params, &window);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-
-	rc = panel_create(window, true, &panel);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-
-	rect.p0.x = 0;
-	rect.p0.y = 0;
-	rect.p1.x = 10;
-	rect.p1.y = 4; // XXX Assuming this makes page size 2
-	panel_set_rect(panel, &rect);
-
-	PCUT_ASSERT_INT_EQUALS(2, panel_page_size(panel));
-
-	/* Add tree entries (more than page size, which is 2) */
-
-	panel_entry_attr_init(&attr);
-
-	attr.name = "a";
-	attr.size = 1;
-	rc = panel_entry_append(panel, &attr);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-
-	attr.name = "b";
-	attr.size = 2;
-	rc = panel_entry_append(panel, &attr);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-
-	attr.name = "c";
-	attr.size = 3;
-	rc = panel_entry_append(panel, &attr);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-
-	/* Cursor to the last entry and page start to the next-to-last entry */
-	panel->cursor = panel_last(panel);
-	panel->cursor_idx = 2;
-	panel->page = panel_prev(panel->cursor);
-	panel->page_idx = 1;
-
-	/* Move cursor one entry up */
-	panel_cursor_up(panel);
-
-	/* Cursor and page start should now both be at the second entry */
-	PCUT_ASSERT_STR_EQUALS("b", panel->cursor->name);
-	PCUT_ASSERT_INT_EQUALS(2, panel->cursor->size);
-	PCUT_ASSERT_INT_EQUALS(1, panel->cursor_idx);
-	PCUT_ASSERT_EQUALS(panel->cursor, panel->page);
-	PCUT_ASSERT_INT_EQUALS(1, panel->page_idx);
-
-	/* Move cursor one entry up. This should scroll up. */
-	panel_cursor_up(panel);
-
-	/* Cursor and page start should now both be at the first entry */
-	PCUT_ASSERT_STR_EQUALS("a", panel->cursor->name);
-	PCUT_ASSERT_INT_EQUALS(1, panel->cursor->size);
-	PCUT_ASSERT_INT_EQUALS(0, panel->cursor_idx);
-	PCUT_ASSERT_EQUALS(panel->cursor, panel->page);
-	PCUT_ASSERT_INT_EQUALS(0, panel->page_idx);
-
-	/* Moving further up should do nothing (we are at the top). */
-	panel_cursor_up(panel);
-
-	/* Cursor and page start should still be at the first entry */
-	PCUT_ASSERT_STR_EQUALS("a", panel->cursor->name);
-	PCUT_ASSERT_INT_EQUALS(1, panel->cursor->size);
-	PCUT_ASSERT_INT_EQUALS(0, panel->cursor_idx);
-	PCUT_ASSERT_EQUALS(panel->cursor, panel->page);
-	PCUT_ASSERT_INT_EQUALS(0, panel->page_idx);
-
-	panel_destroy(panel);
-	ui_window_destroy(window);
-	ui_destroy(ui);
-}
-
-/** panel_cursor_down() moves cursor one entry down */
-PCUT_TEST(cursor_down)
-{
-	ui_t *ui;
-	ui_window_t *window;
-	ui_wnd_params_t params;
-	panel_t *panel;
-	panel_entry_attr_t attr;
-	gfx_rect_t rect;
-	errno_t rc;
-
-	rc = ui_create_disp(NULL, &ui);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-
-	ui_wnd_params_init(&params);
-	params.caption = "Test";
-
-	rc = ui_window_create(ui, &params, &window);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-
-	rc = panel_create(window, true, &panel);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-
-	rect.p0.x = 0;
-	rect.p0.y = 0;
-	rect.p1.x = 10;
-	rect.p1.y = 4; // XXX Assuming this makes page size 2
-	panel_set_rect(panel, &rect);
-
-	PCUT_ASSERT_INT_EQUALS(2, panel_page_size(panel));
-
-	/* Add tree entries (more than page size, which is 2) */
-
-	panel_entry_attr_init(&attr);
-
-	attr.name = "a";
-	attr.size = 1;
-	rc = panel_entry_append(panel, &attr);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-
-	attr.name = "b";
-	attr.size = 2;
-	rc = panel_entry_append(panel, &attr);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-
-	attr.name = "c";
-	attr.size = 3;
-	rc = panel_entry_append(panel, &attr);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-
-	/* Cursor and page start to the first entry */
-	panel->cursor = panel_first(panel);
-	panel->cursor_idx = 0;
-	panel->page = panel->cursor;
-	panel->page_idx = 0;
-
-	/* Move cursor one entry down */
-	panel_cursor_down(panel);
-
-	/* Cursor should now be at the second entry, page stays the same */
-	PCUT_ASSERT_STR_EQUALS("b", panel->cursor->name);
-	PCUT_ASSERT_INT_EQUALS(2, panel->cursor->size);
-	PCUT_ASSERT_INT_EQUALS(1, panel->cursor_idx);
-	PCUT_ASSERT_EQUALS(panel_first(panel), panel->page);
-	PCUT_ASSERT_INT_EQUALS(0, panel->page_idx);
-
-	/* Move cursor one entry down. This should scroll down. */
-	panel_cursor_down(panel);
-
-	/* Cursor should now be at the third and page at the second entry. */
-	PCUT_ASSERT_STR_EQUALS("c", panel->cursor->name);
-	PCUT_ASSERT_INT_EQUALS(3, panel->cursor->size);
-	PCUT_ASSERT_INT_EQUALS(2, panel->cursor_idx);
-	PCUT_ASSERT_STR_EQUALS("b", panel->page->name);
-	PCUT_ASSERT_INT_EQUALS(2, panel->page->size);
-	PCUT_ASSERT_INT_EQUALS(1, panel->page_idx);
-
-	/* Moving further down should do nothing (we are at the bottom). */
-	panel_cursor_down(panel);
-
-	/* Cursor should still be at the third and page at the second entry. */
-	PCUT_ASSERT_STR_EQUALS("c", panel->cursor->name);
-	PCUT_ASSERT_INT_EQUALS(3, panel->cursor->size);
-	PCUT_ASSERT_INT_EQUALS(2, panel->cursor_idx);
-	PCUT_ASSERT_STR_EQUALS("b", panel->page->name);
-	PCUT_ASSERT_INT_EQUALS(2, panel->page->size);
-	PCUT_ASSERT_INT_EQUALS(1, panel->page_idx);
-
-	panel_destroy(panel);
-	ui_window_destroy(window);
-	ui_destroy(ui);
-}
-
-/** panel_cursor_top() moves cursor to the first entry */
-PCUT_TEST(cursor_top)
-{
-	ui_t *ui;
-	ui_window_t *window;
-	ui_wnd_params_t params;
-	panel_t *panel;
-	panel_entry_attr_t attr;
-	gfx_rect_t rect;
-	errno_t rc;
-
-	rc = ui_create_disp(NULL, &ui);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-
-	ui_wnd_params_init(&params);
-	params.caption = "Test";
-
-	rc = ui_window_create(ui, &params, &window);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-
-	rc = panel_create(window, true, &panel);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-
-	rect.p0.x = 0;
-	rect.p0.y = 0;
-	rect.p1.x = 10;
-	rect.p1.y = 4; // XXX Assuming this makes page size 2
-	panel_set_rect(panel, &rect);
-
-	PCUT_ASSERT_INT_EQUALS(2, panel_page_size(panel));
-
-	/* Add tree entries (more than page size, which is 2) */
-
-	panel_entry_attr_init(&attr);
-
-	attr.name = "a";
-	attr.size = 1;
-	rc = panel_entry_append(panel, &attr);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-
-	attr.name = "b";
-	attr.size = 2;
-	rc = panel_entry_append(panel, &attr);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-
-	attr.name = "c";
-	attr.size = 3;
-	rc = panel_entry_append(panel, &attr);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-
-	/* Cursor to the last entry and page start to the next-to-last entry */
-	panel->cursor = panel_last(panel);
-	panel->cursor_idx = 2;
-	panel->page = panel_prev(panel->cursor);
-	panel->page_idx = 1;
-
-	/* Move cursor to the top. This should scroll up. */
-	panel_cursor_top(panel);
-
-	/* Cursor and page start should now both be at the first entry */
-	PCUT_ASSERT_STR_EQUALS("a", panel->cursor->name);
-	PCUT_ASSERT_INT_EQUALS(1, panel->cursor->size);
-	PCUT_ASSERT_INT_EQUALS(0, panel->cursor_idx);
-	PCUT_ASSERT_EQUALS(panel->cursor, panel->page);
-	PCUT_ASSERT_INT_EQUALS(0, panel->page_idx);
-
-	panel_destroy(panel);
-	ui_window_destroy(window);
-	ui_destroy(ui);
-}
-
-/** panel_cursor_bottom() moves cursor to the last entry */
-PCUT_TEST(cursor_bottom)
-{
-	ui_t *ui;
-	ui_window_t *window;
-	ui_wnd_params_t params;
-	panel_t *panel;
-	panel_entry_attr_t attr;
-	gfx_rect_t rect;
-	errno_t rc;
-
-	rc = ui_create_disp(NULL, &ui);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-
-	ui_wnd_params_init(&params);
-	params.caption = "Test";
-
-	rc = ui_window_create(ui, &params, &window);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-
-	rc = panel_create(window, true, &panel);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-
-	rect.p0.x = 0;
-	rect.p0.y = 0;
-	rect.p1.x = 10;
-	rect.p1.y = 4; // XXX Assuming this makes page size 2
-	panel_set_rect(panel, &rect);
-
-	PCUT_ASSERT_INT_EQUALS(2, panel_page_size(panel));
-
-	/* Add tree entries (more than page size, which is 2) */
-
-	panel_entry_attr_init(&attr);
-
-	attr.name = "a";
-	attr.size = 1;
-	rc = panel_entry_append(panel, &attr);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-
-	attr.name = "b";
-	attr.size = 2;
-	rc = panel_entry_append(panel, &attr);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-
-	attr.name = "c";
-	attr.size = 3;
-	rc = panel_entry_append(panel, &attr);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-
-	/* Cursor and page start to the first entry */
-	panel->cursor = panel_first(panel);
-	panel->cursor_idx = 0;
-	panel->page = panel->cursor;
-	panel->page_idx = 0;
-
-	/* Move cursor to the bottom. This should scroll down. */
-	panel_cursor_bottom(panel);
-
-	/* Cursor should now be at the third and page at the second entry. */
-	PCUT_ASSERT_STR_EQUALS("c", panel->cursor->name);
-	PCUT_ASSERT_INT_EQUALS(3, panel->cursor->size);
-	PCUT_ASSERT_INT_EQUALS(2, panel->cursor_idx);
-	PCUT_ASSERT_STR_EQUALS("b", panel->page->name);
-	PCUT_ASSERT_INT_EQUALS(2, panel->page->size);
-	PCUT_ASSERT_INT_EQUALS(1, panel->page_idx);
-
-	panel_destroy(panel);
-	ui_window_destroy(window);
-	ui_destroy(ui);
-}
-
-/** panel_page_up() moves one page up */
-PCUT_TEST(page_up)
-{
-	ui_t *ui;
-	ui_window_t *window;
-	ui_wnd_params_t params;
-	panel_t *panel;
-	panel_entry_attr_t attr;
-	gfx_rect_t rect;
-	errno_t rc;
-
-	rc = ui_create_disp(NULL, &ui);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-
-	ui_wnd_params_init(&params);
-	params.caption = "Test";
-
-	rc = ui_window_create(ui, &params, &window);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-
-	rc = panel_create(window, true, &panel);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-
-	rect.p0.x = 0;
-	rect.p0.y = 0;
-	rect.p1.x = 10;
-	rect.p1.y = 4; // XXX Assuming this makes page size 2
-	panel_set_rect(panel, &rect);
-
-	PCUT_ASSERT_INT_EQUALS(2, panel_page_size(panel));
-
-	/* Add five entries (2 full pages, one partial) */
-
-	panel_entry_attr_init(&attr);
-
-	attr.name = "a";
-	attr.size = 1;
-	rc = panel_entry_append(panel, &attr);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-
-	attr.name = "b";
-	attr.size = 2;
-	rc = panel_entry_append(panel, &attr);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-
-	attr.name = "c";
-	attr.size = 3;
-	rc = panel_entry_append(panel, &attr);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-
-	attr.name = "d";
-	attr.size = 4;
-	rc = panel_entry_append(panel, &attr);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-
-	attr.name = "e";
-	attr.size = 5;
-	rc = panel_entry_append(panel, &attr);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-
-	/* Cursor to the last entry and page start to the next-to-last entry */
-	panel->cursor = panel_last(panel);
-	panel->cursor_idx = 4;
-	panel->page = panel_prev(panel->cursor);
-	panel->page_idx = 3;
-
-	/* Move one page up */
-	panel_page_up(panel);
-
-	/* Page should now start at second entry and cursor at third */
-	PCUT_ASSERT_STR_EQUALS("c", panel->cursor->name);
-	PCUT_ASSERT_INT_EQUALS(3, panel->cursor->size);
-	PCUT_ASSERT_INT_EQUALS(2, panel->cursor_idx);
-	PCUT_ASSERT_STR_EQUALS("b", panel->page->name);
-	PCUT_ASSERT_INT_EQUALS(2, panel->page->size);
-	PCUT_ASSERT_INT_EQUALS(1, panel->page_idx);
-
-	/* Move one page up again. */
-	panel_page_up(panel);
-
-	/* Cursor and page start should now both be at the first entry */
-	PCUT_ASSERT_STR_EQUALS("a", panel->cursor->name);
-	PCUT_ASSERT_INT_EQUALS(1, panel->cursor->size);
-	PCUT_ASSERT_INT_EQUALS(0, panel->cursor_idx);
-	PCUT_ASSERT_EQUALS(panel->cursor, panel->page);
-	PCUT_ASSERT_INT_EQUALS(0, panel->page_idx);
-
-	/* Moving further up should do nothing (we are at the top). */
-	panel_page_up(panel);
-
-	/* Cursor and page start should still be at the first entry */
-	PCUT_ASSERT_STR_EQUALS("a", panel->cursor->name);
-	PCUT_ASSERT_INT_EQUALS(1, panel->cursor->size);
-	PCUT_ASSERT_INT_EQUALS(0, panel->cursor_idx);
-	PCUT_ASSERT_EQUALS(panel->cursor, panel->page);
-	PCUT_ASSERT_INT_EQUALS(0, panel->page_idx);
-
-	panel_destroy(panel);
-	ui_window_destroy(window);
-	ui_destroy(ui);
-}
-
-/** panel_page_up() moves one page down */
-PCUT_TEST(page_down)
-{
-	ui_t *ui;
-	ui_window_t *window;
-	ui_wnd_params_t params;
-	panel_t *panel;
-	panel_entry_attr_t attr;
-	gfx_rect_t rect;
-	errno_t rc;
-
-	rc = ui_create_disp(NULL, &ui);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-
-	ui_wnd_params_init(&params);
-	params.caption = "Test";
-
-	rc = ui_window_create(ui, &params, &window);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-
-	rc = panel_create(window, true, &panel);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-
-	rect.p0.x = 0;
-	rect.p0.y = 0;
-	rect.p1.x = 10;
-	rect.p1.y = 4; // XXX Assuming this makes page size 2
-	panel_set_rect(panel, &rect);
-
-	PCUT_ASSERT_INT_EQUALS(2, panel_page_size(panel));
-
-	/* Add five entries (2 full pages, one partial) */
-
-	panel_entry_attr_init(&attr);
-
-	attr.name = "a";
-	attr.size = 1;
-	rc = panel_entry_append(panel, &attr);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-
-	attr.name = "b";
-	attr.size = 2;
-	rc = panel_entry_append(panel, &attr);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-
-	attr.name = "c";
-	attr.size = 3;
-	rc = panel_entry_append(panel, &attr);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-
-	attr.name = "d";
-	attr.size = 4;
-	rc = panel_entry_append(panel, &attr);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-
-	attr.name = "e";
-	attr.size = 5;
-	rc = panel_entry_append(panel, &attr);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-
-	/* Cursor and page to the first entry */
-	panel->cursor = panel_first(panel);
-	panel->cursor_idx = 0;
-	panel->page = panel->cursor;
-	panel->page_idx = 0;
-
-	/* Move one page down */
-	panel_page_down(panel);
-
-	/* Page and cursor should point to the third entry */
-	PCUT_ASSERT_STR_EQUALS("c", panel->cursor->name);
-	PCUT_ASSERT_INT_EQUALS(3, panel->cursor->size);
-	PCUT_ASSERT_INT_EQUALS(2, panel->cursor_idx);
-	PCUT_ASSERT_STR_EQUALS("c", panel->page->name);
-	PCUT_ASSERT_INT_EQUALS(3, panel->page->size);
-	PCUT_ASSERT_INT_EQUALS(2, panel->page_idx);
-
-	/* Move one page down again. */
-	panel_page_down(panel);
-
-	/* Cursor should point to last and page to next-to-last entry */
-	PCUT_ASSERT_STR_EQUALS("e", panel->cursor->name);
-	PCUT_ASSERT_INT_EQUALS(5, panel->cursor->size);
-	PCUT_ASSERT_INT_EQUALS(4, panel->cursor_idx);
-	PCUT_ASSERT_STR_EQUALS("d", panel->page->name);
-	PCUT_ASSERT_INT_EQUALS(4, panel->page->size);
-	PCUT_ASSERT_INT_EQUALS(3, panel->page_idx);
-
-	/* Moving further down should do nothing (we are at the bottom). */
-	panel_page_down(panel);
-
-	/* Cursor should still point to last and page to next-to-last entry */
-	PCUT_ASSERT_STR_EQUALS("e", panel->cursor->name);
-	PCUT_ASSERT_INT_EQUALS(5, panel->cursor->size);
-	PCUT_ASSERT_INT_EQUALS(4, panel->cursor_idx);
-	PCUT_ASSERT_STR_EQUALS("d", panel->page->name);
-	PCUT_ASSERT_INT_EQUALS(4, panel->page->size);
-	PCUT_ASSERT_INT_EQUALS(3, panel->page_idx);
-
-	panel_destroy(panel);
-	ui_window_destroy(window);
-	ui_destroy(ui);
-}
-
-/** panel_open() opens a directory entry */
-PCUT_TEST(open)
-{
-	ui_t *ui;
-	ui_window_t *window;
-	ui_wnd_params_t params;
-	panel_t *panel;
-	panel_entry_t *entry;
-	char buf[L_tmpnam];
-	char *sdname;
-	char *p;
-	errno_t rc;
-	int rv;
-
-	rc = ui_create_disp(NULL, &ui);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-
-	ui_wnd_params_init(&params);
-	params.caption = "Test";
-
-	rc = ui_window_create(ui, &params, &window);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-
-	/* Create name for temporary directory */
-	p = tmpnam(buf);
-	PCUT_ASSERT_NOT_NULL(p);
-
-	/* Create temporary directory */
-	rc = vfs_link_path(p, KIND_DIRECTORY, NULL);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-
-	rv = asprintf(&sdname, "%s/%s", p, "a");
-	PCUT_ASSERT_TRUE(rv >= 0);
-
-	/* Create sub-directory */
-	rc = vfs_link_path(sdname, KIND_DIRECTORY, NULL);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-
-	rc = panel_create(window, true, &panel);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-
-	rc = panel_read_dir(panel, p);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-	PCUT_ASSERT_STR_EQUALS(p, panel->dir);
-
-	PCUT_ASSERT_INT_EQUALS(2, list_count(&panel->entries));
-
-	entry = panel_first(panel);
-	PCUT_ASSERT_NOT_NULL(entry);
-	PCUT_ASSERT_STR_EQUALS("..", entry->name);
-
-	entry = panel_next(entry);
-	PCUT_ASSERT_NOT_NULL(entry);
-	PCUT_ASSERT_STR_EQUALS("a", entry->name);
-	PCUT_ASSERT_TRUE(entry->isdir);
-
-	rc = panel_open(panel, entry);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-
-	PCUT_ASSERT_STR_EQUALS(sdname, panel->dir);
-
-	panel_destroy(panel);
-	ui_window_destroy(window);
-	ui_destroy(ui);
-
-	rv = remove(sdname);
-	PCUT_ASSERT_INT_EQUALS(0, rv);
-
-	rv = remove(p);
-	PCUT_ASSERT_INT_EQUALS(0, rv);
-
-	free(sdname);
-}
-
 /** panel_activate_req() sends activation request */
 PCUT_TEST(activate_req)
Index: uspace/app/nav/types/panel.h
===================================================================
--- uspace/app/nav/types/panel.h	(revision 453f96458238442bcb1a283b9281342c84c76a9d)
+++ uspace/app/nav/types/panel.h	(revision 54ddb59be14baa078affd298a0ddbf53724d7b29)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2021 Jiri Svoboda
+ * Copyright (c) 2022 Jiri Svoboda
  * All rights reserved.
  *
@@ -37,38 +37,9 @@
 #define TYPES_PANEL_H
 
-#include <adt/list.h>
 #include <gfx/color.h>
 #include <gfx/coord.h>
-#include <ipc/loc.h>
+#include <ui/filelist.h>
 #include <ui/window.h>
 #include <stdint.h>
-
-/** Panel entry attributes */
-typedef struct {
-	/** File name */
-	const char *name;
-	/** File size */
-	uint64_t size;
-	/** @c true iff entry is a directory */
-	bool isdir;
-	/** Service number for service special entries */
-	service_id_t svc;
-} panel_entry_attr_t;
-
-/** Panel entry */
-typedef struct {
-	/** Containing panel */
-	struct panel *panel;
-	/** Link to @c panel->entries */
-	link_t lentries;
-	/** File name */
-	char *name;
-	/** File size */
-	uint64_t size;
-	/** @c true iff entry is a directory */
-	bool isdir;
-	/** Service number for service special entries */
-	service_id_t svc;
-} panel_entry_t;
 
 /** Navigator panel
@@ -95,39 +66,12 @@
 	gfx_color_t *color;
 
-	/** Panel cursor color */
-	gfx_color_t *curs_color;
-
 	/** Active border color */
 	gfx_color_t *act_border_color;
-
-	/** Directory-type entry color */
-	gfx_color_t *dir_color;
-
-	/** Service-type entry color */
-	gfx_color_t *svc_color;
-
-	/** Panel entries (list of panel_entry_t) */
-	list_t entries;
-
-	/** Number of entries */
-	size_t entries_cnt;
-
-	/** First entry of current page */
-	panel_entry_t *page;
-
-	/** Index of first entry of current page */
-	size_t page_idx;
-
-	/** Cursor position */
-	panel_entry_t *cursor;
-
-	/** Index of entry under cursor */
-	size_t cursor_idx;
 
 	/** @c true iff the panel is active */
 	bool active;
 
-	/** Directory */
-	char *dir;
+	/** File list */
+	ui_file_list_t *flist;
 } panel_t;
 
Index: uspace/lib/ui/include/ui/filelist.h
===================================================================
--- uspace/lib/ui/include/ui/filelist.h	(revision 453f96458238442bcb1a283b9281342c84c76a9d)
+++ uspace/lib/ui/include/ui/filelist.h	(revision 54ddb59be14baa078affd298a0ddbf53724d7b29)
@@ -54,4 +54,6 @@
 extern errno_t ui_file_list_activate(ui_file_list_t *);
 extern void ui_file_list_deactivate(ui_file_list_t *);
+extern errno_t ui_file_list_open(ui_file_list_t *, ui_file_list_entry_t *);
+extern ui_file_list_entry_t *ui_file_list_get_cursor(ui_file_list_t *);
 
 #endif
Index: uspace/lib/ui/private/filelist.h
===================================================================
--- uspace/lib/ui/private/filelist.h	(revision 453f96458238442bcb1a283b9281342c84c76a9d)
+++ uspace/lib/ui/private/filelist.h	(revision 54ddb59be14baa078affd298a0ddbf53724d7b29)
@@ -164,5 +164,4 @@
 extern void ui_file_list_scroll_page_down(ui_file_list_t *);
 extern void ui_file_list_scroll_pos(ui_file_list_t *, size_t);
-extern errno_t ui_file_list_open(ui_file_list_t *, ui_file_list_entry_t *);
 extern errno_t ui_file_list_open_dir(ui_file_list_t *, ui_file_list_entry_t *);
 extern errno_t ui_file_list_open_file(ui_file_list_t *, ui_file_list_entry_t *);
Index: uspace/lib/ui/src/filelist.c
===================================================================
--- uspace/lib/ui/src/filelist.c	(revision 453f96458238442bcb1a283b9281342c84c76a9d)
+++ uspace/lib/ui/src/filelist.c	(revision 54ddb59be14baa078affd298a0ddbf53724d7b29)
@@ -1079,4 +1079,14 @@
 }
 
+/** Get entry under cursor.
+ *
+ * @param flist File list
+ * @return Current cursor
+ */
+ui_file_list_entry_t *ui_file_list_get_cursor(ui_file_list_t *flist)
+{
+	return flist->cursor;
+}
+
 /** Move cursor to a new position, possibly scrolling.
  *
