Window tiling/cascading
This commit is contained in:
parent
7129035bed
commit
b5488a6a9e
3 changed files with 354 additions and 1 deletions
312
dvx/dvxApp.c
312
dvx/dvxApp.c
|
|
@ -1039,6 +1039,80 @@ const char *dvxClipboardGet(int32_t *outLen) {
|
|||
}
|
||||
|
||||
|
||||
// ============================================================
|
||||
// dvxCascadeWindows
|
||||
// ============================================================
|
||||
//
|
||||
// Arrange all visible, non-minimized windows in a staggered
|
||||
// diagonal pattern from the top-left corner.
|
||||
|
||||
void dvxCascadeWindows(AppContextT *ctx) {
|
||||
int32_t screenW = ctx->display.width;
|
||||
int32_t screenH = ctx->display.height;
|
||||
int32_t offsetX = 0;
|
||||
int32_t offsetY = 0;
|
||||
int32_t step = CHROME_TITLE_HEIGHT + CHROME_BORDER_WIDTH;
|
||||
|
||||
// Default cascade size: 2/3 of screen
|
||||
int32_t winW = screenW * 2 / 3;
|
||||
int32_t winH = screenH * 2 / 3;
|
||||
|
||||
if (winW < MIN_WINDOW_W) {
|
||||
winW = MIN_WINDOW_W;
|
||||
}
|
||||
|
||||
if (winH < MIN_WINDOW_H) {
|
||||
winH = MIN_WINDOW_H;
|
||||
}
|
||||
|
||||
for (int32_t i = 0; i < ctx->stack.count; i++) {
|
||||
WindowT *win = ctx->stack.windows[i];
|
||||
|
||||
if (win->minimized || !win->visible) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Un-maximize if needed
|
||||
if (win->maximized) {
|
||||
win->maximized = false;
|
||||
}
|
||||
|
||||
// Dirty old position
|
||||
dirtyListAdd(&ctx->dirty, win->x, win->y, win->w, win->h);
|
||||
|
||||
win->x = offsetX;
|
||||
win->y = offsetY;
|
||||
win->w = winW;
|
||||
win->h = winH;
|
||||
|
||||
wmUpdateContentRect(win);
|
||||
wmReallocContentBuf(win, &ctx->display);
|
||||
|
||||
if (win->onResize) {
|
||||
win->onResize(win, win->contentW, win->contentH);
|
||||
}
|
||||
|
||||
if (win->onPaint) {
|
||||
RectT fullRect = {0, 0, win->contentW, win->contentH};
|
||||
win->onPaint(win, &fullRect);
|
||||
win->contentDirty = true;
|
||||
}
|
||||
|
||||
// Dirty new position
|
||||
dirtyListAdd(&ctx->dirty, win->x, win->y, win->w, win->h);
|
||||
|
||||
offsetX += step;
|
||||
offsetY += step;
|
||||
|
||||
// Wrap around if we'd go off screen
|
||||
if (offsetX + winW > screenW || offsetY + winH > screenH) {
|
||||
offsetX = 0;
|
||||
offsetY = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ============================================================
|
||||
// dvxCreateAccelTable
|
||||
// ============================================================
|
||||
|
|
@ -1347,6 +1421,244 @@ int32_t dvxSetWindowIcon(AppContextT *ctx, WindowT *win, const char *path) {
|
|||
}
|
||||
|
||||
|
||||
// ============================================================
|
||||
// dvxTileWindows
|
||||
// ============================================================
|
||||
//
|
||||
// Tile all visible, non-minimized windows in a grid pattern.
|
||||
// Columns = ceil(sqrt(count)), rows = ceil(count / cols).
|
||||
// Last row may have fewer windows, which are widened to fill.
|
||||
|
||||
void dvxTileWindows(AppContextT *ctx) {
|
||||
int32_t screenW = ctx->display.width;
|
||||
int32_t screenH = ctx->display.height;
|
||||
|
||||
// Count eligible windows
|
||||
int32_t count = 0;
|
||||
|
||||
for (int32_t i = 0; i < ctx->stack.count; i++) {
|
||||
WindowT *win = ctx->stack.windows[i];
|
||||
|
||||
if (!win->minimized && win->visible) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
if (count == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Integer ceil(sqrt(count)) for column count
|
||||
int32_t cols = 1;
|
||||
|
||||
while (cols * cols < count) {
|
||||
cols++;
|
||||
}
|
||||
|
||||
int32_t rows = (count + cols - 1) / cols;
|
||||
int32_t tileW = screenW / cols;
|
||||
int32_t tileH = screenH / rows;
|
||||
|
||||
if (tileW < MIN_WINDOW_W) {
|
||||
tileW = MIN_WINDOW_W;
|
||||
}
|
||||
|
||||
if (tileH < MIN_WINDOW_H) {
|
||||
tileH = MIN_WINDOW_H;
|
||||
}
|
||||
|
||||
int32_t slot = 0;
|
||||
|
||||
for (int32_t i = 0; i < ctx->stack.count; i++) {
|
||||
WindowT *win = ctx->stack.windows[i];
|
||||
|
||||
if (win->minimized || !win->visible) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (win->maximized) {
|
||||
win->maximized = false;
|
||||
}
|
||||
|
||||
dirtyListAdd(&ctx->dirty, win->x, win->y, win->w, win->h);
|
||||
|
||||
int32_t row = slot / cols;
|
||||
int32_t col = slot % cols;
|
||||
|
||||
// Last row: fewer windows get wider tiles
|
||||
int32_t remaining = count - row * cols;
|
||||
int32_t rowCols = (remaining < cols) ? remaining : cols;
|
||||
int32_t cellW = screenW / rowCols;
|
||||
|
||||
win->x = col * cellW;
|
||||
win->y = row * tileH;
|
||||
win->w = cellW;
|
||||
win->h = tileH;
|
||||
|
||||
wmUpdateContentRect(win);
|
||||
wmReallocContentBuf(win, &ctx->display);
|
||||
|
||||
if (win->onResize) {
|
||||
win->onResize(win, win->contentW, win->contentH);
|
||||
}
|
||||
|
||||
if (win->onPaint) {
|
||||
RectT fullRect = {0, 0, win->contentW, win->contentH};
|
||||
win->onPaint(win, &fullRect);
|
||||
win->contentDirty = true;
|
||||
}
|
||||
|
||||
dirtyListAdd(&ctx->dirty, win->x, win->y, win->w, win->h);
|
||||
|
||||
slot++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ============================================================
|
||||
// dvxTileWindowsH
|
||||
// ============================================================
|
||||
//
|
||||
// Tile all visible, non-minimized windows horizontally:
|
||||
// side by side left to right, each window gets full screen height.
|
||||
|
||||
void dvxTileWindowsH(AppContextT *ctx) {
|
||||
int32_t screenW = ctx->display.width;
|
||||
int32_t screenH = ctx->display.height;
|
||||
|
||||
// Count eligible windows
|
||||
int32_t count = 0;
|
||||
|
||||
for (int32_t i = 0; i < ctx->stack.count; i++) {
|
||||
WindowT *win = ctx->stack.windows[i];
|
||||
|
||||
if (!win->minimized && win->visible) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
if (count == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
int32_t tileW = screenW / count;
|
||||
|
||||
if (tileW < MIN_WINDOW_W) {
|
||||
tileW = MIN_WINDOW_W;
|
||||
}
|
||||
|
||||
int32_t slot = 0;
|
||||
|
||||
for (int32_t i = 0; i < ctx->stack.count; i++) {
|
||||
WindowT *win = ctx->stack.windows[i];
|
||||
|
||||
if (win->minimized || !win->visible) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (win->maximized) {
|
||||
win->maximized = false;
|
||||
}
|
||||
|
||||
dirtyListAdd(&ctx->dirty, win->x, win->y, win->w, win->h);
|
||||
|
||||
win->x = slot * tileW;
|
||||
win->y = 0;
|
||||
win->w = tileW;
|
||||
win->h = screenH;
|
||||
|
||||
wmUpdateContentRect(win);
|
||||
wmReallocContentBuf(win, &ctx->display);
|
||||
|
||||
if (win->onResize) {
|
||||
win->onResize(win, win->contentW, win->contentH);
|
||||
}
|
||||
|
||||
if (win->onPaint) {
|
||||
RectT fullRect = {0, 0, win->contentW, win->contentH};
|
||||
win->onPaint(win, &fullRect);
|
||||
win->contentDirty = true;
|
||||
}
|
||||
|
||||
dirtyListAdd(&ctx->dirty, win->x, win->y, win->w, win->h);
|
||||
|
||||
slot++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ============================================================
|
||||
// dvxTileWindowsV
|
||||
// ============================================================
|
||||
//
|
||||
// Tile all visible, non-minimized windows vertically:
|
||||
// stacked top to bottom, each window gets full screen width.
|
||||
|
||||
void dvxTileWindowsV(AppContextT *ctx) {
|
||||
int32_t screenW = ctx->display.width;
|
||||
int32_t screenH = ctx->display.height;
|
||||
|
||||
// Count eligible windows
|
||||
int32_t count = 0;
|
||||
|
||||
for (int32_t i = 0; i < ctx->stack.count; i++) {
|
||||
WindowT *win = ctx->stack.windows[i];
|
||||
|
||||
if (!win->minimized && win->visible) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
if (count == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
int32_t tileH = screenH / count;
|
||||
|
||||
if (tileH < MIN_WINDOW_H) {
|
||||
tileH = MIN_WINDOW_H;
|
||||
}
|
||||
|
||||
int32_t slot = 0;
|
||||
|
||||
for (int32_t i = 0; i < ctx->stack.count; i++) {
|
||||
WindowT *win = ctx->stack.windows[i];
|
||||
|
||||
if (win->minimized || !win->visible) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (win->maximized) {
|
||||
win->maximized = false;
|
||||
}
|
||||
|
||||
dirtyListAdd(&ctx->dirty, win->x, win->y, win->w, win->h);
|
||||
|
||||
win->x = 0;
|
||||
win->y = slot * tileH;
|
||||
win->w = screenW;
|
||||
win->h = tileH;
|
||||
|
||||
wmUpdateContentRect(win);
|
||||
wmReallocContentBuf(win, &ctx->display);
|
||||
|
||||
if (win->onResize) {
|
||||
win->onResize(win, win->contentW, win->contentH);
|
||||
}
|
||||
|
||||
if (win->onPaint) {
|
||||
RectT fullRect = {0, 0, win->contentW, win->contentH};
|
||||
win->onPaint(win, &fullRect);
|
||||
win->contentDirty = true;
|
||||
}
|
||||
|
||||
dirtyListAdd(&ctx->dirty, win->x, win->y, win->w, win->h);
|
||||
|
||||
slot++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ============================================================
|
||||
// executeSysMenuCmd
|
||||
// ============================================================
|
||||
|
|
|
|||
12
dvx/dvxApp.h
12
dvx/dvxApp.h
|
|
@ -118,6 +118,18 @@ void dvxFreeAccelTable(AccelTableT *table);
|
|||
// Add an entry to an accelerator table
|
||||
void dvxAddAccel(AccelTableT *table, int32_t key, int32_t modifiers, int32_t cmdId);
|
||||
|
||||
// Arrange windows in a staggered diagonal cascade pattern
|
||||
void dvxCascadeWindows(AppContextT *ctx);
|
||||
|
||||
// Tile windows in a grid pattern
|
||||
void dvxTileWindows(AppContextT *ctx);
|
||||
|
||||
// Tile windows horizontally (side by side left to right, full height)
|
||||
void dvxTileWindowsH(AppContextT *ctx);
|
||||
|
||||
// Tile windows vertically (stacked top to bottom, full width)
|
||||
void dvxTileWindowsV(AppContextT *ctx);
|
||||
|
||||
// Copy text to the shared clipboard
|
||||
void dvxClipboardCopy(const char *text, int32_t len);
|
||||
|
||||
|
|
|
|||
|
|
@ -32,7 +32,11 @@
|
|||
#define CMD_VIEW_SIZE_SMALL 307
|
||||
#define CMD_VIEW_SIZE_MED 308
|
||||
#define CMD_VIEW_SIZE_LARGE 309
|
||||
#define CMD_HELP_ABOUT 400
|
||||
#define CMD_WIN_CASCADE 350
|
||||
#define CMD_WIN_TILE_H 351
|
||||
#define CMD_WIN_TILE_V 352
|
||||
#define CMD_WIN_TILE 353
|
||||
#define CMD_HELP_ABOUT 400
|
||||
#define CMD_CTX_CUT 500
|
||||
#define CMD_CTX_COPY 501
|
||||
#define CMD_CTX_PASTE 502
|
||||
|
|
@ -205,6 +209,22 @@ static void onMenuCb(WindowT *win, int32_t menuId) {
|
|||
setupControlsWindow(ctx);
|
||||
break;
|
||||
|
||||
case CMD_WIN_CASCADE:
|
||||
dvxCascadeWindows(ctx);
|
||||
break;
|
||||
|
||||
case CMD_WIN_TILE_H:
|
||||
dvxTileWindowsH(ctx);
|
||||
break;
|
||||
|
||||
case CMD_WIN_TILE_V:
|
||||
dvxTileWindowsV(ctx);
|
||||
break;
|
||||
|
||||
case CMD_WIN_TILE:
|
||||
dvxTileWindows(ctx);
|
||||
break;
|
||||
|
||||
case CMD_HELP_ABOUT:
|
||||
dvxMessageBox(sCtx, "About DV/X Demo",
|
||||
"DV/X GUI Demonstration\n\n"
|
||||
|
|
@ -855,6 +875,15 @@ static void setupMainWindow(AppContextT *ctx) {
|
|||
}
|
||||
}
|
||||
|
||||
MenuT *winMenu = wmAddMenu(bar, "&Window");
|
||||
|
||||
if (winMenu) {
|
||||
wmAddMenuItem(winMenu, "&Cascade", CMD_WIN_CASCADE);
|
||||
wmAddMenuItem(winMenu, "Tile &Grid", CMD_WIN_TILE);
|
||||
wmAddMenuItem(winMenu, "Tile &Horizontal", CMD_WIN_TILE_H);
|
||||
wmAddMenuItem(winMenu, "Tile &Vertical", CMD_WIN_TILE_V);
|
||||
}
|
||||
|
||||
MenuT *helpMenu = wmAddMenu(bar, "&Help");
|
||||
|
||||
if (helpMenu) {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue