diff --git a/src/newifc/genapi.c b/src/newifc/genapi.c
index f7b34c1e52f837a3f9977ed561b45d576c41a7fd..cc324be5cda77862f30a6ca012925fd05a28cbdc 100644
--- a/src/newifc/genapi.c
+++ b/src/newifc/genapi.c
@@ -45,6 +45,7 @@ enum attribute_impl {
 	attr_impl_global,
 	attr_impl_object,
 	attr_impl_root,
+	attr_impl_global_custom_setter,
 };
 
 struct attribute_info {
@@ -62,7 +63,9 @@ attributes[] = {
 	{"child_xpos", NI_attr_type_uint16_t, attr_impl_global, 1},
 	{"child_ypos", NI_attr_type_uint16_t, attr_impl_global, 1},
 	{"dirty", NI_attr_type_bool, attr_impl_root, 1},
+	{"focus", NI_attr_type_bool, attr_impl_global_custom_setter, 0},
 	{"height", NI_attr_type_uint16_t, attr_impl_global, 0},
+	{"hidden", NI_attr_type_bool, attr_impl_global, 1},
 	{"higherpeer", NI_attr_type_NewIfcObj, attr_impl_global, 1},
 	{"last_error", NI_attr_type_NI_err, attr_impl_global, 1},
 	{"locked", NI_attr_type_bool, attr_impl_root, 0},
@@ -98,6 +101,7 @@ error_inf[] = {
 	{"error_out_of_range", -1},
 	{"error_wont_fit", -1},
 	{"error_cancelled", -1},
+	{"error_skip_subtree", -1},
 };
 
 struct handler_info {
@@ -157,12 +161,14 @@ main(int argc, char **argv)
 	fputs("NI_err NI_copy(NewIfcObj obj, NewIfcObj *newobj);\n", header);
 	fputs("NI_err NI_create(enum NewIfc_object obj, NewIfcObj parent, NewIfcObj *newobj);\n", header);
 	fputs("NI_err NI_error(NewIfcObj obj);\n", header);
-	fputs("NI_err NI_walk_children(NewIfcObj obj, NI_err (*cb)(NewIfcObj obj, void *cb_data), void *cbdata);\n\n", header);
+	fputs("NI_err NI_walk_children(NewIfcObj obj, bool top_down, NI_err (*cb)(NewIfcObj obj, void *cb_data), void *cbdata);\n\n", header);
 
 	nitems = sizeof(attributes) / sizeof(attributes[0]);
 	for (i = 0; i < nitems; i++) {
-		if (!attributes[i].read_only)
+		if (!attributes[i].read_only) {
+			fprintf(header, "NI_err NI_add_%s_handler(NewIfcObj obj, NI_err (*handler)(NewIfcObj obj, %s newval, void *cbdata), void *cbdata);\n", attributes[i].name, type_str[attributes[i].type].type);
 			fprintf(header, "NI_err NI_set_%s(NewIfcObj obj, %s value);\n", attributes[i].name, type_str[attributes[i].type].type);
+		}
 		fprintf(header, "NI_err NI_get_%s(NewIfcObj obj, %s* value);\n", attributes[i].name, type_str[attributes[i].type].type);
 	}
 	fputs("\n#endif\n", header);
@@ -212,7 +218,7 @@ main(int argc, char **argv)
 	      "	NI_err (*set)(NewIfcObj niobj, const int attr, ...);\n"
 	      "	NI_err (*get)(NewIfcObj niobj, const int attr, ...);\n"
 	      "	NI_err (*copy)(NewIfcObj obj, NewIfcObj *newobj);\n"
-	      "	struct NewIfc_handler *handlers;\n"
+	      "	struct NewIfc_handler **handlers;\n"
 	      "	size_t handlers_sz;\n"
 	      "	NewIfcObj root;\n"
 	      "	NewIfcObj parent;\n"
@@ -232,6 +238,8 @@ main(int argc, char **argv)
 	      "	uint16_t child_width;\n"
 	      "	uint16_t child_xpos;\n"
 	      "	uint16_t child_ypos;\n"
+	      "	unsigned focus:1;\n"
+	      "	unsigned hidden:1;\n"
 	      "};\n\n", internal_header);
 
 	fputs("enum NewIfc_attribute {\n", internal_header);
@@ -325,9 +333,10 @@ main(int argc, char **argv)
 		                "{\n"
 		                "	if (obj->handlers == NULL)\n"
 		                "		return NewIfc_error_none;\n"
-		                "	struct NewIfc_handler *h = bsearch(&type, obj->handlers, obj->handlers_sz, sizeof(obj->handlers[0]), handler_compar);\n"
-		                "	if (h == NULL)\n"
+		                "	struct NewIfc_handler **head = bsearch(&type, obj->handlers, obj->handlers_sz, sizeof(obj->handlers[0]), handler_bsearch_compar);\n"
+		                "	if (head == NULL)\n"
 		                "		return NewIfc_error_none;\n"
+		                "	struct NewIfc_handler *h = *head;\n"
 		                "	while (h != NULL) {\n"
 		                "		NI_err ret = h->on_%s_change(obj, newval, h->cbdata);\n"
 		                "		if (ret != NewIfc_error_none)\n"
@@ -398,6 +407,8 @@ main(int argc, char **argv)
 					                "}\n\n", attributes[i].name, type_str[attributes[i].type].type, attributes[i].name, attributes[i].name);
 				}
 
+				// Fall-through
+			case attr_impl_global_custom_setter:
 				fprintf(c_code, "NI_err\n"
 				                "NI_get_%s(NewIfcObj obj, %s* value) {\n"
 				                "	NI_err ret;\n"
@@ -442,6 +453,30 @@ main(int argc, char **argv)
 				                "}\n\n", attributes[i].name, type_str[attributes[i].type].type, attributes[i].name);
 				break;
 		}
+		fprintf(c_code, "NI_err\n"
+		                "NI_add_%s_handler(NewIfcObj obj, NI_err (*handler)(NewIfcObj obj, %s newval, void *cbdata), void *cbdata)\n"
+		                "{\n"
+		                "	NI_err ret;\n"
+		                "	if (obj == NULL || handler == NULL)\n"
+		                "		return NewIfc_error_invalid_arg;\n"
+		                "	if (NI_set_locked(obj, true)) {\n"
+		                "		struct NewIfc_handler *h = malloc(sizeof(struct NewIfc_handler));\n"
+		                "		if (h == NULL)\n"
+		                "			return NewIfc_error_allocation_failure;\n"
+		                "		h->on_%s_change = handler;\n"
+		                "		h->cbdata = cbdata;\n"
+		                "		h->event = NewIfc_on_%s_change;\n"
+		                "		h->next = NULL;\n"
+		                "		ret = NI_install_handler(obj, h);\n"
+		                "		NI_set_locked(obj, false);\n"
+		                "	}\n"
+		                "	else\n"
+		                "		ret = NewIfc_error_lock_failed;\n"
+		                "	return ret;\n"
+		                "}\n\n", attributes[i].name, type_str[attributes[i].type].type, type_str[attributes[i].type].var_name, attributes[i].name);
 	}
+
+	fputs("#include \"newifc_nongen_after.c\"\n\n", c_code);
+
 	fclose(c_code);
 }
diff --git a/src/newifc/newifc_nongen.c b/src/newifc/newifc_nongen.c
index bf290feeec4abda817a8b2c230da7ac85985fc55..b9c7a861f12f1c09648c16a904a83845766a9315 100644
--- a/src/newifc/newifc_nongen.c
+++ b/src/newifc/newifc_nongen.c
@@ -28,27 +28,41 @@ NI_copy(NewIfcObj obj, NewIfcObj *newobj) {
 }
 
 static NI_err
