I'm trying to draw a scrollable 2D OpenGL image, with labels identifying various things on it. Since OpenGL doesn't support text, my options are:
1) Use an OpenGL text library. Such things exist, but I'd prefer not to introduce additional dependencies like that if GTK can cover me.
2) Use GTK labels which appear at fixed positions on top of the OpenGL drawing. This may not be possible since I've read that GTK doesn't like overlapping widgets. Yet, it appears to not cause too many issues at the moment; the label is merely
behind the OpenGL drawing.
3) Draw text directly with GDK. I haven't tried this yet.
Even if labels aren't possible, I'd like to get a zoom control working which controls the size of the drawing_area within the scrollable_window; the glViewport call will ensure that the OpenGL scales along with the drawing_area's size. This is mostly done; the trick is getting the scrollbars to show the same relative portion of the drawing_area when the zoom is changed. I have code below that *should* do this, but in practice it tends to "shake" the image a bit as I drag the zoom control. The amount of stutter depends on how fast I drag it. Eliminating this stutter is my primary concern just now; getting labels working is second.
In simplified form:
Code:
static gboolean reshapeGL(GtkWidget *widget, GdkEvent *evnt, gpointer data)
{
GdkGLContext *glcontext = gtk_widget_get_gl_context (widget);
GdkGLDrawable *gldrawable = gtk_widget_get_gl_drawable (widget);
if (!inited)
initGL(widget,evnt,data);
if (!gdk_gl_drawable_gl_begin (gldrawable, glcontext))
return FALSE;
glViewport(0,0,800*currScale,800*currScale);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(xmin,xmax,ymin,ymax,0,1);
gdk_gl_drawable_gl_end (gldrawable);
return FALSE;
}
static gboolean drawGL(GtkWidget *widget, GdkEvent *evnt, gpointer data)
{
GdkGLContext *glcontext = gtk_widget_get_gl_context (widget);
GdkGLDrawable *gldrawable = gtk_widget_get_gl_drawable (widget);
if (!gdk_gl_drawable_gl_begin (gldrawable, glcontext))
return FALSE;
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
draw(currentscene);// Not important, just a bunch of glVertex etc calls
if (gdk_gl_drawable_is_double_buffered(gldrawable))
gdk_gl_drawable_swap_buffers(gldrawable);
else
glFlush();
gdk_gl_drawable_gl_end (gldrawable);
return FALSE;
}
static gboolean adjustZoom(GtkRange *range, GtkScrollType scroll, gdouble value, gpointer data)
{
GtkWidget *drawarea = GTK_WIDGET(data);
gtk_widget_set_size_request(drawarea, value*800,value*800);
return FALSE;
}
static gboolean scrollbar_bounds_changed(GtkAdjustment *adj, gpointer data)
{
double prevperc = *((double*)data);
currScale = (adj->upper - adj->lower)/800.0;
adj->value = prevperc * (adj->upper - adj->lower) - adj->page_size/2.0;
g_signal_emit_by_name(G_OBJECT(adj), "value-changed");
return FALSE;
}
static gboolean scrollbar_value_changed(GtkAdjustment *adj, gpointer data)
{
double *prevperc = (double*)data;
*prevperc = (adj->value + adj->page_size/2.0) / (adj->upper - adj->lower);
return FALSE;
}
int main(int argc, char*argv[])
{
GtkWidget *window, *vbox, *layout, *scrolledwindow, *drawspace, *graphlabel;
GtkWidget *zoomscale;
GdkGLConfig *config;
double v_scroll_perc = 0.5, h_scroll_perc = 0.5;
gtk_init (&argc, &argv);
gtk_gl_init(&argc, &argv);
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
gtk_window_set_title (GTK_WINDOW (window), "Argon ST Character Graph");
gtk_container_set_border_width (GTK_CONTAINER (window), 10);
gtk_container_set_reallocate_redraws(GTK_CONTAINER (window), TRUE);
vbox = gtk_vbox_new(FALSE, 10);
gtk_container_add(GTK_CONTAINER(window), vbox);
gtk_widget_show(vbox);
layout = gtk_fixed_new();
gtk_box_pack_start(GTK_BOX(vbox), layout, FALSE, FALSE, 0);
gtk_widget_show(layout);
config = gdk_gl_config_new_by_mode (GdkGLConfigMode(GDK_GL_MODE_RGB | GDK_GL_MODE_DOUBLE));
scrolledwindow = gtk_scrolled_window_new(NULL,NULL);
gtk_widget_set_size_request(scrolledwindow, 800,800);
gtk_fixed_put(GTK_FIXED(layout), scrolledwindow, 0,0);
gtk_widget_show(scrolledwindow);
drawspace = gtk_drawing_area_new();
gtk_widget_set_size_request(drawspace, 800,800);
gtk_widget_add_events(drawspace, GDK_VISIBILITY_NOTIFY_MASK);
gtk_widget_set_gl_capability(drawspace, config, NULL, TRUE, GDK_GL_RGBA_TYPE);
gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrolledwindow), drawspace);
gtk_widget_show(drawspace);
graphlabel = gtk_label_new("<span foreground='green' font_desc='36'>Dummy Label</span>");
gtk_label_set_use_markup(GTK_LABEL(graphlabel),TRUE);
gtk_fixed_put(GTK_FIXED(layout), graphlabel, 0,0);
gtk_widget_show(graphlabel);
zoomscale = gtk_hscale_new_with_range(1.0,15.0,.2);
gtk_box_pack_start(GTK_BOX(vbox), zoomscale, TRUE, TRUE, 0);
gtk_widget_show(zoomscale);
g_signal_connect(G_OBJECT (zoomscale), "change-value", G_CALLBACK (adjustZoom), drawspace);
g_signal_connect(G_OBJECT (gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(scrolledwindow))), "changed",
G_CALLBACK(scrollbar_bounds_changed), &v_scroll_perc);
g_signal_connect(G_OBJECT (gtk_scrolled_window_get_hadjustment(GTK_SCROLLED_WINDOW(scrolledwindow))), "changed",
G_CALLBACK(scrollbar_bounds_changed), &h_scroll_perc);
g_signal_connect(G_OBJECT (gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(scrolledwindow))), "value-changed",
G_CALLBACK(scrollbar_value_changed), &v_scroll_perc);
g_signal_connect(G_OBJECT (gtk_scrolled_window_get_hadjustment(GTK_SCROLLED_WINDOW(scrolledwindow))), "value-changed",
G_CALLBACK(scrollbar_value_changed), &h_scroll_perc);
g_signal_connect(G_OBJECT (drawspace), "realize", G_CALLBACK (initGL), NULL);
g_signal_connect(G_OBJECT (drawspace), "configure_event", G_CALLBACK (reshapeGL), NULL);
g_signal_connect(G_OBJECT (drawspace), "expose_event", G_CALLBACK (drawGL), NULL);
g_signal_connect(G_OBJECT (window), "destroy", G_CALLBACK (destroy), NULL);
// Set up current figure to draw
gtk_widget_show(window);
gtk_main();
return 0;
}