andlabs-ui/popover/main_windows.c

228 lines
6.3 KiB
C

// 9 october 2014
#include "../wininclude_windows.h"
#include "popover.h"
// #qo LIBS: user32 kernel32 gdi32
// TODO
// - should the parent window appear deactivated?
HWND popoverWindow;
void xpanic(char *msg, DWORD err)
{
printf("%d | %s\n", err, msg);
abort();
}
popover *p;
HRGN makePopoverRegion(HDC dc, LONG width, LONG height)
{
popoverPoint ppt[20];
POINT pt[20];
int i, n;
HRGN region;
n = popoverMakeFramePoints(p, (intptr_t) width, (intptr_t) height, ppt);
for (i = 0; i < n; i++) {
pt[i].x = (LONG) (ppt[i].x);
pt[i].y = (LONG) (ppt[i].y);
}
if (BeginPath(dc) == 0)
xpanic("error beginning path for Popover shape", GetLastError());
if (Polyline(dc, pt, n) == 0)
xpanic("error drawing lines in Popover shape", GetLastError());
if (EndPath(dc) == 0)
xpanic("error ending path for Popover shape", GetLastError());
region = PathToRegion(dc);
if (region == NULL)
xpanic("error converting Popover shape path to region", GetLastError());
return region;
}
#define msgPopoverPrepareLeftRight (WM_APP+50)
#define msgPopoverPrepareTopBottom (WM_APP+51)
LRESULT CALLBACK popoverproc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
PAINTSTRUCT ps;
HDC dc;
HRGN region;
RECT r;
LONG width;
LONG height;
WINDOWPOS *wp;
HBRUSH brush;
switch (uMsg) {
case WM_NCPAINT:
if (GetWindowRect(hwnd, &r) == 0)
xpanic("error getting Popover window rect for shape redraw", GetLastError());
width = r.right - r.left;
height = r.bottom - r.top;
dc = GetWindowDC(hwnd);
if (dc == NULL)
xpanic("error getting Popover window DC for drawing border", GetLastError());
region = makePopoverRegion(dc, width, height);
// don't call FillRgn(); WM_ERASEBKGND seems to do this to the non-client area for us already :S (TODO confirm)
// TODO arrow is black in wine
brush = (HBRUSH) GetStockObject(BLACK_BRUSH);
if (brush == NULL)
xpanic("error getting Popover border brush", GetLastError());
if (FrameRgn(dc, region, brush, 1, 1) == 0)
xpanic("error drawing Popover border", GetLastError());
if (DeleteObject(region) == 0)
xpanic("error deleting Popover shape region", GetLastError());
if (ReleaseDC(hwnd, dc) == 0)
xpanic("error releasing Popover window DC for shape drawing", GetLastError());
return 0;
case WM_WINDOWPOSCHANGED:
// this must be here; if it's in WM_NCPAINT weird things happen (see http://stackoverflow.com/questions/26288303/why-is-my-client-rectangle-drawing-behaving-bizarrely-pictures-provided-if-i-t)
wp = (WINDOWPOS *) lParam;
if ((wp->flags & SWP_NOSIZE) == 0) {
dc = GetWindowDC(hwnd);
if (dc == NULL)
xpanic("error getting Popover window DC for reshaping", GetLastError());
region = makePopoverRegion(dc, wp->cx, wp->cy);
if (SetWindowRgn(hwnd, region, TRUE) == 0)
xpanic("error setting Popover shape", GetLastError());
// don't delete the region; the window manager owns it now
if (ReleaseDC(hwnd, dc) == 0)
xpanic("error releasing Popover window DC for reshaping", GetLastError());
}
break; // defer to DefWindowProc()
case WM_NCCALCSIZE:
{
RECT *r = (RECT *) lParam;
NCCALCSIZE_PARAMS *np = (NCCALCSIZE_PARAMS *) lParam;
popoverRect pr;
if (wParam != FALSE)
r = &np->rgrc[0];
pr.left = (intptr_t) (r->left);
pr.top = (intptr_t) (r->top);
pr.right = (intptr_t) (r->right);
pr.bottom = (intptr_t) (r->bottom);
popoverWindowSizeToClientSize(p, &pr);
r->left = (LONG) (pr.left);
r->top = (LONG) (pr.top);
r->right = (LONG) (pr.right);
r->bottom = (LONG) (pr.bottom);
return 0;
}
case WM_PAINT:
dc = BeginPaint(hwnd, &ps);
GetClientRect(hwnd, &r);
FillRect(dc, &r, GetSysColorBrush(COLOR_ACTIVECAPTION));
FrameRect(dc, &r, GetStockPen(WHITE_BRUSH));
EndPaint(hwnd, &ps);
return 0;
case msgPopoverPrepareLeftRight:
case msgPopoverPrepareTopBottom:
// TODO window edge detection
{
RECT r;
LONG width = 200, height = 200;
popoverRect control;
uintptr_t side;
popoverRect out;
if (GetWindowRect((HWND) wParam, &r) == 0)
xpanic("error getting window rect of Popover target", GetLastError());
control.left = (intptr_t) (r.left);
control.top = (intptr_t) (r.top);
control.right = (intptr_t) (r.right);
control.bottom = (intptr_t) (r.bottom);
switch (uMsg) {
case msgPopoverPrepareLeftRight:
side = popoverPointLeft;
break;
case msgPopoverPrepareTopBottom:
side = popoverPointTop;
break;
}
out = popoverPointAt(p, control, (intptr_t) width, (intptr_t) height, side);
if (MoveWindow(hwnd, out.left, out.top, out.right - out.left, out.bottom - out.top, TRUE) == 0)
xpanic("error repositioning Popover", GetLastError());
}
return 0;
}
return DefWindowProcW(hwnd, uMsg, wParam, lParam);
}
HWND button;
LRESULT CALLBACK wndproc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg) {
case WM_COMMAND:
if (HIWORD(wParam) == BN_CLICKED && LOWORD(wParam) == 100) {
SendMessageW(popoverWindow, msgPopoverPrepareLeftRight, (WPARAM) button, 0);
ShowWindow(popoverWindow, SW_SHOW);
UpdateWindow(popoverWindow);
return 0;
}
break;
case WM_CLOSE:
PostQuitMessage(0);
return 0;
}
return DefWindowProcW(hwnd, uMsg, wParam, lParam);
}
int main(int argc, char *argv[])
{
WNDCLASSW wc;
HWND mainwin;
MSG msg;
p = popoverDataNew(NULL);
// TODO null check
ZeroMemory(&wc, sizeof (WNDCLASSW));
wc.lpszClassName = L"popover";
wc.lpfnWndProc = popoverproc;
wc.hbrBackground = (HBRUSH) (COLOR_BTNFACE + 1);
wc.style = CS_DROPSHADOW | CS_NOCLOSE;
if (RegisterClassW(&wc) == 0)
abort();
popoverWindow = CreateWindowExW(WS_EX_TOPMOST,
L"popover", L"",
WS_POPUP,
0, 0, 150, 100,
NULL, NULL, NULL, NULL);
if (popoverWindow == NULL)
abort();
ZeroMemory(&wc, sizeof (WNDCLASSW));
wc.lpszClassName = L"mainwin";
wc.lpfnWndProc = wndproc;
wc.hbrBackground = (HBRUSH) (COLOR_BTNFACE + 1);
if (RegisterClassW(&wc) == 0)
abort();
mainwin = CreateWindowExW(0,
L"mainwin", L"Main Window",
WS_OVERLAPPEDWINDOW,
0, 0, 150, 100,
NULL, NULL, NULL, NULL);
if (mainwin == NULL)
abort();
button = CreateWindowExW(0,
L"button", L"Click Me",
BS_PUSHBUTTON | WS_CHILD | WS_VISIBLE,
20, 20, 100, 40,
mainwin, (HMENU) 100, NULL, NULL);
if (button == NULL)
abort();
ShowWindow(mainwin, SW_SHOWDEFAULT);
if (UpdateWindow(mainwin) == 0)
abort();
while (GetMessageW(&msg, NULL, 0, 0) > 0) {
TranslateMessage(&msg);
DispatchMessageW(&msg);
}
return 0;
}