DVX_GUI/dvx/widgets/widgetImageButton.c

181 lines
5.5 KiB
C

// widgetImageButton.c -- Image button widget (button with image instead of text)
//
// Combines a Button's beveled border and press behavior with an Image's
// bitmap rendering. The image is centered within the button bounds and
// shifts by 1px on press, just like text in a regular button.
//
// Uses the same two-phase press model as Button: mouse press stores in
// sPressedButton, keyboard press (Space/Enter) stores in sKeyPressedBtn,
// and the event dispatcher handles release/cancel. The onClick callback
// fires on release, not press.
//
// The widget takes ownership of the image data buffer -- if widget creation
// fails, the data is freed to prevent leaks.
//
// The 4px added to min size (widgetImageButtonCalcMinSize) accounts for
// the 2px bevel on each side -- no extra padding is added beyond that,
// keeping image buttons compact for toolbar use.
#include "widgetInternal.h"
// ============================================================
// wgtImageButton
// ============================================================
WidgetT *wgtImageButton(WidgetT *parent, uint8_t *data, int32_t w, int32_t h, int32_t pitch) {
if (!parent || !data || w <= 0 || h <= 0) {
return NULL;
}
WidgetT *wgt = widgetAlloc(parent, WidgetImageButtonE);
if (wgt) {
wgt->as.imageButton.data = data;
wgt->as.imageButton.imgW = w;
wgt->as.imageButton.imgH = h;
wgt->as.imageButton.imgPitch = pitch;
wgt->as.imageButton.pressed = false;
} else {
free(data);
}
return wgt;
}
// ============================================================
// wgtImageButtonFromFile
// ============================================================
//
// Load an image from disk and create an ImageButton widget.
// Delegates to dvxLoadImage for format decoding and pixel conversion.
WidgetT *wgtImageButtonFromFile(WidgetT *parent, const char *path) {
if (!parent || !path) {
return NULL;
}
AppContextT *ctx = wgtGetContext(parent);
if (!ctx) {
return NULL;
}
int32_t imgW;
int32_t imgH;
int32_t pitch;
uint8_t *buf = dvxLoadImage(ctx, path, &imgW, &imgH, &pitch);
if (!buf) {
return NULL;
}
return wgtImageButton(parent, buf, imgW, imgH, pitch);
}
// ============================================================
// wgtImageButtonSetData
// ============================================================
void wgtImageButtonSetData(WidgetT *w, uint8_t *data, int32_t imgW, int32_t imgH, int32_t pitch) {
VALIDATE_WIDGET_VOID(w, WidgetImageButtonE);
free(w->as.imageButton.data);
w->as.imageButton.data = data;
w->as.imageButton.imgW = imgW;
w->as.imageButton.imgH = imgH;
w->as.imageButton.imgPitch = pitch;
wgtInvalidate(w);
}
// ============================================================
// widgetImageButtonDestroy
// ============================================================
void widgetImageButtonDestroy(WidgetT *w) {
free(w->as.imageButton.data);
}
// ============================================================
// widgetImageButtonCalcMinSize
// ============================================================
void widgetImageButtonCalcMinSize(WidgetT *w, const BitmapFontT *font) {
(void)font;
// Bevel border only, no extra padding
w->calcMinW = w->as.imageButton.imgW + 4;
w->calcMinH = w->as.imageButton.imgH + 4;
}
// ============================================================
// widgetImageButtonOnKey
// ============================================================
void widgetImageButtonOnKey(WidgetT *w, int32_t key, int32_t mod) {
(void)mod;
if (key == ' ' || key == 0x0D) {
w->as.imageButton.pressed = true;
sKeyPressedBtn = w;
wgtInvalidatePaint(w);
}
}
// ============================================================
// widgetImageButtonOnMouse
// ============================================================
void widgetImageButtonOnMouse(WidgetT *w, WidgetT *root, int32_t vx, int32_t vy) {
(void)root;
(void)vx;
(void)vy;
w->focused = true;
w->as.imageButton.pressed = true;
sPressedButton = w;
}
// ============================================================
// widgetImageButtonPaint
// ============================================================
void widgetImageButtonPaint(WidgetT *w, DisplayT *d, const BlitOpsT *ops, const BitmapFontT *font, const ColorSchemeT *colors) {
(void)font;
uint32_t bgFace = w->bgColor ? w->bgColor : colors->buttonFace;
bool pressed = w->as.imageButton.pressed && w->enabled;
BevelStyleT bevel;
bevel.highlight = pressed ? colors->windowShadow : colors->windowHighlight;
bevel.shadow = pressed ? colors->windowHighlight : colors->windowShadow;
bevel.face = bgFace;
bevel.width = 2;
drawBevel(d, ops, w->x, w->y, w->w, w->h, &bevel);
if (w->as.imageButton.data) {
int32_t imgX = w->x + (w->w - w->as.imageButton.imgW) / 2;
int32_t imgY = w->y + (w->h - w->as.imageButton.imgH) / 2;
if (pressed) {
imgX++;
imgY++;
}
rectCopy(d, ops, imgX, imgY,
w->as.imageButton.data, w->as.imageButton.imgPitch,
0, 0, w->as.imageButton.imgW, w->as.imageButton.imgH);
}
if (w->focused) {
uint32_t fg = w->fgColor ? w->fgColor : colors->contentFg;
int32_t off = pressed ? 1 : 0;
drawFocusRect(d, ops, w->x + 3 + off, w->y + 3 + off, w->w - 6, w->h - 6, fg);
}
}