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
|
// 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
|
// executeSysMenuCmd
|
||||||
// ============================================================
|
// ============================================================
|
||||||
|
|
|
||||||
12
dvx/dvxApp.h
12
dvx/dvxApp.h
|
|
@ -118,6 +118,18 @@ void dvxFreeAccelTable(AccelTableT *table);
|
||||||
// Add an entry to an accelerator table
|
// Add an entry to an accelerator table
|
||||||
void dvxAddAccel(AccelTableT *table, int32_t key, int32_t modifiers, int32_t cmdId);
|
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
|
// Copy text to the shared clipboard
|
||||||
void dvxClipboardCopy(const char *text, int32_t len);
|
void dvxClipboardCopy(const char *text, int32_t len);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,10 @@
|
||||||
#define CMD_VIEW_SIZE_SMALL 307
|
#define CMD_VIEW_SIZE_SMALL 307
|
||||||
#define CMD_VIEW_SIZE_MED 308
|
#define CMD_VIEW_SIZE_MED 308
|
||||||
#define CMD_VIEW_SIZE_LARGE 309
|
#define CMD_VIEW_SIZE_LARGE 309
|
||||||
|
#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_HELP_ABOUT 400
|
||||||
#define CMD_CTX_CUT 500
|
#define CMD_CTX_CUT 500
|
||||||
#define CMD_CTX_COPY 501
|
#define CMD_CTX_COPY 501
|
||||||
|
|
@ -205,6 +209,22 @@ static void onMenuCb(WindowT *win, int32_t menuId) {
|
||||||
setupControlsWindow(ctx);
|
setupControlsWindow(ctx);
|
||||||
break;
|
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:
|
case CMD_HELP_ABOUT:
|
||||||
dvxMessageBox(sCtx, "About DV/X Demo",
|
dvxMessageBox(sCtx, "About DV/X Demo",
|
||||||
"DV/X GUI Demonstration\n\n"
|
"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");
|
MenuT *helpMenu = wmAddMenu(bar, "&Help");
|
||||||
|
|
||||||
if (helpMenu) {
|
if (helpMenu) {
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue