Window movement and resizing clamped to prevent moving them entirely offscreen.

This commit is contained in:
Scott Duensing 2026-03-18 00:45:35 -05:00
parent 1e4951c4cb
commit 0cede78932
3 changed files with 74 additions and 11 deletions

View file

@ -770,7 +770,7 @@ static void dispatchEvents(AppContextT *ctx) {
// Handle active drag // Handle active drag
if (ctx->stack.dragWindow >= 0) { if (ctx->stack.dragWindow >= 0) {
if (buttons & 1) { if (buttons & 1) {
wmDragMove(&ctx->stack, &ctx->dirty, mx, my); wmDragMove(&ctx->stack, &ctx->dirty, mx, my, ctx->display.width, ctx->display.height);
} else { } else {
wmDragEnd(&ctx->stack); wmDragEnd(&ctx->stack);
} }
@ -2782,21 +2782,39 @@ static void pollKeyboard(AppContextT *ctx) {
} }
if (ctx->kbMoveResize.mode == KbModeMoveE) { if (ctx->kbMoveResize.mode == KbModeMoveE) {
int32_t oldX = kbWin->x;
int32_t oldY = kbWin->y;
if (ascii == 0 && scancode == 0x48) { if (ascii == 0 && scancode == 0x48) {
dirtyListAdd(&ctx->dirty, kbWin->x, kbWin->y, kbWin->w, kbWin->h);
kbWin->y -= KB_MOVE_STEP; kbWin->y -= KB_MOVE_STEP;
dirtyListAdd(&ctx->dirty, kbWin->x, kbWin->y, kbWin->w, kbWin->h);
} else if (ascii == 0 && scancode == 0x50) { } else if (ascii == 0 && scancode == 0x50) {
dirtyListAdd(&ctx->dirty, kbWin->x, kbWin->y, kbWin->w, kbWin->h);
kbWin->y += KB_MOVE_STEP; kbWin->y += KB_MOVE_STEP;
dirtyListAdd(&ctx->dirty, kbWin->x, kbWin->y, kbWin->w, kbWin->h);
} else if (ascii == 0 && scancode == 0x4B) { } else if (ascii == 0 && scancode == 0x4B) {
dirtyListAdd(&ctx->dirty, kbWin->x, kbWin->y, kbWin->w, kbWin->h);
kbWin->x -= KB_MOVE_STEP; kbWin->x -= KB_MOVE_STEP;
dirtyListAdd(&ctx->dirty, kbWin->x, kbWin->y, kbWin->w, kbWin->h);
} else if (ascii == 0 && scancode == 0x4D) { } else if (ascii == 0 && scancode == 0x4D) {
dirtyListAdd(&ctx->dirty, kbWin->x, kbWin->y, kbWin->w, kbWin->h);
kbWin->x += KB_MOVE_STEP; kbWin->x += KB_MOVE_STEP;
}
// Clamp: keep title bar reachable
int32_t screenW = ctx->display.width;
int32_t screenH = ctx->display.height;
int32_t minVisible = 50;
if (kbWin->y < 0) {
kbWin->y = 0;
}
if (kbWin->y + CHROME_BORDER_WIDTH + CHROME_TITLE_HEIGHT > screenH) {
kbWin->y = screenH - CHROME_BORDER_WIDTH - CHROME_TITLE_HEIGHT;
}
if (kbWin->x + kbWin->w < minVisible) {
kbWin->x = minVisible - kbWin->w;
}
if (kbWin->x > screenW - minVisible) {
kbWin->x = screenW - minVisible;
}
if (kbWin->x != oldX || kbWin->y != oldY) {
dirtyListAdd(&ctx->dirty, oldX, oldY, kbWin->w, kbWin->h);
dirtyListAdd(&ctx->dirty, kbWin->x, kbWin->y, kbWin->w, kbWin->h); dirtyListAdd(&ctx->dirty, kbWin->x, kbWin->y, kbWin->w, kbWin->h);
} }
} else { } else {
@ -2830,6 +2848,18 @@ static void pollKeyboard(AppContextT *ctx) {
newH = kbWin->maxH; newH = kbWin->maxH;
} }
// Clamp to screen boundaries
int32_t screenW = ctx->display.width;
int32_t screenH = ctx->display.height;
if (kbWin->x + newW > screenW) {
newW = screenW - kbWin->x;
}
if (kbWin->y + newH > screenH) {
newH = screenH - kbWin->y;
}
if (newW != kbWin->w || newH != kbWin->h) { if (newW != kbWin->w || newH != kbWin->h) {
dirtyListAdd(&ctx->dirty, kbWin->x, kbWin->y, kbWin->w, kbWin->h); dirtyListAdd(&ctx->dirty, kbWin->x, kbWin->y, kbWin->w, kbWin->h);
kbWin->w = newW; kbWin->w = newW;

View file

@ -1193,18 +1193,33 @@ void wmDragEnd(WindowStackT *stack) {
// we don't need to ask the app to repaint during the drag, just blit from // we don't need to ask the app to repaint during the drag, just blit from
// its buffer at the new position. // its buffer at the new position.
void wmDragMove(WindowStackT *stack, DirtyListT *dl, int32_t mouseX, int32_t mouseY) { void wmDragMove(WindowStackT *stack, DirtyListT *dl, int32_t mouseX, int32_t mouseY, int32_t screenW, int32_t screenH) {
if (stack->dragWindow < 0 || stack->dragWindow >= stack->count) { if (stack->dragWindow < 0 || stack->dragWindow >= stack->count) {
return; return;
} }
WindowT *win = stack->windows[stack->dragWindow]; WindowT *win = stack->windows[stack->dragWindow];
int32_t minVisible = 50;
dirtyListAdd(dl, win->x, win->y, win->w, win->h); dirtyListAdd(dl, win->x, win->y, win->w, win->h);
win->x = mouseX - stack->dragOffX; win->x = mouseX - stack->dragOffX;
win->y = mouseY - stack->dragOffY; win->y = mouseY - stack->dragOffY;
// Clamp: keep title bar reachable
if (win->y < 0) {
win->y = 0;
}
if (win->y + CHROME_BORDER_WIDTH + CHROME_TITLE_HEIGHT > screenH) {
win->y = screenH - CHROME_BORDER_WIDTH - CHROME_TITLE_HEIGHT;
}
if (win->x + win->w < minVisible) {
win->x = minVisible - win->w;
}
if (win->x > screenW - minVisible) {
win->x = screenW - minVisible;
}
dirtyListAdd(dl, win->x, win->y, win->w, win->h); dirtyListAdd(dl, win->x, win->y, win->w, win->h);
} }
@ -2022,6 +2037,11 @@ void wmResizeMove(WindowStackT *stack, DirtyListT *dl, const DisplayT *d, int32_
newW = maxW; newW = maxW;
} }
if (newX < 0) {
newW += newX;
newX = 0;
}
if (newW >= minW) { if (newW >= minW) {
win->x = newX; win->x = newX;
win->w = newW; win->w = newW;
@ -2036,6 +2056,10 @@ void wmResizeMove(WindowStackT *stack, DirtyListT *dl, const DisplayT *d, int32_
newW = maxW; newW = maxW;
} }
if (win->x + newW > d->width) {
newW = d->width - win->x;
}
if (newW >= minW) { if (newW >= minW) {
win->w = newW; win->w = newW;
appliedX = true; appliedX = true;
@ -2051,6 +2075,11 @@ void wmResizeMove(WindowStackT *stack, DirtyListT *dl, const DisplayT *d, int32_
newH = maxH; newH = maxH;
} }
if (newY < 0) {
newH += newY;
newY = 0;
}
if (newH >= minH) { if (newH >= minH) {
win->y = newY; win->y = newY;
win->h = newH; win->h = newH;
@ -2065,6 +2094,10 @@ void wmResizeMove(WindowStackT *stack, DirtyListT *dl, const DisplayT *d, int32_
newH = maxH; newH = maxH;
} }
if (win->y + newH > d->height) {
newH = d->height - win->y;
}
if (newH >= minH) { if (newH >= minH) {
win->h = newH; win->h = newH;
appliedY = true; appliedY = true;

View file

@ -131,7 +131,7 @@ int32_t wmResizeEdgeHit(const WindowT *win, int32_t mx, int32_t my);
// event while dragWindow is active. Dirties both the old and new window // event while dragWindow is active. Dirties both the old and new window
// positions. The drag offset (mouse position relative to window origin at // positions. The drag offset (mouse position relative to window origin at
// drag start) is applied so the window tracks the mouse smoothly. // drag start) is applied so the window tracks the mouse smoothly.
void wmDragMove(WindowStackT *stack, DirtyListT *dl, int32_t mouseX, int32_t mouseY); void wmDragMove(WindowStackT *stack, DirtyListT *dl, int32_t mouseX, int32_t mouseY, int32_t screenW, int32_t screenH);
// Update window dimensions during an active resize. Enforces MIN_WINDOW_W/H // Update window dimensions during an active resize. Enforces MIN_WINDOW_W/H
// and optional maxW/maxH constraints. Reallocates the content buffer if the // and optional maxW/maxH constraints. Reallocates the content buffer if the