summaryrefslogtreecommitdiff
path: root/prev/popover
diff options
context:
space:
mode:
Diffstat (limited to 'prev/popover')
-rw-r--r--prev/popover/main_windows.c227
-rw-r--r--prev/popover/popover.c174
-rw-r--r--prev/popover/popover.h30
-rw-r--r--prev/popover/popover_unix.c117
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;
+}