From: Elizabeth Figura zfigura@codeweavers.com
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=46197 --- dlls/user32/mdi.c | 25 ------ dlls/user32/tests/win.c | 180 ++++++++++++++++++++++++++++++++++++++++ dlls/user32/win.c | 102 +++++++++++++++++++++++ 3 files changed, 282 insertions(+), 25 deletions(-)
diff --git a/dlls/user32/mdi.c b/dlls/user32/mdi.c index b782d481a00..11c7f98081f 100644 --- a/dlls/user32/mdi.c +++ b/dlls/user32/mdi.c @@ -1792,31 +1792,6 @@ done: }
-/****************************************************************************** - * TileWindows (USER32.@) Tiles MDI child windows - * - * RETURNS - * Success: Number of tiled windows. - * Failure: 0 - */ -WORD WINAPI -TileWindows (HWND hwndParent, UINT wFlags, const RECT *lpRect, - UINT cKids, const HWND *lpKids) -{ - FIXME("(%p,0x%08x,...,%u,...): stub\n", hwndParent, wFlags, cKids); - return 0; -} - - -/*********************************************************************** - * TileChildWindows (USER32.@) - */ -WORD WINAPI TileChildWindows( HWND parent, UINT flags ) -{ - return TileWindows( parent, flags, NULL, 0, NULL ); -} - - /************************************************************************ * "More Windows..." functionality */ diff --git a/dlls/user32/tests/win.c b/dlls/user32/tests/win.c index 05534e5c3f5..4823a774562 100644 --- a/dlls/user32/tests/win.c +++ b/dlls/user32/tests/win.c @@ -14005,6 +14005,185 @@ static void test_cascade_windows(void) DestroyWindow(parent); }
+static void test_tile_windows(void) +{ + static const unsigned int zorder[] = {1, 3, 13, 10, 16, 5, 11, 2, 9, 12, 4, 8, 15, 14, 6, 0, 7}; + RECT orig = {100, 200, 300, 400}, parent_client, rect, expect; + unsigned int column, row; + HWND parent, hwnds[17]; + POINT pt = {0}; + WORD ret; + + parent = CreateWindowA("static", "parent", WS_OVERLAPPEDWINDOW, + 0, 0, 600, 300, NULL, 0, 0, NULL); + ok(!!parent, "failed to create window, error %lu\n", GetLastError()); + + GetClientRect(parent, &parent_client); + ClientToScreen(parent, &pt); + + SetLastError(0xdeadbeef); + ret = TileWindows(parent, 0, NULL, 0, NULL); + ok(!ret, "got %d\n", ret); + ok(GetLastError() == 0xdeadbeef, "got error %lu\n", GetLastError()); + + for (unsigned int i = 0; i < ARRAY_SIZE(hwnds); ++i) + { + DWORD style = WS_CHILD | WS_CAPTION | WS_THICKFRAME | WS_VISIBLE; + unsigned int index = zorder[i]; + + if (index == 2) + style &= ~WS_DLGFRAME; + if (index == 3) + style &= ~WS_VISIBLE; + if (index == 4) + style &= ~WS_THICKFRAME; + if (index == 6) + style |= WS_DISABLED; + if (index == 7) + style |= WS_MAXIMIZE; + if (index == 8) + style &= ~WS_BORDER; + + hwnds[index] = CreateWindowA("MainWindowClass", "child", style, + orig.left, orig.top, orig.right - orig.left, orig.bottom - orig.top, parent, 0, 0, NULL); + ok(!!hwnds[index], "failed to create window %u, error %lu\n", index, GetLastError()); + SetWindowPos(hwnds[index], HWND_TOP, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE); + } + + ret = TileWindows(parent, MDITILE_SKIPDISABLED | MDITILE_HORIZONTAL, NULL, 0, NULL); + ok(ret == 13, "got %d\n", ret); + + /* 3 columns: 4, 4, 5 */ + + row = column = 0; + for (unsigned int i = 0; i < ARRAY_SIZE(hwnds); ++i) + { + unsigned int width = (parent_client.right - parent_client.left) / 3; + unsigned int height = (parent_client.bottom - parent_client.top) / (column < 2 ? 4 : 5); + unsigned int index = zorder[ARRAY_SIZE(hwnds) - 1 - i]; + + GetWindowRect(hwnds[index], &rect); + OffsetRect(&rect, -pt.x, -pt.y); + + if (index == 3 /* invisible */ || index == 6 /* disabled */ + || index == 8 /* no border */ || index == 2 /* no dlgframe */) + { + ok(EqualRect(&rect, &orig), "hwnd %u: expected rect %s, got %s\n", + index, wine_dbgstr_rect(&orig), wine_dbgstr_rect(&rect)); + continue; + } + + if (index == 4 /* no THICKFRAME */) + SetRect(&expect, column * width, row * height, (column * width) + 200, (row * height) + 200); + else + SetRect(&expect, column * width, row * height, (column + 1) * width, (row + 1) * height); + + ok(EqualRect(&rect, &expect), "hwnd %u: expected rect %s, got %s\n", + index, wine_dbgstr_rect(&expect), wine_dbgstr_rect(&rect)); + + ++row; + if (row == 4 && column < 2) + { + row = 0; + ++column; + } + } + + SetRect(&rect, 10, 10, 300, 200); + ret = TileWindows(parent, 0, &rect, 0, NULL); + ok(ret == 14, "got %d\n", ret); + + /* 4 columns: 3, 3, 4, 4 */ + + row = column = 0; + for (unsigned int i = 0; i < ARRAY_SIZE(hwnds); ++i) + { + unsigned int width = (300 - 10) / 4; + unsigned int height = (200 - 10) / (column < 2 ? 3 : 4); + unsigned int index = zorder[ARRAY_SIZE(hwnds) - 1 - i]; + + GetWindowRect(hwnds[index], &rect); + OffsetRect(&rect, -pt.x, -pt.y); + + if (index == 3 /* invisible */ || index == 8 /* no border */ || index == 2 /* no dlgframe */) + { + ok(EqualRect(&rect, &orig), "hwnd %u: expected rect %s, got %s\n", + index, wine_dbgstr_rect(&orig), wine_dbgstr_rect(&rect)); + continue; + } + + if (index == 4 /* no THICKFRAME */) + SetRect(&expect, column * width, row * height, (column * width) + 200, (row * height) + 200); + else + SetRect(&expect, column * width, row * height, (column + 1) * width, (row + 1) * height); + OffsetRect(&expect, 10, 10); + expect.right = max(expect.right, expect.left + GetSystemMetrics(SM_CXMIN)); + + ok(EqualRect(&rect, &expect), "hwnd %u: expected rect %s, got %s\n", + index, wine_dbgstr_rect(&expect), wine_dbgstr_rect(&rect)); + + ++row; + if ((row == 3 && column < 2) || row == 4) + { + row = 0; + ++column; + } + } + + /* Pass a list. */ + + /* Destroy one child and replace it with a non-child. */ + DestroyWindow(hwnds[5]); + hwnds[5] = CreateWindowA("MainWindowClass", "child", WS_OVERLAPPEDWINDOW | WS_VISIBLE, + orig.left, orig.top, orig.right - orig.left, orig.bottom - orig.top, parent, 0, 0, NULL); + + MoveWindow(hwnds[6], orig.left, orig.top, orig.right - orig.left, orig.bottom - orig.top, FALSE); + ret = TileWindows(parent, MDITILE_SKIPDISABLED, NULL, ARRAY_SIZE(hwnds), hwnds); + ok(ret == 12, "got %d\n", ret); + + /* 4 columns: 3, 3, 3, 3 */ + + row = column = 0; + for (unsigned int i = 0; i < ARRAY_SIZE(hwnds); ++i) + { + unsigned int width = (parent_client.right - parent_client.left) / 4; + unsigned int height = (parent_client.bottom - parent_client.top) / 3; + unsigned int index = i; + + GetWindowRect(hwnds[index], &rect); + if (index != 5) + OffsetRect(&rect, -pt.x, -pt.y); + + if (index == 3 /* invisible */ || index == 6 /* disabled */ || index == 5 /* not a child */ + || index == 8 /* no border */ || index == 2 /* no dlgframe */) + { + ok(EqualRect(&rect, &orig), "hwnd %u: expected rect %s, got %s\n", + index, wine_dbgstr_rect(&orig), wine_dbgstr_rect(&rect)); + continue; + } + + if (index == 4 /* no THICKFRAME */) + SetRect(&expect, column * width, row * height, (column * width) + 200, (row * height) + 200); + else + SetRect(&expect, column * width, row * height, (column + 1) * width, (row + 1) * height); + expect.right = max(expect.right, expect.left + GetSystemMetrics(SM_CXMIN)); + + ok(EqualRect(&rect, &expect), "hwnd %u: expected rect %s, got %s\n", + index, wine_dbgstr_rect(&expect), wine_dbgstr_rect(&rect)); + + ++row; + if (row == 3) + { + row = 0; + ++column; + } + } + + for (unsigned int i = 0; i < ARRAY_SIZE(hwnds); ++i) + DestroyWindow(hwnds[i]); + DestroyWindow(parent); +} + START_TEST(win) { char **argv; @@ -14199,6 +14378,7 @@ START_TEST(win) test_SetProcessLaunchForegroundPolicy(); test_startupinfo_showwindow(argv); test_cascade_windows(); + test_tile_windows();
/* add the tests above this line */ if (hhook) UnhookWindowsHookEx(hhook); diff --git a/dlls/user32/win.c b/dlls/user32/win.c index 6192e9f6384..f19e66635ea 100644 --- a/dlls/user32/win.c +++ b/dlls/user32/win.c @@ -1752,3 +1752,105 @@ WORD WINAPI CascadeChildWindows( HWND parent, UINT flags ) { return CascadeWindows( parent, flags, NULL, 0, NULL ); } + +/********************************************************************** + * TileWindows (USER32.@) + */ +WORD WINAPI TileWindows( HWND parent, UINT flags, const RECT *rect, UINT count, const HWND *windows ) +{ + unsigned int tile_count = 0, column = 0, row = 0; + unsigned int root, columns, rows, light_columns; + HWND *children = NULL; + RECT client; + + TRACE( "parent %p, flags %#x, rect %s, count %u, windows %p\n", + parent, flags, wine_dbgstr_rect(rect), count, windows ); + + if (flags & ~(MDITILE_SKIPDISABLED | MDITILE_HORIZONTAL)) + FIXME( "ignoring flags %#x\n", flags & ~(MDITILE_SKIPDISABLED | MDITILE_HORIZONTAL) ); + + if (!parent) + parent = GetDesktopWindow(); + + if (!rect) + { + GetClientRect( parent, &client ); + rect = &client; + } + + if (!windows) + { + if (!(children = WIN_ListChildren( parent ))) + return 0; + for (count = 0; children[count]; ++count) + ; + windows = children; + } + + for (unsigned int i = 0; i < count; ++i) + { + if (should_move_window( windows[i], parent, flags )) + ++tile_count; + } + + /* Determine root = ⌊√tile_count⌋. This is how many columns (if horizontal) + * or rows (if vertical) we have. */ + for (root = 0; root * root <= tile_count; ++root) + ; + --root; + + /* Because the number of windows might not evenly divide the number of + * columns, Windows might give one extra window to some columns, + * starting from the right. These are referred to as "heavy" columns here. + * "rows" describes the number of rows in "light" columns, + * and light_columns is the number of columns which are light. */ + + if (flags & MDITILE_HORIZONTAL) + { + columns = root; + rows = tile_count / columns; + light_columns = columns - (tile_count % columns); + } + else + { + rows = root; + columns = tile_count / rows; + light_columns = columns - (tile_count % rows); + } + + for (unsigned int i = 0; i < count; ++i) + { + unsigned int current_rows = (column < light_columns ? rows : rows + 1); + unsigned int width = (rect->right - rect->left) / columns; + unsigned int height = (rect->bottom - rect->top) / current_rows; + HWND child = windows[i]; + DWORD style = GetWindowLongW( child, GWL_STYLE ); + DWORD swp_flags = SWP_NOZORDER | SWP_NOACTIVATE; + + if (!should_move_window( child, parent, flags )) + continue; + + if (!(style & WS_THICKFRAME)) + swp_flags |= SWP_NOSIZE; + NtUserSetWindowPos( child, 0, rect->left + column * width, rect->top + row * height, width, height, swp_flags ); + + ++row; + if ((row == rows && column < light_columns) || row == rows + 1) + { + row = 0; + ++column; + } + } + + HeapFree( GetProcessHeap(), 0, children ); + + return tile_count; +} + +/********************************************************************** + * TileChildWindows (USER32.@) + */ +WORD WINAPI TileChildWindows( HWND parent, UINT flags ) +{ + return TileWindows( parent, flags, NULL, 0, NULL ); +}