Started the replacement GTK+ Popover.
This commit is contained in:
parent
64274907d3
commit
63d59e7421
|
@ -0,0 +1,193 @@
|
|||
// 10 october 2014
|
||||
// #qo pkg-config: gtk+-3.0
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
typedef gint LONG;
|
||||
|
||||
typedef struct POINT POINT;
|
||||
|
||||
struct POINT {
|
||||
LONG x;
|
||||
LONG y;
|
||||
};
|
||||
|
||||
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!
|
||||
LONG arrowLeft;
|
||||
LONG arrowRight;
|
||||
LONG arrowTop;
|
||||
LONG arrowBottom;
|
||||
};
|
||||
|
||||
struct popover _p = { NULL, -1, -1, 20, -1 };
|
||||
struct popover *p = &_p;
|
||||
|
||||
#define ARROWHEIGHT 8
|
||||
#define ARROWWIDTH 8 /* should be the same for smooth lines */
|
||||
|
||||
void makePopoverPath(cairo_t *cr, LONG width, LONG height)
|
||||
{
|
||||
POINT pt[20];
|
||||
int n;
|
||||
LONG xmax, ymax;
|
||||
|
||||
cairo_new_path(cr);
|
||||
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++;
|
||||
|
||||
int i;
|
||||
|
||||
// TODO bypass subpixel rendering
|
||||
cairo_move_to(cr, pt[0].x, pt[0].y);
|
||||
for (i = 1; i < n; i++)
|
||||
cairo_line_to(cr, pt[i].x, pt[i].y);
|
||||
}
|
||||
|
||||
void drawPopoverFrame(GtkWidget *widget, cairo_t *cr, LONG width, LONG height, int forceAlpha)
|
||||
{
|
||||
GtkStyleContext *context;
|
||||
GdkRGBA background, border;
|
||||
|
||||
// TODO see what GtkPopover itself does
|
||||
// TODO drop shadow
|
||||
context = gtk_widget_get_style_context(widget);
|
||||
gtk_style_context_get_background_color(context, GTK_STATE_FLAG_NORMAL, &background);
|
||||
gtk_style_context_get_border_color(context, GTK_STATE_FLAG_NORMAL, &border);
|
||||
if (forceAlpha) {
|
||||
background.alpha = 1;
|
||||
border.alpha = 1;
|
||||
}
|
||||
makePopoverPath(cr, width, height);
|
||||
cairo_set_source_rgba(cr, background.red, background.green, background.blue, background.alpha);
|
||||
cairo_fill_preserve(cr);
|
||||
cairo_set_source_rgba(cr, border.red, border.green, border.blue, border.alpha);
|
||||
cairo_stroke(cr);
|
||||
}
|
||||
|
||||
gboolean popoverDraw(GtkWidget *widget, cairo_t *cr, gpointer data)
|
||||
{
|
||||
gint width, height;
|
||||
|
||||
width = gtk_widget_get_allocated_width(widget);
|
||||
height = gtk_widget_get_allocated_height(widget);
|
||||
drawPopoverFrame(widget, cr, width, height, 0);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
void popoverSetSize(GtkWidget *widget, LONG width, LONG height)
|
||||
{
|
||||
GdkWindow *gdkwin;
|
||||
cairo_t *cr;
|
||||
cairo_surface_t *cs;
|
||||
cairo_region_t *region;
|
||||
|
||||
gtk_window_resize(GTK_WINDOW(widget), width, height);
|
||||
gdkwin = gtk_widget_get_window(widget);
|
||||
gdk_window_shape_combine_region(gdkwin, NULL, 0, 0);
|
||||
// TODO check errors
|
||||
cs = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height);
|
||||
cr = cairo_create(cs);
|
||||
drawPopoverFrame(widget, cr, width, height, 1);
|
||||
region = gdk_cairo_region_create_from_surface(cs);
|
||||
gdk_window_shape_combine_region(gdkwin, region, 0, 0);
|
||||
cairo_destroy(cr);
|
||||
cairo_surface_destroy(cs);
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
GtkWidget *w;
|
||||
|
||||
gtk_init(NULL, NULL);
|
||||
w = gtk_window_new(GTK_WINDOW_POPUP);
|
||||
gtk_window_set_decorated(GTK_WINDOW(w), FALSE);
|
||||
gtk_widget_set_app_paintable(w, TRUE);
|
||||
g_signal_connect(w, "draw", G_CALLBACK(popoverDraw), NULL);
|
||||
gtk_widget_realize(w);
|
||||
popoverSetSize(w, 200, 200);
|
||||
gtk_widget_show_all(w);
|
||||
gtk_main();
|
||||
return 0;
|
||||
}
|
Loading…
Reference in New Issue