How do I get the tab control area coordinates then add buttons in this area? I don't know which one should I use TabCtrl_GetItemRect(), GetWindowRect() or something else, because it seems I'm not getting the coordinates I'd like, which is the tab control area, where I could put the buttons. It currently is positioned far from the tab control white-area, where I'd like to place them, as you can see in the image below. What's the proper way to put them within this tab control?
the function that deal with this is like this:
void CreateButtons(HWND hwnd)
{
RECT rt = {0};
//TabCtrl_GetItemRect(hTab, 0, &rt);
GetWindowRect(hTab, &rt);
//GetClientRect(hTab, &rt);
/*
wchar_t buffer[256] = {0};
wsprintf(buffer,
L"top = %d, bottom = %d, left = %d, right = %d",
rt.top, rt.bottom, rt.left, rt.right);
MessageBox(NULL, buffer, L"", MB_OK);
*/
int id = 4;
static const wchar_t *title[] = { L"Button A", L"Button B", L"Button C",
L"Button D", L"Button E", L"Button F",
L"Button G" , L"Button 001", L"Button 002",
L"Button 003", L"Button 004" };
const int nMaxButtonPerRow = 3;
const int cx_margin = 10;
const int cy_bottomMarge = 10;
const int cy_breakSize = 25;
int cx_initPos = (int)rt.left;
int cx = cx_initPos;
int cy = 50;
const int width = 80;
const int height = 25;
for(int i = 0; i < sizeof(title)/sizeof(title[0]); ++i)
{
if(i != 0 && (i % nMaxButtonPerRow) == 0) {
cy += cy_breakSize + cy_bottomMarge;
cx = cx_initPos;
}
CreateWindow(L"button", title[i],
WS_VISIBLE | WS_CHILD | WS_TABSTOP,
cx,
cy,
width,
height,
hwnd, (HMENU) id++, NULL, NULL);
cx += width + cx_margin;
}
}
full code:
#pragma comment(lib, "user32.lib")
#pragma comment(lib, "Comctl32.lib")
#pragma comment(lib, "Gdi32.lib")
#define WIN32_LEAN_AND_MEAN
#define UNICODE
#define _UNICODE
#include <windows.h>
#include <Commctrl.h>
#include <crtdbg.h>
#include <strsafe.h>
#include <string.h>
#include <assert.h>
#ifdef UNICODE
#define STRSPLIT wcsrchr
#else
#define STRSPLIT strrchr
#endif
#define __FILENAME__ (STRSPLIT(TEXT(__FILE__), '/') ? STRSPLIT(TEXT(__FILE__), '/') + 1 : TEXT(__FILE__))
#define NAMEOF(s) TEXT(#s)
#define COUNTOF(a) (sizeof(a)/sizeof(a[0]))
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
LRESULT CALLBACK CreateTabProc(HWND, UINT, WPARAM, LPARAM);
void ErrorExit(LPWSTR lpszFunction, int line, LPWSTR filename);
void InitComControls();
void ErrorExit(LPWSTR lpszFunction, int line, LPWSTR filename);
DWORD ShowLastError(LPWSTR lpszFunction, int line, LPWSTR filename);
void InitComControls();
void CreateTab(HWND hwnd);
void InsertTabItem(HWND tabHwnd, UINT id, LPWSTR text);
void CreateButtons(HWND hwnd);
HINSTANCE ghInstance;
HWND hTab;
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
PWSTR pCmdLine, int nCmdShow)
{
MSG msg = {0};
HWND hwnd;
WNDCLASSW wc = {0};
wc.lpszClassName = L"Window";
wc.hInstance = hInstance;
wc.hbrBackground = GetSysColorBrush(COLOR_3DFACE);
wc.lpfnWndProc = WndProc;
wc.hCursor = LoadCursor(0, IDC_ARROW);
InitComControls();
if(!RegisterClass(&wc)) {
ErrorExit(NAMEOF(RegisterClass), __LINE__, __FILENAME__);
}
int width = 500;
int height = 350;
int screenWidth = GetSystemMetrics(SM_CXSCREEN);
int screenHeight = GetSystemMetrics(SM_CYSCREEN);
int cx = (screenWidth - width) / 2;
int cy = (screenHeight - height) / 2;
hwnd = CreateWindowW(wc.lpszClassName, L"Window",
WS_OVERLAPPEDWINDOW | WS_VISIBLE,
cx, cy, width, height, NULL, NULL,
hInstance, NULL);
ghInstance = hInstance;
while (GetMessage(&msg, NULL, 0, 0))
{
if (!IsDialogMessage(hwnd, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return (int) msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
case WM_CREATE:
CreateWindowW(L"Static", L"This is label 1...",
WS_VISIBLE | WS_CHILD | WS_TABSTOP,
50, 10, 130, 25, hwnd, (HMENU) 18, NULL, NULL);
CreateWindowW(L"Static", L"This is label 2...",
WS_VISIBLE | WS_CHILD | WS_TABSTOP,
50, 40, 130, 25, hwnd, (HMENU) 19, NULL, NULL);
CreateTab(hwnd);
CreateButtons(hwnd);
//ChangeToDefaultFont(hwnd);
break;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hwnd, msg, wParam, lParam);
}
void CreateTab(HWND hwnd)
{
hTab =
CreateWindow(WC_TABCONTROLW, NULL,
WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_TABSTOP | WS_EX_CONTROLPARENT,
50, 80, 400, 250,
hwnd,
(HMENU) 1,
NULL,
NULL);
InsertTabItem(hTab, 2, L"Tab 1");
InsertTabItem(hTab, 3, L"Tab b");
}
void CreateButtons(HWND hwnd)
{
RECT rt = {0};
//TabCtrl_GetItemRect(hTab, 0, &rt);
GetWindowRect(hTab, &rt);
//GetClientRect(hTab, &rt);
/*
wchar_t buffer[256] = {0};
wsprintf(buffer,
L"top = %d, bottom = %d, left = %d, right = %d",
rt.top, rt.bottom, rt.left, rt.right);
MessageBox(NULL, buffer, L"", MB_OK);
*/
int id = 4;
static const wchar_t *title[] = { L"Button A", L"Button B", L"Button C",
L"Button D", L"Button E", L"Button F",
L"Button G" , L"Button 001", L"Button 002",
L"Button 003", L"Button 004" };
const int nMaxButtonPerRow = 3;
const int cx_margin = 10;
const int cy_bottomMarge = 10;
const int cy_breakSize = 25;
int cx_initPos = (int)rt.left;
int cx = cx_initPos;
int cy = 50;
const int width = 80;
const int height = 25;
for(int i = 0; i < sizeof(title)/sizeof(title[0]); ++i)
{
if(i != 0 && (i % nMaxButtonPerRow) == 0) {
cy += cy_breakSize + cy_bottomMarge;
cx = cx_initPos;
}
CreateWindow(L"button", title[i],
WS_VISIBLE | WS_CHILD | WS_TABSTOP,
cx,
cy,
width,
height,
hwnd, (HMENU) id++, NULL, NULL);
cx += width + cx_margin;
}
}
void InsertTabItem(HWND tabHwnd, UINT id, LPWSTR text)
{
TCITEMW tci = {0};
tci.mask = TCIF_TEXT;
tci.pszText = text;
tci.cchTextMax = lstrlenW(text);
SendMessage(tabHwnd, TCM_INSERTITEMW, id, (LPARAM) &tci);
}
void InitComControls()
{
INITCOMMONCONTROLSEX icex;
icex.dwSize = sizeof(INITCOMMONCONTROLSEX);
icex.dwICC = ICC_TAB_CLASSES;
InitCommonControlsEx(&icex);
}
void ErrorExit(LPWSTR lpszFunction, int line, LPWSTR filename)
{
DWORD dw = ShowLastError(lpszFunction, line, filename);
ExitProcess(dw);
}
DWORD ShowLastError(LPWSTR lpszFunction, int line, LPWSTR filename)
{
#define MAX_DIGITS 16
DWORD dw = GetLastError();
LPVOID lpMsgBuf;
LPVOID lpDisplayBuf;
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
dw,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR) &lpMsgBuf,
0,
NULL
);
lpDisplayBuf = (LPVOID) LocalAlloc(LMEM_ZEROINIT,
(lstrlen((LPCTSTR)lpMsgBuf) +
lstrlen((LPCTSTR)lpszFunction) + 40 +
(line > 0 ? MAX_DIGITS : 0) +
(filename != NULL ? lstrlen(filename) : 0)) *
sizeof(TCHAR)
);
StringCchPrintf((LPTSTR)lpDisplayBuf,
LocalSize(lpDisplayBuf) / sizeof(TCHAR),
TEXT("%s failed with %d: %s"),
lpszFunction, dw, lpMsgBuf
);
MessageBox(NULL, (LPCTSTR)lpDisplayBuf, TEXT("Error"), MB_OK);
LocalFree(lpMsgBuf);
LocalFree(lpDisplayBuf);
return dw;
}
UPDATE 1: managed to do that with GetWindowRect()
and MapWindowPoints()
. I was missing the former one. see this post. It gets the correct tab control coordinates but not yet the display area (the white area) where the controls are supposed to be on. How can I get the coordinates to this white area? if I manage to get the window region size where the tabs buttons (the ones inserted by TCM_INSERTITEM
message) are located, would also work (not sure if quite elegant) would just skip them when setting the cx_initPos
value.
up-to-date code:
void CreateButtons(HWND hwnd)
{
RECT rt = GetLocalCoordinates(hTab);
int id = 4;
static const wchar_t *title[] = { L"Button A", L"Button B", L"Button C",
L"Button D", L"Button E", L"Button F",
L"Button G" , L"Button 001", L"Button 002",
L"Button 003", L"Button 004" };
const int nMaxButtonPerRow = 3;
const int cx_margin = 10;
const int cy_bottomMarge = 10;
const int cy_breakSize = 25;
int cx_initPos = rt.left;
int cx = cx_initPos;
int cy = rt.top;
const int width = 80;
const int height = 25;
for(int i = 0; i < sizeof(title)/sizeof(title[0]); ++i)
{
if(i != 0 && (i % nMaxButtonPerRow) == 0) {
cy += cy_breakSize + cy_bottomMarge;
cx = cx_initPos;
}
CreateWindow(L"button", title[i],
WS_VISIBLE | WS_CHILD | WS_TABSTOP,
cx,
cy,
width,
height,
hwnd, (HMENU) id++, NULL, NULL);
cx += width + cx_margin;
}
}
RECT GetLocalCoordinates(HWND hWnd)
{
RECT Rect;
GetWindowRect(hWnd, &Rect);
MapWindowPoints(HWND_DESKTOP, GetParent(hWnd), (LPPOINT) &Rect, 2);
return Rect;
}
what it looks like now: