Some minor widget behavior updates.
This commit is contained in:
parent
685c8041c1
commit
b6c5ee3cf3
7 changed files with 454 additions and 14 deletions
133
README.md
133
README.md
|
|
@ -5,7 +5,9 @@ VESA VBE 2.0+ linear framebuffer.
|
||||||
|
|
||||||
Motif-style beveled chrome, dirty-rectangle compositing, draggable and
|
Motif-style beveled chrome, dirty-rectangle compositing, draggable and
|
||||||
resizable windows, dropdown menus, scrollbars, and a declarative widget/layout
|
resizable windows, dropdown menus, scrollbars, and a declarative widget/layout
|
||||||
system.
|
system with buttons, checkboxes, radios, text inputs, dropdowns, combo boxes,
|
||||||
|
sliders, progress bars, tab controls, tree views, toolbars, status bars,
|
||||||
|
images, and drawable canvases.
|
||||||
|
|
||||||
## Building
|
## Building
|
||||||
|
|
||||||
|
|
@ -431,7 +433,10 @@ Static text label. Sized to fit its text.
|
||||||
```c
|
```c
|
||||||
WidgetT *wgtButton(WidgetT *parent, const char *text);
|
WidgetT *wgtButton(WidgetT *parent, const char *text);
|
||||||
```
|
```
|
||||||
Beveled push button. Set `onClick` to handle clicks.
|
Beveled push button with standard press/release behavior: the button
|
||||||
|
visually depresses on mouse-down, tracks the cursor while held, and
|
||||||
|
fires `onClick` on release if still over the button. Dragging off the
|
||||||
|
button cancels the click.
|
||||||
|
|
||||||
```c
|
```c
|
||||||
WidgetT *wgtCheckbox(WidgetT *parent, const char *text);
|
WidgetT *wgtCheckbox(WidgetT *parent, const char *text);
|
||||||
|
|
@ -465,13 +470,117 @@ WidgetT *wgtTextArea(WidgetT *parent, int32_t maxLen);
|
||||||
```
|
```
|
||||||
Multi-line text area (basic).
|
Multi-line text area (basic).
|
||||||
|
|
||||||
|
### Dropdown and ComboBox
|
||||||
|
|
||||||
|
```c
|
||||||
|
WidgetT *wgtDropdown(WidgetT *parent);
|
||||||
|
void wgtDropdownSetItems(WidgetT *w, const char **items, int32_t count);
|
||||||
|
int32_t wgtDropdownGetSelected(const WidgetT *w);
|
||||||
|
void wgtDropdownSetSelected(WidgetT *w, int32_t idx);
|
||||||
|
```
|
||||||
|
Non-editable selection widget. Clicking the arrow button opens a popup
|
||||||
|
list below (or above if no room). Clicking the arrow again while the
|
||||||
|
popup is open closes it. Set `onChange` to be notified of selection
|
||||||
|
changes. The items array must remain valid for the widget's lifetime.
|
||||||
|
|
||||||
|
```c
|
||||||
|
WidgetT *wgtComboBox(WidgetT *parent, int32_t maxLen);
|
||||||
|
void wgtComboBoxSetItems(WidgetT *w, const char **items, int32_t count);
|
||||||
|
int32_t wgtComboBoxGetSelected(const WidgetT *w);
|
||||||
|
void wgtComboBoxSetSelected(WidgetT *w, int32_t idx);
|
||||||
|
```
|
||||||
|
Editable text field with a dropdown list. The text area accepts keyboard
|
||||||
|
input (same editing keys as `wgtTextInput`). Clicking the arrow button
|
||||||
|
toggles the popup; clicking the text area focuses it for editing.
|
||||||
|
Selecting an item copies its text into the edit buffer. Default weight
|
||||||
|
is 100.
|
||||||
|
|
||||||
|
### ProgressBar and Slider
|
||||||
|
|
||||||
|
```c
|
||||||
|
WidgetT *wgtProgressBar(WidgetT *parent);
|
||||||
|
void wgtProgressBarSetValue(WidgetT *w, int32_t value);
|
||||||
|
int32_t wgtProgressBarGetValue(const WidgetT *w);
|
||||||
|
```
|
||||||
|
Horizontal progress indicator. Value is 0--100 (percentage). The filled
|
||||||
|
portion draws left-to-right with the system highlight color.
|
||||||
|
|
||||||
|
```c
|
||||||
|
WidgetT *wgtSlider(WidgetT *parent, int32_t minVal, int32_t maxVal);
|
||||||
|
void wgtSliderSetValue(WidgetT *w, int32_t value);
|
||||||
|
int32_t wgtSliderGetValue(const WidgetT *w);
|
||||||
|
```
|
||||||
|
Draggable slider/trackbar. Horizontal by default; set
|
||||||
|
`w->as.slider.vertical = true` after creation for vertical orientation.
|
||||||
|
The thumb tracks the mouse while held. Set `onChange` to be notified.
|
||||||
|
|
||||||
|
### TabControl
|
||||||
|
|
||||||
|
```c
|
||||||
|
WidgetT *wgtTabControl(WidgetT *parent);
|
||||||
|
WidgetT *wgtTabPage(WidgetT *parent, const char *title);
|
||||||
|
void wgtTabControlSetActive(WidgetT *w, int32_t idx);
|
||||||
|
int32_t wgtTabControlGetActive(const WidgetT *w);
|
||||||
|
```
|
||||||
|
Tabbed container. Create a tab control, then add pages as children.
|
||||||
|
Each page is a VBox that holds its own widget subtree. Only the active
|
||||||
|
page is visible and receives layout; inactive pages are hidden. Clicking
|
||||||
|
a tab header switches the active page.
|
||||||
|
|
||||||
|
### StatusBar and Toolbar
|
||||||
|
|
||||||
|
```c
|
||||||
|
WidgetT *wgtStatusBar(WidgetT *parent);
|
||||||
|
WidgetT *wgtToolbar(WidgetT *parent);
|
||||||
|
```
|
||||||
|
Horizontal containers with specialized chrome. StatusBar draws a sunken
|
||||||
|
panel background; Toolbar draws a raised background. Both use horizontal
|
||||||
|
layout with default spacing. Add child widgets (labels, buttons, etc.)
|
||||||
|
as with any HBox. StatusBar children with `weight > 0` stretch to fill.
|
||||||
|
|
||||||
|
### TreeView
|
||||||
|
|
||||||
|
```c
|
||||||
|
WidgetT *wgtTreeView(WidgetT *parent);
|
||||||
|
WidgetT *wgtTreeItem(WidgetT *parent, const char *text);
|
||||||
|
void wgtTreeItemSetExpanded(WidgetT *w, bool expanded);
|
||||||
|
bool wgtTreeItemIsExpanded(const WidgetT *w);
|
||||||
|
```
|
||||||
|
Hierarchical tree with expand/collapse support. Create a tree view, then
|
||||||
|
add items as children. Nest items by adding them as children of other
|
||||||
|
items. Items with children show a `[+]`/`[-]` expand box.
|
||||||
|
|
||||||
|
The tree view manages its own internal scrollbars: a vertical scrollbar
|
||||||
|
appears when expanded items exceed the visible height, and a horizontal
|
||||||
|
scrollbar appears when item text extends beyond the visible width. Both
|
||||||
|
scrollbars have arrow buttons, proportional thumbs, and page
|
||||||
|
scrolling on trough clicks.
|
||||||
|
|
||||||
|
Default weight is 100. Set `onClick` on individual tree items to handle
|
||||||
|
selection, or `onChange` to be notified of expand/collapse.
|
||||||
|
|
||||||
|
### Image
|
||||||
|
|
||||||
|
```c
|
||||||
|
WidgetT *wgtImage(WidgetT *parent, uint8_t *data,
|
||||||
|
int32_t w, int32_t h, int32_t pitch);
|
||||||
|
WidgetT *wgtImageFromFile(WidgetT *parent, const char *path);
|
||||||
|
void wgtImageSetData(WidgetT *w, uint8_t *data,
|
||||||
|
int32_t imgW, int32_t imgH, int32_t pitch);
|
||||||
|
```
|
||||||
|
Display a bitmap image. `wgtImage` takes ownership of the pixel buffer
|
||||||
|
(freed on destroy). `wgtImageFromFile` loads any format supported by
|
||||||
|
stb_image and converts to the display pixel format. Set `onClick` to
|
||||||
|
make the image clickable.
|
||||||
|
|
||||||
### Canvas
|
### Canvas
|
||||||
|
|
||||||
```c
|
```c
|
||||||
WidgetT *wgtCanvas(WidgetT *parent, int32_t w, int32_t h);
|
WidgetT *wgtCanvas(WidgetT *parent, int32_t w, int32_t h);
|
||||||
```
|
```
|
||||||
Drawable bitmap canvas with a sunken bevel border. Supports freehand
|
Drawable bitmap canvas with a sunken bevel border. Supports both
|
||||||
drawing with Bresenham-interpolated strokes.
|
interactive freehand drawing (mouse strokes are Bresenham-interpolated)
|
||||||
|
and programmatic drawing from the API.
|
||||||
|
|
||||||
```c
|
```c
|
||||||
void wgtCanvasClear(WidgetT *w, uint32_t color);
|
void wgtCanvasClear(WidgetT *w, uint32_t color);
|
||||||
|
|
@ -484,6 +593,22 @@ int32_t wgtCanvasLoad(WidgetT *w, const char *path);
|
||||||
pixel format to RGB). `wgtCanvasLoad` reads any image format supported by
|
pixel format to RGB). `wgtCanvasLoad` reads any image format supported by
|
||||||
stb_image and converts it to display pixel format.
|
stb_image and converts it to display pixel format.
|
||||||
|
|
||||||
|
Programmatic drawing primitives (all coordinates are in canvas space):
|
||||||
|
|
||||||
|
```c
|
||||||
|
void wgtCanvasSetPixel(WidgetT *w, int32_t x, int32_t y, uint32_t color);
|
||||||
|
uint32_t wgtCanvasGetPixel(const WidgetT *w, int32_t x, int32_t y);
|
||||||
|
void wgtCanvasDrawLine(WidgetT *w, int32_t x0, int32_t y0, int32_t x1, int32_t y1);
|
||||||
|
void wgtCanvasDrawRect(WidgetT *w, int32_t x, int32_t y, int32_t width, int32_t height);
|
||||||
|
void wgtCanvasFillRect(WidgetT *w, int32_t x, int32_t y, int32_t width, int32_t height);
|
||||||
|
void wgtCanvasFillCircle(WidgetT *w, int32_t cx, int32_t cy, int32_t radius);
|
||||||
|
```
|
||||||
|
`SetPixel`/`GetPixel` take explicit colors. `DrawLine` uses the current
|
||||||
|
pen color and pen size. `DrawRect` draws a 1px outline, `FillRect` fills
|
||||||
|
solid, and `FillCircle` fills a circle -- all using the current pen
|
||||||
|
color. All operations clip to the canvas bounds. Colors are in display
|
||||||
|
pixel format (use `packColor()` to create them).
|
||||||
|
|
||||||
### Spacing and dividers
|
### Spacing and dividers
|
||||||
|
|
||||||
```c
|
```c
|
||||||
|
|
|
||||||
|
|
@ -416,6 +416,14 @@ int32_t wgtCanvasSave(WidgetT *w, const char *path);
|
||||||
// Load a PNG file onto the canvas. Returns 0 on success, -1 on failure.
|
// Load a PNG file onto the canvas. Returns 0 on success, -1 on failure.
|
||||||
int32_t wgtCanvasLoad(WidgetT *w, const char *path);
|
int32_t wgtCanvasLoad(WidgetT *w, const char *path);
|
||||||
|
|
||||||
|
// Programmatic drawing (coordinates are in canvas space)
|
||||||
|
void wgtCanvasDrawLine(WidgetT *w, int32_t x0, int32_t y0, int32_t x1, int32_t y1);
|
||||||
|
void wgtCanvasDrawRect(WidgetT *w, int32_t x, int32_t y, int32_t width, int32_t height);
|
||||||
|
void wgtCanvasFillRect(WidgetT *w, int32_t x, int32_t y, int32_t width, int32_t height);
|
||||||
|
void wgtCanvasFillCircle(WidgetT *w, int32_t cx, int32_t cy, int32_t radius);
|
||||||
|
void wgtCanvasSetPixel(WidgetT *w, int32_t x, int32_t y, uint32_t color);
|
||||||
|
uint32_t wgtCanvasGetPixel(const WidgetT *w, int32_t x, int32_t y);
|
||||||
|
|
||||||
// ============================================================
|
// ============================================================
|
||||||
// Operations
|
// Operations
|
||||||
// ============================================================
|
// ============================================================
|
||||||
|
|
|
||||||
|
|
@ -35,13 +35,7 @@ void widgetButtonCalcMinSize(WidgetT *w, const BitmapFontT *font) {
|
||||||
|
|
||||||
void widgetButtonOnMouse(WidgetT *hit) {
|
void widgetButtonOnMouse(WidgetT *hit) {
|
||||||
hit->as.button.pressed = true;
|
hit->as.button.pressed = true;
|
||||||
wgtInvalidate(hit);
|
sPressedButton = hit;
|
||||||
|
|
||||||
if (hit->onClick) {
|
|
||||||
hit->onClick(hit);
|
|
||||||
}
|
|
||||||
|
|
||||||
hit->as.button.pressed = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -245,6 +245,234 @@ void wgtCanvasClear(WidgetT *w, uint32_t color) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// wgtCanvasDrawLine
|
||||||
|
// ============================================================
|
||||||
|
//
|
||||||
|
// Draw a line using the current pen color and pen size.
|
||||||
|
|
||||||
|
void wgtCanvasDrawLine(WidgetT *w, int32_t x0, int32_t y0, int32_t x1, int32_t y1) {
|
||||||
|
if (!w || w->type != WidgetCanvasE || !w->as.canvas.data) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
canvasDrawLine(w, x0, y0, x1, y1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// wgtCanvasDrawRect
|
||||||
|
// ============================================================
|
||||||
|
//
|
||||||
|
// Draw a 1px outlined rectangle using the current pen color.
|
||||||
|
|
||||||
|
void wgtCanvasDrawRect(WidgetT *w, int32_t x, int32_t y, int32_t width, int32_t height) {
|
||||||
|
if (!w || w->type != WidgetCanvasE || !w->as.canvas.data) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (width <= 0 || height <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t bpp = w->as.canvas.canvasPitch / w->as.canvas.canvasW;
|
||||||
|
int32_t pitch = w->as.canvas.canvasPitch;
|
||||||
|
uint8_t *data = w->as.canvas.data;
|
||||||
|
int32_t cw = w->as.canvas.canvasW;
|
||||||
|
int32_t ch = w->as.canvas.canvasH;
|
||||||
|
uint32_t color = w->as.canvas.penColor;
|
||||||
|
|
||||||
|
// Top and bottom edges
|
||||||
|
for (int32_t px = x; px < x + width; px++) {
|
||||||
|
if (px < 0 || px >= cw) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (y >= 0 && y < ch) {
|
||||||
|
uint8_t *dst = data + y * pitch + px * bpp;
|
||||||
|
|
||||||
|
if (bpp == 1) {
|
||||||
|
*dst = (uint8_t)color;
|
||||||
|
} else if (bpp == 2) {
|
||||||
|
*(uint16_t *)dst = (uint16_t)color;
|
||||||
|
} else {
|
||||||
|
*(uint32_t *)dst = color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t by = y + height - 1;
|
||||||
|
|
||||||
|
if (by >= 0 && by < ch && by != y) {
|
||||||
|
uint8_t *dst = data + by * pitch + px * bpp;
|
||||||
|
|
||||||
|
if (bpp == 1) {
|
||||||
|
*dst = (uint8_t)color;
|
||||||
|
} else if (bpp == 2) {
|
||||||
|
*(uint16_t *)dst = (uint16_t)color;
|
||||||
|
} else {
|
||||||
|
*(uint32_t *)dst = color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Left and right edges (excluding corners already drawn)
|
||||||
|
for (int32_t py = y + 1; py < y + height - 1; py++) {
|
||||||
|
if (py < 0 || py >= ch) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (x >= 0 && x < cw) {
|
||||||
|
uint8_t *dst = data + py * pitch + x * bpp;
|
||||||
|
|
||||||
|
if (bpp == 1) {
|
||||||
|
*dst = (uint8_t)color;
|
||||||
|
} else if (bpp == 2) {
|
||||||
|
*(uint16_t *)dst = (uint16_t)color;
|
||||||
|
} else {
|
||||||
|
*(uint32_t *)dst = color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t rx = x + width - 1;
|
||||||
|
|
||||||
|
if (rx >= 0 && rx < cw && rx != x) {
|
||||||
|
uint8_t *dst = data + py * pitch + rx * bpp;
|
||||||
|
|
||||||
|
if (bpp == 1) {
|
||||||
|
*dst = (uint8_t)color;
|
||||||
|
} else if (bpp == 2) {
|
||||||
|
*(uint16_t *)dst = (uint16_t)color;
|
||||||
|
} else {
|
||||||
|
*(uint32_t *)dst = color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// wgtCanvasFillCircle
|
||||||
|
// ============================================================
|
||||||
|
//
|
||||||
|
// Draw a filled circle using the current pen color.
|
||||||
|
|
||||||
|
void wgtCanvasFillCircle(WidgetT *w, int32_t cx, int32_t cy, int32_t radius) {
|
||||||
|
if (!w || w->type != WidgetCanvasE || !w->as.canvas.data) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (radius <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t bpp = w->as.canvas.canvasPitch / w->as.canvas.canvasW;
|
||||||
|
int32_t pitch = w->as.canvas.canvasPitch;
|
||||||
|
uint8_t *data = w->as.canvas.data;
|
||||||
|
int32_t cw = w->as.canvas.canvasW;
|
||||||
|
int32_t ch = w->as.canvas.canvasH;
|
||||||
|
uint32_t color = w->as.canvas.penColor;
|
||||||
|
int32_t r2 = radius * radius;
|
||||||
|
|
||||||
|
for (int32_t dy = -radius; dy <= radius; dy++) {
|
||||||
|
int32_t py = cy + dy;
|
||||||
|
|
||||||
|
if (py < 0 || py >= ch) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int32_t dx = -radius; dx <= radius; dx++) {
|
||||||
|
int32_t px = cx + dx;
|
||||||
|
|
||||||
|
if (px < 0 || px >= cw) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dx * dx + dy * dy <= r2) {
|
||||||
|
uint8_t *dst = data + py * pitch + px * bpp;
|
||||||
|
|
||||||
|
if (bpp == 1) {
|
||||||
|
*dst = (uint8_t)color;
|
||||||
|
} else if (bpp == 2) {
|
||||||
|
*(uint16_t *)dst = (uint16_t)color;
|
||||||
|
} else {
|
||||||
|
*(uint32_t *)dst = color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// wgtCanvasFillRect
|
||||||
|
// ============================================================
|
||||||
|
//
|
||||||
|
// Draw a filled rectangle using the current pen color.
|
||||||
|
|
||||||
|
void wgtCanvasFillRect(WidgetT *w, int32_t x, int32_t y, int32_t width, int32_t height) {
|
||||||
|
if (!w || w->type != WidgetCanvasE || !w->as.canvas.data) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (width <= 0 || height <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t bpp = w->as.canvas.canvasPitch / w->as.canvas.canvasW;
|
||||||
|
int32_t pitch = w->as.canvas.canvasPitch;
|
||||||
|
uint8_t *data = w->as.canvas.data;
|
||||||
|
int32_t cw = w->as.canvas.canvasW;
|
||||||
|
int32_t ch = w->as.canvas.canvasH;
|
||||||
|
uint32_t color = w->as.canvas.penColor;
|
||||||
|
|
||||||
|
// Clip to canvas bounds
|
||||||
|
int32_t x0 = x < 0 ? 0 : x;
|
||||||
|
int32_t y0 = y < 0 ? 0 : y;
|
||||||
|
int32_t x1 = x + width > cw ? cw : x + width;
|
||||||
|
int32_t y1 = y + height > ch ? ch : y + height;
|
||||||
|
|
||||||
|
for (int32_t py = y0; py < y1; py++) {
|
||||||
|
for (int32_t px = x0; px < x1; px++) {
|
||||||
|
uint8_t *dst = data + py * pitch + px * bpp;
|
||||||
|
|
||||||
|
if (bpp == 1) {
|
||||||
|
*dst = (uint8_t)color;
|
||||||
|
} else if (bpp == 2) {
|
||||||
|
*(uint16_t *)dst = (uint16_t)color;
|
||||||
|
} else {
|
||||||
|
*(uint32_t *)dst = color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// wgtCanvasGetPixel
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
uint32_t wgtCanvasGetPixel(const WidgetT *w, int32_t x, int32_t y) {
|
||||||
|
if (!w || w->type != WidgetCanvasE || !w->as.canvas.data) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (x < 0 || x >= w->as.canvas.canvasW || y < 0 || y >= w->as.canvas.canvasH) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t bpp = w->as.canvas.canvasPitch / w->as.canvas.canvasW;
|
||||||
|
const uint8_t *src = w->as.canvas.data + y * w->as.canvas.canvasPitch + x * bpp;
|
||||||
|
|
||||||
|
if (bpp == 1) {
|
||||||
|
return *src;
|
||||||
|
} else if (bpp == 2) {
|
||||||
|
return *(const uint16_t *)src;
|
||||||
|
} else {
|
||||||
|
return *(const uint32_t *)src;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// ============================================================
|
// ============================================================
|
||||||
// wgtCanvasLoad
|
// wgtCanvasLoad
|
||||||
// ============================================================
|
// ============================================================
|
||||||
|
|
@ -397,6 +625,32 @@ void wgtCanvasSetPenSize(WidgetT *w, int32_t size) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// wgtCanvasSetPixel
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
void wgtCanvasSetPixel(WidgetT *w, int32_t x, int32_t y, uint32_t color) {
|
||||||
|
if (!w || w->type != WidgetCanvasE || !w->as.canvas.data) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (x < 0 || x >= w->as.canvas.canvasW || y < 0 || y >= w->as.canvas.canvasH) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t bpp = w->as.canvas.canvasPitch / w->as.canvas.canvasW;
|
||||||
|
uint8_t *dst = w->as.canvas.data + y * w->as.canvas.canvasPitch + x * bpp;
|
||||||
|
|
||||||
|
if (bpp == 1) {
|
||||||
|
*dst = (uint8_t)color;
|
||||||
|
} else if (bpp == 2) {
|
||||||
|
*(uint16_t *)dst = (uint16_t)color;
|
||||||
|
} else {
|
||||||
|
*(uint32_t *)dst = color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// ============================================================
|
// ============================================================
|
||||||
// widgetCanvasCalcMinSize
|
// widgetCanvasCalcMinSize
|
||||||
// ============================================================
|
// ============================================================
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@
|
||||||
|
|
||||||
bool sDebugLayout = false;
|
bool sDebugLayout = false;
|
||||||
WidgetT *sOpenPopup = NULL;
|
WidgetT *sOpenPopup = NULL;
|
||||||
|
WidgetT *sPressedButton = NULL;
|
||||||
WidgetT *sDragSlider = NULL;
|
WidgetT *sDragSlider = NULL;
|
||||||
WidgetT *sDrawingCanvas = NULL;
|
WidgetT *sDrawingCanvas = NULL;
|
||||||
int32_t sDragOffset = 0;
|
int32_t sDragOffset = 0;
|
||||||
|
|
@ -101,6 +102,10 @@ void widgetDestroyChildren(WidgetT *w) {
|
||||||
sOpenPopup = NULL;
|
sOpenPopup = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (sPressedButton == child) {
|
||||||
|
sPressedButton = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
if (sDragSlider == child) {
|
if (sDragSlider == child) {
|
||||||
sDragSlider = NULL;
|
sDragSlider = NULL;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -249,6 +249,7 @@ void widgetOnKey(WindowT *win, int32_t key, int32_t mod) {
|
||||||
|
|
||||||
void widgetOnMouse(WindowT *win, int32_t x, int32_t y, int32_t buttons) {
|
void widgetOnMouse(WindowT *win, int32_t x, int32_t y, int32_t buttons) {
|
||||||
WidgetT *root = win->widgetRoot;
|
WidgetT *root = win->widgetRoot;
|
||||||
|
WidgetT *closedPopup = NULL;
|
||||||
|
|
||||||
if (!root) {
|
if (!root) {
|
||||||
return;
|
return;
|
||||||
|
|
@ -327,6 +328,52 @@ void widgetOnMouse(WindowT *win, int32_t x, int32_t y, int32_t buttons) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handle button press release
|
||||||
|
if (sPressedButton && !(buttons & 1)) {
|
||||||
|
sPressedButton->as.button.pressed = false;
|
||||||
|
|
||||||
|
// Fire onClick if released over the same button in the same window
|
||||||
|
if (sPressedButton->window == win) {
|
||||||
|
int32_t scrollX = win->hScroll ? win->hScroll->value : 0;
|
||||||
|
int32_t scrollY = win->vScroll ? win->vScroll->value : 0;
|
||||||
|
int32_t vx = x + scrollX;
|
||||||
|
int32_t vy = y + scrollY;
|
||||||
|
|
||||||
|
if (vx >= sPressedButton->x && vx < sPressedButton->x + sPressedButton->w &&
|
||||||
|
vy >= sPressedButton->y && vy < sPressedButton->y + sPressedButton->h) {
|
||||||
|
if (sPressedButton->onClick) {
|
||||||
|
sPressedButton->onClick(sPressedButton);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
wgtInvalidate(sPressedButton);
|
||||||
|
sPressedButton = NULL;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle button press tracking (mouse move while held)
|
||||||
|
if (sPressedButton && (buttons & 1)) {
|
||||||
|
bool over = false;
|
||||||
|
|
||||||
|
if (sPressedButton->window == win) {
|
||||||
|
int32_t scrollX = win->hScroll ? win->hScroll->value : 0;
|
||||||
|
int32_t scrollY = win->vScroll ? win->vScroll->value : 0;
|
||||||
|
int32_t vx = x + scrollX;
|
||||||
|
int32_t vy = y + scrollY;
|
||||||
|
|
||||||
|
over = (vx >= sPressedButton->x && vx < sPressedButton->x + sPressedButton->w &&
|
||||||
|
vy >= sPressedButton->y && vy < sPressedButton->y + sPressedButton->h);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sPressedButton->as.button.pressed != over) {
|
||||||
|
sPressedButton->as.button.pressed = over;
|
||||||
|
wgtInvalidate(sPressedButton);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Handle open popup clicks
|
// Handle open popup clicks
|
||||||
if (sOpenPopup && (buttons & 1)) {
|
if (sOpenPopup && (buttons & 1)) {
|
||||||
AppContextT *ctx = (AppContextT *)root->userData;
|
AppContextT *ctx = (AppContextT *)root->userData;
|
||||||
|
|
@ -385,7 +432,9 @@ void widgetOnMouse(WindowT *win, int32_t x, int32_t y, int32_t buttons) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Click outside popup — close it
|
// Click outside popup — close it and remember which widget it was
|
||||||
|
closedPopup = sOpenPopup;
|
||||||
|
|
||||||
if (sOpenPopup->type == WidgetDropdownE) {
|
if (sOpenPopup->type == WidgetDropdownE) {
|
||||||
sOpenPopup->as.dropdown.open = false;
|
sOpenPopup->as.dropdown.open = false;
|
||||||
} else if (sOpenPopup->type == WidgetComboBoxE) {
|
} else if (sOpenPopup->type == WidgetComboBoxE) {
|
||||||
|
|
@ -455,12 +504,16 @@ void widgetOnMouse(WindowT *win, int32_t x, int32_t y, int32_t buttons) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hit->type == WidgetDropdownE && hit->enabled) {
|
if (hit->type == WidgetDropdownE && hit->enabled) {
|
||||||
|
if (hit != closedPopup) {
|
||||||
widgetDropdownOnMouse(hit);
|
widgetDropdownOnMouse(hit);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (hit->type == WidgetComboBoxE && hit->enabled) {
|
if (hit->type == WidgetComboBoxE && hit->enabled) {
|
||||||
|
if (hit != closedPopup || vx < hit->x + hit->w - DROPDOWN_BTN_WIDTH) {
|
||||||
widgetComboBoxOnMouse(hit, root, vx);
|
widgetComboBoxOnMouse(hit, root, vx);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (hit->type == WidgetSliderE && hit->enabled) {
|
if (hit->type == WidgetSliderE && hit->enabled) {
|
||||||
widgetSliderOnMouse(hit, vx, vy);
|
widgetSliderOnMouse(hit, vx, vy);
|
||||||
|
|
|
||||||
|
|
@ -46,6 +46,7 @@
|
||||||
|
|
||||||
extern bool sDebugLayout;
|
extern bool sDebugLayout;
|
||||||
extern WidgetT *sOpenPopup;
|
extern WidgetT *sOpenPopup;
|
||||||
|
extern WidgetT *sPressedButton;
|
||||||
extern WidgetT *sDragSlider;
|
extern WidgetT *sDragSlider;
|
||||||
extern WidgetT *sDrawingCanvas;
|
extern WidgetT *sDrawingCanvas;
|
||||||
extern int32_t sDragOffset;
|
extern int32_t sDragOffset;
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue