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
|
||||
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
|
||||
|
||||
|
|
@ -431,7 +433,10 @@ Static text label. Sized to fit its text.
|
|||
```c
|
||||
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
|
||||
WidgetT *wgtCheckbox(WidgetT *parent, const char *text);
|
||||
|
|
@ -465,13 +470,117 @@ WidgetT *wgtTextArea(WidgetT *parent, int32_t maxLen);
|
|||
```
|
||||
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
|
||||
|
||||
```c
|
||||
WidgetT *wgtCanvas(WidgetT *parent, int32_t w, int32_t h);
|
||||
```
|
||||
Drawable bitmap canvas with a sunken bevel border. Supports freehand
|
||||
drawing with Bresenham-interpolated strokes.
|
||||
Drawable bitmap canvas with a sunken bevel border. Supports both
|
||||
interactive freehand drawing (mouse strokes are Bresenham-interpolated)
|
||||
and programmatic drawing from the API.
|
||||
|
||||
```c
|
||||
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
|
||||
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
|
||||
|
||||
```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.
|
||||
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
|
||||
// ============================================================
|
||||
|
|
|
|||
|
|
@ -35,13 +35,7 @@ void widgetButtonCalcMinSize(WidgetT *w, const BitmapFontT *font) {
|
|||
|
||||
void widgetButtonOnMouse(WidgetT *hit) {
|
||||
hit->as.button.pressed = true;
|
||||
wgtInvalidate(hit);
|
||||
|
||||
if (hit->onClick) {
|
||||
hit->onClick(hit);
|
||||
}
|
||||
|
||||
hit->as.button.pressed = false;
|
||||
sPressedButton = hit;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
// ============================================================
|
||||
|
|
@ -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
|
||||
// ============================================================
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
bool sDebugLayout = false;
|
||||
WidgetT *sOpenPopup = NULL;
|
||||
WidgetT *sPressedButton = NULL;
|
||||
WidgetT *sDragSlider = NULL;
|
||||
WidgetT *sDrawingCanvas = NULL;
|
||||
int32_t sDragOffset = 0;
|
||||
|
|
@ -101,6 +102,10 @@ void widgetDestroyChildren(WidgetT *w) {
|
|||
sOpenPopup = NULL;
|
||||
}
|
||||
|
||||
if (sPressedButton == child) {
|
||||
sPressedButton = NULL;
|
||||
}
|
||||
|
||||
if (sDragSlider == child) {
|
||||
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) {
|
||||
WidgetT *root = win->widgetRoot;
|
||||
WidgetT *closedPopup = NULL;
|
||||
|
||||
if (!root) {
|
||||
return;
|
||||
|
|
@ -327,6 +328,52 @@ void widgetOnMouse(WindowT *win, int32_t x, int32_t y, int32_t buttons) {
|
|||
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
|
||||
if (sOpenPopup && (buttons & 1)) {
|
||||
AppContextT *ctx = (AppContextT *)root->userData;
|
||||
|
|
@ -385,7 +432,9 @@ void widgetOnMouse(WindowT *win, int32_t x, int32_t y, int32_t buttons) {
|
|||
return;
|
||||
}
|
||||
|
||||
// Click outside popup — close it
|
||||
// Click outside popup — close it and remember which widget it was
|
||||
closedPopup = sOpenPopup;
|
||||
|
||||
if (sOpenPopup->type == WidgetDropdownE) {
|
||||
sOpenPopup->as.dropdown.open = false;
|
||||
} 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 != closedPopup) {
|
||||
widgetDropdownOnMouse(hit);
|
||||
}
|
||||
}
|
||||
|
||||
if (hit->type == WidgetComboBoxE && hit->enabled) {
|
||||
if (hit != closedPopup || vx < hit->x + hit->w - DROPDOWN_BTN_WIDTH) {
|
||||
widgetComboBoxOnMouse(hit, root, vx);
|
||||
}
|
||||
}
|
||||
|
||||
if (hit->type == WidgetSliderE && hit->enabled) {
|
||||
widgetSliderOnMouse(hit, vx, vy);
|
||||
|
|
|
|||
|
|
@ -46,6 +46,7 @@
|
|||
|
||||
extern bool sDebugLayout;
|
||||
extern WidgetT *sOpenPopup;
|
||||
extern WidgetT *sPressedButton;
|
||||
extern WidgetT *sDragSlider;
|
||||
extern WidgetT *sDrawingCanvas;
|
||||
extern int32_t sDragOffset;
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue