Index: uspace/lib/ui/include/ui/menu.h
===================================================================
--- uspace/lib/ui/include/ui/menu.h	(revision 9754ed27d13eaf9da59abb744c63d0e3f5ab5ea2)
+++ uspace/lib/ui/include/ui/menu.h	(revision 59768c77459332a9e19b4be9e1a0aa6659f1b7c9)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2021 Jiri Svoboda
+ * Copyright (c) 2022 Jiri Svoboda
  * All rights reserved.
  *
@@ -40,4 +40,5 @@
 #include <gfx/coord.h>
 #include <io/pos_event.h>
+#include <stdbool.h>
 #include <types/ui/menu.h>
 #include <types/ui/menubar.h>
@@ -48,8 +49,11 @@
 extern ui_menu_t *ui_menu_first(ui_menu_bar_t *);
 extern ui_menu_t *ui_menu_next(ui_menu_t *);
+extern ui_menu_t *ui_menu_last(ui_menu_bar_t *);
+extern ui_menu_t *ui_menu_prev(ui_menu_t *);
 extern const char *ui_menu_caption(ui_menu_t *);
 extern void ui_menu_get_rect(ui_menu_t *, gfx_coord2_t *, gfx_rect_t *);
 extern errno_t ui_menu_open(ui_menu_t *, gfx_rect_t *);
 extern void ui_menu_close(ui_menu_t *);
+extern bool ui_menu_is_open(ui_menu_t *);
 extern errno_t ui_menu_paint(ui_menu_t *, gfx_coord2_t *);
 extern ui_evclaim_t ui_menu_pos_event(ui_menu_t *, gfx_coord2_t *,
Index: uspace/lib/ui/include/ui/menubar.h
===================================================================
--- uspace/lib/ui/include/ui/menubar.h	(revision 9754ed27d13eaf9da59abb744c63d0e3f5ab5ea2)
+++ uspace/lib/ui/include/ui/menubar.h	(revision 59768c77459332a9e19b4be9e1a0aa6659f1b7c9)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2021 Jiri Svoboda
+ * Copyright (c) 2022 Jiri Svoboda
  * All rights reserved.
  *
@@ -53,6 +53,9 @@
 extern void ui_menu_bar_set_rect(ui_menu_bar_t *, gfx_rect_t *);
 extern errno_t ui_menu_bar_paint(ui_menu_bar_t *);
+extern ui_evclaim_t ui_menu_bar_kbd_event(ui_menu_bar_t *, kbd_event_t *);
 extern ui_evclaim_t ui_menu_bar_pos_event(ui_menu_bar_t *, pos_event_t *);
 extern void ui_menu_bar_unfocus(ui_menu_bar_t *);
+extern void ui_menu_bar_activate(ui_menu_bar_t *);
+extern void ui_menu_bar_deactivate(ui_menu_bar_t *);
 
 #endif
Index: uspace/lib/ui/include/ui/menuentry.h
===================================================================
--- uspace/lib/ui/include/ui/menuentry.h	(revision 9754ed27d13eaf9da59abb744c63d0e3f5ab5ea2)
+++ uspace/lib/ui/include/ui/menuentry.h	(revision 59768c77459332a9e19b4be9e1a0aa6659f1b7c9)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2021 Jiri Svoboda
+ * Copyright (c) 2022 Jiri Svoboda
  * All rights reserved.
  *
@@ -40,4 +40,5 @@
 #include <gfx/coord.h>
 #include <io/pos_event.h>
+#include <stdbool.h>
 #include <types/ui/menu.h>
 #include <types/ui/menuentry.h>
@@ -51,5 +52,7 @@
     void *);
 extern ui_menu_entry_t *ui_menu_entry_first(ui_menu_t *);
+extern ui_menu_entry_t *ui_menu_entry_last(ui_menu_t *);
 extern ui_menu_entry_t *ui_menu_entry_next(ui_menu_entry_t *);
+extern ui_menu_entry_t *ui_menu_entry_prev(ui_menu_entry_t *);
 extern gfx_coord_t ui_menu_entry_calc_width(ui_menu_t *,
     gfx_coord_t, gfx_coord_t);
@@ -58,8 +61,10 @@
 extern gfx_coord_t ui_menu_entry_height(ui_menu_entry_t *);
 extern errno_t ui_menu_entry_paint(ui_menu_entry_t *, gfx_coord2_t *);
+extern bool ui_menu_entry_selectable(ui_menu_entry_t *);
 extern void ui_menu_entry_press(ui_menu_entry_t *, gfx_coord2_t *);
 extern void ui_menu_entry_release(ui_menu_entry_t *);
 extern void ui_menu_entry_enter(ui_menu_entry_t *, gfx_coord2_t *);
 extern void ui_menu_entry_leave(ui_menu_entry_t *, gfx_coord2_t *);
+extern void ui_menu_entry_activate(ui_menu_entry_t *);
 extern ui_evclaim_t ui_menu_entry_pos_event(ui_menu_entry_t *, gfx_coord2_t *,
     pos_event_t *);
Index: uspace/lib/ui/private/menu.h
===================================================================
--- uspace/lib/ui/private/menu.h	(revision 9754ed27d13eaf9da59abb744c63d0e3f5ab5ea2)
+++ uspace/lib/ui/private/menu.h	(revision 59768c77459332a9e19b4be9e1a0aa6659f1b7c9)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2021 Jiri Svoboda
+ * Copyright (c) 2022 Jiri Svoboda
  * All rights reserved.
  *
@@ -84,4 +84,6 @@
 extern errno_t ui_menu_paint_bg_gfx(ui_menu_t *, gfx_coord2_t *);
 extern errno_t ui_menu_paint_bg_text(ui_menu_t *, gfx_coord2_t *);
+extern void ui_menu_up(ui_menu_t *);
+extern void ui_menu_down(ui_menu_t *);
 
 #endif
Index: uspace/lib/ui/private/menubar.h
===================================================================
--- uspace/lib/ui/private/menubar.h	(revision 9754ed27d13eaf9da59abb744c63d0e3f5ab5ea2)
+++ uspace/lib/ui/private/menubar.h	(revision 59768c77459332a9e19b4be9e1a0aa6659f1b7c9)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2021 Jiri Svoboda
+ * Copyright (c) 2022 Jiri Svoboda
  * All rights reserved.
  *
@@ -40,4 +40,5 @@
 #include <adt/list.h>
 #include <gfx/coord.h>
+#include <stdbool.h>
 #include <types/ui/menu.h>
 #include <types/ui/menubar.h>
@@ -56,4 +57,6 @@
 	/** Menu bar rectangle */
 	gfx_rect_t rect;
+	/** Menu bar is active */
+	bool active;
 	/** Selected menu or @c NULL */
 	struct ui_menu *selected;
@@ -62,5 +65,9 @@
 };
 
-extern void ui_menu_bar_select(ui_menu_bar_t *, gfx_rect_t *, ui_menu_t *);
+extern void ui_menu_bar_select(ui_menu_bar_t *, ui_menu_t *, bool);
+extern void ui_menu_bar_left(ui_menu_bar_t *);
+extern void ui_menu_bar_right(ui_menu_bar_t *);
+extern ui_evclaim_t ui_menu_bar_key_press_unmod(ui_menu_bar_t *, kbd_event_t *);
+extern void ui_menu_bar_entry_rect(ui_menu_bar_t *, ui_menu_t *, gfx_rect_t *);
 
 #endif
Index: uspace/lib/ui/src/menu.c
===================================================================
--- uspace/lib/ui/src/menu.c	(revision 9754ed27d13eaf9da59abb744c63d0e3f5ab5ea2)
+++ uspace/lib/ui/src/menu.c	(revision 59768c77459332a9e19b4be9e1a0aa6659f1b7c9)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2021 Jiri Svoboda
+ * Copyright (c) 2022 Jiri Svoboda
  * All rights reserved.
  *
@@ -47,4 +47,5 @@
 #include <ui/popup.h>
 #include <ui/menu.h>
+#include <ui/menubar.h>
 #include <ui/menuentry.h>
 #include <ui/resource.h>
@@ -63,8 +64,10 @@
 
 static void ui_menu_popup_close(ui_popup_t *, void *);
+static void ui_menu_popup_kbd(ui_popup_t *, void *, kbd_event_t *);
 static void ui_menu_popup_pos(ui_popup_t *, void *, pos_event_t *);
 
 static ui_popup_cb_t ui_menu_popup_cb = {
 	.close = ui_menu_popup_close,
+	.kbd = ui_menu_popup_kbd,
 	.pos = ui_menu_popup_pos
 };
@@ -149,4 +152,36 @@
 
 	link = list_next(&cur->lmenus, &cur->mbar->menus);
+	if (link == NULL)
+		return NULL;
+
+	return list_get_instance(link, ui_menu_t, lmenus);
+}
+
+/** Get last menu in menu bar.
+ *
+ * @param mbar Menu bar
+ * @return Last menu or @c NULL if there is none
+ */
+ui_menu_t *ui_menu_last(ui_menu_bar_t *mbar)
+{
+	link_t *link;
+
+	link = list_last(&mbar->menus);
+	if (link == NULL)
+		return NULL;
+
+	return list_get_instance(link, ui_menu_t, lmenus);
+}
+
+/** Get previous menu in menu bar.
+ *
+ * @param cur Current menu
+ * @return Previous menu or @c NULL if @a cur is the fist one
+ */
+ui_menu_t *ui_menu_prev(ui_menu_t *cur)
+{
+	link_t *link;
+
+	link = list_prev(&cur->lmenus, &cur->mbar->menus);
 	if (link == NULL)
 		return NULL;
@@ -240,4 +275,7 @@
 	errno_t rc;
 
+	/* Select first entry */
+	menu->selected = ui_menu_entry_first(menu);
+
 	/* Determine menu dimensions */
 
@@ -269,4 +307,14 @@
 	ui_popup_destroy(menu->popup);
 	menu->popup = NULL;
+}
+
+/** Determine if menu is open.
+ *
+ * @param menu Menu
+ * @return @c true iff menu is open
+ */
+bool ui_menu_is_open(ui_menu_t *menu)
+{
+	return menu->popup != NULL;
 }
 
@@ -439,5 +487,5 @@
 		/* Press outside menu - close it */
 		if (event->type == POS_PRESS)
-			ui_menu_bar_select(menu->mbar, NULL, NULL);
+			ui_menu_bar_deactivate(menu->mbar);
 	}
 
@@ -454,6 +502,128 @@
 	ui_menu_t *menu = (ui_menu_t *)arg;
 
-	/* Close the menu */
-	ui_menu_bar_select(menu->mbar, NULL, NULL);
+	/* Deactivate menu bar, close menu */
+	ui_menu_bar_deactivate(menu->mbar);
+}
+
+/** Move one entry up.
+ *
+ * Non-selectable entries are skipped. If we are already at the top,
+ * we wrap around.
+ *
+ * @param menu Menu
+ */
+void ui_menu_up(ui_menu_t *menu)
+{
+	gfx_coord2_t mpos;
+	ui_menu_entry_t *nentry;
+
+	if (menu->selected == NULL)
+		return;
+
+	nentry = ui_menu_entry_prev(menu->selected);
+	if (nentry == NULL)
+		nentry = ui_menu_entry_last(menu);
+
+	/* Need to find a selectable entry */
+	while (!ui_menu_entry_selectable(nentry)) {
+		nentry = ui_menu_entry_prev(nentry);
+		if (nentry == NULL)
+			nentry = ui_menu_entry_last(menu);
+
+		/* Went completely around and found nothing? */
+		if (nentry == menu->selected)
+			return;
+	}
+
+	menu->selected = nentry;
+
+	mpos.x = 0;
+	mpos.y = 0;
+	(void) ui_menu_paint(menu, &mpos);
+}
+
+/** Move one entry down.
+ *
+ * Non-selectable entries are skipped. If we are already at the bottom,
+ * we wrap around.
+ *
+ * @param menu Menu
+ */
+void ui_menu_down(ui_menu_t *menu)
+{
+	gfx_coord2_t mpos;
+	ui_menu_entry_t *nentry;
+
+	if (menu->selected == NULL)
+		return;
+
+	nentry = ui_menu_entry_next(menu->selected);
+	if (nentry == NULL)
+		nentry = ui_menu_entry_first(menu);
+
+	/* Need to find a selectable entry */
+	while (!ui_menu_entry_selectable(nentry)) {
+		nentry = ui_menu_entry_next(nentry);
+		if (nentry == NULL)
+			nentry = ui_menu_entry_first(menu);
+
+		/* Went completely around and found nothing? */
+		if (nentry == menu->selected)
+			return;
+	}
+
+	menu->selected = nentry;
+
+	mpos.x = 0;
+	mpos.y = 0;
+	(void) ui_menu_paint(menu, &mpos);
+}
+
+/** Handle key press without modifiers in menu popup window.
+ *
+ * @param menu Menu
+ * @param event Keyboard event
+ */
+static void ui_menu_key_press_unmod(ui_menu_t *menu, kbd_event_t *event)
+{
+	switch (event->key) {
+	case KC_ESCAPE:
+		ui_menu_bar_deactivate(menu->mbar);
+		break;
+	case KC_LEFT:
+		ui_menu_bar_left(menu->mbar);
+		break;
+	case KC_RIGHT:
+		ui_menu_bar_right(menu->mbar);
+		break;
+	case KC_UP:
+		ui_menu_up(menu);
+		break;
+	case KC_DOWN:
+		ui_menu_down(menu);
+		break;
+	case KC_ENTER:
+		if (menu->selected != NULL)
+			ui_menu_entry_activate(menu->selected);
+		break;
+	default:
+		break;
+	}
+}
+
+/** Handle keyboard event in menu popup window.
+ *
+ * @param popup Menu popup window
+ * @param arg Argument (ui_menu_t *)
+ * @param event Keyboard event
+ */
+static void ui_menu_popup_kbd(ui_popup_t *popup, void *arg, kbd_event_t *event)
+{
+	ui_menu_t *menu = (ui_menu_t *)arg;
+
+	if (event->type == KEY_PRESS && (event->mods &
+	    (KM_CTRL | KM_ALT | KM_SHIFT)) == 0) {
+		ui_menu_key_press_unmod(menu, event);
+	}
 }
 
Index: uspace/lib/ui/src/menubar.c
===================================================================
--- uspace/lib/ui/src/menubar.c	(revision 9754ed27d13eaf9da59abb744c63d0e3f5ab5ea2)
+++ uspace/lib/ui/src/menubar.c	(revision 59768c77459332a9e19b4be9e1a0aa6659f1b7c9)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2021 Jiri Svoboda
+ * Copyright (c) 2022 Jiri Svoboda
  * All rights reserved.
  *
@@ -60,4 +60,5 @@
 static void ui_menu_bar_ctl_destroy(void *);
 static errno_t ui_menu_bar_ctl_paint(void *);
+static ui_evclaim_t ui_menu_bar_ctl_kbd_event(void *, kbd_event_t *);
 static ui_evclaim_t ui_menu_bar_ctl_pos_event(void *, pos_event_t *);
 
@@ -66,5 +67,6 @@
 	.destroy = ui_menu_bar_ctl_destroy,
 	.paint = ui_menu_bar_ctl_paint,
-	.pos_event = ui_menu_bar_ctl_pos_event,
+	.kbd_event = ui_menu_bar_ctl_kbd_event,
+	.pos_event = ui_menu_bar_ctl_pos_event
 };
 
@@ -236,11 +238,12 @@
  *
  * @param mbar Menu bar
- * @param rect Menu bar entry rectangle
  * @param menu Menu to select (or deselect if selected) or @c NULL
- */
-void ui_menu_bar_select(ui_menu_bar_t *mbar, gfx_rect_t *rect,
-    ui_menu_t *menu)
+ * @param openup Open menu even if not currently open
+ */
+void ui_menu_bar_select(ui_menu_bar_t *mbar, ui_menu_t *menu, bool openup)
 {
 	ui_menu_t *old_menu;
+	gfx_rect_t rect;
+	bool was_open;
 
 	old_menu = mbar->selected;
@@ -252,12 +255,125 @@
 
 	/* Close previously open menu */
-	if (old_menu != NULL)
+	if (old_menu != NULL && ui_menu_is_open(old_menu)) {
+		was_open = true;
 		(void) ui_menu_close(old_menu);
+	} else {
+		was_open = false;
+	}
 
 	(void) ui_menu_bar_paint(mbar);
 
 	if (mbar->selected != NULL) {
-		(void) ui_menu_open(mbar->selected, rect);
-	}
+		ui_menu_bar_entry_rect(mbar, mbar->selected, &rect);
+		if (openup || was_open) {
+			/*
+			 * Open the newly selected menu if either
+			 * the old menu was open or @a openup was
+			 * specified.
+			 */
+			(void) ui_menu_open(mbar->selected, &rect);
+		}
+	}
+}
+
+/** Move one entry left.
+ *
+ * If the selected menu is open, the newly selected menu will be open
+ * as well. If we are already at the first entry, we wrap around.
+ *
+ * @param mbar Menu bar
+ */
+void ui_menu_bar_left(ui_menu_bar_t *mbar)
+{
+	ui_menu_t *nmenu;
+
+	if (mbar->selected == NULL)
+		return;
+
+	nmenu = ui_menu_prev(mbar->selected);
+	if (nmenu == NULL)
+		nmenu = ui_menu_last(mbar);
+
+	ui_menu_bar_select(mbar, nmenu, false);
+}
+
+/** Move one entry right.
+ *
+ * If the selected menu is open, the newly selected menu will be open
+ * as well. If we are already at the last entry, we wrap around.
+ *
+ * @param mbar Menu bar
+ */
+void ui_menu_bar_right(ui_menu_bar_t *mbar)
+{
+	ui_menu_t *nmenu;
+
+	if (mbar->selected == NULL)
+		return;
+
+	nmenu = ui_menu_next(mbar->selected);
+	if (nmenu == NULL)
+		nmenu = ui_menu_first(mbar);
+
+	ui_menu_bar_select(mbar, nmenu, false);
+}
+
+/** Handle menu bar key press without modifiers.
+ *
+ * @param mbar Menu bar
+ * @param kbd_event Keyboard event
+ * @return @c ui_claimed iff the event is claimed
+ */
+ui_evclaim_t ui_menu_bar_key_press_unmod(ui_menu_bar_t *mbar, kbd_event_t *event)
+{
+	gfx_rect_t rect;
+
+	if (event->key == KC_F10) {
+		ui_menu_bar_activate(mbar);
+		return ui_claimed;
+	}
+
+	if (!mbar->active)
+		return ui_unclaimed;
+
+	if (event->key == KC_ESCAPE) {
+		ui_menu_bar_deactivate(mbar);
+		return ui_claimed;
+	}
+
+	if (event->key == KC_LEFT)
+		ui_menu_bar_left(mbar);
+
+	if (event->key == KC_RIGHT)
+		ui_menu_bar_right(mbar);
+
+	if (event->key == KC_ENTER || event->key == KC_DOWN) {
+		if (mbar->selected != NULL && !ui_menu_is_open(mbar->selected)) {
+			ui_menu_bar_entry_rect(mbar, mbar->selected,
+			    &rect);
+			ui_menu_open(mbar->selected, &rect);
+		}
+
+		return ui_claimed;
+	}
+
+	return ui_claimed;
+}
+
+/** Handle menu bar keyboard event.
+ *
+ * @param mbar Menu bar
+ * @param kbd_event Keyboard event
+ * @return @c ui_claimed iff the event is claimed
+ */
+ui_evclaim_t ui_menu_bar_kbd_event(ui_menu_bar_t *mbar, kbd_event_t *event)
+{
+	if (event->type == KEY_PRESS && (event->mods &
+	    (KM_CTRL | KM_ALT | KM_SHIFT)) == 0) {
+		ui_menu_bar_key_press_unmod(mbar, event);
+		return ui_claimed;
+	}
+
+	return ui_claimed;
 }
 
@@ -304,5 +420,6 @@
 		if (event->type == POS_PRESS &&
 		    gfx_pix_inside_rect(&ppos, &rect)) {
-			ui_menu_bar_select(mbar, &rect, menu);
+			mbar->active = true;
+			ui_menu_bar_select(mbar, menu, true);
 			return ui_claimed;
 		}
@@ -315,4 +432,75 @@
 }
 
+/** Handle menu bar position event.
+ *
+ * @param mbar Menu bar
+ * @param menu Menu whose entry's rectangle is to be returned
+ * @param rrect Place to store entry rectangle
+ */
+void ui_menu_bar_entry_rect(ui_menu_bar_t *mbar, ui_menu_t *menu,
+    gfx_rect_t *rrect)
+{
+	ui_resource_t *res;
+	gfx_coord2_t pos;
+	gfx_rect_t rect;
+	ui_menu_t *cur;
+	const char *caption;
+	gfx_coord_t width;
+	gfx_coord_t hpad;
+
+	res = ui_window_get_res(mbar->window);
+
+	if (res->textmode) {
+		hpad = menubar_hpad_text;
+	} else {
+		hpad = menubar_hpad;
+	}
+
+	pos = mbar->rect.p0;
+
+	cur = ui_menu_first(mbar);
+	while (cur != NULL) {
+		caption = ui_menu_caption(cur);
+		width = gfx_text_width(res->font, caption) + 2 * hpad;
+
+		rect.p0 = pos;
+		rect.p1.x = rect.p0.x + width;
+		rect.p1.y = mbar->rect.p1.y;
+
+		if (cur == menu) {
+			*rrect = rect;
+			return;
+		}
+
+		pos.x += width;
+		cur = ui_menu_next(cur);
+	}
+
+	/* We should never get here */
+	assert(false);
+}
+
+/** Activate menu bar.
+ *
+ * @param mbar Menu bar
+ */
+void ui_menu_bar_activate(ui_menu_bar_t *mbar)
+{
+	if (mbar->active)
+		return;
+
+	mbar->active = true;
+	if (mbar->selected == NULL)
+		mbar->selected = ui_menu_first(mbar);
+
+	(void) ui_menu_bar_paint(mbar);
+}
+
+void ui_menu_bar_deactivate(ui_menu_bar_t *mbar)
+{
+	ui_menu_bar_select(mbar, NULL, false);
+	mbar->active = false;
+}
+
 /** Destroy menu bar control.
  *
@@ -338,5 +526,5 @@
 }
 
-/** Handle menu bar control position event.
+/** Handle menu bar control keyboard event.
  *
  * @param arg Argument (ui_menu_bar_t *)
@@ -344,4 +532,17 @@
  * @return @c ui_claimed iff the event is claimed
  */
+ui_evclaim_t ui_menu_bar_ctl_kbd_event(void *arg, kbd_event_t *event)
+{
+	ui_menu_bar_t *mbar = (ui_menu_bar_t *) arg;
+
+	return ui_menu_bar_kbd_event(mbar, event);
+}
+
+/** Handle menu bar control position event.
+ *
+ * @param arg Argument (ui_menu_bar_t *)
+ * @param pos_event Position event
+ * @return @c ui_claimed iff the event is claimed
+ */
 ui_evclaim_t ui_menu_bar_ctl_pos_event(void *arg, pos_event_t *event)
 {
Index: uspace/lib/ui/src/menuentry.c
===================================================================
--- uspace/lib/ui/src/menuentry.c	(revision 9754ed27d13eaf9da59abb744c63d0e3f5ab5ea2)
+++ uspace/lib/ui/src/menuentry.c	(revision 59768c77459332a9e19b4be9e1a0aa6659f1b7c9)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2021 Jiri Svoboda
+ * Copyright (c) 2022 Jiri Svoboda
  * All rights reserved.
  *
@@ -45,4 +45,5 @@
 #include <ui/control.h>
 #include <ui/paint.h>
+#include <ui/menubar.h>
 #include <ui/menuentry.h>
 #include <ui/window.h>
@@ -177,4 +178,20 @@
 }
 
+/** Get last menu entry in menu.
+ *
+ * @param menu Menu
+ * @return Last menu entry or @c NULL if there is none
+ */
+ui_menu_entry_t *ui_menu_entry_last(ui_menu_t *menu)
+{
+	link_t *link;
+
+	link = list_last(&menu->entries);
+	if (link == NULL)
+		return NULL;
+
+	return list_get_instance(link, ui_menu_entry_t, lentries);
+}
+
 /** Get next menu entry in menu.
  *
@@ -187,4 +204,20 @@
 
 	link = list_next(&cur->lentries, &cur->menu->entries);
+	if (link == NULL)
+		return NULL;
+
+	return list_get_instance(link, ui_menu_entry_t, lentries);
+}
+
+/** Get previous menu entry in menu.
+ *
+ * @param cur Current menu entry
+ * @return Next menu entry or @c NULL if @a cur is the last one
+ */
+ui_menu_entry_t *ui_menu_entry_prev(ui_menu_entry_t *cur)
+{
+	link_t *link;
+
+	link = list_prev(&cur->lentries, &cur->menu->entries);
 	if (link == NULL)
 		return NULL;
@@ -378,4 +411,13 @@
 }
 
+/** Determine if entry is selectable.
+ *
+ * @return @c true iff entry is selectable
+ */
+bool ui_menu_entry_selectable(ui_menu_entry_t *mentry)
+{
+	return !mentry->separator;
+}
+
 /** Handle button press in menu entry.
  *
@@ -407,11 +449,18 @@
 	mentry->held = false;
 
-	if (mentry->inside) {
-		/* Close menu */
-		ui_menu_bar_select(mentry->menu->mbar, NULL, NULL);
-
-		/* Call back */
-		ui_menu_entry_cb(mentry);
-	}
+	ui_menu_entry_activate(mentry);
+}
+
+/** Activate menu entry.
+ *
+ * @param mentry Menu entry
+ */
+void ui_menu_entry_activate(ui_menu_entry_t *mentry)
+{
+	/* Deactivate menu bar, close menu */
+	ui_menu_bar_deactivate(mentry->menu->mbar);
+
+	/* Call back */
+	ui_menu_entry_cb(mentry);
 }
 
Index: uspace/lib/ui/src/popup.c
===================================================================
--- uspace/lib/ui/src/popup.c	(revision 9754ed27d13eaf9da59abb744c63d0e3f5ab5ea2)
+++ uspace/lib/ui/src/popup.c	(revision 59768c77459332a9e19b4be9e1a0aa6659f1b7c9)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2021 Jiri Svoboda
+ * Copyright (c) 2022 Jiri Svoboda
  * All rights reserved.
  *
@@ -36,5 +36,5 @@
 #include <errno.h>
 #include <gfx/context.h>
-//#include <io/kbd_event.h>
+#include <io/kbd_event.h>
 #include <io/pos_event.h>
 #include <mem.h>
@@ -47,8 +47,10 @@
 
 static void ui_popup_window_close(ui_window_t *, void *);
+static void ui_popup_window_kbd(ui_window_t *, void *, kbd_event_t *);
 static void ui_popup_window_pos(ui_window_t *, void *, pos_event_t *);
 
 static ui_window_cb_t ui_popup_window_cb = {
 	.close = ui_popup_window_close,
+	.kbd = ui_popup_window_kbd,
 	.pos = ui_popup_window_pos
 };
@@ -200,4 +202,19 @@
 }
 
+/** Handle keyboard event in popup window.
+ *
+ * @param window Window
+ * @param arg Argument (ui_popup_t *)
+ * @param event Keyboard event
+ */
+static void ui_popup_window_kbd(ui_window_t *window, void *arg,
+    kbd_event_t *event)
+{
+	ui_popup_t *popup = (ui_popup_t *)arg;
+
+	if (popup->cb != NULL && popup->cb->kbd != NULL)
+		popup->cb->kbd(popup, popup->arg, event);
+}
+
 /** Handle position event in popup window.
  *
Index: uspace/lib/ui/test/menu.c
===================================================================
--- uspace/lib/ui/test/menu.c	(revision 9754ed27d13eaf9da59abb744c63d0e3f5ab5ea2)
+++ uspace/lib/ui/test/menu.c	(revision 59768c77459332a9e19b4be9e1a0aa6659f1b7c9)
@@ -36,4 +36,5 @@
 #include <ui/menu.h>
 #include <ui/menubar.h>
+#include <ui/menuentry.h>
 #include <ui/ui.h>
 #include <ui/window.h>
@@ -122,4 +123,52 @@
 }
 
+/** ui_menu_last() / ui_menu_prev() iterate over menus in reverse */
+PCUT_TEST(last_prev)
+{
+	ui_t *ui = NULL;
+	ui_window_t *window = NULL;
+	ui_wnd_params_t params;
+	ui_menu_bar_t *mbar = NULL;
+	ui_menu_t *menu1 = NULL;
+	ui_menu_t *menu2 = NULL;
+	ui_menu_t *m;
+	errno_t rc;
+
+	rc = ui_create_disp(NULL, &ui);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	ui_wnd_params_init(&params);
+	params.caption = "Hello";
+
+	rc = ui_window_create(ui, &params, &window);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(window);
+
+	rc = ui_menu_bar_create(ui, window, &mbar);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(mbar);
+
+	rc = ui_menu_create(mbar, "Test 1", &menu1);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(menu1);
+
+	rc = ui_menu_create(mbar, "Test 1", &menu2);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(menu2);
+
+	m = ui_menu_last(mbar);
+	PCUT_ASSERT_EQUALS(menu2, m);
+
+	m = ui_menu_prev(m);
+	PCUT_ASSERT_EQUALS(menu1, m);
+
+	m = ui_menu_prev(m);
+	PCUT_ASSERT_NULL(m);
+
+	ui_menu_bar_destroy(mbar);
+	ui_window_destroy(window);
+	ui_destroy(ui);
+}
+
 /** ui_menu_caption() returns the menu's caption */
 PCUT_TEST(caption)
@@ -209,6 +258,6 @@
 }
 
-/** Paint background in graphics mode */
-PCUT_TEST(paint_bg_gfx)
+/** Open and close menu with ui_menu_open() / ui_menu_close() */
+PCUT_TEST(open_close)
 {
 	ui_t *ui = NULL;
@@ -218,5 +267,4 @@
 	ui_menu_t *menu = NULL;
 	gfx_rect_t prect;
-	gfx_coord2_t pos;
 	errno_t rc;
 
@@ -244,20 +292,17 @@
 	prect.p1.y = 0;
 
-	/* Menu needs to be open to be able to paint it */
+	/* Open and close */
 	rc = ui_menu_open(menu, &prect);
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
-	pos.x = 0;
-	pos.y = 0;
-	rc = ui_menu_paint_bg_gfx(menu, &pos);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-
-	ui_menu_bar_destroy(mbar);
-	ui_window_destroy(window);
-	ui_destroy(ui);
-}
-
-/** Paint background in text mode */
-PCUT_TEST(paint_bg_text)
+	ui_menu_close(menu);
+
+	ui_menu_bar_destroy(mbar);
+	ui_window_destroy(window);
+	ui_destroy(ui);
+}
+
+/** ui_menu_is_open() correctly returns menu state */
+PCUT_TEST(is_open)
 {
 	ui_t *ui = NULL;
@@ -267,5 +312,5 @@
 	ui_menu_t *menu = NULL;
 	gfx_rect_t prect;
-	gfx_coord2_t pos;
+	bool open;
 	errno_t rc;
 
@@ -293,20 +338,25 @@
 	prect.p1.y = 0;
 
-	/* Menu needs to be open to be able to paint it */
+	open = ui_menu_is_open(menu);
+	PCUT_ASSERT_FALSE(open);
+
 	rc = ui_menu_open(menu, &prect);
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
-	pos.x = 0;
-	pos.y = 0;
-	rc = ui_menu_paint_bg_text(menu, &pos);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-
-	ui_menu_bar_destroy(mbar);
-	ui_window_destroy(window);
-	ui_destroy(ui);
-}
-
-/** Paint menu */
-PCUT_TEST(paint)
+	open = ui_menu_is_open(menu);
+	PCUT_ASSERT_TRUE(open);
+
+	ui_menu_close(menu);
+
+	open = ui_menu_is_open(menu);
+	PCUT_ASSERT_FALSE(open);
+
+	ui_menu_bar_destroy(mbar);
+	ui_window_destroy(window);
+	ui_destroy(ui);
+}
+
+/** Paint background in graphics mode */
+PCUT_TEST(paint_bg_gfx)
 {
 	ui_t *ui = NULL;
@@ -348,6 +398,336 @@
 	pos.x = 0;
 	pos.y = 0;
+	rc = ui_menu_paint_bg_gfx(menu, &pos);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	ui_menu_bar_destroy(mbar);
+	ui_window_destroy(window);
+	ui_destroy(ui);
+}
+
+/** Paint background in text mode */
+PCUT_TEST(paint_bg_text)
+{
+	ui_t *ui = NULL;
+	ui_window_t *window = NULL;
+	ui_wnd_params_t params;
+	ui_menu_bar_t *mbar = NULL;
+	ui_menu_t *menu = NULL;
+	gfx_rect_t prect;
+	gfx_coord2_t pos;
+	errno_t rc;
+
+	rc = ui_create_disp(NULL, &ui);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	ui_wnd_params_init(&params);
+	params.caption = "Hello";
+
+	rc = ui_window_create(ui, &params, &window);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(window);
+
+	rc = ui_menu_bar_create(ui, window, &mbar);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(mbar);
+
+	rc = ui_menu_create(mbar, "Test", &menu);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(menu);
+
+	prect.p0.x = 0;
+	prect.p0.y = 0;
+	prect.p1.x = 0;
+	prect.p1.y = 0;
+
+	/* Menu needs to be open to be able to paint it */
+	rc = ui_menu_open(menu, &prect);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	pos.x = 0;
+	pos.y = 0;
+	rc = ui_menu_paint_bg_text(menu, &pos);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	ui_menu_bar_destroy(mbar);
+	ui_window_destroy(window);
+	ui_destroy(ui);
+}
+
+/** Paint menu */
+PCUT_TEST(paint)
+{
+	ui_t *ui = NULL;
+	ui_window_t *window = NULL;
+	ui_wnd_params_t params;
+	ui_menu_bar_t *mbar = NULL;
+	ui_menu_t *menu = NULL;
+	gfx_rect_t prect;
+	gfx_coord2_t pos;
+	errno_t rc;
+
+	rc = ui_create_disp(NULL, &ui);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	ui_wnd_params_init(&params);
+	params.caption = "Hello";
+
+	rc = ui_window_create(ui, &params, &window);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(window);
+
+	rc = ui_menu_bar_create(ui, window, &mbar);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(mbar);
+
+	rc = ui_menu_create(mbar, "Test", &menu);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(menu);
+
+	prect.p0.x = 0;
+	prect.p0.y = 0;
+	prect.p1.x = 0;
+	prect.p1.y = 0;
+
+	/* Menu needs to be open to be able to paint it */
+	rc = ui_menu_open(menu, &prect);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	pos.x = 0;
+	pos.y = 0;
 	rc = ui_menu_paint(menu, &pos);
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	ui_menu_bar_destroy(mbar);
+	ui_window_destroy(window);
+	ui_destroy(ui);
+}
+
+/** ui_menu_up() with empty menu does nothing */
+PCUT_TEST(up_empty)
+{
+	ui_t *ui = NULL;
+	ui_window_t *window = NULL;
+	ui_wnd_params_t params;
+	ui_menu_bar_t *mbar = NULL;
+	ui_menu_t *menu = NULL;
+	gfx_rect_t prect;
+	errno_t rc;
+
+	rc = ui_create_disp(NULL, &ui);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	ui_wnd_params_init(&params);
+	params.caption = "Hello";
+
+	rc = ui_window_create(ui, &params, &window);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(window);
+
+	rc = ui_menu_bar_create(ui, window, &mbar);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(mbar);
+
+	rc = ui_menu_create(mbar, "Test", &menu);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(menu);
+
+	prect.p0.x = 0;
+	prect.p0.y = 0;
+	prect.p1.x = 0;
+	prect.p1.y = 0;
+
+	/* Menu needs to be open to be able to move around it */
+	rc = ui_menu_open(menu, &prect);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	ui_menu_up(menu);
+
+	ui_menu_bar_destroy(mbar);
+	ui_window_destroy(window);
+	ui_destroy(ui);
+}
+
+/** ui_menu_up() moves one entry up, skips separators, wraps around */
+PCUT_TEST(up)
+{
+	ui_t *ui = NULL;
+	ui_window_t *window = NULL;
+	ui_wnd_params_t params;
+	ui_menu_bar_t *mbar = NULL;
+	ui_menu_t *menu = NULL;
+	ui_menu_entry_t *mentry1 = NULL;
+	ui_menu_entry_t *mentry2 = NULL;
+	ui_menu_entry_t *mentry3 = NULL;
+	gfx_rect_t prect;
+	errno_t rc;
+
+	rc = ui_create_disp(NULL, &ui);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	ui_wnd_params_init(&params);
+	params.caption = "Hello";
+
+	rc = ui_window_create(ui, &params, &window);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(window);
+
+	rc = ui_menu_bar_create(ui, window, &mbar);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(mbar);
+
+	rc = ui_menu_create(mbar, "Test", &menu);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(menu);
+
+	rc = ui_menu_entry_create(menu, "Foo", "F1", &mentry1);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(mentry1);
+
+	rc = ui_menu_entry_sep_create(menu, &mentry2);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(mentry2);
+
+	rc = ui_menu_entry_create(menu, "Bar", "F2", &mentry3);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(mentry3);
+
+	prect.p0.x = 0;
+	prect.p0.y = 0;
+	prect.p1.x = 0;
+	prect.p1.y = 0;
+
+	/* Menu needs to be open to be able to move around it */
+	rc = ui_menu_open(menu, &prect);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	/* When menu is open, the first entry is selected */
+	PCUT_ASSERT_EQUALS(mentry1, menu->selected);
+
+	ui_menu_up(menu);
+
+	/* Now we've wrapped around to the last entry */
+	PCUT_ASSERT_EQUALS(mentry3, menu->selected);
+
+	ui_menu_up(menu);
+
+	/* mentry2 is a separator and was skipped */
+	PCUT_ASSERT_EQUALS(mentry1, menu->selected);
+
+	ui_menu_bar_destroy(mbar);
+	ui_window_destroy(window);
+	ui_destroy(ui);
+}
+
+/** ui_menu_down() with empty menu does nothing */
+PCUT_TEST(down_empty)
+{
+	ui_t *ui = NULL;
+	ui_window_t *window = NULL;
+	ui_wnd_params_t params;
+	ui_menu_bar_t *mbar = NULL;
+	ui_menu_t *menu = NULL;
+	gfx_rect_t prect;
+	errno_t rc;
+
+	rc = ui_create_disp(NULL, &ui);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	ui_wnd_params_init(&params);
+	params.caption = "Hello";
+
+	rc = ui_window_create(ui, &params, &window);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(window);
+
+	rc = ui_menu_bar_create(ui, window, &mbar);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(mbar);
+
+	rc = ui_menu_create(mbar, "Test", &menu);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(menu);
+
+	prect.p0.x = 0;
+	prect.p0.y = 0;
+	prect.p1.x = 0;
+	prect.p1.y = 0;
+
+	/* Menu needs to be open to be able to move around it */
+	rc = ui_menu_open(menu, &prect);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	ui_menu_down(menu);
+
+	ui_menu_bar_destroy(mbar);
+	ui_window_destroy(window);
+	ui_destroy(ui);
+}
+
+/** ui_menu_down() moves one entry down, skips separators, wraps around */
+PCUT_TEST(down)
+{
+	ui_t *ui = NULL;
+	ui_window_t *window = NULL;
+	ui_wnd_params_t params;
+	ui_menu_bar_t *mbar = NULL;
+	ui_menu_t *menu = NULL;
+	ui_menu_entry_t *mentry1 = NULL;
+	ui_menu_entry_t *mentry2 = NULL;
+	ui_menu_entry_t *mentry3 = NULL;
+	gfx_rect_t prect;
+	errno_t rc;
+
+	rc = ui_create_disp(NULL, &ui);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	ui_wnd_params_init(&params);
+	params.caption = "Hello";
+
+	rc = ui_window_create(ui, &params, &window);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(window);
+
+	rc = ui_menu_bar_create(ui, window, &mbar);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(mbar);
+
+	rc = ui_menu_create(mbar, "Test", &menu);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(menu);
+
+	rc = ui_menu_entry_create(menu, "Foo", "F1", &mentry1);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(mentry1);
+
+	rc = ui_menu_entry_sep_create(menu, &mentry2);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(mentry2);
+
+	rc = ui_menu_entry_create(menu, "Bar", "F2", &mentry3);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(mentry3);
+
+	prect.p0.x = 0;
+	prect.p0.y = 0;
+	prect.p1.x = 0;
+	prect.p1.y = 0;
+
+	/* Menu needs to be open to be able to move around it */
+	rc = ui_menu_open(menu, &prect);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	/* When menu is open, the first entry is selected */
+	PCUT_ASSERT_EQUALS(mentry1, menu->selected);
+
+	ui_menu_down(menu);
+
+	/* mentry2 is a separator and was skipped */
+	PCUT_ASSERT_EQUALS(mentry3, menu->selected);
+
+	ui_menu_up(menu);
+
+	/* Now we've wrapped around to the first entry */
+	PCUT_ASSERT_EQUALS(mentry1, menu->selected);
 
 	ui_menu_bar_destroy(mbar);
Index: uspace/lib/ui/test/menubar.c
===================================================================
--- uspace/lib/ui/test/menubar.c	(revision 9754ed27d13eaf9da59abb744c63d0e3f5ab5ea2)
+++ uspace/lib/ui/test/menubar.c	(revision 59768c77459332a9e19b4be9e1a0aa6659f1b7c9)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2021 Jiri Svoboda
+ * Copyright (c) 2022 Jiri Svoboda
  * All rights reserved.
  *
@@ -149,6 +149,6 @@
 }
 
-/** Press event on menu bar entry selects menu */
-PCUT_TEST(pos_event_select)
+/** Deliver menu bar keyboard event */
+PCUT_TEST(kbd_event)
 {
 	ui_t *ui = NULL;
@@ -158,5 +158,5 @@
 	ui_menu_t *menu = NULL;
 	ui_evclaim_t claimed;
-	pos_event_t event;
+	kbd_event_t event;
 	gfx_rect_t rect;
 	errno_t rc;
@@ -186,4 +186,52 @@
 	PCUT_ASSERT_NOT_NULL(menu);
 
+	event.type = KEY_PRESS;
+	event.key = KC_ESCAPE;
+	claimed = ui_menu_kbd_pos_event(mbar, &event);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_EQUALS(ui_claimed, claimed);
+
+	ui_menu_bar_destroy(mbar);
+	ui_window_destroy(window);
+	ui_destroy(ui);
+}
+
+/** Press event on menu bar entry selects menu */
+PCUT_TEST(pos_event_select)
+{
+	ui_t *ui = NULL;
+	ui_window_t *window = NULL;
+	ui_wnd_params_t params;
+	ui_menu_bar_t *mbar = NULL;
+	ui_menu_t *menu = NULL;
+	ui_evclaim_t claimed;
+	pos_event_t event;
+	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 = "Hello";
+
+	rc = ui_window_create(ui, &params, &window);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(window);
+
+	rc = ui_menu_bar_create(ui, window, &mbar);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(mbar);
+
+	rect.p0.x = 0;
+	rect.p0.y = 0;
+	rect.p1.x = 50;
+	rect.p1.y = 25;
+	ui_menu_bar_set_rect(mbar, &rect);
+
+	rc = ui_menu_create(mbar, "Test", &menu);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(menu);
+
 	event.type = POS_PRESS;
 	event.hpos = 4;
@@ -209,5 +257,4 @@
 	ui_menu_bar_t *mbar = NULL;
 	ui_menu_t *menu = NULL;
-	gfx_rect_t rect;
 	errno_t rc;
 
@@ -230,13 +277,9 @@
 	PCUT_ASSERT_NOT_NULL(menu);
 
-	rect.p0.x = 0;
-	rect.p0.y = 0;
-	rect.p1.x = 0;
-	rect.p1.y = 0;
-	ui_menu_bar_select(mbar, &rect, menu);
+	ui_menu_bar_select(mbar, menu, true);
 	PCUT_ASSERT_EQUALS(menu, mbar->selected);
 
 	/* Selecting again should unselect the menu */
-	ui_menu_bar_select(mbar, &rect, menu);
+	ui_menu_bar_select(mbar, menu, true);
 	PCUT_ASSERT_NULL(mbar->selected);
 
@@ -255,5 +298,4 @@
 	ui_menu_t *menu1 = NULL;
 	ui_menu_t *menu2 = NULL;
-	gfx_rect_t rect;
 	errno_t rc;
 
@@ -280,13 +322,9 @@
 	PCUT_ASSERT_NOT_NULL(menu2);
 
-	rect.p0.x = 0;
-	rect.p0.y = 0;
-	rect.p1.x = 0;
-	rect.p1.y = 0;
-	ui_menu_bar_select(mbar, &rect, menu1);
+	ui_menu_bar_select(mbar, menu1, true);
 	PCUT_ASSERT_EQUALS(menu1, mbar->selected);
 
 	/* Selecting different menu should select it */
-	ui_menu_bar_select(mbar, &rect, menu2);
+	ui_menu_bar_select(mbar, menu2, true);
 	PCUT_ASSERT_EQUALS(menu2, mbar->selected);
 
@@ -296,3 +334,42 @@
 }
 
+/** ui_menu_bar_activate() activates/deactivates menu bar */
+PCUT_TEST(activate_deactivate)
+{
+	ui_t *ui = NULL;
+	ui_window_t *window = NULL;
+	ui_wnd_params_t params;
+	ui_menu_bar_t *mbar = NULL;
+	ui_menu_t *menu = NULL;
+	errno_t rc;
+
+	rc = ui_create_disp(NULL, &ui);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	ui_wnd_params_init(&params);
+	params.caption = "Hello";
+
+	rc = ui_window_create(ui, &params, &window);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(window);
+
+	rc = ui_menu_bar_create(ui, window, &mbar);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(mbar);
+
+	rc = ui_menu_create(mbar, "Test", &menu);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(menu);
+
+	ui_menu_bar_activate(mbar);
+	PCUT_ASSERT_EQUALS(menu, mbar->selected);
+
+	ui_menu_bar_deactivate(mbar);
+	PCUT_ASSERT_NULL(mbar->selected);
+
+	ui_menu_bar_destroy(mbar);
+	ui_window_destroy(window);
+	ui_destroy(ui);
+}
+
 PCUT_EXPORT(menubar);
Index: uspace/lib/ui/test/menuentry.c
===================================================================
--- uspace/lib/ui/test/menuentry.c	(revision 9754ed27d13eaf9da59abb744c63d0e3f5ab5ea2)
+++ uspace/lib/ui/test/menuentry.c	(revision 59768c77459332a9e19b4be9e1a0aa6659f1b7c9)
@@ -236,4 +236,57 @@
 }
 
+/** ui_menu_entry_last() / ui_menu_entry_prev() iterate over entries in reverse */
+PCUT_TEST(last_prev)
+{
+	ui_t *ui = NULL;
+	ui_window_t *window = NULL;
+	ui_wnd_params_t params;
+	ui_menu_bar_t *mbar = NULL;
+	ui_menu_t *menu = NULL;
+	ui_menu_entry_t *entry1 = NULL;
+	ui_menu_entry_t *entry2 = NULL;
+	ui_menu_entry_t *e;
+	errno_t rc;
+
+	rc = ui_create_disp(NULL, &ui);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	ui_wnd_params_init(&params);
+	params.caption = "Hello";
+
+	rc = ui_window_create(ui, &params, &window);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(window);
+
+	rc = ui_menu_bar_create(ui, window, &mbar);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(mbar);
+
+	rc = ui_menu_create(mbar, "Test", &menu);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(menu);
+
+	rc = ui_menu_entry_create(menu, "Foo", "F1", &entry1);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(entry1);
+
+	rc = ui_menu_entry_create(menu, "Bar", "F2", &entry2);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(entry2);
+
+	e = ui_menu_entry_last(menu);
+	PCUT_ASSERT_EQUALS(entry2, e);
+
+	e = ui_menu_entry_prev(e);
+	PCUT_ASSERT_EQUALS(entry1, e);
+
+	e = ui_menu_entry_prev(e);
+	PCUT_ASSERT_NULL(e);
+
+	ui_menu_bar_destroy(mbar);
+	ui_window_destroy(window);
+	ui_destroy(ui);
+}
+
 /** ui_menu_entry_column_widths() / ui_menu_entry_height() */
 PCUT_TEST(widths_height)
@@ -335,4 +388,58 @@
 	rc = ui_menu_entry_paint(mentry, &pos);
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	ui_menu_bar_destroy(mbar);
+	ui_window_destroy(window);
+	ui_destroy(ui);
+}
+
+/** ui_menu_entry_selectable() returns correct value based on entry type */
+PCUT_TEST(selectable)
+{
+	ui_t *ui = NULL;
+	ui_window_t *window = NULL;
+	ui_wnd_params_t params;
+	ui_menu_bar_t *mbar = NULL;
+	ui_menu_t *menu = NULL;
+	ui_menu_entry_t *mentry = NULL;
+	errno_t rc;
+
+	rc = ui_create_disp(NULL, &ui);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	ui_wnd_params_init(&params);
+	params.caption = "Hello";
+
+	rc = ui_window_create(ui, &params, &window);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(window);
+
+	rc = ui_menu_bar_create(ui, window, &mbar);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(mbar);
+
+	rc = ui_menu_create(mbar, "Test", &menu);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(menu);
+
+	/* Selectable entry */
+
+	rc = ui_menu_entry_create(menu, "Foo", "F1", &mentry);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(mentry);
+
+	PCUT_ASSERT_TRUE(ui_menu_entry_selectable(mentry));
+
+	ui_menu_entry_destroy(mentry);
+
+	/* Non-selectable separator entry */
+
+	rc = ui_menu_entry_sep_create(menu, &mentry);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(mentry);
+
+	PCUT_ASSERT_FALSE(ui_menu_entry_selectable(mentry));
+
+	ui_menu_entry_destroy(mentry);
 
 	ui_menu_bar_destroy(mbar);
@@ -545,4 +652,61 @@
 }
 
+/** ui_menu_entry_activate() activates menu entry */
+PCUT_TEST(activate)
+{
+	ui_t *ui = NULL;
+	ui_window_t *window = NULL;
+	ui_wnd_params_t params;
+	ui_menu_bar_t *mbar = NULL;
+	ui_menu_t *menu = NULL;
+	ui_menu_entry_t *mentry = NULL;
+	gfx_rect_t prect;
+	test_resp_t resp;
+	errno_t rc;
+
+	rc = ui_create_disp(NULL, &ui);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	ui_wnd_params_init(&params);
+	params.caption = "Hello";
+
+	rc = ui_window_create(ui, &params, &window);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(window);
+
+	rc = ui_menu_bar_create(ui, window, &mbar);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(mbar);
+
+	rc = ui_menu_create(mbar, "Test", &menu);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(menu);
+
+	rc = ui_menu_entry_create(menu, "X", "Y", &mentry);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(mentry);
+
+	ui_menu_entry_set_cb(mentry, test_entry_cb, &resp);
+	resp.activated = false;
+
+	prect.p0.x = 0;
+	prect.p0.y = 0;
+	prect.p1.x = 0;
+	prect.p1.y = 0;
+
+	rc = ui_menu_open(menu, &prect);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	PCUT_ASSERT_FALSE(resp.activated);
+	ui_menu_entry_activate(mentry);
+
+	ui_menu_entry_release(mentry);
+	PCUT_ASSERT_TRUE(resp.activated);
+
+	ui_menu_bar_destroy(mbar);
+	ui_window_destroy(window);
+	ui_destroy(ui);
+}
+
 /** Press event inside menu entry */
 PCUT_TEST(pos_press_inside)
