VTable optimizations

This commit is contained in:
Scott Duensing 2026-03-26 15:38:58 -05:00
parent 0ceae99da3
commit 8886cee933
28 changed files with 374 additions and 493 deletions

View file

@ -165,19 +165,13 @@ The vtable has 26 function slots plus a flags field:
| `getText` | `(w)` | Return widget text | | `getText` | `(w)` | Return widget text |
| `setText` | `(w, text)` | Set widget text | | `setText` | `(w, text)` | Set widget text |
| `clearSelection` | `(w)` | Clear text/item selection | | `clearSelection` | `(w)` | Clear text/item selection |
| `dragSelect` | `(w, root, vx, vy)` | Handle drag selection |
| `openPopup` | `(w)` | Open dropdown popup |
| `closePopup` | `(w)` | Close dropdown popup | | `closePopup` | `(w)` | Close dropdown popup |
| `getPopupItemCount` | `(w)` | Return number of popup items |
| `getPopupRect` | `(w, font, contentH, popX, popY, popW, popH)` | Compute popup rectangle | | `getPopupRect` | `(w, font, contentH, popX, popY, popW, popH)` | Compute popup rectangle |
| `setPressed` | `(w, pressed)` | Set button press visual state | | `onDragUpdate` | `(w, root, x, y)` | Mouse move during drag |
| `reorderDrop` | `(w)` | Complete drag-reorder operation | | `onDragEnd` | `(w, root, x, y)` | Mouse release after drag |
| `reorderUpdate` | `(w, root, x, y)` | Update drag-reorder position |
| `getCursorShape` | `(w, vx, vy)` | Return cursor ID for this position | | `getCursorShape` | `(w, vx, vy)` | Return cursor ID for this position |
| `poll` | `(w, win)` | Periodic polling (AnsiTerm comms) | | `poll` | `(w, win)` | Periodic polling (AnsiTerm comms) |
| `quickRepaint` | `(w, outY, outH)` | Fast incremental repaint | | `quickRepaint` | `(w, outY, outH)` | Fast incremental repaint |
| `getTextFieldWidth` | `(w)` | Text field width for scroll calc |
| `scrollDragUpdate` | `(w, orient, dragOff, mouseX, mouseY)` | Scrollbar drag update |
### WidgetClassT Flags ### WidgetClassT Flags

View file

@ -2657,12 +2657,12 @@ bool dvxUpdate(AppContextT *ctx) {
// onClick. The one-frame delay ensures the pressed visual state // onClick. The one-frame delay ensures the pressed visual state
// renders before the callback runs (which may open a dialog, etc.). // renders before the callback runs (which may open a dialog, etc.).
if (sKeyPressedBtn) { if (sKeyPressedBtn) {
if (sKeyPressedBtn->wclass && sKeyPressedBtn->wclass->setPressed) { if (sKeyPressedBtn->wclass && sKeyPressedBtn->wclass->onDragEnd) {
sKeyPressedBtn->wclass->setPressed(sKeyPressedBtn, false); // Pass button center as coordinates so bounds check succeeds and onClick fires
} WidgetT *root = sKeyPressedBtn->window ? sKeyPressedBtn->window->widgetRoot : sKeyPressedBtn;
sKeyPressedBtn->wclass->onDragEnd(sKeyPressedBtn, root,
if (sKeyPressedBtn->onClick) { sKeyPressedBtn->x + sKeyPressedBtn->w / 2,
sKeyPressedBtn->onClick(sKeyPressedBtn); sKeyPressedBtn->y + sKeyPressedBtn->h / 2);
} }
wgtInvalidate(sKeyPressedBtn); wgtInvalidate(sKeyPressedBtn);
@ -4551,14 +4551,12 @@ static void updateCursorShape(AppContextT *ctx) {
newCursor = CURSOR_RESIZE_V; newCursor = CURSOR_RESIZE_V;
} }
} }
// Active ListView column resize drag // Active widget drag -- query cursor shape from the dragged widget
else if (sResizeListView) { else if (sDragWidget && sDragWidget->wclass && sDragWidget->wclass->getCursorShape) {
newCursor = CURSOR_RESIZE_H; int32_t shape = sDragWidget->wclass->getCursorShape(sDragWidget, 0, 0);
}
// Active splitter drag if (shape > 0) {
else if (sDragSplitter) { newCursor = shape;
if (sDragSplitter->wclass && sDragSplitter->wclass->getCursorShape) {
newCursor = sDragSplitter->wclass->getCursorShape(sDragSplitter, 0, 0);
} }
} }
// Not in an active drag/resize -- check what we're hovering // Not in an active drag/resize -- check what we're hovering

View file

@ -168,22 +168,18 @@ typedef struct WidgetClassT {
const char *(*getText)(const struct WidgetT *w); const char *(*getText)(const struct WidgetT *w);
void (*setText)(struct WidgetT *w, const char *text); void (*setText)(struct WidgetT *w, const char *text);
// Selection / drag-select // Selection
bool (*clearSelection)(struct WidgetT *w); bool (*clearSelection)(struct WidgetT *w);
void (*dragSelect)(struct WidgetT *w, struct WidgetT *root, int32_t vx, int32_t vy);
// Popup (dropdown/combobox) // Popup (dropdown/combobox)
void (*openPopup)(struct WidgetT *w);
void (*closePopup)(struct WidgetT *w); void (*closePopup)(struct WidgetT *w);
int32_t (*getPopupItemCount)(const struct WidgetT *w);
void (*getPopupRect)(const struct WidgetT *w, const BitmapFontT *font, int32_t contentH, int32_t *popX, int32_t *popY, int32_t *popW, int32_t *popH); void (*getPopupRect)(const struct WidgetT *w, const BitmapFontT *font, int32_t contentH, int32_t *popX, int32_t *popY, int32_t *popW, int32_t *popH);
// Button press state // Generic drag (button press, slider, scrollbar, text select, reorder, etc.)
void (*setPressed)(struct WidgetT *w, bool pressed); // Core sets sDragWidget on mouse-down; calls onDragUpdate on mouse-move
// and onDragEnd on mouse-up. Each widget stores its own drag state.
// Drag reorder (listbox/listview/treeview) void (*onDragUpdate)(struct WidgetT *w, struct WidgetT *root, int32_t x, int32_t y);
void (*reorderDrop)(struct WidgetT *w); void (*onDragEnd)(struct WidgetT *w, struct WidgetT *root, int32_t x, int32_t y);
void (*reorderUpdate)(struct WidgetT *w, struct WidgetT *root, int32_t x, int32_t y);
// Cursor shape (returns cursor ID, 0 = default) // Cursor shape (returns cursor ID, 0 = default)
int32_t (*getCursorShape)(const struct WidgetT *w, int32_t vx, int32_t vy); int32_t (*getCursorShape)(const struct WidgetT *w, int32_t vx, int32_t vy);
@ -194,14 +190,8 @@ typedef struct WidgetClassT {
// Fast incremental repaint (returns dirty row count, 0 = none) // Fast incremental repaint (returns dirty row count, 0 = none)
int32_t (*quickRepaint)(struct WidgetT *w, int32_t *outY, int32_t *outH); int32_t (*quickRepaint)(struct WidgetT *w, int32_t *outY, int32_t *outH);
// Text field width (for scroll calculations; 0 = use widget width)
int32_t (*getTextFieldWidth)(const struct WidgetT *w);
// Scroll a child widget into the visible area (ScrollPane, etc.) // Scroll a child widget into the visible area (ScrollPane, etc.)
void (*scrollChildIntoView)(struct WidgetT *parent, const struct WidgetT *child); void (*scrollChildIntoView)(struct WidgetT *parent, const struct WidgetT *child);
// Scrollbar drag update (orient: 0=vert, 1=horiz)
void (*scrollDragUpdate)(struct WidgetT *w, int32_t orient, int32_t dragOff, int32_t mouseX, int32_t mouseY);
} WidgetClassT; } WidgetClassT;
// ============================================================ // ============================================================

View file

@ -101,18 +101,7 @@ extern WidgetT *sClosedPopup;
extern WidgetT *sFocusedWidget; extern WidgetT *sFocusedWidget;
extern WidgetT *sKeyPressedBtn; extern WidgetT *sKeyPressedBtn;
extern WidgetT *sOpenPopup; extern WidgetT *sOpenPopup;
extern WidgetT *sPressedButton; extern WidgetT *sDragWidget; // widget being dragged (any drag type)
extern WidgetT *sDragSlider;
extern WidgetT *sDrawingCanvas;
extern WidgetT *sDragTextSelect;
extern int32_t sDragOffset;
extern WidgetT *sResizeListView;
extern WidgetT *sDragSplitter;
extern int32_t sDragSplitStart;
extern WidgetT *sDragReorder;
extern WidgetT *sDragScrollbar;
extern int32_t sDragScrollbarOff;
extern int32_t sDragScrollbarOrient;
extern void (*sCursorBlinkFn)(void); extern void (*sCursorBlinkFn)(void);
// ============================================================ // ============================================================
@ -167,7 +156,6 @@ typedef enum {
void widgetDrawScrollbarH(DisplayT *d, const BlitOpsT *ops, const ColorSchemeT *colors, int32_t sbX, int32_t sbY, int32_t sbW, int32_t totalSize, int32_t visibleSize, int32_t scrollPos); void widgetDrawScrollbarH(DisplayT *d, const BlitOpsT *ops, const ColorSchemeT *colors, int32_t sbX, int32_t sbY, int32_t sbW, int32_t totalSize, int32_t visibleSize, int32_t scrollPos);
void widgetDrawScrollbarV(DisplayT *d, const BlitOpsT *ops, const ColorSchemeT *colors, int32_t sbX, int32_t sbY, int32_t sbH, int32_t totalSize, int32_t visibleSize, int32_t scrollPos); void widgetDrawScrollbarV(DisplayT *d, const BlitOpsT *ops, const ColorSchemeT *colors, int32_t sbX, int32_t sbY, int32_t sbH, int32_t totalSize, int32_t visibleSize, int32_t scrollPos);
void widgetScrollbarDragUpdate(WidgetT *w, int32_t orient, int32_t dragOff, int32_t mouseX, int32_t mouseY);
ScrollHitE widgetScrollbarHitTest(int32_t sbLen, int32_t relPos, int32_t totalSize, int32_t visibleSize, int32_t scrollPos); ScrollHitE widgetScrollbarHitTest(int32_t sbLen, int32_t relPos, int32_t totalSize, int32_t visibleSize, int32_t scrollPos);
// ============================================================ // ============================================================
@ -202,14 +190,4 @@ void widgetPaintOverlays(WidgetT *root, DisplayT *d, const BlitOpsT *ops, const
// listhelp/listHelp.h -- dropdown arrow, popup list, keyboard nav // listhelp/listHelp.h -- dropdown arrow, popup list, keyboard nav
// Widget DXEs that use these include the headers directly. // Widget DXEs that use these include the headers directly.
// widgetTextDragUpdate stays in core (widgetCore.c)
void widgetTextDragUpdate(WidgetT *w, WidgetT *root, int32_t vx, int32_t vy);
// ============================================================
// Drag-reorder helpers (widgetEvent.c)
// ============================================================
void widgetReorderUpdate(WidgetT *w, WidgetT *root, int32_t x, int32_t y);
void widgetReorderDrop(WidgetT *w);
#endif // DVX_WIDGET_PLUGIN_H #endif // DVX_WIDGET_PLUGIN_H

View file

@ -46,18 +46,7 @@ clock_t sDblClickTicks = 0; // set from ctx->dblClickTicks during first p
bool sDebugLayout = false; bool sDebugLayout = false;
WidgetT *sFocusedWidget = NULL; // currently focused widget (O(1) access, avoids tree walk) WidgetT *sFocusedWidget = NULL; // currently focused widget (O(1) access, avoids tree walk)
WidgetT *sOpenPopup = NULL; // dropdown/combobox with open popup list WidgetT *sOpenPopup = NULL; // dropdown/combobox with open popup list
WidgetT *sPressedButton = NULL; // button being held down (tracks mouse in/out) WidgetT *sDragWidget = NULL; // widget being dragged (any drag type)
WidgetT *sDragSlider = NULL; // slider being dragged
WidgetT *sDrawingCanvas = NULL; // canvas receiving paint strokes
WidgetT *sDragTextSelect = NULL; // text widget in drag-select mode
int32_t sDragOffset = 0; // pixel offset from drag start to thumb center
WidgetT *sResizeListView = NULL; // ListView undergoing column resize
WidgetT *sDragSplitter = NULL; // splitter being dragged
int32_t sDragSplitStart = 0; // mouse offset from splitter edge at drag start
WidgetT *sDragReorder = NULL; // list/tree widget in drag-reorder mode
WidgetT *sDragScrollbar = NULL; // widget whose scrollbar thumb is being dragged
int32_t sDragScrollbarOff = 0; // mouse offset within thumb at drag start
int32_t sDragScrollbarOrient = 0; // 0=vertical, 1=horizontal
// Shared clipboard -- process-wide, not per-widget. // Shared clipboard -- process-wide, not per-widget.
#define CLIPBOARD_MAX 4096 #define CLIPBOARD_MAX 4096
@ -275,24 +264,8 @@ void widgetDestroyChildren(WidgetT *w) {
sOpenPopup = NULL; sOpenPopup = NULL;
} }
if (sPressedButton == child) { if (sDragWidget == child) {
sPressedButton = NULL; sDragWidget = NULL;
}
if (sDragSlider == child) {
sDragSlider = NULL;
}
if (sDrawingCanvas == child) {
sDrawingCanvas = NULL;
}
if (sResizeListView == child) {
sResizeListView = NULL;
}
if (sDragScrollbar == child) {
sDragScrollbar = NULL;
} }
free(child); free(child);
@ -657,18 +630,3 @@ void widgetRemoveChild(WidgetT *parent, WidgetT *child) {
} }
// ============================================================
// widgetTextDragUpdate -- update selection during mouse drag
// ============================================================
//
// Called by the event loop on mouse-move while sDragTextSelect is set.
// Extends the selection from the anchor to the current mouse position.
// Handles auto-scroll: when the mouse is past the widget edges, the
// scroll offset is nudged by one unit per event, creating a smooth
// scroll-while-dragging effect.
void widgetTextDragUpdate(WidgetT *w, WidgetT *root, int32_t vx, int32_t vy) {
if (w->wclass && w->wclass->dragSelect) {
w->wclass->dragSelect(w, root, vx, vy);
}
}

View file

@ -214,23 +214,29 @@ void widgetOnMouse(WindowT *win, int32_t x, int32_t y, int32_t buttons) {
sOpenPopup = NULL; sOpenPopup = NULL;
} }
// Handle text drag-select release // Handle drag release
if (sDragTextSelect && !(buttons & MOUSE_LEFT)) { if (sDragWidget && !(buttons & MOUSE_LEFT)) {
sDragTextSelect = NULL; if (sDragWidget->wclass && sDragWidget->wclass->onDragEnd) {
sDragWidget->wclass->onDragEnd(sDragWidget, root, x, y);
}
wgtInvalidatePaint(root);
sDragWidget = NULL;
return; return;
} }
// Handle text drag-select (mouse move while pressed) // Handle drag move
if (sDragTextSelect && (buttons & MOUSE_LEFT)) { if (sDragWidget && (buttons & MOUSE_LEFT)) {
widgetTextDragUpdate(sDragTextSelect, root, x, y); if (sDragWidget->wclass && sDragWidget->wclass->onDragUpdate) {
sDragWidget->wclass->onDragUpdate(sDragWidget, root, x, y);
}
if (sDragTextSelect->wclass && sDragTextSelect->wclass->quickRepaint) { // quickRepaint fast path for text drag (dirty rect instead of full repaint)
// Fast path: repaint only dirty rows into the content if (sDragWidget->wclass && sDragWidget->wclass->quickRepaint) {
// buffer, then dirty just that screen stripe.
int32_t dirtyY = 0; int32_t dirtyY = 0;
int32_t dirtyH = 0; int32_t dirtyH = 0;
if (sDragTextSelect->wclass->quickRepaint(sDragTextSelect, &dirtyY, &dirtyH) > 0) { if (sDragWidget->wclass->quickRepaint(sDragWidget, &dirtyY, &dirtyH) > 0) {
AppContextT *ctx = (AppContextT *)root->userData; AppContextT *ctx = (AppContextT *)root->userData;
int32_t scrollY2 = win->vScroll ? win->vScroll->value : 0; int32_t scrollY2 = win->vScroll ? win->vScroll->value : 0;
int32_t rectX = win->x + win->contentX; int32_t rectX = win->x + win->contentX;
@ -238,108 +244,13 @@ void widgetOnMouse(WindowT *win, int32_t x, int32_t y, int32_t buttons) {
int32_t rectW = win->contentW; int32_t rectW = win->contentW;
win->contentDirty = true; win->contentDirty = true;
dirtyListAdd(&ctx->dirty, rectX, rectY, rectW, dirtyH); dirtyListAdd(&ctx->dirty, rectX, rectY, rectW, dirtyH);
return;
} }
} else {
wgtInvalidate(root);
} }
return; // Scroll containers need full relayout
} if (sDragWidget->wclass && (sDragWidget->wclass->flags & WCLASS_RELAYOUT_ON_SCROLL)) {
wgtInvalidate(sDragWidget);
// Handle canvas drawing release
if (sDrawingCanvas && !(buttons & MOUSE_LEFT)) {
sDrawingCanvas = NULL;
wgtInvalidatePaint(root);
return;
}
// Handle canvas drawing (mouse move while pressed)
if (sDrawingCanvas && (buttons & MOUSE_LEFT)) {
if (sDrawingCanvas->wclass && sDrawingCanvas->wclass->onMouse) {
sDrawingCanvas->wclass->onMouse(sDrawingCanvas, root, x, y);
}
wgtInvalidatePaint(root);
return;
}
// Handle slider drag release
if (sDragSlider && !(buttons & MOUSE_LEFT)) {
sDragSlider = NULL;
wgtInvalidatePaint(root);
return;
}
// Handle slider drag (mouse move while pressed)
if (sDragSlider && (buttons & MOUSE_LEFT)) {
if (sDragSlider->wclass && sDragSlider->wclass->scrollDragUpdate) {
sDragSlider->wclass->scrollDragUpdate(sDragSlider, 0, sDragOffset, x, y);
wgtInvalidatePaint(root);
}
return;
}
// Handle ListView column resize release
if (sResizeListView && !(buttons & MOUSE_LEFT)) {
sResizeListView = NULL;
return;
}
// Handle ListView column resize drag via vtable
if (sResizeListView && (buttons & MOUSE_LEFT)) {
if (sResizeListView->wclass && sResizeListView->wclass->scrollDragUpdate) {
sResizeListView->wclass->scrollDragUpdate(sResizeListView, -1, 0, x, y);
wgtInvalidatePaint(root);
}
return;
}
// Handle drag-reorder release
if (sDragReorder && !(buttons & MOUSE_LEFT)) {
widgetReorderDrop(sDragReorder);
sDragReorder = NULL;
wgtInvalidatePaint(root);
return;
}
// Handle drag-reorder move
if (sDragReorder && (buttons & MOUSE_LEFT)) {
widgetReorderUpdate(sDragReorder, root, x, y);
wgtInvalidatePaint(root);
return;
}
// Handle splitter drag release
if (sDragSplitter && !(buttons & MOUSE_LEFT)) {
sDragSplitter = NULL;
return;
}
// Handle splitter drag via vtable
if (sDragSplitter && (buttons & MOUSE_LEFT)) {
if (sDragSplitter->wclass && sDragSplitter->wclass->scrollDragUpdate) {
sDragSplitter->wclass->scrollDragUpdate(sDragSplitter, 0, sDragSplitStart, x, y);
}
return;
}
// Handle scrollbar thumb drag release
if (sDragScrollbar && !(buttons & MOUSE_LEFT)) {
sDragScrollbar = NULL;
wgtInvalidatePaint(root);
return;
}
// Handle scrollbar thumb drag (mouse move while pressed)
if (sDragScrollbar && (buttons & MOUSE_LEFT)) {
widgetScrollbarDragUpdate(sDragScrollbar, sDragScrollbarOrient, sDragScrollbarOff, x, y);
// Scroll containers need full relayout because they position children
// at absolute coordinates incorporating the scroll offset.
if (sDragScrollbar->wclass && (sDragScrollbar->wclass->flags & WCLASS_RELAYOUT_ON_SCROLL)) {
wgtInvalidate(sDragScrollbar);
} else { } else {
wgtInvalidatePaint(root); wgtInvalidatePaint(root);
} }
@ -347,44 +258,6 @@ void widgetOnMouse(WindowT *win, int32_t x, int32_t y, int32_t buttons) {
return; return;
} }
// Handle button press release
if (sPressedButton && !(buttons & MOUSE_LEFT)) {
if (sPressedButton->wclass && sPressedButton->wclass->setPressed) {
sPressedButton->wclass->setPressed(sPressedButton, false);
}
// Fire onClick if released over the same button in the same window
if (sPressedButton->window == win) {
if (x >= sPressedButton->x && x < sPressedButton->x + sPressedButton->w &&
y >= sPressedButton->y && y < sPressedButton->y + sPressedButton->h) {
if (sPressedButton->onClick) {
sPressedButton->onClick(sPressedButton);
}
}
}
wgtInvalidatePaint(sPressedButton);
sPressedButton = NULL;
return;
}
// Handle button press tracking (mouse move while held)
if (sPressedButton && (buttons & MOUSE_LEFT)) {
bool over = false;
if (sPressedButton->window == win) {
over = (x >= sPressedButton->x && x < sPressedButton->x + sPressedButton->w &&
y >= sPressedButton->y && y < sPressedButton->y + sPressedButton->h);
}
if (sPressedButton->wclass && sPressedButton->wclass->setPressed) {
sPressedButton->wclass->setPressed(sPressedButton, over);
}
wgtInvalidatePaint(sPressedButton);
return;
}
// Handle open popup clicks // Handle open popup clicks
if (sOpenPopup && (buttons & MOUSE_LEFT)) { if (sOpenPopup && (buttons & MOUSE_LEFT)) {
AppContextT *ctx = (AppContextT *)root->userData; AppContextT *ctx = (AppContextT *)root->userData;
@ -627,29 +500,3 @@ void widgetOnScroll(WindowT *win, ScrollbarOrientE orient, int32_t value) {
} }
// ============================================================
// widgetReorderDrop -- finalize drag-reorder on mouse release
// ============================================================
//
// Dispatches to the widget's reorderDrop vtable method, which handles
// the type-specific logic for completing the drag-reorder operation.
void widgetReorderDrop(WidgetT *w) {
if (w->wclass && w->wclass->reorderDrop) {
w->wclass->reorderDrop(w);
}
}
// ============================================================
// widgetReorderUpdate -- update drop position during drag
// ============================================================
//
// Dispatches to the widget's reorderUpdate vtable method, which handles
// type-specific drop indicator positioning and auto-scrolling.
void widgetReorderUpdate(WidgetT *w, WidgetT *root, int32_t x, int32_t y) {
if (w->wclass && w->wclass->reorderUpdate) {
w->wclass->reorderUpdate(w, root, x, y);
}
}

View file

@ -184,12 +184,8 @@ void wgtDestroy(WidgetT *w) {
sOpenPopup = NULL; sOpenPopup = NULL;
} }
if (sDragSlider == w) { if (sDragWidget == w) {
sDragSlider = NULL; sDragWidget = NULL;
}
if (sDrawingCanvas == w) {
sDrawingCanvas = NULL;
} }
// If this is the root, clear the window's reference // If this is the root, clear the window's reference

View file

@ -136,23 +136,6 @@ void widgetDrawScrollbarV(DisplayT *d, const BlitOpsT *ops, const ColorSchemeT *
} }
// ============================================================
// widgetScrollbarDragUpdate
// ============================================================
// Handles ongoing scrollbar thumb drag for widget-internal scrollbars.
// Dispatches to the widget's scrollDragUpdate vtable method, which
// handles type-specific geometry extraction and scroll position update.
void widgetScrollbarDragUpdate(WidgetT *w, int32_t orient, int32_t dragOff, int32_t mouseX, int32_t mouseY) {
// Dispatch to the widget's scrollDragUpdate vtable method, which
// handles type-specific geometry extraction, thumb computation,
// and scroll position update.
if (w->wclass && w->wclass->scrollDragUpdate) {
w->wclass->scrollDragUpdate(w, orient, dragOff, mouseX, mouseY);
}
}
// ============================================================ // ============================================================
// widgetScrollbarHitTest // widgetScrollbarHitTest
// ============================================================ // ============================================================

View file

@ -45,7 +45,8 @@ index, clamped to valid range.
```c ```c
void widgetDropdownPopupRect(WidgetT *w, const BitmapFontT *font, void widgetDropdownPopupRect(WidgetT *w, const BitmapFontT *font,
int32_t contentH, int32_t *popX, int32_t *popY, int32_t contentH, int32_t itemCount,
int32_t *popX, int32_t *popY,
int32_t *popW, int32_t *popH); int32_t *popW, int32_t *popH);
``` ```

View file

@ -154,13 +154,7 @@ void widgetPaintPopupList(DisplayT *d, const BlitOpsT *ops, const BitmapFontT *f
// Shared between Dropdown and ComboBox since they have identical // Shared between Dropdown and ComboBox since they have identical
// popup positioning logic. // popup positioning logic.
void widgetDropdownPopupRect(WidgetT *w, const BitmapFontT *font, int32_t contentH, int32_t *popX, int32_t *popY, int32_t *popW, int32_t *popH) { void widgetDropdownPopupRect(WidgetT *w, const BitmapFontT *font, int32_t contentH, int32_t itemCount, int32_t *popX, int32_t *popY, int32_t *popW, int32_t *popH) {
int32_t itemCount = 0;
if (w->wclass && w->wclass->getPopupItemCount) {
itemCount = w->wclass->getPopupItemCount(w);
}
int32_t visibleItems = itemCount; int32_t visibleItems = itemCount;
if (visibleItems > DROPDOWN_MAX_VISIBLE) { if (visibleItems > DROPDOWN_MAX_VISIBLE) {

View file

@ -34,7 +34,7 @@ int32_t widgetNavigateIndex(int32_t key, int32_t current, int32_t count, int32_t
// Popup rect calculation // Popup rect calculation
// ============================================================ // ============================================================
void widgetDropdownPopupRect(WidgetT *w, const BitmapFontT *font, int32_t contentH, int32_t *popX, int32_t *popY, int32_t *popW, int32_t *popH); void widgetDropdownPopupRect(WidgetT *w, const BitmapFontT *font, int32_t contentH, int32_t itemCount, int32_t *popX, int32_t *popY, int32_t *popW, int32_t *popH);
// ============================================================ // ============================================================
// Popup list painting // Popup list painting

View file

@ -62,7 +62,8 @@ void widgetTextEditOnKey(WidgetT *w, int32_t key, int32_t mod,
char *buf, int32_t bufSize, int32_t *pLen, char *buf, int32_t bufSize, int32_t *pLen,
int32_t *pCursor, int32_t *pScrollOff, int32_t *pCursor, int32_t *pScrollOff,
int32_t *pSelStart, int32_t *pSelEnd, int32_t *pSelStart, int32_t *pSelEnd,
char *undoBuf, int32_t *pUndoLen, int32_t *pUndoCursor); char *undoBuf, int32_t *pUndoLen, int32_t *pUndoCursor,
int32_t fieldWidth);
``` ```
Handles all keyboard input: character insertion, deletion (Backspace, Handles all keyboard input: character insertion, deletion (Backspace,

View file

@ -231,7 +231,7 @@ void widgetTextEditMouseClick(WidgetT *w, int32_t vx, int32_t vy, int32_t textLe
*pSelStart = 0; *pSelStart = 0;
*pSelEnd = len; *pSelEnd = len;
*pCursorPos = len; *pCursorPos = len;
sDragTextSelect = NULL; sDragWidget = NULL;
return; return;
} }
@ -248,7 +248,7 @@ void widgetTextEditMouseClick(WidgetT *w, int32_t vx, int32_t vy, int32_t textLe
*pCursorPos = len; *pCursorPos = len;
} }
sDragTextSelect = NULL; sDragWidget = NULL;
return; return;
} }
@ -256,7 +256,7 @@ void widgetTextEditMouseClick(WidgetT *w, int32_t vx, int32_t vy, int32_t textLe
*pCursorPos = charPos; *pCursorPos = charPos;
*pSelStart = charPos; *pSelStart = charPos;
*pSelEnd = charPos; *pSelEnd = charPos;
sDragTextSelect = dragSelect ? w : NULL; sDragWidget = dragSelect ? w : NULL;
} }
@ -267,7 +267,7 @@ void widgetTextEditMouseClick(WidgetT *w, int32_t vx, int32_t vy, int32_t textLe
// This is the core single-line text editing engine, parameterized by // This is the core single-line text editing engine, parameterized by
// pointer to allow reuse across TextInput, Spinner, and ComboBox. // pointer to allow reuse across TextInput, Spinner, and ComboBox.
void widgetTextEditOnKey(WidgetT *w, int32_t key, int32_t mod, char *buf, int32_t bufSize, int32_t *pLen, int32_t *pCursor, int32_t *pScrollOff, int32_t *pSelStart, int32_t *pSelEnd, char *undoBuf, int32_t *pUndoLen, int32_t *pUndoCursor) { void widgetTextEditOnKey(WidgetT *w, int32_t key, int32_t mod, char *buf, int32_t bufSize, int32_t *pLen, int32_t *pCursor, int32_t *pScrollOff, int32_t *pSelStart, int32_t *pSelEnd, char *undoBuf, int32_t *pUndoLen, int32_t *pUndoCursor, int32_t fieldWidth) {
bool shift = (mod & KEY_MOD_SHIFT) != 0; bool shift = (mod & KEY_MOD_SHIFT) != 0;
bool hasSel = (pSelStart && pSelEnd && *pSelStart >= 0 && *pSelEnd >= 0 && *pSelStart != *pSelEnd); bool hasSel = (pSelStart && pSelEnd && *pSelStart >= 0 && *pSelEnd >= 0 && *pSelStart != *pSelEnd);
int32_t selLo = hasSel ? (*pSelStart < *pSelEnd ? *pSelStart : *pSelEnd) : -1; int32_t selLo = hasSel ? (*pSelStart < *pSelEnd ? *pSelStart : *pSelEnd) : -1;
@ -638,11 +638,7 @@ adjustScroll:
{ {
AppContextT *ctx = wgtGetContext(w); AppContextT *ctx = wgtGetContext(w);
const BitmapFontT *font = &ctx->font; const BitmapFontT *font = &ctx->font;
int32_t fieldW = w->w; int32_t fieldW = fieldWidth > 0 ? fieldWidth : w->w;
if (w->wclass && w->wclass->getTextFieldWidth) {
fieldW = w->wclass->getTextFieldWidth(w);
}
int32_t visibleChars = (fieldW - TEXT_INPUT_PAD * 2) / font->charWidth; int32_t visibleChars = (fieldW - TEXT_INPUT_PAD * 2) / font->charWidth;

View file

@ -38,7 +38,7 @@ int32_t wordStart(const char *buf, int32_t pos);
void widgetTextEditDragUpdateLine(int32_t vx, int32_t leftEdge, int32_t maxChars, const BitmapFontT *font, int32_t len, int32_t *pCursorPos, int32_t *pScrollOff, int32_t *pSelEnd); void widgetTextEditDragUpdateLine(int32_t vx, int32_t leftEdge, int32_t maxChars, const BitmapFontT *font, int32_t len, int32_t *pCursorPos, int32_t *pScrollOff, int32_t *pSelEnd);
void widgetTextEditMouseClick(WidgetT *w, int32_t vx, int32_t vy, int32_t textLeftX, const BitmapFontT *font, const char *buf, int32_t len, int32_t scrollOff, int32_t *pCursorPos, int32_t *pSelStart, int32_t *pSelEnd, bool wordSelect, bool dragSelect); void widgetTextEditMouseClick(WidgetT *w, int32_t vx, int32_t vy, int32_t textLeftX, const BitmapFontT *font, const char *buf, int32_t len, int32_t scrollOff, int32_t *pCursorPos, int32_t *pSelStart, int32_t *pSelEnd, bool wordSelect, bool dragSelect);
void widgetTextEditOnKey(WidgetT *w, int32_t key, int32_t mod, char *buf, int32_t bufSize, int32_t *pLen, int32_t *pCursor, int32_t *pScrollOff, int32_t *pSelStart, int32_t *pSelEnd, char *undoBuf, int32_t *pUndoLen, int32_t *pUndoCursor); void widgetTextEditOnKey(WidgetT *w, int32_t key, int32_t mod, char *buf, int32_t bufSize, int32_t *pLen, int32_t *pCursor, int32_t *pScrollOff, int32_t *pSelStart, int32_t *pSelEnd, char *undoBuf, int32_t *pUndoLen, int32_t *pUndoCursor, int32_t fieldWidth);
void widgetTextEditPaintLine(DisplayT *d, const BlitOpsT *ops, const BitmapFontT *font, const ColorSchemeT *colors, int32_t textX, int32_t textY, const char *buf, int32_t visLen, int32_t scrollOff, int32_t cursorPos, int32_t selStart, int32_t selEnd, uint32_t fg, uint32_t bg, bool showCursor, int32_t cursorMinX, int32_t cursorMaxX); void widgetTextEditPaintLine(DisplayT *d, const BlitOpsT *ops, const BitmapFontT *font, const ColorSchemeT *colors, int32_t textX, int32_t textY, const char *buf, int32_t visLen, int32_t scrollOff, int32_t cursorPos, int32_t selStart, int32_t selEnd, uint32_t fg, uint32_t bg, bool showCursor, int32_t cursorMinX, int32_t cursorMaxX);
#endif // TEXT_HELP_H #endif // TEXT_HELP_H

View file

@ -807,7 +807,7 @@ void widgetAnsiTermOnMouse(WidgetT *hit, WidgetT *root, int32_t vx, int32_t vy)
if (clicks >= 3) { if (clicks >= 3) {
at->selStartLine = lineIndex; at->selStartCol = 0; at->selStartLine = lineIndex; at->selStartCol = 0;
at->selEndLine = lineIndex; at->selEndCol = cols; at->selEndLine = lineIndex; at->selEndCol = cols;
at->selecting = false; sDragTextSelect = NULL; at->selecting = false; sDragWidget = NULL;
} else if (clicks == 2) { } else if (clicks == 2) {
const uint8_t *lineData = ansiTermGetLine(hit, lineIndex); const uint8_t *lineData = ansiTermGetLine(hit, lineIndex);
int32_t ws = clickCol; int32_t we = clickCol; int32_t ws = clickCol; int32_t we = clickCol;
@ -815,11 +815,11 @@ void widgetAnsiTermOnMouse(WidgetT *hit, WidgetT *root, int32_t vx, int32_t vy)
while (we < cols && isWordChar((char)lineData[we * 2])) { we++; } while (we < cols && isWordChar((char)lineData[we * 2])) { we++; }
at->selStartLine = lineIndex; at->selStartCol = ws; at->selStartLine = lineIndex; at->selStartCol = ws;
at->selEndLine = lineIndex; at->selEndCol = we; at->selEndLine = lineIndex; at->selEndCol = we;
at->selecting = false; sDragTextSelect = NULL; at->selecting = false; sDragWidget = NULL;
} else { } else {
at->selStartLine = lineIndex; at->selStartCol = clickCol; at->selStartLine = lineIndex; at->selStartCol = clickCol;
at->selEndLine = lineIndex; at->selEndCol = clickCol; at->selEndLine = lineIndex; at->selEndCol = clickCol;
at->selecting = true; sDragTextSelect = hit; at->selecting = true; sDragWidget = hit;
} }
at->dirtyRows = 0xFFFFFFFF; at->dirtyRows = 0xFFFFFFFF;
return; return;
@ -925,7 +925,7 @@ static bool widgetAnsiTermClearSelection(WidgetT *w) {
} }
static void widgetAnsiTermDragSelect(WidgetT *w, WidgetT *root, int32_t vx, int32_t vy) { static void widgetAnsiTermOnDragUpdate(WidgetT *w, WidgetT *root, int32_t vx, int32_t vy) {
(void)root; (void)root;
AnsiTermDataT *at = (AnsiTermDataT *)w->data; AnsiTermDataT *at = (AnsiTermDataT *)w->data;
AppContextT *ctx = wgtGetContext(w); AppContextT *ctx = wgtGetContext(w);
@ -971,7 +971,7 @@ static const WidgetClassT sClassAnsiTerm = {
.getText = NULL, .getText = NULL,
.setText = NULL, .setText = NULL,
.clearSelection = widgetAnsiTermClearSelection, .clearSelection = widgetAnsiTermClearSelection,
.dragSelect = widgetAnsiTermDragSelect, .onDragUpdate = widgetAnsiTermOnDragUpdate,
.poll = widgetAnsiTermPollVtable, .poll = widgetAnsiTermPollVtable,
.quickRepaint = wgtAnsiTermRepaint .quickRepaint = wgtAnsiTermRepaint
}; };

View file

@ -116,7 +116,7 @@ void widgetButtonOnMouse(WidgetT *w, WidgetT *root, int32_t vx, int32_t vy) {
ButtonDataT *d = (ButtonDataT *)w->data; ButtonDataT *d = (ButtonDataT *)w->data;
w->focused = true; w->focused = true;
d->pressed = true; d->pressed = true;
sPressedButton = w; sDragWidget = w;
} }
@ -165,12 +165,35 @@ void widgetButtonPaint(WidgetT *w, DisplayT *d, const BlitOpsT *ops, const Bitma
// ============================================================ // ============================================================
// widgetButtonSetPressed // widgetButtonOnDragEnd
// ============================================================ // ============================================================
void widgetButtonSetPressed(WidgetT *w, bool pressed) { static void widgetButtonOnDragEnd(WidgetT *w, WidgetT *root, int32_t x, int32_t y) {
ButtonDataT *d = (ButtonDataT *)w->data; ButtonDataT *d = (ButtonDataT *)w->data;
d->pressed = pressed; d->pressed = false;
if (w->window == root->window &&
x >= w->x && x < w->x + w->w &&
y >= w->y && y < w->y + w->h) {
if (w->onClick) {
w->onClick(w);
}
}
wgtInvalidatePaint(w);
}
// ============================================================
// widgetButtonOnDragUpdate
// ============================================================
static void widgetButtonOnDragUpdate(WidgetT *w, WidgetT *root, int32_t x, int32_t y) {
ButtonDataT *d = (ButtonDataT *)w->data;
bool over = (w->window == root->window &&
x >= w->x && x < w->x + w->w &&
y >= w->y && y < w->y + w->h);
d->pressed = over;
wgtInvalidatePaint(w); wgtInvalidatePaint(w);
} }
@ -203,7 +226,8 @@ static const WidgetClassT sClassButton = {
.destroy = widgetButtonDestroy, .destroy = widgetButtonDestroy,
.getText = widgetButtonGetText, .getText = widgetButtonGetText,
.setText = widgetButtonSetText, .setText = widgetButtonSetText,
.setPressed = widgetButtonSetPressed .onDragUpdate = widgetButtonOnDragUpdate,
.onDragEnd = widgetButtonOnDragEnd
}; };
// ============================================================ // ============================================================

View file

@ -581,22 +581,41 @@ void widgetCanvasOnMouse(WidgetT *hit, WidgetT *root, int32_t vx, int32_t vy) {
int32_t cx = vx - hit->x - CANVAS_BORDER; int32_t cx = vx - hit->x - CANVAS_BORDER;
int32_t cy = vy - hit->y - CANVAS_BORDER; int32_t cy = vy - hit->y - CANVAS_BORDER;
bool drag = (sDrawingCanvas == hit); sDragWidget = hit;
cd->lastX = -1;
if (!drag) { cd->lastY = -1;
sDrawingCanvas = hit;
cd->lastX = -1;
cd->lastY = -1;
}
cd->lastX = cx; cd->lastX = cx;
cd->lastY = cy; cd->lastY = cy;
cd->onMouse(hit, cx, cy, drag); cd->onMouse(hit, cx, cy, false);
wgtInvalidatePaint(hit); wgtInvalidatePaint(hit);
} }
// ============================================================
// widgetCanvasOnDragUpdate
// ============================================================
static void widgetCanvasOnDragUpdate(WidgetT *w, WidgetT *root, int32_t vx, int32_t vy) {
(void)root;
CanvasDataT *cd = (CanvasDataT *)w->data;
if (!cd->onMouse) {
return;
}
int32_t cx = vx - w->x - CANVAS_BORDER;
int32_t cy = vy - w->y - CANVAS_BORDER;
cd->lastX = cx;
cd->lastY = cy;
cd->onMouse(w, cx, cy, true);
wgtInvalidatePaint(w);
}
// ============================================================ // ============================================================
// widgetCanvasPaint // widgetCanvasPaint
// ============================================================ // ============================================================
@ -644,6 +663,7 @@ static const WidgetClassT sClassCanvas = {
.calcMinSize = widgetCanvasCalcMinSize, .calcMinSize = widgetCanvasCalcMinSize,
.layout = NULL, .layout = NULL,
.onMouse = widgetCanvasOnMouse, .onMouse = widgetCanvasOnMouse,
.onDragUpdate = widgetCanvasOnDragUpdate,
.onKey = NULL, .onKey = NULL,
.destroy = widgetCanvasDestroy, .destroy = widgetCanvasDestroy,
.getText = NULL, .getText = NULL,

View file

@ -19,7 +19,7 @@
// //
// Text selection supports single-click (cursor placement + drag start), // Text selection supports single-click (cursor placement + drag start),
// double-click (word select), and triple-click (select all). Drag-select // double-click (word select), and triple-click (select all). Drag-select
// is tracked via the sDragTextSelect global. // is tracked via the sDragWidget global.
#include "dvxWidgetPlugin.h" #include "dvxWidgetPlugin.h"
#include "../texthelp/textHelp.h" #include "../texthelp/textHelp.h"
@ -190,7 +190,8 @@ void widgetComboBoxOnKey(WidgetT *w, int32_t key, int32_t mod) {
&d->scrollOff, &d->scrollOff,
&d->selStart, &d->selEnd, &d->selStart, &d->selEnd,
d->undoBuf, &d->undoLen, d->undoBuf, &d->undoLen,
&d->undoCursor); &d->undoCursor,
w->w - DROPDOWN_BTN_WIDTH);
} }
@ -211,7 +212,7 @@ void widgetComboBoxOnMouse(WidgetT *w, WidgetT *root, int32_t vx, int32_t vy) {
int32_t popW; int32_t popW;
int32_t popH; int32_t popH;
widgetDropdownPopupRect(w, font, w->window->contentH, &popX, &popY, &popW, &popH); widgetDropdownPopupRect(w, font, w->window->contentH, d->itemCount, &popX, &popY, &popW, &popH);
int32_t itemIdx = d->listScrollPos + (vy - popY - 2) / font->charHeight; int32_t itemIdx = d->listScrollPos + (vy - popY - 2) / font->charHeight;
@ -329,7 +330,7 @@ void widgetComboBoxPaintPopup(WidgetT *w, DisplayT *disp, const BlitOpsT *ops, c
int32_t popW; int32_t popW;
int32_t popH; int32_t popH;
widgetDropdownPopupRect(w, font, disp->clipH, &popX, &popY, &popW, &popH); widgetDropdownPopupRect(w, font, disp->clipH, d->itemCount, &popX, &popY, &popW, &popH);
widgetPaintPopupList(disp, ops, font, colors, popX, popY, popW, popH, d->items, d->itemCount, d->hoverIdx, d->listScrollPos); widgetPaintPopupList(disp, ops, font, colors, popX, popY, popW, popH, d->items, d->itemCount, d->hoverIdx, d->listScrollPos);
} }
@ -378,35 +379,13 @@ void widgetComboBoxClosePopup(WidgetT *w) {
} }
// ============================================================
// widgetComboBoxGetPopupItemCount
// ============================================================
int32_t widgetComboBoxGetPopupItemCount(const WidgetT *w) {
const ComboBoxDataT *d = (const ComboBoxDataT *)w->data;
return d->itemCount;
}
// ============================================================
// widgetComboBoxOpenPopup
// ============================================================
void widgetComboBoxOpenPopup(WidgetT *w) {
ComboBoxDataT *d = (ComboBoxDataT *)w->data;
d->open = true;
d->hoverIdx = d->selectedIdx;
sOpenPopup = w;
wgtInvalidatePaint(w);
}
// ============================================================ // ============================================================
// DXE registration // DXE registration
// ============================================================ // ============================================================
static void widgetComboBoxGetPopupRect(const WidgetT *w, const BitmapFontT *font, int32_t contentH, int32_t *popX, int32_t *popY, int32_t *popW, int32_t *popH) { static void widgetComboBoxGetPopupRect(const WidgetT *w, const BitmapFontT *font, int32_t contentH, int32_t *popX, int32_t *popY, int32_t *popW, int32_t *popH) {
widgetDropdownPopupRect((WidgetT *)w, font, contentH, popX, popY, popW, popH); const ComboBoxDataT *d = (const ComboBoxDataT *)w->data;
widgetDropdownPopupRect((WidgetT *)w, font, contentH, d->itemCount, popX, popY, popW, popH);
} }
@ -423,7 +402,7 @@ static bool widgetComboBoxClearSelection(WidgetT *w) {
} }
static void widgetComboBoxDragSelect(WidgetT *w, WidgetT *root, int32_t vx, int32_t vy) { static void widgetComboBoxOnDragUpdate(WidgetT *w, WidgetT *root, int32_t vx, int32_t vy) {
(void)root; (void)root;
(void)vy; (void)vy;
ComboBoxDataT *d = (ComboBoxDataT *)w->data; ComboBoxDataT *d = (ComboBoxDataT *)w->data;
@ -447,11 +426,9 @@ static const WidgetClassT sClassComboBox = {
.destroy = widgetComboBoxDestroy, .destroy = widgetComboBoxDestroy,
.getText = widgetComboBoxGetText, .getText = widgetComboBoxGetText,
.setText = widgetComboBoxSetText, .setText = widgetComboBoxSetText,
.openPopup = widgetComboBoxOpenPopup,
.closePopup = widgetComboBoxClosePopup, .closePopup = widgetComboBoxClosePopup,
.getPopupItemCount = widgetComboBoxGetPopupItemCount,
.clearSelection = widgetComboBoxClearSelection, .clearSelection = widgetComboBoxClearSelection,
.dragSelect = widgetComboBoxDragSelect, .onDragUpdate = widgetComboBoxOnDragUpdate,
.getPopupRect = widgetComboBoxGetPopupRect .getPopupRect = widgetComboBoxGetPopupRect
}; };

View file

@ -173,7 +173,7 @@ void widgetDropdownOnMouse(WidgetT *w, WidgetT *root, int32_t vx, int32_t vy) {
int32_t popW; int32_t popW;
int32_t popH; int32_t popH;
widgetDropdownPopupRect(w, font, w->window->contentH, &popX, &popY, &popW, &popH); widgetDropdownPopupRect(w, font, w->window->contentH, d->itemCount, &popX, &popY, &popW, &popH);
int32_t itemIdx = d->scrollPos + (vy - popY - 2) / font->charHeight; int32_t itemIdx = d->scrollPos + (vy - popY - 2) / font->charHeight;
@ -267,7 +267,7 @@ void widgetDropdownPaintPopup(WidgetT *w, DisplayT *disp, const BlitOpsT *ops, c
int32_t popW; int32_t popW;
int32_t popH; int32_t popH;
widgetDropdownPopupRect(w, font, disp->clipH, &popX, &popY, &popW, &popH); widgetDropdownPopupRect(w, font, disp->clipH, d->itemCount, &popX, &popY, &popW, &popH);
widgetPaintPopupList(disp, ops, font, colors, popX, popY, popW, popH, d->items, d->itemCount, d->hoverIdx, d->scrollPos); widgetPaintPopupList(disp, ops, font, colors, popX, popY, popW, popH, d->items, d->itemCount, d->hoverIdx, d->scrollPos);
} }
@ -297,36 +297,14 @@ void widgetDropdownClosePopup(WidgetT *w) {
} }
// ============================================================
// widgetDropdownGetPopupItemCount
// ============================================================
int32_t widgetDropdownGetPopupItemCount(const WidgetT *w) {
const DropdownDataT *d = (const DropdownDataT *)w->data;
return d->itemCount;
}
// ============================================================
// widgetDropdownOpenPopup
// ============================================================
void widgetDropdownOpenPopup(WidgetT *w) {
DropdownDataT *d = (DropdownDataT *)w->data;
d->open = true;
d->hoverIdx = d->selectedIdx;
sOpenPopup = w;
wgtInvalidatePaint(w);
}
// ============================================================ // ============================================================
// DXE registration // DXE registration
// ============================================================ // ============================================================
static void widgetDropdownGetPopupRect(const WidgetT *w, const BitmapFontT *font, int32_t contentH, int32_t *popX, int32_t *popY, int32_t *popW, int32_t *popH) { static void widgetDropdownGetPopupRect(const WidgetT *w, const BitmapFontT *font, int32_t contentH, int32_t *popX, int32_t *popY, int32_t *popW, int32_t *popH) {
widgetDropdownPopupRect((WidgetT *)w, font, contentH, popX, popY, popW, popH); const DropdownDataT *d = (const DropdownDataT *)w->data;
widgetDropdownPopupRect((WidgetT *)w, font, contentH, d->itemCount, popX, popY, popW, popH);
} }
@ -342,9 +320,7 @@ static const WidgetClassT sClassDropdown = {
.destroy = widgetDropdownDestroy, .destroy = widgetDropdownDestroy,
.getText = widgetDropdownGetText, .getText = widgetDropdownGetText,
.setText = NULL, .setText = NULL,
.openPopup = widgetDropdownOpenPopup,
.closePopup = widgetDropdownClosePopup, .closePopup = widgetDropdownClosePopup,
.getPopupItemCount = widgetDropdownGetPopupItemCount,
.getPopupRect = widgetDropdownGetPopupRect .getPopupRect = widgetDropdownGetPopupRect
}; };

View file

@ -85,7 +85,7 @@ void widgetImageButtonOnMouse(WidgetT *w, WidgetT *root, int32_t vx, int32_t vy)
ImageButtonDataT *d = (ImageButtonDataT *)w->data; ImageButtonDataT *d = (ImageButtonDataT *)w->data;
w->focused = true; w->focused = true;
d->pressed = true; d->pressed = true;
sPressedButton = w; sDragWidget = w;
} }
@ -143,12 +143,35 @@ void widgetImageButtonAccelActivate(WidgetT *w, WidgetT *root) {
// ============================================================ // ============================================================
// widgetImageButtonSetPressed // widgetImageButtonOnDragEnd
// ============================================================ // ============================================================
void widgetImageButtonSetPressed(WidgetT *w, bool pressed) { static void widgetImageButtonOnDragEnd(WidgetT *w, WidgetT *root, int32_t x, int32_t y) {
ImageButtonDataT *d = (ImageButtonDataT *)w->data; ImageButtonDataT *d = (ImageButtonDataT *)w->data;
d->pressed = pressed; d->pressed = false;
if (w->window == root->window &&
x >= w->x && x < w->x + w->w &&
y >= w->y && y < w->y + w->h) {
if (w->onClick) {
w->onClick(w);
}
}
wgtInvalidatePaint(w);
}
// ============================================================
// widgetImageButtonOnDragUpdate
// ============================================================
static void widgetImageButtonOnDragUpdate(WidgetT *w, WidgetT *root, int32_t x, int32_t y) {
ImageButtonDataT *d = (ImageButtonDataT *)w->data;
bool over = (w->window == root->window &&
x >= w->x && x < w->x + w->w &&
y >= w->y && y < w->y + w->h);
d->pressed = over;
wgtInvalidatePaint(w); wgtInvalidatePaint(w);
} }
@ -169,7 +192,8 @@ static const WidgetClassT sClassImageButton = {
.destroy = widgetImageButtonDestroy, .destroy = widgetImageButtonDestroy,
.getText = NULL, .getText = NULL,
.setText = NULL, .setText = NULL,
.setPressed = widgetImageButtonSetPressed .onDragUpdate = widgetImageButtonOnDragUpdate,
.onDragEnd = widgetImageButtonOnDragEnd
}; };

View file

@ -47,6 +47,8 @@ typedef struct {
bool reorderable; bool reorderable;
int32_t dragIdx; int32_t dragIdx;
int32_t dropIdx; int32_t dropIdx;
int32_t sbDragOrient;
int32_t sbDragOff;
} ListBoxDataT; } ListBoxDataT;
#include <stdlib.h> #include <stdlib.h>
@ -309,9 +311,9 @@ void widgetListBoxOnMouse(WidgetT *hit, WidgetT *root, int32_t vx, int32_t vy) {
int32_t thumbSize; int32_t thumbSize;
widgetScrollbarThumb(trackLen, d->itemCount, visibleRows, d->scrollPos, &thumbPos, &thumbSize); widgetScrollbarThumb(trackLen, d->itemCount, visibleRows, d->scrollPos, &thumbPos, &thumbSize);
sDragScrollbar = hit; sDragWidget = hit;
sDragScrollbarOrient = 0; d->sbDragOrient = 0;
sDragScrollbarOff = relY - WGT_SB_W - thumbPos; d->sbDragOff = relY - WGT_SB_W - thumbPos;
} }
hit->focused = true; hit->focused = true;
@ -368,7 +370,7 @@ void widgetListBoxOnMouse(WidgetT *hit, WidgetT *root, int32_t vx, int32_t vy) {
if (d->reorderable && !shift && !ctrl) { if (d->reorderable && !shift && !ctrl) {
d->dragIdx = idx; d->dragIdx = idx;
d->dropIdx = idx; d->dropIdx = idx;
sDragReorder = hit; sDragWidget = hit;
} }
} }
@ -586,6 +588,40 @@ static void widgetListBoxReorderDrop(WidgetT *w) {
} }
// ============================================================
// widgetListBoxOnDragEnd
// ============================================================
static void widgetListBoxOnDragEnd(WidgetT *w, WidgetT *root, int32_t x, int32_t y) {
(void)root;
(void)x;
(void)y;
ListBoxDataT *d = (ListBoxDataT *)w->data;
if (d->dragIdx >= 0) {
widgetListBoxReorderDrop(w);
d->dragIdx = -1;
}
wgtInvalidatePaint(w);
}
// ============================================================
// widgetListBoxOnDragUpdate
// ============================================================
static void widgetListBoxOnDragUpdate(WidgetT *w, WidgetT *root, int32_t x, int32_t y) {
ListBoxDataT *d = (ListBoxDataT *)w->data;
if (d->dragIdx >= 0) {
widgetListBoxReorderUpdate(w, root, x, y);
} else {
widgetListBoxScrollDragUpdate(w, d->sbDragOrient, d->sbDragOff, x, y);
}
}
static const WidgetClassT sClassListBox = { static const WidgetClassT sClassListBox = {
.flags = WCLASS_FOCUSABLE | WCLASS_SCROLLABLE, .flags = WCLASS_FOCUSABLE | WCLASS_SCROLLABLE,
.paint = widgetListBoxPaint, .paint = widgetListBoxPaint,
@ -597,9 +633,8 @@ static const WidgetClassT sClassListBox = {
.destroy = widgetListBoxDestroy, .destroy = widgetListBoxDestroy,
.getText = NULL, .getText = NULL,
.setText = NULL, .setText = NULL,
.reorderDrop = widgetListBoxReorderDrop, .onDragUpdate = widgetListBoxOnDragUpdate,
.reorderUpdate = widgetListBoxReorderUpdate, .onDragEnd = widgetListBoxOnDragEnd
.scrollDragUpdate = widgetListBoxScrollDragUpdate
}; };
// ============================================================ // ============================================================

View file

@ -94,6 +94,8 @@ typedef struct {
int32_t resizeStartX; int32_t resizeStartX;
int32_t resizeOrigW; int32_t resizeOrigW;
bool resizeDragging; bool resizeDragging;
int32_t sbDragOrient;
int32_t sbDragOff;
} ListViewDataT; } ListViewDataT;
@ -625,9 +627,9 @@ void widgetListViewOnMouse(WidgetT *hit, WidgetT *root, int32_t vx, int32_t vy)
int32_t thumbSize; int32_t thumbSize;
widgetScrollbarThumb(trackLen, lv->rowCount, visibleRows, lv->scrollPos, &thumbPos, &thumbSize); widgetScrollbarThumb(trackLen, lv->rowCount, visibleRows, lv->scrollPos, &thumbPos, &thumbSize);
sDragScrollbar = hit; sDragWidget = hit;
sDragScrollbarOrient = 0; lv->sbDragOrient = 0;
sDragScrollbarOff = relY - WGT_SB_W - thumbPos; lv->sbDragOff = relY - WGT_SB_W - thumbPos;
} }
return; return;
@ -663,9 +665,9 @@ void widgetListViewOnMouse(WidgetT *hit, WidgetT *root, int32_t vx, int32_t vy)
int32_t thumbSize; int32_t thumbSize;
widgetScrollbarThumb(trackLen, totalColW, innerW, lv->scrollPosH, &thumbPos, &thumbSize); widgetScrollbarThumb(trackLen, totalColW, innerW, lv->scrollPosH, &thumbPos, &thumbSize);
sDragScrollbar = hit; sDragWidget = hit;
sDragScrollbarOrient = 1; lv->sbDragOrient = 1;
sDragScrollbarOff = relX - WGT_SB_W - thumbPos; lv->sbDragOff = relX - WGT_SB_W - thumbPos;
} }
lv->scrollPosH = clampInt(lv->scrollPosH, 0, maxScrollH); lv->scrollPosH = clampInt(lv->scrollPosH, 0, maxScrollH);
@ -730,7 +732,7 @@ void widgetListViewOnMouse(WidgetT *hit, WidgetT *root, int32_t vx, int32_t vy)
wgtInvalidatePaint(hit); wgtInvalidatePaint(hit);
} else { } else {
// Start column resize drag (deferred until mouse moves) // Start column resize drag (deferred until mouse moves)
sResizeListView = hit; sDragWidget = hit;
lv->resizeCol = c; lv->resizeCol = c;
lv->resizeStartX = vx; lv->resizeStartX = vx;
lv->resizeOrigW = cw; lv->resizeOrigW = cw;
@ -858,7 +860,7 @@ void widgetListViewOnMouse(WidgetT *hit, WidgetT *root, int32_t vx, int32_t vy)
if (lv->reorderable && !shift && !ctrl) { if (lv->reorderable && !shift && !ctrl) {
lv->dragIdx = dataRow; lv->dragIdx = dataRow;
lv->dropIdx = dataRow; lv->dropIdx = dataRow;
sDragReorder = hit; sDragWidget = hit;
} }
} }
@ -1494,6 +1496,43 @@ static void widgetListViewReorderDrop(WidgetT *w) {
} }
// ============================================================
// widgetListViewOnDragEnd
// ============================================================
static void widgetListViewOnDragEnd(WidgetT *w, WidgetT *root, int32_t x, int32_t y) {
(void)root;
(void)x;
(void)y;
ListViewDataT *lv = (ListViewDataT *)w->data;
if (lv->dragIdx >= 0) {
widgetListViewReorderDrop(w);
lv->dragIdx = -1;
}
lv->resizeDragging = false;
wgtInvalidatePaint(w);
}
// ============================================================
// widgetListViewOnDragUpdate
// ============================================================
static void widgetListViewOnDragUpdate(WidgetT *w, WidgetT *root, int32_t x, int32_t y) {
ListViewDataT *lv = (ListViewDataT *)w->data;
if (lv->resizeDragging) {
widgetListViewScrollDragUpdate(w, -1, 0, x, y);
} else if (lv->dragIdx >= 0) {
widgetListViewReorderUpdate(w, root, x, y);
} else {
widgetListViewScrollDragUpdate(w, lv->sbDragOrient, lv->sbDragOff, x, y);
}
}
static const WidgetClassT sClassListView = { static const WidgetClassT sClassListView = {
.flags = WCLASS_FOCUSABLE | WCLASS_NO_HIT_RECURSE | WCLASS_SCROLLABLE, .flags = WCLASS_FOCUSABLE | WCLASS_NO_HIT_RECURSE | WCLASS_SCROLLABLE,
.paint = widgetListViewPaint, .paint = widgetListViewPaint,
@ -1506,9 +1545,8 @@ static const WidgetClassT sClassListView = {
.getText = NULL, .getText = NULL,
.setText = NULL, .setText = NULL,
.getCursorShape = widgetListViewGetCursorShape, .getCursorShape = widgetListViewGetCursorShape,
.reorderDrop = widgetListViewReorderDrop, .onDragUpdate = widgetListViewOnDragUpdate,
.reorderUpdate = widgetListViewReorderUpdate, .onDragEnd = widgetListViewOnDragEnd
.scrollDragUpdate = widgetListViewScrollDragUpdate
}; };

View file

@ -35,6 +35,8 @@ static int32_t sTypeId = -1;
typedef struct { typedef struct {
int32_t scrollPosV; int32_t scrollPosV;
int32_t scrollPosH; int32_t scrollPosH;
int32_t sbDragOrient;
int32_t sbDragOff;
} ScrollPaneDataT; } ScrollPaneDataT;
#define SP_BORDER 2 #define SP_BORDER 2
@ -50,7 +52,7 @@ static void drawSPHScrollbar(WidgetT *w, DisplayT *d, const BlitOpsT *ops, const
static void drawSPVScrollbar(WidgetT *w, DisplayT *d, const BlitOpsT *ops, const ColorSchemeT *colors, int32_t sbX, int32_t sbY, int32_t sbH, int32_t totalH, int32_t visibleH); static void drawSPVScrollbar(WidgetT *w, DisplayT *d, const BlitOpsT *ops, const ColorSchemeT *colors, int32_t sbX, int32_t sbY, int32_t sbH, int32_t totalH, int32_t visibleH);
static void spCalcNeeds(WidgetT *w, const BitmapFontT *font, int32_t *contentMinW, int32_t *contentMinH, int32_t *innerW, int32_t *innerH, bool *needVSb, bool *needHSb); static void spCalcNeeds(WidgetT *w, const BitmapFontT *font, int32_t *contentMinW, int32_t *contentMinH, int32_t *innerW, int32_t *innerH, bool *needVSb, bool *needHSb);
static void widgetScrollPaneDestroy(WidgetT *w); static void widgetScrollPaneDestroy(WidgetT *w);
static void widgetScrollPaneScrollDragUpdate(WidgetT *w, int32_t orient, int32_t dragOff, int32_t mouseX, int32_t mouseY); static void widgetScrollPaneOnDragUpdate(WidgetT *w, WidgetT *root, int32_t mouseX, int32_t mouseY);
void wgtScrollPaneScrollToChild(WidgetT *w, const WidgetT *child); void wgtScrollPaneScrollToChild(WidgetT *w, const WidgetT *child);
@ -527,9 +529,9 @@ void widgetScrollPaneOnMouse(WidgetT *hit, WidgetT *root, int32_t vx, int32_t vy
} else if (trackRelY >= thumbPos + thumbSize) { } else if (trackRelY >= thumbPos + thumbSize) {
sp->scrollPosV += pageSize; sp->scrollPosV += pageSize;
} else { } else {
sDragScrollbar = hit; sDragWidget = hit;
sDragScrollbarOrient = 0; sp->sbDragOrient = 0;
sDragScrollbarOff = trackRelY - thumbPos; sp->sbDragOff = trackRelY - thumbPos;
return; return;
} }
} }
@ -571,9 +573,9 @@ void widgetScrollPaneOnMouse(WidgetT *hit, WidgetT *root, int32_t vx, int32_t vy
} else if (trackRelX >= thumbPos + thumbSize) { } else if (trackRelX >= thumbPos + thumbSize) {
sp->scrollPosH += pageSize; sp->scrollPosH += pageSize;
} else { } else {
sDragScrollbar = hit; sDragWidget = hit;
sDragScrollbarOrient = 1; sp->sbDragOrient = 1;
sDragScrollbarOff = trackRelX - thumbPos; sp->sbDragOff = trackRelX - thumbPos;
return; return;
} }
} }
@ -626,13 +628,16 @@ void widgetScrollPaneOnMouse(WidgetT *hit, WidgetT *root, int32_t vx, int32_t vy
// ============================================================ // ============================================================
// widgetScrollPaneScrollDragUpdate // widgetScrollPaneOnDragUpdate
// ============================================================ // ============================================================
// Handle scrollbar thumb drag for vertical and horizontal scrollbars. // Handle scrollbar thumb drag for vertical and horizontal scrollbars.
// Uses spCalcNeeds to determine content and viewport dimensions. // Uses spCalcNeeds to determine content and viewport dimensions.
static void widgetScrollPaneScrollDragUpdate(WidgetT *w, int32_t orient, int32_t dragOff, int32_t mouseX, int32_t mouseY) { static void widgetScrollPaneOnDragUpdate(WidgetT *w, WidgetT *root, int32_t mouseX, int32_t mouseY) {
ScrollPaneDataT *sp = (ScrollPaneDataT *)w->data; (void)root;
ScrollPaneDataT *sp = (ScrollPaneDataT *)w->data;
int32_t orient = sp->sbDragOrient;
int32_t dragOff = sp->sbDragOff;
AppContextT *ctx = wgtGetContext(w); AppContextT *ctx = wgtGetContext(w);
const BitmapFontT *font = &ctx->font; const BitmapFontT *font = &ctx->font;
@ -782,7 +787,7 @@ static const WidgetClassT sClassScrollPane = {
.getText = NULL, .getText = NULL,
.setText = NULL, .setText = NULL,
.scrollChildIntoView = wgtScrollPaneScrollToChild, .scrollChildIntoView = wgtScrollPaneScrollToChild,
.scrollDragUpdate = widgetScrollPaneScrollDragUpdate .onDragUpdate = widgetScrollPaneOnDragUpdate
}; };

View file

@ -33,6 +33,7 @@ typedef struct {
int32_t minValue; int32_t minValue;
int32_t maxValue; int32_t maxValue;
bool vertical; bool vertical;
int32_t dragOffset;
} SliderDataT; } SliderDataT;
@ -152,8 +153,8 @@ void widgetSliderOnMouse(WidgetT *hit, WidgetT *root, int32_t vx, int32_t vy) {
if (mousePos >= thumbPos && mousePos < thumbPos + SLIDER_THUMB_W) { if (mousePos >= thumbPos && mousePos < thumbPos + SLIDER_THUMB_W) {
// Click on thumb -- start drag // Click on thumb -- start drag
sDragSlider = hit; sDragWidget = hit;
sDragOffset = mousePos - thumbPos; d->dragOffset = mousePos - thumbPos;
} else { } else {
// Click on track -- jump to position // Click on track -- jump to position
int32_t newVal = d->minValue + ((mousePos - SLIDER_THUMB_W / 2) * range) / thumbRange; int32_t newVal = d->minValue + ((mousePos - SLIDER_THUMB_W / 2) * range) / thumbRange;
@ -174,8 +175,8 @@ void widgetSliderOnMouse(WidgetT *hit, WidgetT *root, int32_t vx, int32_t vy) {
if (mousePos >= thumbPos && mousePos < thumbPos + SLIDER_THUMB_W) { if (mousePos >= thumbPos && mousePos < thumbPos + SLIDER_THUMB_W) {
// Click on thumb -- start drag // Click on thumb -- start drag
sDragSlider = hit; sDragWidget = hit;
sDragOffset = mousePos - thumbPos; d->dragOffset = mousePos - thumbPos;
} else { } else {
// Click on track -- jump to position // Click on track -- jump to position
int32_t newVal = d->minValue + ((mousePos - SLIDER_THUMB_W / 2) * range) / thumbRange; int32_t newVal = d->minValue + ((mousePos - SLIDER_THUMB_W / 2) * range) / thumbRange;
@ -265,14 +266,14 @@ void widgetSliderPaint(WidgetT *w, DisplayT *d, const BlitOpsT *ops, const Bitma
// ============================================================ // ============================================================
// widgetSliderScrollDragUpdate // widgetSliderOnDragUpdate
// ============================================================ // ============================================================
// //
// Called by the event loop during slider thumb drag. Computes the // Called by the event loop during slider thumb drag. Computes the
// new value from mouse position and the stored drag offset. // new value from mouse position and the stored drag offset.
static void widgetSliderScrollDragUpdate(WidgetT *w, int32_t orient, int32_t dragOff, int32_t mouseX, int32_t mouseY) { static void widgetSliderOnDragUpdate(WidgetT *w, WidgetT *root, int32_t mouseX, int32_t mouseY) {
(void)orient; (void)root;
SliderDataT *d = (SliderDataT *)w->data; SliderDataT *d = (SliderDataT *)w->data;
int32_t range = d->maxValue - d->minValue; int32_t range = d->maxValue - d->minValue;
@ -284,11 +285,11 @@ static void widgetSliderScrollDragUpdate(WidgetT *w, int32_t orient, int32_t dra
if (d->vertical) { if (d->vertical) {
int32_t thumbRange = w->h - SLIDER_THUMB_W; int32_t thumbRange = w->h - SLIDER_THUMB_W;
int32_t relY = mouseY - w->y - dragOff; int32_t relY = mouseY - w->y - d->dragOffset;
newVal = d->minValue + (relY * range) / thumbRange; newVal = d->minValue + (relY * range) / thumbRange;
} else { } else {
int32_t thumbRange = w->w - SLIDER_THUMB_W; int32_t thumbRange = w->w - SLIDER_THUMB_W;
int32_t relX = mouseX - w->x - dragOff; int32_t relX = mouseX - w->x - d->dragOffset;
newVal = d->minValue + (relX * range) / thumbRange; newVal = d->minValue + (relX * range) / thumbRange;
} }
@ -329,7 +330,7 @@ static const WidgetClassT sClassSlider = {
.destroy = widgetSliderDestroy, .destroy = widgetSliderDestroy,
.getText = NULL, .getText = NULL,
.setText = NULL, .setText = NULL,
.scrollDragUpdate = widgetSliderScrollDragUpdate .onDragUpdate = widgetSliderOnDragUpdate
}; };
// ============================================================ // ============================================================

View file

@ -287,7 +287,8 @@ void widgetSpinnerOnKey(WidgetT *w, int32_t key, int32_t mod) {
&d->scrollOff, &d->scrollOff,
&d->selStart, &d->selEnd, &d->selStart, &d->selEnd,
d->undoBuf, &d->undoLen, d->undoBuf, &d->undoLen,
&d->undoCursor); &d->undoCursor,
w->w - SPINNER_BORDER * 2 - SPINNER_BTN_W);
// Validate buffer after paste -- reject non-numeric content. // Validate buffer after paste -- reject non-numeric content.
// Allow optional leading minus and digits only. // Allow optional leading minus and digits only.

View file

@ -38,6 +38,7 @@ static int32_t sTypeId = -1;
typedef struct { typedef struct {
int32_t dividerPos; int32_t dividerPos;
bool vertical; bool vertical;
int32_t dragStart;
} SplitterDataT; } SplitterDataT;
@ -48,7 +49,7 @@ typedef struct {
static WidgetT *spFirstChild(WidgetT *w); static WidgetT *spFirstChild(WidgetT *w);
static WidgetT *spSecondChild(WidgetT *w); static WidgetT *spSecondChild(WidgetT *w);
static void widgetSplitterDestroy(WidgetT *w); static void widgetSplitterDestroy(WidgetT *w);
static void widgetSplitterScrollDragUpdate(WidgetT *w, int32_t orient, int32_t dragOff, int32_t mouseX, int32_t mouseY); static void widgetSplitterOnDragUpdate(WidgetT *w, WidgetT *root, int32_t mouseX, int32_t mouseY);
// ============================================================ // ============================================================
@ -248,12 +249,12 @@ void widgetSplitterOnMouse(WidgetT *hit, WidgetT *root, int32_t vx, int32_t vy)
if (onDivider) { if (onDivider) {
// Start dragging // Start dragging
sDragSplitter = hit; sDragWidget = hit;
if (d->vertical) { if (d->vertical) {
sDragSplitStart = vx - hit->x - pos; d->dragStart = vx - hit->x - pos;
} else { } else {
sDragSplitStart = vy - hit->y - pos; d->dragStart = vy - hit->y - pos;
} }
return; return;
@ -380,7 +381,7 @@ int32_t widgetSplitterGetCursorShape(const WidgetT *w, int32_t vx, int32_t vy) {
SplitterDataT *d = (SplitterDataT *)w->data; SplitterDataT *d = (SplitterDataT *)w->data;
// During active drag, always show resize cursor // During active drag, always show resize cursor
if (sDragSplitter == w) { if (sDragWidget == w) {
return d->vertical ? CURSOR_RESIZE_H : CURSOR_RESIZE_V; return d->vertical ? CURSOR_RESIZE_H : CURSOR_RESIZE_V;
} }
@ -428,18 +429,18 @@ static void widgetSplitterDestroy(WidgetT *w) {
// ============================================================ // ============================================================
// widgetSplitterScrollDragUpdate // widgetSplitterOnDragUpdate
// ============================================================ // ============================================================
static void widgetSplitterScrollDragUpdate(WidgetT *w, int32_t orient, int32_t dragOff, int32_t mouseX, int32_t mouseY) { static void widgetSplitterOnDragUpdate(WidgetT *w, WidgetT *root, int32_t mouseX, int32_t mouseY) {
(void)orient; (void)root;
SplitterDataT *d = (SplitterDataT *)w->data; SplitterDataT *d = (SplitterDataT *)w->data;
int32_t pos; int32_t pos;
if (d->vertical) { if (d->vertical) {
pos = mouseX - w->x - dragOff; pos = mouseX - w->x - d->dragStart;
} else { } else {
pos = mouseY - w->y - dragOff; pos = mouseY - w->y - d->dragStart;
} }
widgetSplitterClampPos(w, &pos); widgetSplitterClampPos(w, &pos);
@ -472,7 +473,7 @@ static const WidgetClassT sClassSplitter = {
.getText = NULL, .getText = NULL,
.setText = NULL, .setText = NULL,
.getCursorShape = widgetSplitterGetCursorShape, .getCursorShape = widgetSplitterGetCursorShape,
.scrollDragUpdate = widgetSplitterScrollDragUpdate .onDragUpdate = widgetSplitterOnDragUpdate
}; };

View file

@ -98,6 +98,9 @@ typedef struct {
int32_t undoCursor; int32_t undoCursor;
int32_t cachedLines; int32_t cachedLines;
int32_t cachedMaxLL; int32_t cachedMaxLL;
int32_t sbDragOrient;
int32_t sbDragOff;
bool sbDragging;
} TextAreaDataT; } TextAreaDataT;
#include <ctype.h> #include <ctype.h>
@ -133,7 +136,8 @@ static int32_t textAreaLineStart(const char *buf, int32_t len, int32_t row);
static int32_t textAreaMaxLineLen(const char *buf, int32_t len); static int32_t textAreaMaxLineLen(const char *buf, int32_t len);
static void textAreaOffToRowCol(const char *buf, int32_t off, int32_t *row, int32_t *col); static void textAreaOffToRowCol(const char *buf, int32_t off, int32_t *row, int32_t *col);
static void textEditSaveUndo(char *buf, int32_t len, int32_t cursor, char *undoBuf, int32_t *pUndoLen, int32_t *pUndoCursor, int32_t bufSize); static void textEditSaveUndo(char *buf, int32_t len, int32_t cursor, char *undoBuf, int32_t *pUndoLen, int32_t *pUndoCursor, int32_t bufSize);
static void widgetTextAreaScrollDragUpdate(WidgetT *w, int32_t orient, int32_t dragOff, int32_t mouseX, int32_t mouseY); static void widgetTextAreaDragSelect(WidgetT *w, WidgetT *root, int32_t vx, int32_t vy);
static void widgetTextAreaOnDragUpdate(WidgetT *w, WidgetT *root, int32_t x, int32_t y);
static int32_t wordBoundaryLeft(const char *buf, int32_t pos); static int32_t wordBoundaryLeft(const char *buf, int32_t pos);
static int32_t wordBoundaryRight(const char *buf, int32_t len, int32_t pos); static int32_t wordBoundaryRight(const char *buf, int32_t len, int32_t pos);
WidgetT *wgtTextInput(WidgetT *parent, int32_t maxLen); WidgetT *wgtTextInput(WidgetT *parent, int32_t maxLen);
@ -1324,7 +1328,7 @@ navigation:
// clicks. Content clicks convert pixel coordinates to row/col using // clicks. Content clicks convert pixel coordinates to row/col using
// font metrics and scroll offset. Multi-click: double-click selects // font metrics and scroll offset. Multi-click: double-click selects
// word, triple-click selects entire line. Single click starts a // word, triple-click selects entire line. Single click starts a
// drag-select (sets sDragTextSelect which the event loop monitors // drag-select (sets sDragWidget which the event loop monitors
// on mouse-move to extend the selection). The drag-select global // on mouse-move to extend the selection). The drag-select global
// is cleared on double/triple click since the selection is already // is cleared on double/triple click since the selection is already
// complete. // complete.
@ -1404,9 +1408,10 @@ void widgetTextAreaOnMouse(WidgetT *w, WidgetT *root, int32_t vx, int32_t vy) {
ta->scrollCol += visCols; ta->scrollCol += visCols;
ta->scrollCol = clampInt(ta->scrollCol, 0, maxHScroll); ta->scrollCol = clampInt(ta->scrollCol, 0, maxHScroll);
} else { } else {
sDragScrollbar = w; sDragWidget = w;
sDragScrollbarOrient = 1; ta->sbDragOrient = 1;
sDragScrollbarOff = trackRelX - thumbPos; ta->sbDragging = true;
ta->sbDragOff = trackRelX - thumbPos;
return; return;
} }
} }
@ -1448,9 +1453,10 @@ void widgetTextAreaOnMouse(WidgetT *w, WidgetT *root, int32_t vx, int32_t vy) {
ta->scrollRow += visRows; ta->scrollRow += visRows;
ta->scrollRow = clampInt(ta->scrollRow, 0, maxScroll); ta->scrollRow = clampInt(ta->scrollRow, 0, maxScroll);
} else { } else {
sDragScrollbar = w; sDragWidget = w;
sDragScrollbarOrient = 0; ta->sbDragOrient = 0;
sDragScrollbarOff = trackRelY - thumbPos; ta->sbDragging = true;
ta->sbDragOff = trackRelY - thumbPos;
return; return;
} }
} }
@ -1495,7 +1501,7 @@ void widgetTextAreaOnMouse(WidgetT *w, WidgetT *root, int32_t vx, int32_t vy) {
ta->desiredCol = lineL; ta->desiredCol = lineL;
ta->selAnchor = lineStart; ta->selAnchor = lineStart;
ta->selCursor = lineEnd; ta->selCursor = lineEnd;
sDragTextSelect = NULL; sDragWidget = NULL;
return; return;
} }
@ -1514,7 +1520,7 @@ void widgetTextAreaOnMouse(WidgetT *w, WidgetT *root, int32_t vx, int32_t vy) {
ta->desiredCol = weCol; ta->desiredCol = weCol;
ta->selAnchor = ws; ta->selAnchor = ws;
ta->selCursor = we; ta->selCursor = we;
sDragTextSelect = NULL; sDragWidget = NULL;
return; return;
} }
@ -1526,7 +1532,8 @@ void widgetTextAreaOnMouse(WidgetT *w, WidgetT *root, int32_t vx, int32_t vy) {
int32_t anchorOff = textAreaCursorToOff(ta->buf, ta->len, clickRow, clickCol); int32_t anchorOff = textAreaCursorToOff(ta->buf, ta->len, clickRow, clickCol);
ta->selAnchor = anchorOff; ta->selAnchor = anchorOff;
ta->selCursor = anchorOff; ta->selCursor = anchorOff;
sDragTextSelect = w; sDragWidget = w;
ta->sbDragging = false;
} }
@ -1909,7 +1916,8 @@ void widgetTextInputOnKey(WidgetT *w, int32_t key, int32_t mod) {
&ti->scrollOff, &ti->scrollOff,
&ti->selStart, &ti->selEnd, &ti->selStart, &ti->selEnd,
ti->undoBuf, &ti->undoLen, ti->undoBuf, &ti->undoLen,
&ti->undoCursor); &ti->undoCursor,
w->w - TEXT_INPUT_PAD * 2);
} }
@ -1922,7 +1930,7 @@ void widgetTextInputOnKey(WidgetT *w, int32_t key, int32_t mod) {
// scrollOff). Multi-click: double-click selects word (using // scrollOff). Multi-click: double-click selects word (using
// wordStart/wordEnd), triple-click selects all. Single click starts // wordStart/wordEnd), triple-click selects all. Single click starts
// drag-select by setting both selStart and selEnd to the click // drag-select by setting both selStart and selEnd to the click
// position and registering sDragTextSelect. // position and registering sDragWidget.
void widgetTextInputOnMouse(WidgetT *w, WidgetT *root, int32_t vx, int32_t vy) { void widgetTextInputOnMouse(WidgetT *w, WidgetT *root, int32_t vx, int32_t vy) {
TextInputDataT *ti = (TextInputDataT *)w->data; TextInputDataT *ti = (TextInputDataT *)w->data;
w->focused = true; w->focused = true;
@ -2007,15 +2015,6 @@ void widgetTextInputSetText(WidgetT *w, const char *text) {
} }
// ============================================================
// widgetTextInputGetFieldWidth
// ============================================================
int32_t widgetTextInputGetFieldWidth(const WidgetT *w) {
return w->w - TEXT_INPUT_PAD * 2;
}
// ============================================================ // ============================================================
// DXE registration // DXE registration
// ============================================================ // ============================================================
@ -2034,7 +2033,7 @@ static bool widgetTextInputClearSelection(WidgetT *w) {
} }
static void widgetTextInputDragSelect(WidgetT *w, WidgetT *root, int32_t vx, int32_t vy) { static void widgetTextInputOnDragUpdate(WidgetT *w, WidgetT *root, int32_t vx, int32_t vy) {
(void)root; (void)root;
(void)vy; (void)vy;
TextInputDataT *ti = (TextInputDataT *)w->data; TextInputDataT *ti = (TextInputDataT *)w->data;
@ -2057,8 +2056,7 @@ static const WidgetClassT sClassTextInput = {
.getText = widgetTextInputGetText, .getText = widgetTextInputGetText,
.setText = widgetTextInputSetText, .setText = widgetTextInputSetText,
.clearSelection = widgetTextInputClearSelection, .clearSelection = widgetTextInputClearSelection,
.dragSelect = widgetTextInputDragSelect, .onDragUpdate = widgetTextInputOnDragUpdate
.getTextFieldWidth = widgetTextInputGetFieldWidth
}; };
// ============================================================ // ============================================================
@ -2129,6 +2127,17 @@ static void widgetTextAreaScrollDragUpdate(WidgetT *w, int32_t orient, int32_t d
} }
static void widgetTextAreaOnDragUpdate(WidgetT *w, WidgetT *root, int32_t x, int32_t y) {
TextAreaDataT *ta = (TextAreaDataT *)w->data;
if (ta->sbDragging) {
widgetTextAreaScrollDragUpdate(w, ta->sbDragOrient, ta->sbDragOff, x, y);
} else {
widgetTextAreaDragSelect(w, root, x, y);
}
}
static bool widgetTextAreaClearSelection(WidgetT *w) { static bool widgetTextAreaClearSelection(WidgetT *w) {
TextAreaDataT *ta = (TextAreaDataT *)w->data; TextAreaDataT *ta = (TextAreaDataT *)w->data;
@ -2203,8 +2212,7 @@ static const WidgetClassT sClassTextArea = {
.getText = widgetTextAreaGetText, .getText = widgetTextAreaGetText,
.setText = widgetTextAreaSetText, .setText = widgetTextAreaSetText,
.clearSelection = widgetTextAreaClearSelection, .clearSelection = widgetTextAreaClearSelection,
.dragSelect = widgetTextAreaDragSelect, .onDragUpdate = widgetTextAreaOnDragUpdate
.scrollDragUpdate = widgetTextAreaScrollDragUpdate
}; };
// ============================================================ // ============================================================

View file

@ -72,6 +72,8 @@ typedef struct {
int32_t cachedTotalHeight; int32_t cachedTotalHeight;
int32_t cachedMaxWidth; int32_t cachedMaxWidth;
bool dimsValid; bool dimsValid;
int32_t sbDragOrient;
int32_t sbDragOff;
} TreeViewDataT; } TreeViewDataT;
typedef struct { typedef struct {
@ -1028,9 +1030,9 @@ void widgetTreeViewOnMouse(WidgetT *hit, WidgetT *root, int32_t vx, int32_t vy)
int32_t thumbSize; int32_t thumbSize;
widgetScrollbarThumb(trackLen, totalH, innerH, tv->scrollPos, &thumbPos, &thumbSize); widgetScrollbarThumb(trackLen, totalH, innerH, tv->scrollPos, &thumbPos, &thumbSize);
sDragScrollbar = hit; sDragWidget = hit;
sDragScrollbarOrient = 0; tv->sbDragOrient = 0;
sDragScrollbarOff = relY - WGT_SB_W - thumbPos; tv->sbDragOff = relY - WGT_SB_W - thumbPos;
} }
tv->scrollPos = clampInt(tv->scrollPos, 0, maxScrollV); tv->scrollPos = clampInt(tv->scrollPos, 0, maxScrollV);
@ -1066,9 +1068,9 @@ void widgetTreeViewOnMouse(WidgetT *hit, WidgetT *root, int32_t vx, int32_t vy)
int32_t thumbSize; int32_t thumbSize;
widgetScrollbarThumb(trackLen, totalW, innerW, tv->scrollPosH, &thumbPos, &thumbSize); widgetScrollbarThumb(trackLen, totalW, innerW, tv->scrollPosH, &thumbPos, &thumbSize);
sDragScrollbar = hit; sDragWidget = hit;
sDragScrollbarOrient = 1; tv->sbDragOrient = 1;
sDragScrollbarOff = relX - WGT_SB_W - thumbPos; tv->sbDragOff = relX - WGT_SB_W - thumbPos;
} }
tv->scrollPosH = clampInt(tv->scrollPosH, 0, maxScrollH); tv->scrollPosH = clampInt(tv->scrollPosH, 0, maxScrollH);
@ -1192,7 +1194,7 @@ void widgetTreeViewOnMouse(WidgetT *hit, WidgetT *root, int32_t vx, int32_t vy)
if (tv->reorderable && !clickedExpandIcon && !shift && !ctrl) { if (tv->reorderable && !clickedExpandIcon && !shift && !ctrl) {
tv->dragItem = item; tv->dragItem = item;
tv->dropTarget = NULL; tv->dropTarget = NULL;
sDragReorder = hit; sDragWidget = hit;
} }
} }
@ -1483,6 +1485,40 @@ static void widgetTreeViewReorderDrop(WidgetT *w) {
} }
// ============================================================
// widgetTreeViewOnDragEnd
// ============================================================
static void widgetTreeViewOnDragEnd(WidgetT *w, WidgetT *root, int32_t x, int32_t y) {
(void)root;
(void)x;
(void)y;
TreeViewDataT *tv = (TreeViewDataT *)w->data;
if (tv->dragItem) {
widgetTreeViewReorderDrop(w);
tv->dragItem = NULL;
}
wgtInvalidatePaint(w);
}
// ============================================================
// widgetTreeViewOnDragUpdate
// ============================================================
static void widgetTreeViewOnDragUpdate(WidgetT *w, WidgetT *root, int32_t x, int32_t y) {
TreeViewDataT *tv = (TreeViewDataT *)w->data;
if (tv->dragItem) {
widgetTreeViewReorderUpdate(w, root, x, y);
} else {
widgetTreeViewScrollDragUpdate(w, tv->sbDragOrient, tv->sbDragOff, x, y);
}
}
static const WidgetClassT sClassTreeView = { static const WidgetClassT sClassTreeView = {
.flags = WCLASS_FOCUSABLE | WCLASS_PAINTS_CHILDREN | WCLASS_NO_HIT_RECURSE | WCLASS_SCROLLABLE, .flags = WCLASS_FOCUSABLE | WCLASS_PAINTS_CHILDREN | WCLASS_NO_HIT_RECURSE | WCLASS_SCROLLABLE,
.paint = widgetTreeViewPaint, .paint = widgetTreeViewPaint,
@ -1495,9 +1531,8 @@ static const WidgetClassT sClassTreeView = {
.getText = NULL, .getText = NULL,
.setText = NULL, .setText = NULL,
.onChildChanged = widgetTreeViewOnChildChanged, .onChildChanged = widgetTreeViewOnChildChanged,
.reorderDrop = widgetTreeViewReorderDrop, .onDragUpdate = widgetTreeViewOnDragUpdate,
.reorderUpdate = widgetTreeViewReorderUpdate, .onDragEnd = widgetTreeViewOnDragEnd
.scrollDragUpdate = widgetTreeViewScrollDragUpdate
}; };
static const WidgetClassT sClassTreeItem = { static const WidgetClassT sClassTreeItem = {