Index: uspace/lib/ui/src/pbutton.c
===================================================================
--- uspace/lib/ui/src/pbutton.c	(revision cd74fa821c3e13e287082a3dd6e4d121434cd360)
+++ uspace/lib/ui/src/pbutton.c	(revision 3c54869a5b618845c19d4f6200ee90ac99a446aa)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2021 Jiri Svoboda
+ * Copyright (c) 2023 Jiri Svoboda
  * All rights reserved.
  *
@@ -32,4 +32,7 @@
 /**
  * @file Push button
+ *
+ * Push button either uses text as decoration, or it can use a caller-provided
+ * function to paint the decoration.
  */
 
@@ -51,5 +54,7 @@
 enum {
 	ui_pb_press_dx = 1,
-	ui_pb_press_dy = 1
+	ui_pb_press_dy = 1,
+	ui_pb_pad_x = 2,
+	ui_pb_pad_x_text = 1
 };
 
@@ -111,4 +116,5 @@
 
 	ui_control_delete(pbutton->control);
+	free(pbutton->caption);
 	free(pbutton);
 }
@@ -136,4 +142,27 @@
 }
 
+/** Set push button decoration ops.
+ *
+ * @param pbutton Push button
+ * @param ops Push button decoration callbacks
+ * @param arg Decoration ops argument
+ */
+void ui_pbutton_set_decor_ops(ui_pbutton_t *pbutton,
+    ui_pbutton_decor_ops_t *ops, void *arg)
+{
+	pbutton->decor_ops = ops;
+	pbutton->decor_arg = arg;
+}
+
+/** Set push button flag.s
+ *
+ * @param pbutton Push button
+ * @param flags Flags
+ */
+void ui_pbutton_set_flags(ui_pbutton_t *pbutton, ui_pbutton_flags_t flags)
+{
+	pbutton->flags = flags;
+}
+
 /** Set button rectangle.
  *
@@ -157,4 +186,43 @@
 {
 	pbutton->isdefault = isdefault;
+}
+
+/** Get button light status.
+ *
+ * @param pbutton Button
+ * @return @c true iff light is on
+ */
+bool ui_pbutton_get_light(ui_pbutton_t *pbutton)
+{
+	return pbutton->light;
+}
+
+/** Turn button light on or off.
+ *
+ * @param pbutton Button
+ * @param light @c true iff button should be lit
+ */
+void ui_pbutton_set_light(ui_pbutton_t *pbutton, bool light)
+{
+	pbutton->light = light;
+}
+
+/** Set push button caption.
+ *
+ * @param pbutton Button
+ * @param caption New caption
+ * @return EOK on success, ENOMEM if out of memory
+ */
+errno_t ui_pbutton_set_caption(ui_pbutton_t *pbutton, const char *caption)
+{
+	char *dcaption;
+
+	dcaption = str_dup(caption);
+	if (dcaption == NULL)
+		return ENOMEM;
+
+	free(pbutton->caption);
+	pbutton->caption = dcaption;
+	return EOK;
 }
 
@@ -277,5 +345,7 @@
 	gfx_text_fmt_t fmt;
 	gfx_rect_t rect;
+	gfx_rect_t irect;
 	gfx_coord_t thickness;
+	gfx_color_t *color;
 	bool depressed;
 	errno_t rc;
@@ -289,5 +359,8 @@
 	rect.p1.y = pbutton->rect.p1.y - thickness;
 
-	rc = gfx_set_color(pbutton->res->gc, pbutton->res->btn_face_color);
+	color = pbutton->light ? pbutton->res->btn_face_lit_color :
+	    pbutton->res->btn_face_color;
+
+	rc = gfx_set_color(pbutton->res->gc, color);
 	if (rc != EOK)
 		goto error;
@@ -306,12 +379,25 @@
 	}
 
-	gfx_text_fmt_init(&fmt);
-	fmt.color = pbutton->res->btn_text_color;
-	fmt.halign = gfx_halign_center;
-	fmt.valign = gfx_valign_center;
-
-	rc = gfx_puttext(pbutton->res->font, &pos, &fmt, pbutton->caption);
-	if (rc != EOK)
-		goto error;
+	if (pbutton->decor_ops != NULL && pbutton->decor_ops->paint != NULL) {
+		/* Custom decoration */
+		rc = pbutton->decor_ops->paint(pbutton, pbutton->decor_arg,
+		    &pos);
+		if (rc != EOK)
+			goto error;
+	} else {
+		/* Text decoration */
+		ui_paint_get_inset_frame_inside(pbutton->res, &rect, &irect);
+		gfx_text_fmt_init(&fmt);
+		fmt.font = pbutton->res->font;
+		fmt.color = pbutton->res->btn_text_color;
+		fmt.halign = gfx_halign_center;
+		fmt.valign = gfx_valign_center;
+		fmt.abbreviate = true;
+		fmt.width = irect.p1.x - irect.p0.x - 2 * ui_pb_pad_x;
+
+		rc = gfx_puttext(&pos, &fmt, pbutton->caption);
+		if (rc != EOK)
+			goto error;
+	}
 
 	rc = ui_pbutton_paint_frame(pbutton);
@@ -351,5 +437,8 @@
 	errno_t rc;
 
-	depressed = pbutton->held && pbutton->inside;
+	if ((pbutton->flags & ui_pbf_no_text_depress) == 0)
+		depressed = pbutton->held && pbutton->inside;
+	else
+		depressed = false;
 
 	rc = gfx_set_color(pbutton->res->gc, pbutton->res->wnd_face_color);
@@ -366,5 +455,5 @@
 	rect.p1.y = pbutton->rect.p0.y + 1;
 
-	rc = gfx_set_color(pbutton->res->gc, pbutton->res->btn_highlight_color);
+	rc = gfx_set_color(pbutton->res->gc, pbutton->res->btn_face_color);
 	if (rc != EOK)
 		goto error;
@@ -379,9 +468,14 @@
 
 	gfx_text_fmt_init(&fmt);
+	fmt.font = pbutton->res->font;
 	fmt.color = pbutton->res->btn_text_color;
 	fmt.halign = gfx_halign_center;
 	fmt.valign = gfx_valign_center;
-
-	rc = gfx_puttext(pbutton->res->font, &pos, &fmt, pbutton->caption);
+	fmt.abbreviate = true;
+	fmt.width = rect.p1.x - rect.p0.x - 2 * ui_pb_pad_x_text;
+	if (fmt.width < 1)
+		fmt.width = 1;
+
+	rc = gfx_puttext(&pos, &fmt, pbutton->caption);
 	if (rc != EOK)
 		goto error;
@@ -427,4 +521,5 @@
 	pbutton->held = true;
 	(void) ui_pbutton_paint(pbutton);
+	ui_pbutton_down(pbutton);
 }
 
@@ -439,4 +534,5 @@
 
 	pbutton->held = false;
+	ui_pbutton_up(pbutton);
 
 	if (pbutton->inside) {
@@ -474,5 +570,5 @@
 }
 
-/** Button was clicked.
+/** Send button clicked event.
  *
  * @param pbutton Push button
@@ -482,4 +578,24 @@
 	if (pbutton->cb != NULL && pbutton->cb->clicked != NULL)
 		pbutton->cb->clicked(pbutton, pbutton->arg);
+}
+
+/** Send button down event.
+ *
+ * @param pbutton Push button
+ */
+void ui_pbutton_down(ui_pbutton_t *pbutton)
+{
+	if (pbutton->cb != NULL && pbutton->cb->down != NULL)
+		pbutton->cb->down(pbutton, pbutton->arg);
+}
+
+/** Send button up event.
+ *
+ * @param pbutton Push button
+ */
+void ui_pbutton_up(ui_pbutton_t *pbutton)
+{
+	if (pbutton->cb != NULL && pbutton->cb->up != NULL)
+		pbutton->cb->up(pbutton, pbutton->arg);
 }
 
@@ -521,4 +637,6 @@
 		}
 		break;
+	case POS_DCLICK:
+		break;
 	}
 
