libui/doc/misctests/winrebarexplorertheme.cpp

466 lines
12 KiB
C++

// 9 october 2018
#define UNICODE
#define _UNICODE
#define STRICT
#define STRICT_TYPED_ITEMIDS
#define WINVER 0x0600 /* from Microsoft's winnls.h */
#define _WIN32_WINNT 0x0600
#define _WIN32_WINDOWS 0x0600 /* from Microsoft's pdh.h */
#define _WIN32_IE 0x0700
#define NTDDI_VERSION 0x06000000
#include <windows.h>
#include <commctrl.h>
#include <uxtheme.h>
#include <vsstyle.h>
#include <vssym32.h>
#include <stdio.h>
#include <stdlib.h>
void diele(const char *func)
{
DWORD le;
le = GetLastError();
fprintf(stderr, "%s: %I32u\n", func, le);
exit(EXIT_FAILURE);
}
void diehr(const char *func, HRESULT hr)
{
fprintf(stderr, "%s: 0x%08I32X\n", func, hr);
exit(EXIT_FAILURE);
}
HINSTANCE hInstance;
HWND rebar;
HWND leftbar;
HWND rightbar;
HWND rebarCombo;
HWND toolbarCombo;
HWND toolbarTransparentCheckbox;
#define toolbarStyles (WS_CHILD | CCS_NODIVIDER | CCS_NOPARENTALIGN | CCS_NORESIZE | TBSTYLE_FLAT | TBSTYLE_LIST)
static struct {
const WCHAR *text;
BOOL dropdown;
} leftbarButtons[] = {
{ L"Organize", TRUE },
{ L"Include in library", TRUE },
{ L"Share with", TRUE },
{ L"Burn", FALSE },
{ L"New folder", FALSE },
};
LRESULT customDrawVista(NMCUSTOMDRAW *nm)
{
return CDRF_DODEFAULT;
}
LRESULT customDraw7(NMCUSTOMDRAW *nm)
{
return CDRF_DODEFAULT;
}
static struct {
const WCHAR *text;
LRESULT (*handle)(NMCUSTOMDRAW *nm);
} drawmodes[] = {
{ L"SetWindowTheme()", NULL },
{ L"Custom Draw Vista", customDrawVista },
{ L"Custom Draw 7", customDraw7 },
{ NULL, NULL },
};
int drawmode = 0;
static const WCHAR *rebarThemes[] = {
L"NULL",
L"",
L"AlternateRebar",
L"BrowserTabBar",
L"Communications",
L"Default",
L"ExplorerBar",
L"Help",
L"InactiveNavbar",
L"InactiveNavbarComposited",
L"ITBarBase",
L"MaxInactiveNavbar",
L"MaxInactiveNavbarComposited",
L"MaxNavbar",
L"MaxNavbarComposited",
L"Media",
L"Navbar",
L"NavbarBase",
L"NavbarComposited",
L"NavbarNonTopmost",
L"Rebar",
L"RebarStyle",
L"TaskBar",
L"TaskBarComposited",
NULL,
};
static WCHAR *toolbarThemes[] = {
L"NULL",
L"",
L"Alternate",
L"BB",
L"BBComposited",
L"Communications",
L"ExplorerMenu",
L"Go",
L"GoComposited",
L"InactiveBB",
L"InactiveBBComposited",
L"InactiveGo",
L"InactiveGoComposited",
L"InfoPaneToolbar",
L"LVPopup",
L"LVPopupBottom",
L"MaxBB",
L"MaxBBComposited",
L"MaxGo",
L"MaxGoComposited",
L"MaxInactiveBB",
L"MaxInactiveBBComposited",
L"MaxInactiveGo",
L"MaxInactiveGoComposited",
L"Media",
L"Placesbar",
L"SearchButton",
L"SearchButtonComposited",
L"StartMenu",
L"TaskBar",
L"TaskBarComposited",
L"TaskBarVert",
L"TaskBarVertComposited",
L"Toolbar",
L"ToolbarStyle",
L"TrayNotify",
L"TrayNotifyComposited",
NULL,
};
// TODO toolbarThemes
void onWM_CREATE(HWND hwnd)
{
TBBUTTON tbb[5];
RECT btnrect;
DWORD tbbtnsize;
LONG tbsizex, tbsizey;
REBARBANDINFOW rbi;
HWND button;
LONG buttonx, buttony;
LONG combox, comboy;
int i;
rebar = CreateWindowExW(0,
REBARCLASSNAMEW, NULL,
WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | CCS_NODIVIDER | CCS_TOP,
0, 0, 0, 0,
hwnd, (HMENU) 100, hInstance, NULL);
if (rebar == NULL)
diele("CreateWindowExW(REBARCLASSNAMEW)");
leftbar = CreateWindowExW(0,
TOOLBARCLASSNAMEW, NULL,
toolbarStyles | TBSTYLE_TRANSPARENT,
0, 0, 0, 0,
hwnd, (HMENU) 101, hInstance, NULL);
SendMessageW(leftbar, TB_BUTTONSTRUCTSIZE, sizeof (TBBUTTON), 0);
// I_IMAGENONE causes the button text to be left-aligned; don't use it
if (SendMessageW(leftbar, TB_SETBITMAPSIZE, 0, 0) == FALSE)
diele("TB_SETBITMAPSIZE");
SendMessageW(leftbar, TB_SETEXTENDEDSTYLE, 0, TBSTYLE_EX_DRAWDDARROWS | TBSTYLE_EX_HIDECLIPPEDBUTTONS | TBSTYLE_EX_MIXEDBUTTONS);
// TODO this *should* be DIPs...
// TODO figure out where the *2 is documented
SendMessageW(leftbar, TB_SETPADDING, 0, MAKELONG(6 * 2, 5 * 2));
ZeroMemory(tbb, 5 * sizeof (TBBUTTON));
for (i = 0; i < 5; i++) {
tbb[i].iBitmap = 0;
tbb[i].idCommand = i;
tbb[i].fsState = TBSTATE_ENABLED;
tbb[i].fsStyle = BTNS_AUTOSIZE | BTNS_BUTTON | BTNS_NOPREFIX | BTNS_SHOWTEXT;
if (leftbarButtons[i].dropdown)
tbb[i].fsStyle |= BTNS_DROPDOWN | BTNS_WHOLEDROPDOWN;
tbb[i].iString = (INT_PTR) (leftbarButtons[i].text);
}
if (SendMessageW(leftbar, TB_ADDBUTTONSW, 5, (LPARAM) tbb) == FALSE)
diele("TB_ADDBUTTONSW");
tbsizex = 0;
for (i = 0; i < 5; i++) {
if (SendMessageW(leftbar, TB_GETITEMRECT, (WPARAM) i, (LPARAM) (&btnrect)) == FALSE)
diele("TB_GETITEMRECT");
tbsizex += btnrect.right - btnrect.left;
}
tbbtnsize = (DWORD) SendMessageW(leftbar, TB_GETBUTTONSIZE, 0, 0);
tbsizey = HIWORD(tbbtnsize);
ZeroMemory(&rbi, sizeof (REBARBANDINFOW));
rbi.cbSize = sizeof (REBARBANDINFOW);
rbi.fMask = RBBIM_CHILD | RBBIM_STYLE | RBBIM_SIZE | RBBIM_CHILDSIZE | RBBIM_IDEALSIZE;
rbi.fStyle = RBBS_NOGRIPPER | RBBS_USECHEVRON | RBBS_HIDETITLE;
rbi.hwndChild = leftbar;
rbi.cx = tbsizex;
rbi.cyChild = tbsizey;
rbi.cxMinChild = 0;
rbi.cyMinChild = tbsizey;
rbi.cxIdeal = tbsizex;
if (SendMessageW(rebar, RB_INSERTBANDW, (WPARAM) (-1), (LPARAM) (&rbi)) == 0)
diele("RB_INSERTBANDW leftbar");
buttonx = 10;
buttony = 40;
#define buttonwid 200
#define buttonht 25
for (i = 0; drawmodes[i].text != NULL; i++) {
button = CreateWindowExW(0,
L"BUTTON", drawmodes[i].text,
WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
buttonx, buttony,
buttonwid, buttonht,
hwnd, (HMENU) (200 + i), hInstance, NULL);
if (button == NULL)
diele("CreateWIndowExW(L\"BUTTON\")");
if (i == 0) {
combox = buttonx + buttonwid + 5;
comboy = buttony;
}
buttony += buttonht + 5;
}
rebarCombo = CreateWindowExW(WS_EX_CLIENTEDGE,
L"COMBOBOX", L"",
WS_CHILD | WS_VISIBLE | CBS_DROPDOWN,
combox, comboy,
buttonwid, buttonht,
hwnd, (HMENU) 300, hInstance, NULL);
if (rebarCombo == NULL)
diele("CreateWindowExW(L\"COMBOBOX\")");
for (i = 0; rebarThemes[i] != NULL; i++)
// TODO check error
SendMessageW(rebarCombo, CB_ADDSTRING, 0, (LPARAM) (rebarThemes[i]));
comboy += buttonht + 5;
toolbarCombo = CreateWindowExW(WS_EX_CLIENTEDGE,
L"COMBOBOX", L"",
WS_CHILD | WS_VISIBLE | CBS_DROPDOWN,
combox, comboy,
buttonwid, buttonht,
hwnd, (HMENU) 301, hInstance, NULL);
if (toolbarCombo == NULL)
diele("CreateWindowExW(L\"COMBOBOX\")");
for (i = 0; toolbarThemes[i] != NULL; i++)
// TODO check error
SendMessageW(toolbarCombo, CB_ADDSTRING, 0, (LPARAM) (toolbarThemes[i]));
comboy += buttonht + 5;
toolbarTransparentCheckbox = CreateWindowExW(0,
L"BUTTON", L"Transparent toolbar",
WS_CHILD | WS_VISIBLE | BS_CHECKBOX,
combox, comboy,
buttonwid, buttonht,
hwnd, (HMENU) 302, hInstance, NULL);
if (toolbarTransparentCheckbox == NULL)
diele("CreateWindowExW(L\"BUTTON\")");
SendMessage(toolbarTransparentCheckbox, BM_SETCHECK, BST_CHECKED, 0);
}
// TODO it seems like I shouldn't have to do this?
void repositionRebar(HWND hwnd)
{
RECT win, rb;
if (GetClientRect(hwnd, &win) == 0)
diele("GetClientRect()");
if (GetWindowRect(rebar, &rb) == 0)
diele("GetWindowRect()");
if (SetWindowPos(rebar, NULL,
0, 0, win.right - win.left, rb.bottom - rb.top,
SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOOWNERZORDER | SWP_NOZORDER) == 0)
diele("SetWindowPos()");
SendMessageW(rebar, RB_SETBANDWIDTH, 0, win.right - win.left);
}
// TODO check errors
void handleEvents(HWND hwnd, WPARAM wParam)
{
LRESULT n;
const WCHAR *selRebar = NULL, *selToolbar = NULL;
WCHAR *bufRebar = NULL, *bufToolbar = NULL;
BOOL changeRebar = FALSE, changeToolbar = FALSE;
BOOL invalidate = FALSE;
WPARAM check;
switch (wParam) {
case MAKEWPARAM(300, CBN_SELCHANGE):
n = SendMessageW(rebarCombo, CB_GETCURSEL, 0, 0);
selRebar = rebarThemes[n];
changeRebar = TRUE;
break;
case MAKEWPARAM(301, CBN_SELCHANGE):
n = SendMessageW(toolbarCombo, CB_GETCURSEL, 0, 0);
selToolbar = toolbarThemes[n];
changeToolbar = TRUE;
break;
case MAKEWPARAM(200, BN_CLICKED):
drawmode = 0;
n = SendMessageW(rebarCombo, WM_GETTEXTLENGTH, 0, 0);
bufRebar = new WCHAR[n + 1];
GetWindowTextW(rebarCombo, bufRebar, n + 1);
n = SendMessageW(toolbarCombo, WM_GETTEXTLENGTH, 0, 0);
bufToolbar = new WCHAR[n + 1];
GetWindowTextW(toolbarCombo, bufToolbar, n + 1);
selRebar = bufRebar;
selToolbar = bufToolbar;
changeRebar = TRUE;
changeToolbar = TRUE;
break;
case MAKEWPARAM(201, BN_CLICKED):
drawmode = 1;
invalidate = TRUE;
break;
case MAKEWPARAM(202, BN_CLICKED):
drawmode = 2;
invalidate = TRUE;
break;
case MAKEWPARAM(302, BN_CLICKED):
ShowWindow(leftbar, SW_HIDE);
check = BST_CHECKED;
if (SendMessage(toolbarTransparentCheckbox, BM_GETCHECK, 0, 0) == BST_CHECKED)
check = BST_UNCHECKED;
SendMessage(toolbarTransparentCheckbox, BM_SETCHECK, check, 0);
if (check == BST_CHECKED)
SendMessageW(leftbar, TB_SETSTYLE, 0, toolbarStyles | TBSTYLE_TRANSPARENT);
else
SendMessageW(leftbar, TB_SETSTYLE, 0, toolbarStyles);
ShowWindow(leftbar, SW_SHOW);
break;
}
if (changeRebar) {
if (selRebar != NULL && wcscmp(selRebar, L"NULL") == 0)
selRebar = NULL;
if (selRebar != NULL && *selRebar != L'\0')
SendMessageW(rebar, RB_SETWINDOWTHEME, 0, (LPARAM) selRebar);
else
SetWindowTheme(rebar, selRebar, selRebar);
invalidate = TRUE;
}
if (changeToolbar) {
if (selToolbar != NULL && wcscmp(selToolbar, L"NULL") == 0)
selToolbar = NULL;
if (selToolbar != NULL && *selToolbar != L'\0')
SendMessageW(leftbar, TB_SETWINDOWTHEME, 0, (LPARAM) selToolbar);
else
SetWindowTheme(leftbar, selToolbar, selToolbar);
invalidate = TRUE;
}
if (invalidate)
InvalidateRect(hwnd, NULL, TRUE);
if (bufRebar != NULL)
delete[] bufRebar;
if (bufToolbar != NULL)
delete[] bufToolbar;
}
LRESULT CALLBACK wndproc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
NMHDR *nm = (NMHDR *) lParam;
switch (uMsg) {
case WM_CREATE:
onWM_CREATE(hwnd);
break;
case WM_CLOSE:
PostQuitMessage(0);
break;
case WM_SIZE:
repositionRebar(hwnd);
break;
case WM_COMMAND:
handleEvents(hwnd, wParam);
break;
case WM_NOTIFY:
switch (nm->code) {
case NM_CUSTOMDRAW:
if (nm->hwndFrom != rebar)
break;
if (drawmode == 0)
break;
return (*(drawmodes[drawmode].handle))((NMCUSTOMDRAW *) nm);
}
break;
}
return DefWindowProcW(hwnd, uMsg, wParam, lParam);
}
EXTERN_C IMAGE_DOS_HEADER __ImageBase;
int main(void)
{
STARTUPINFOW si;
int nCmdShow;
INITCOMMONCONTROLSEX icc;
HICON hDefaultIcon;
HCURSOR hDefaultCursor;
WNDCLASSW wc;
HWND mainwin;
MSG msg;
hInstance = (HINSTANCE) (&__ImageBase);
nCmdShow = SW_SHOWDEFAULT;
GetStartupInfoW(&si);
if ((si.dwFlags & STARTF_USESHOWWINDOW) != 0)
nCmdShow = si.wShowWindow;
ZeroMemory(&icc, sizeof (INITCOMMONCONTROLSEX));
icc.dwSize = sizeof (INITCOMMONCONTROLSEX);
icc.dwICC = ICC_STANDARD_CLASSES | ICC_BAR_CLASSES | ICC_COOL_CLASSES;
if (InitCommonControlsEx(&icc) == 0)
diele("InitCommonControlsEx()");
hDefaultIcon = LoadIconW(NULL, IDI_APPLICATION);
if (hDefaultIcon == NULL)
diele("LoadIconW(IDI_APPLICATION)");
hDefaultCursor = LoadCursorW(NULL, IDC_ARROW);
if (hDefaultCursor == NULL)
diele("LoadCursorW(IDC_ARROW)");
ZeroMemory(&wc, sizeof (WNDCLASSW));
wc.lpszClassName = L"mainwin";
wc.lpfnWndProc = wndproc;
wc.hInstance = hInstance;
wc.hIcon = hDefaultIcon;
wc.hCursor = hDefaultCursor;
wc.hbrBackground = (HBRUSH) (COLOR_BTNFACE + 1);
if (RegisterClassW(&wc) == 0)
diele("RegisterClassW()");
mainwin = CreateWindowExW(0,
L"mainwin", L"Main Window",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT,
NULL, NULL, hInstance, NULL);
if (mainwin == NULL)
diele("CreateWindowExW(L\"mainwin\")");
ShowWindow(mainwin, nCmdShow);
if (UpdateWindow(mainwin) == 0)
diele("UpdateWindow()");
for (;;) {
int res;
res = GetMessageW(&msg, NULL, 0, 0);
if (res < 0)
diele("GetMessageW()");
if (res == 0)
break;
TranslateMessage(&msg);
DispatchMessageW(&msg);
}
return 0;
}