-NI_walk_children_recurse(NewIfcObj obj, NI_err (*cb)(NewIfcObj obj, void *cb_data), void *cbdata)
+NI_walk_children_recurse(NewIfcObj obj, bool top_down, NI_err (*cb)(NewIfcObj obj, void *cb_data), void *cbdata)
 {
 	NI_err err;
+	NewIfcObj nobj;
 
 	if (!obj)
 		return NewIfc_error_none;
 	err = cb(obj, cbdata);
 	if (err != NewIfc_error_none)
 		return err;
-	if (obj->bottomchild != NULL) {
-		err = NI_walk_children_recurse(obj->bottomchild, cb, cbdata);
-		if (err != NewIfc_error_none)
+	if (top_down)
+		nobj = obj->topchild;
+	else
+		nobj = obj->bottomchild;
+
+	if (nobj != NULL) {
+		err = NI_walk_children_recurse(nobj, top_down, cb, cbdata);
+		if (err != NewIfc_error_none && err != NewIfc_error_skip_subtree)
 			return err;
 	}
-	if (!obj->higherpeer)
+	if (top_down)
+		nobj = obj->lowerpeer;
+	else
+		nobj = obj->higherpeer;
+
+	if (!nobj)
 		return NewIfc_error_none;
-	return NI_walk_children_recurse(obj->higherpeer, cb, cbdata);
+	err = NI_walk_children_recurse(nobj, top_down, cb, cbdata);
+	if (err != NewIfc_error_none && err != NewIfc_error_skip_subtree)
+		return err;
+	return NewIfc_error_none;
 }
 
 NI_err
-NI_walk_children(NewIfcObj obj, NI_err (*cb)(NewIfcObj obj, void *cb_data), void *cbdata)
+NI_walk_children(NewIfcObj obj, bool top_down, NI_err (*cb)(NewIfcObj obj, void *cb_data), void *cbdata)
 {
 	NI_err ret;
 
@@ -58,7 +72,7 @@ NI_walk_children(NewIfcObj obj, NI_err (*cb)(NewIfcObj obj, void *cb_data), void
 		return NewIfc_error_invalid_arg;
 	ret = NI_set_locked(obj, true);
 	if (ret == NewIfc_error_none) {
-		ret = NI_walk_children_recurse(obj->bottomchild, cb, cbdata);
+		ret = NI_walk_children_recurse(obj->bottomchild, top_down, cb, cbdata);
 		NI_set_locked(obj, false);
 	}
 	else
@@ -66,15 +80,59 @@ NI_walk_children(NewIfcObj obj, NI_err (*cb)(NewIfcObj obj, void *cb_data), void
 	return ret;
 }
 
-int
-handler_compar(const void *a, const void *b)
+static int
+handler_bsearch_compar(const void *a, const void *b)
 {
 	const enum NewIfc_event_handlers *key = a;
-	const struct NewIfc_handler *tst = b;
+	const struct NewIfc_handler **tst = (const struct NewIfc_handler **)b;
 
-	if (*key < tst->event)
+	if (*key < (*tst)->event)
 		return -1;
-	if (*key > tst->event)
+	if (*key > (*tst)->event)
 		return 1;
 	return 0;
 }
+
+static int
+handler_qsort_compar(const void *a, const void *b)
+{
+	const struct NewIfc_handler **ap = (const struct NewIfc_handler **)a;
+	const struct NewIfc_handler **bp = (const struct NewIfc_handler **)b;
+
+	return ((*ap)->event - (*bp)->event);
+}
+
+static NI_err
+NI_install_handler(NewIfcObj obj, struct NewIfc_handler *handler)
+{
+	struct NewIfc_handler **head = bsearch(&handler->event, obj->handlers,
+	    obj->handlers_sz, sizeof(obj->handlers[0]), handler_bsearch_compar);
+
+	if (head != NULL) {
+		handler->next = *head;
+		*head = handler;
+		return NewIfc_error_none;
+	}
+	// Increase the size of handlers...
+	// TODO: this is pretty inefficient, a bsearch() for insert point
+	// would be best, but even a linear search for it would likely be
+	// better.
+	size_t new_sz = obj->handlers_sz;
+	struct NewIfc_handler ***na = realloc(obj->handlers, new_sz * sizeof(obj->handlers[0]));
+	if (na == NULL)
+		return NewIfc_error_allocation_failure;
+	obj->handlers[obj->handlers_sz] = handler;
+	obj->handlers_sz = new_sz;
+	qsort(obj->handlers, obj->handlers_sz, sizeof(obj->handlers[0]), handler_qsort_compar);
+	return NewIfc_error_none;
+}
+
+static NI_err
+remove_focus_cb(NewIfcObj obj, void *cbdata)
+{
+	(void)cbdata;
+	if (obj->focus == false)
+		return NewIfc_error_skip_subtree;
+	obj->set(obj, NewIfc_focus, false);
+	return NewIfc_error_none;
+}
diff --git a/src/newifc/root_window.c b/src/newifc/root_window.c
index f92cab99be2a51fefba960ce992331a6dc11b727..36d40332bc086d0578b6899d8b15ec29008b901a 100644
--- a/src/newifc/root_window.c
+++ b/src/newifc/root_window.c
@@ -63,7 +63,7 @@ rw_recalc_child(struct root_window *rw, uint16_t height, uint16_t width)
 		rw->api.last_error = NewIfc_error_wont_fit;
 		return;
 	}
-	if (NI_walk_children((NewIfcObj)rw, rw_recalc_child_cb, &nsz) != NewIfc_error_none) {
+	if (NI_walk_children((NewIfcObj)rw, false, rw_recalc_child_cb, &nsz) != NewIfc_error_none) {
 		rw->api.last_error = NewIfc_error_wont_fit;
 		return;
 	}
@@ -242,6 +242,7 @@ NewIFC_root_window(NewIfcObj *newobj)
 	(*newrw)->api.min_width = 40;
 	// TODO: This is only needed by the unit tests...
 	(*newrw)->api.root = *newobj;
+	(*newrw)->api.focus = true;
 	(*newrw)->show_title = true;
 	(*newrw)->help = true;
 	(*newrw)->title_sz = 0;
@@ -276,6 +277,7 @@ void test_root_window(CuTest *ct)
 	CuAssertTrue(ct, obj->min_width == 40);
 	CuAssertTrue(ct, obj->min_height == 2);
 	CuAssertTrue(ct, obj->last_error == NewIfc_error_none);
+	CuAssertTrue(ct, obj->focus == true);
 	CuAssertTrue(ct, obj->get(obj, NewIfc_show_title, &b) == NewIfc_error_none && b);
 	CuAssertTrue(ct, obj->last_error == NewIfc_error_none);
 	CuAssertTrue(ct, obj->get(obj, NewIfc_show_help, &b) == NewIfc_error_none && b);