diff options
Diffstat (limited to 'prev/popover')
| -rw-r--r-- | prev/popover/main_windows.c | 227 | ||||
| -rw-r--r-- | prev/popover/popover.c | 174 | ||||
| -rw-r--r-- | prev/popover/popover.h | 30 | ||||
| -rw-r--r-- | prev/popover/popover_unix.c | 117 |
4 files changed, 548 insertions, 0 deletions
diff --git a/prev/popover/main_windows.c b/prev/popover/main_windows.c new file mode 100644 index 0000000..24c941a --- /dev/null +++ b/prev/popover/main_windows.c @@ -0,0 +1,227 @@ +// 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; +} diff --git a/prev/popover/popover.c b/prev/popover/popover.c new file mode 100644 index 0000000..2c2bf66 --- /dev/null +++ b/prev/popover/popover.c @@ -0,0 +1,174 @@ +// 9 october 2014 +#include <stdlib.h> +#include <stdint.h> +#include "popover.h" + +#define ARROWHEIGHT 8 +#define ARROWWIDTH 8 /* should be the same for smooth lines on Windows (TODO is there a better/nicer looking way?) */ + +struct popover { + void *gopopover; + + // a nice consequence of this design is that it allows four arrowheads to jut out at once; in practice only one will ever be used, but hey — simple implementation! + intptr_t arrowLeft; + intptr_t arrowTop; + intptr_t arrowRight; + intptr_t arrowBottom; +}; + +popover *popoverDataNew(void *gopopover) +{ + popover *p; + + p = (popover *) malloc(sizeof (popover)); + if (p != NULL) { + p->gopopover = gopopover; + p->arrowLeft = -1; + p->arrowTop = 20;//TODO-1; + p->arrowRight = -1; + p->arrowBottom = -1; + } + return p; +} + +int popoverMakeFramePoints(popover *p, intptr_t width, intptr_t height, popoverPoint pt[20]) +{ + int n; + intptr_t xmax, ymax; + + n = 0; + + // figure out the xmax and ymax of the box + xmax = width; + if (p->arrowRight >= 0) + xmax -= ARROWWIDTH; + ymax = height; + if (p->arrowBottom >= 0) + ymax -= ARROWHEIGHT; + + // the first point is either at (0,0), (0,arrowHeight), (arrowWidth,0), or (arrowWidth,arrowHeight) + pt[n].x = 0; + if (p->arrowLeft >= 0) + pt[n].x = ARROWWIDTH; + pt[n].y = 0; + if (p->arrowTop >= 0) + pt[n].y = ARROWHEIGHT; + n++; + + // the left side + pt[n].x = pt[n - 1].x; + if (p->arrowLeft >= 0) { + pt[n].y = pt[n - 1].y + p->arrowLeft; + n++; + pt[n].x = pt[n - 1].x - ARROWWIDTH; + pt[n].y = pt[n - 1].y + ARROWHEIGHT; + n++; + pt[n].x = pt[n - 1].x + ARROWWIDTH; + pt[n].y = pt[n - 1].y + ARROWHEIGHT; + n++; + pt[n].x = pt[n - 1].x; + } + pt[n].y = ymax; + n++; + + // the bottom side + pt[n].y = pt[n - 1].y; + if (p->arrowBottom >= 0) { + pt[n].x = pt[n - 1].x + p->arrowBottom; + n++; + pt[n].x = pt[n - 1].x + ARROWWIDTH; + pt[n].y = pt[n - 1].y + ARROWHEIGHT; + n++; + pt[n].x = pt[n - 1].x + ARROWWIDTH; + pt[n].y = pt[n - 1].y - ARROWHEIGHT; + n++; + pt[n].y = pt[n - 1].y; + } + pt[n].x = xmax; + n++; + + // the right side + pt[n].x = pt[n - 1].x; + if (p->arrowRight >= 0) { + pt[n].y = pt[0].y + p->arrowRight + (ARROWHEIGHT * 2); + n++; + pt[n].x = pt[n - 1].x + ARROWWIDTH; + pt[n].y = pt[n - 1].y - ARROWHEIGHT; + n++; + pt[n].x = pt[n - 1].x - ARROWWIDTH; + pt[n].y = pt[n - 1].y - ARROWHEIGHT; + n++; + pt[n].x = pt[n - 1].x; + } + pt[n].y = pt[0].y; + n++; + + // the top side + pt[n].y = pt[n - 1].y; + if (p->arrowTop >= 0) { + pt[n].x = pt[0].x + p->arrowTop + (ARROWWIDTH * 2); + n++; + pt[n].x = pt[n - 1].x - ARROWWIDTH; + pt[n].y = pt[n - 1].y - ARROWHEIGHT; + n++; + pt[n].x = pt[n - 1].x - ARROWWIDTH; + pt[n].y = pt[n - 1].y + ARROWHEIGHT; + n++; + pt[n].y = pt[n - 1].y; + } + pt[n].x = pt[0].x; + n++; + + return n; +} + +void popoverWindowSizeToClientSize(popover *p, popoverRect *r) +{ + r->left++; + r->top++; + r->right--; + r->bottom--; + if (p->arrowLeft >= 0) + r->left += ARROWWIDTH; + if (p->arrowRight >= 0) + r->right -= ARROWWIDTH; + if (p->arrowTop >= 0) + r->top += ARROWHEIGHT; + if (p->arrowBottom >= 0) + r->bottom -= ARROWHEIGHT; +} + +// TODO window edge detection +popoverRect popoverPointAt(popover *p, popoverRect control, intptr_t width, intptr_t height, unsigned int side) +{ + intptr_t x, y; + popoverRect out; + + // account for border + width += 2; + height += 2; + p->arrowLeft = -1; + p->arrowRight = -1; + p->arrowTop = -1; + p->arrowBottom = -1; + // TODO right and bottom + switch (side) { + case popoverPointLeft: + width += ARROWWIDTH; + p->arrowLeft = height / 2 - ARROWHEIGHT; + x = control.right; + y = control.top - ((height - (control.bottom - control.top)) / 2); + break; + case popoverPointTop: + height += ARROWHEIGHT; + p->arrowTop = width / 2 - ARROWWIDTH; + x = control.left - ((width - (control.right - control.left)) / 2); + y = control.bottom; + break; + } + out.left = x; + out.top = y; + out.right = x + width; + out.bottom = y + height; + return out; +} diff --git a/prev/popover/popover.h b/prev/popover/popover.h new file mode 100644 index 0000000..28220e4 --- /dev/null +++ b/prev/popover/popover.h @@ -0,0 +1,30 @@ +// 11 october 2014 + +typedef struct popover popover; +typedef struct popoverPoint popoverPoint; +typedef struct popoverRect popoverRect; + +struct popoverPoint { + intptr_t x; + intptr_t y; +}; + +struct popoverRect { + intptr_t left; + intptr_t top; + intptr_t right; + intptr_t bottom; +}; + +// note the order: flipping sides is as easy as side ^ 1 +enum { + popoverPointLeft, + popoverPointRight, + popoverPointTop, + popoverPointBottom, +}; + +popover *popoverDataNew(void *); +int popoverMakeFramePoints(popover *, intptr_t, intptr_t, popoverPoint[20]); +void popoverWindowSizeToClientSize(popover *, popoverRect *); +popoverRect popoverPointAt(popover *, popoverRect, intptr_t, intptr_t, unsigned int); diff --git a/prev/popover/popover_unix.c b/prev/popover/popover_unix.c new file mode 100644 index 0000000..0844844 --- /dev/null +++ b/prev/popover/popover_unix.c @@ -0,0 +1,117 @@ +// 11 october 2014 +// #qo pkg-config: gtk+-3.0 +#include <gtk/gtk.h> + +#define GOPOPOVER_TYPE (goPopover_get_type()) +#define GOPOPOVER(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GOPOPOVER_TYPE, goPopover)) +#define IS_GOPOPOVER(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), GOPOPOVER_TYPE)) +#define GOPOPOVER_CLASS(class) (G_TYPE_CHECK_CLASS_CAST((class), GOPOPOVER_TYPE, goPopoverClass)) +#define IS_GOPOPOVER_CLASS(class) (G_TYPE_CHECK_CLASS_TYPE((class), GOPOPOVER_TYPE)) +#define GOPOPOVER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), GOPOPOVER_TYPE, goPopoverClass)) + +typedef struct goPopover goPopover; +typedef struct goPopoverClass goPopoverClass; + +struct goPopover { + GtkBin parent_instance; + void *gocontainer; + GdkWindow *gdkwin; +}; + +struct goPopoverClass { + GtkBinClass parent_class; +}; + +G_DEFINE_TYPE(goPopover, goPopover, GTK_TYPE_BIN) + +static void goPopover_init(goPopover *p) +{ + gtk_widget_set_has_window(GTK_WIDGET(p), TRUE); +} + +static void goPopover_dispose(GObject *obj) +{ + G_OBJECT_CLASS(goPopover_parent_class)->dispose(obj); +} + +static void goPopover_finalize(GObject *obj) +{ + G_OBJECT_CLASS(goPopover_parent_class)->finalize(obj); +} + +static void goPopover_realize(GtkWidget *widget) +{ + GdkWindowAttr attr; + goPopover *p = GOPOPOVER(widget); + + attr.x = 0; + attr.y = 0; + attr.width = 200; + attr.height = 200; + attr.wclass = GDK_INPUT_OUTPUT; + attr.event_mask = gtk_widget_get_events(GTK_WIDGET(p)) | GDK_POINTER_MOTION_MASK | GDK_BUTTON_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_EXPOSURE_MASK | GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK; + attr.visual = gtk_widget_get_visual(GTK_WIDGET(p)); + attr.window_type = GDK_WINDOW_CHILD; // GtkPopover does this; TODO what does GtkWindow(GTK_WINDOW_POPUP) do? + p->gdkwin = gdk_window_new(gtk_widget_get_parent_window(GTK_WIDGET(p)), + &attr, GDK_WA_VISUAL); + gtk_widget_set_window(GTK_WIDGET(p), p->gdkwin); + gtk_widget_register_window(GTK_WIDGET(p), p->gdkwin); + gtk_widget_set_realized(GTK_WIDGET(p), TRUE); +} + +static void goPopover_map(GtkWidget *widget) +{ + gdk_window_show(GOPOPOVER(widget)->gdkwin); + GTK_WIDGET_CLASS(goPopover_parent_class)->map(widget); +} + +static void goPopover_unmap(GtkWidget *widget) +{ + gdk_window_hide(GOPOPOVER(widget)->gdkwin); + GTK_WIDGET_CLASS(goPopover_parent_class)->unmap(widget); +} + +static gboolean goPopover_draw(GtkWidget *widget, cairo_t *cr) +{ + GtkStyleContext *context; + + context = gtk_widget_get_style_context(widget); + gtk_render_background(context, cr, 0, 0, 200, 200); + return TRUE; +} + +static void goPopover_class_init(goPopoverClass *class) +{ + G_OBJECT_CLASS(class)->dispose = goPopover_dispose; + G_OBJECT_CLASS(class)->finalize = goPopover_finalize; + GTK_WIDGET_CLASS(class)->realize = goPopover_realize; + GTK_WIDGET_CLASS(class)->map = goPopover_map; + GTK_WIDGET_CLASS(class)->unmap = goPopover_unmap; + GTK_WIDGET_CLASS(class)->draw = goPopover_draw; +} + +void buttonClicked(GtkWidget *button, gpointer data) +{ + GtkWidget *popover; + + popover = g_object_new(GOPOPOVER_TYPE, NULL); + gtk_widget_set_parent(popover, gtk_widget_get_parent(button)); + gtk_widget_show(popover); +} + +int main(void) +{ + GtkWidget *mainwin; + GtkWidget *button; + + gtk_init(NULL, NULL); + mainwin = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_window_resize(GTK_WINDOW(mainwin), 150, 50); + g_signal_connect(mainwin, "destroy", gtk_main_quit, NULL); + button = gtk_button_new_with_label("Click Me"); + g_signal_connect(button, "clicked", G_CALLBACK(buttonClicked), NULL); + gtk_container_add(GTK_CONTAINER(mainwin), button); + gtk_widget_show_all(mainwin); + gtk_main(); + return 0; +} |
