Several more minor widget fixes.
This commit is contained in:
parent
094b263c36
commit
7c1eb495e1
10 changed files with 59 additions and 45 deletions
|
|
@ -6743,13 +6743,13 @@ static void onFormWinPaint(WindowT *win, RectT *dirtyArea) {
|
|||
|
||||
// Force measure + relayout only on structural changes (control
|
||||
// add/remove/resize). Selection clicks just need repaint + overlay.
|
||||
if (win->fullRepaint && win->widgetRoot) {
|
||||
if (win->paintNeeded >= PAINT_FULL && win->widgetRoot) {
|
||||
widgetCalcMinSizeTree(win->widgetRoot, &sAc->font);
|
||||
win->widgetRoot->w = 0; // force layout pass
|
||||
}
|
||||
|
||||
// Designer always needs full repaint (handles must be erased/redrawn)
|
||||
win->fullRepaint = true;
|
||||
win->paintNeeded = PAINT_FULL;
|
||||
widgetOnPaint(win, dirtyArea);
|
||||
|
||||
// Then draw selection handles on top
|
||||
|
|
|
|||
|
|
@ -373,7 +373,7 @@ int32_t appMain(DxeAppContextT *ctx) {
|
|||
// Initial paint (dark background)
|
||||
RectT fullRect = {0, 0, sWin->contentW, sWin->contentH};
|
||||
onPaint(sWin, &fullRect);
|
||||
sWin->contentDirty = true;
|
||||
sWin->iconNeedsRefresh = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -197,7 +197,7 @@ Central window object. Each window owns a persistent content backbuffer and rece
|
|||
int32_t contentX, contentY, contentW, contentH Content area inset from frame
|
||||
char title[MAX_TITLE_LEN] Window title text (max 128 chars)
|
||||
bool visible, focused, minimized, maximized, resizable, modal Window state flags
|
||||
bool contentDirty true when contentBuf has changed
|
||||
bool iconNeedsRefresh true when contentBuf changed (for minimized icon refresh)
|
||||
bool needsPaint true until first onPaint call
|
||||
int32_t maxW, maxH Maximum dimensions
|
||||
int32_t preMaxX, preMaxY, preMaxW, preMaxH Saved geometry before maximize
|
||||
|
|
|
|||
|
|
@ -1044,7 +1044,7 @@ static void dispatchEvents(AppContextT *ctx) {
|
|||
if (rWin->onPaint) {
|
||||
RectT fullRect = {0, 0, rWin->contentW, rWin->contentH};
|
||||
rWin->onPaint(rWin, &fullRect);
|
||||
rWin->contentDirty = true;
|
||||
rWin->iconNeedsRefresh = true;
|
||||
}
|
||||
|
||||
dirtyListAdd(&ctx->dirty, rWin->x, rWin->y, rWin->w, rWin->h);
|
||||
|
|
@ -3094,7 +3094,7 @@ static void pollWidgets(AppContextT *ctx) {
|
|||
int32_t dirtyH = 0;
|
||||
|
||||
if (wclsQuickRepaint(w, &dirtyY, &dirtyH) > 0) {
|
||||
win->contentDirty = true;
|
||||
win->iconNeedsRefresh = true;
|
||||
|
||||
if (!win->minimized) {
|
||||
int32_t scrollY = win->vScroll ? win->vScroll->value : 0;
|
||||
|
|
@ -3136,13 +3136,13 @@ static void refreshMinimizedIcons(AppContextT *ctx) {
|
|||
continue;
|
||||
}
|
||||
|
||||
if (!win->iconData && win->contentDirty) {
|
||||
if (!win->iconData && win->iconNeedsRefresh) {
|
||||
if (count >= ctx->iconRefreshIdx) {
|
||||
int32_t ix;
|
||||
int32_t iy;
|
||||
wmMinimizedIconPos(d, iconIdx, &ix, &iy);
|
||||
dirtyListAdd(&ctx->dirty, ix, iy, ICON_TOTAL_SIZE, ICON_TOTAL_SIZE);
|
||||
win->contentDirty = false;
|
||||
win->iconNeedsRefresh = false;
|
||||
ctx->iconRefreshIdx = count + 1;
|
||||
return;
|
||||
}
|
||||
|
|
@ -3192,7 +3192,7 @@ static void repositionWindow(AppContextT *ctx, WindowT *win, int32_t x, int32_t
|
|||
if (win->onPaint) {
|
||||
RectT fullRect = {0, 0, win->contentW, win->contentH};
|
||||
WIN_CALLBACK(ctx, win, win->onPaint(win, &fullRect));
|
||||
win->contentDirty = true;
|
||||
win->iconNeedsRefresh = true;
|
||||
}
|
||||
|
||||
// Dirty new position
|
||||
|
|
@ -3693,7 +3693,7 @@ int32_t dvxChangeVideoMode(AppContextT *ctx, int32_t requestedW, int32_t request
|
|||
if (win->onPaint) {
|
||||
RectT fullRect = {0, 0, win->contentW, win->contentH};
|
||||
WIN_CALLBACK(ctx, win, win->onPaint(win, &fullRect));
|
||||
win->contentDirty = true;
|
||||
win->iconNeedsRefresh = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -4351,7 +4351,7 @@ void dvxInvalidateWindow(AppContextT *ctx, WindowT *win) {
|
|||
WIN_CALLBACK(ctx, win, win->onPaint(win, &fullRect));
|
||||
}
|
||||
|
||||
win->contentDirty = true;
|
||||
win->iconNeedsRefresh = true;
|
||||
dirtyListAdd(&ctx->dirty, win->x, win->y, win->w, win->h);
|
||||
}
|
||||
|
||||
|
|
@ -5081,22 +5081,19 @@ bool dvxUpdate(AppContextT *ctx) {
|
|||
refreshMinimizedIcons(ctx);
|
||||
}
|
||||
|
||||
// Flush deferred widget paints. wgtInvalidatePaint sets
|
||||
// widgetPaintPending instead of calling dvxInvalidateWindow inline,
|
||||
// so multiple invalidations per frame are batched into one tree walk.
|
||||
// Also handles first-paint (fullRepaint) for newly created windows.
|
||||
// Flush deferred paints. paintNeeded is set by wgtInvalidatePaint
|
||||
// (PARTIAL) or wgtInvalidate (FULL). Multiple calls per frame are
|
||||
// batched into one paint — the highest level wins.
|
||||
for (int32_t i = 0; i < ctx->stack.count; i++) {
|
||||
WindowT *win = ctx->stack.windows[i];
|
||||
|
||||
if ((win->widgetPaintPending || win->fullRepaint) && win->onPaint) {
|
||||
win->widgetPaintPending = false;
|
||||
if (win->paintNeeded && win->onPaint) {
|
||||
RectT fullRect = {0, 0, win->contentW, win->contentH};
|
||||
WIN_CALLBACK(ctx, win, win->onPaint(win, &fullRect));
|
||||
// fullRepaint is cleared by widgetOnPaint for widget windows.
|
||||
// For raw-paint windows, clear it here so they don't repaint
|
||||
// every frame forever.
|
||||
win->fullRepaint = false;
|
||||
win->contentDirty = true;
|
||||
// widgetOnPaint clears paintNeeded for widget windows.
|
||||
// For raw-paint windows, clear it here.
|
||||
win->paintNeeded = PAINT_NONE;
|
||||
win->iconNeedsRefresh = true;
|
||||
dirtyListAdd(&ctx->dirty, win->x, win->y, win->w, win->h);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -465,6 +465,11 @@ typedef struct {
|
|||
#define MIN_WINDOW_H 60
|
||||
#define WM_MAX_FROM_SCREEN (-1) // use screen dimension as max (for maxW/maxH)
|
||||
|
||||
// Window paint states (for WindowT.paintNeeded)
|
||||
#define PAINT_NONE 0 // no paint needed
|
||||
#define PAINT_PARTIAL 1 // only dirty widgets (deferred wgtInvalidatePaint)
|
||||
#define PAINT_FULL 2 // clear + relayout + repaint all (wgtInvalidate)
|
||||
|
||||
// Hit test region identifiers (returned via hitPart from wmHitTest)
|
||||
#define HIT_CONTENT 0
|
||||
#define HIT_TITLE 1
|
||||
|
|
@ -506,9 +511,12 @@ typedef struct WindowT {
|
|||
bool maximized;
|
||||
bool resizable;
|
||||
bool modal;
|
||||
bool contentDirty; // true when contentBuf has changed since last icon refresh
|
||||
bool fullRepaint; // true = clear + repaint all widgets; false = only dirty ones
|
||||
bool widgetPaintPending; // deferred widget paint (set by wgtInvalidatePaint)
|
||||
bool iconNeedsRefresh; // true when contentBuf has changed since last icon refresh
|
||||
// Paint state: PAINT_NONE = idle, PAINT_PARTIAL = only dirty widgets,
|
||||
// PAINT_FULL = clear background + relayout + repaint all.
|
||||
// wgtInvalidatePaint sets PARTIAL, wgtInvalidate sets FULL.
|
||||
// Higher values take priority (FULL > PARTIAL > NONE).
|
||||
uint8_t paintNeeded; // 0=none, 1=partial, 2=full
|
||||
int32_t maxW; // maximum width (WM_MAX_FROM_SCREEN = use screen width)
|
||||
int32_t maxH; // maximum height (WM_MAX_FROM_SCREEN = use screen height)
|
||||
// Pre-maximize geometry is saved so wmRestore() can put the window
|
||||
|
|
|
|||
17
core/dvxWm.c
17
core/dvxWm.c
|
|
@ -1323,7 +1323,7 @@ WindowT *wmCreateWindow(WindowStackT *stack, DisplayT *d, const char *title, int
|
|||
memset(win->contentBuf, 0xFF, bufSize);
|
||||
}
|
||||
|
||||
win->fullRepaint = true;
|
||||
win->paintNeeded = PAINT_FULL;
|
||||
|
||||
stack->windows[stack->count] = win;
|
||||
stack->count++;
|
||||
|
|
@ -1419,6 +1419,7 @@ void wmDragBegin(WindowStackT *stack, int32_t idx, int32_t mouseX, int32_t mouse
|
|||
|
||||
void wmDragEnd(WindowStackT *stack) {
|
||||
stack->dragWindow = -1;
|
||||
stack->dragActive = false;
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -1620,7 +1621,7 @@ void wmDrawContent(DisplayT *d, const BlitOpsT *ops, WindowT *win, const RectT *
|
|||
// The live thumbnail (option 2) shows miniature window contents in the
|
||||
// icon/task view, giving a quick preview of each minimized window. The thumbnail
|
||||
// is rendered from the existing content buffer, so no extra rendering
|
||||
// pass is needed. contentDirty tracks whether the content has changed
|
||||
// pass is needed. iconNeedsRefresh tracks whether the content has changed
|
||||
// since the last icon refresh.
|
||||
//
|
||||
// Icons are drawn before windows in the compositing loop (painter's
|
||||
|
|
@ -1902,10 +1903,10 @@ void wmMaximize(WindowStackT *stack, DirtyListT *dl, const DisplayT *d, WindowT
|
|||
}
|
||||
|
||||
if (win->onPaint) {
|
||||
win->fullRepaint = true;
|
||||
win->paintNeeded = PAINT_FULL;
|
||||
RectT fullRect = {0, 0, win->contentW, win->contentH};
|
||||
win->onPaint(win, &fullRect);
|
||||
win->contentDirty = true;
|
||||
win->iconNeedsRefresh = true;
|
||||
}
|
||||
|
||||
dirtyListAdd(dl, win->x, win->y, win->w, win->h);
|
||||
|
|
@ -2420,10 +2421,10 @@ void wmResizeMove(WindowStackT *stack, DirtyListT *dl, const DisplayT *d, int32_
|
|||
}
|
||||
|
||||
if (win->onPaint) {
|
||||
win->fullRepaint = true;
|
||||
win->paintNeeded = PAINT_FULL;
|
||||
RectT fullRect = {0, 0, win->contentW, win->contentH};
|
||||
win->onPaint(win, &fullRect);
|
||||
win->contentDirty = true;
|
||||
win->iconNeedsRefresh = true;
|
||||
}
|
||||
|
||||
// Always update dragOff to the clamped position so the next delta
|
||||
|
|
@ -2474,10 +2475,10 @@ void wmRestore(WindowStackT *stack, DirtyListT *dl, const DisplayT *d, WindowT *
|
|||
}
|
||||
|
||||
if (win->onPaint) {
|
||||
win->fullRepaint = true;
|
||||
win->paintNeeded = PAINT_FULL;
|
||||
RectT fullRect = {0, 0, win->contentW, win->contentH};
|
||||
win->onPaint(win, &fullRect);
|
||||
win->contentDirty = true;
|
||||
win->iconNeedsRefresh = true;
|
||||
}
|
||||
|
||||
// Mark restored position dirty
|
||||
|
|
|
|||
|
|
@ -301,7 +301,7 @@ static void widgetOnMouseInner(WindowT *win, WidgetT *root, int32_t x, int32_t y
|
|||
int32_t rectX = win->x + win->contentX;
|
||||
int32_t rectY = win->y + win->contentY + dirtyY - scrollY2;
|
||||
int32_t rectW = win->contentW;
|
||||
win->contentDirty = true;
|
||||
win->iconNeedsRefresh = true;
|
||||
dirtyListAdd(&ctx->dirty, rectX, rectY, rectW, dirtyH);
|
||||
return;
|
||||
}
|
||||
|
|
@ -507,7 +507,7 @@ void widgetOnBlur(WindowT *win) {
|
|||
// refresh its minimized icon thumbnail if needed.
|
||||
|
||||
void widgetOnFocus(WindowT *win) {
|
||||
win->contentDirty = true;
|
||||
win->iconNeedsRefresh = true;
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -560,8 +560,8 @@ void widgetOnPaint(WindowT *win, RectT *dirtyArea) {
|
|||
cd.clipW = win->contentW;
|
||||
cd.clipH = win->contentH;
|
||||
|
||||
bool full = win->fullRepaint;
|
||||
win->fullRepaint = false;
|
||||
bool full = (win->paintNeeded >= PAINT_FULL);
|
||||
win->paintNeeded = PAINT_NONE;
|
||||
|
||||
if (full) {
|
||||
// Full repaint: clear background, relayout, paint everything
|
||||
|
|
|
|||
|
|
@ -408,7 +408,7 @@ void wgtInvalidate(WidgetT *w) {
|
|||
}
|
||||
|
||||
// Full repaint — layout changed, all widgets need redrawing
|
||||
w->window->fullRepaint = true;
|
||||
w->window->paintNeeded = PAINT_FULL;
|
||||
dvxInvalidateWindow(ctx, w->window);
|
||||
}
|
||||
|
||||
|
|
@ -443,8 +443,10 @@ void wgtInvalidatePaint(WidgetT *w) {
|
|||
|
||||
// Defer the actual paint — it will happen once in the main loop
|
||||
// before compositing, batching multiple invalidations into one
|
||||
// tree walk instead of one per call.
|
||||
w->window->widgetPaintPending = true;
|
||||
// tree walk instead of one per call. Don't downgrade FULL to PARTIAL.
|
||||
if (w->window->paintNeeded < PAINT_PARTIAL) {
|
||||
w->window->paintNeeded = PAINT_PARTIAL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1358,7 +1358,7 @@ img { max-width: 100%; }
|
|||
int32_t contentX, contentY, contentW, contentH Content area inset from frame
|
||||
char title[MAX_TITLE_LEN] Window title text (max 128 chars)
|
||||
bool visible, focused, minimized, maximized, resizable, modal Window state flags
|
||||
bool contentDirty true when contentBuf has changed
|
||||
bool iconNeedsRefresh true when contentBuf changed (for minimized icon refresh)
|
||||
bool needsPaint true until first onPaint call
|
||||
int32_t maxW, maxH Maximum dimensions
|
||||
int32_t preMaxX, preMaxY, preMaxW, preMaxH Saved geometry before maximize
|
||||
|
|
|
|||
|
|
@ -444,8 +444,8 @@ void widgetTabControlPaint(WidgetT *w, DisplayT *d, const BlitOpsT *ops, const B
|
|||
}
|
||||
}
|
||||
|
||||
// Tab headers -- clip to header area
|
||||
int32_t headerLeft = w->x + 2 + (scroll ? TAB_ARROW_W : 0);
|
||||
// Tab headers -- clip to header area (tabH + 2 for the active tab's extra height)
|
||||
int32_t headerLeft = w->x + 2 + (scroll ? TAB_ARROW_W : 0);
|
||||
int32_t headerRight = scroll ? (w->x + w->w - TAB_ARROW_W) : (w->x + w->w);
|
||||
|
||||
int32_t oldClipX = d->clipX;
|
||||
|
|
@ -454,8 +454,14 @@ void widgetTabControlPaint(WidgetT *w, DisplayT *d, const BlitOpsT *ops, const B
|
|||
int32_t oldClipH = d->clipH;
|
||||
setClipRect(d, headerLeft, w->y, headerRight - headerLeft, tabH + 2);
|
||||
|
||||
// Clear the header strip so scrolled-away tab pixels are erased
|
||||
rectFill(d, ops, headerLeft, w->y, headerRight - headerLeft, tabH + 2, colors->contentBg);
|
||||
// Clear the header strip so scrolled-away tab pixels are erased.
|
||||
// Only clear above the content panel border (tabH, not tabH+2).
|
||||
rectFill(d, ops, headerLeft, w->y, headerRight - headerLeft, tabH, colors->contentBg);
|
||||
|
||||
// Draw the content panel top border across the header area.
|
||||
// Individual active tabs will erase it under themselves below.
|
||||
drawHLine(d, ops, headerLeft, w->y + tabH, headerRight - headerLeft, colors->windowHighlight);
|
||||
drawHLine(d, ops, headerLeft, w->y + tabH + 1, headerRight - headerLeft, colors->windowHighlight);
|
||||
|
||||
int32_t tabX = headerLeft - td->scrollOffset;
|
||||
int32_t tabIdx = 0;
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue