diff --git a/gtkarea/area.c b/gtkarea/area.c new file mode 100644 index 00000000..f78d7cc2 --- /dev/null +++ b/gtkarea/area.c @@ -0,0 +1,214 @@ +// 4 september 2015 +#include "area.h" + +struct areaPrivate { + uiArea *a; + uiAreaHandler *ah; + + GtkAdjustment *ha; + GtkAdjustment *va; + int clientWidth; + int clientHeight; + // needed for GtkScrollable + GtkScrollablePolicy hpolicy, vpolicy; +}; + +static void areaWidget_scrollable_init(GtkScrollable *); + +G_DEFINE_ABSTRACT_TYPE_WITH_CODE(areaWidget, areaWidget, GTK_TYPE_DRAWING_AREA, + G_IMPLEMENT_INTERFACE(GTK_TYPE_SCROLLABLE, areaWidget_scrollable_init)) + +static void updateScroll(areaWidget *a) +{ + struct areaPrivate *ap = a->priv; + uintmax_t count, pixelsPer; + + // don't call if too early + if (ap->ha == NULL || ap->va == NULL) + return; + + (*(ap->ah->HScrollConfig))(ap->ah, ap->a, + &count, &pixelsPer); + gtk_adjustment_configure(ap->ha, + gtk_adjustment_get_value(ap->ha), + 0, + count, + 1, + ap->clientWidth / pixelsPer, + ap->clientWidth / pixelsPer); + + (*(ap->ah->VScrollConfig))(ap->ah, ap->a, + &count, &pixelsPer); + gtk_adjustment_configure(ap->va, + gtk_adjustment_get_value(ap->va), + 0, + count, + 1, + ap->clientHeight / pixelsPer, + ap->clientHeight / pixelsPer); + + // TODO notify adjustment changes? + g_object_notify(G_OBJECT(a), "hadjustment"); + g_object_notify(G_OBJECT(a), "vadjustment"); +} + +static void areaWidget_init(areaWidget *a) +{ + a->priv = G_TYPE_INSTANCE_GET_PRIVATE(a, areaWidgetType, struct areaPrivate); + + // for events + gtk_widget_add_events(GTK_WIDGET(a), + GDK_POINTER_MOTION_MASK | + GDK_BUTTON_MOTION_MASK | + GDK_BUTTON_PRESS_MASK | + GDK_BUTTON_RELEASE_MASK | + GDK_KEY_PRESS_MASK | + GDK_KEY_RELEASE_MASK); + + // for scrolling + // TODO do we need GDK_TOUCH_MASK? + gtk_widget_add_events(GTK_WIDGET(a), + GDK_SCROLL_MASK | + GDK_TOUCH_MASK | + GDK_SMOOTH_SCROLL_MASK); + + gtk_widget_set_can_focus(GTK_WIDGET(a), TRUE); +} + +static void areaWidget_dispose(GObject *obj) +{ + struct areaPrivate *ap = areaWidget(obj)->priv; + + if (ap->ha != NULL) { + g_object_unref(ap->ha); + ap->ha = NULL; + } + if (ap->va != NULL) { + g_object_unref(ap->va); + ap->va = NULL; + } + G_OBJECT_CLASS(areaWidget_parent_class)->dispose(obj); +} + +static void areaWidget_finalize(GObject *obj) +{ + G_OBJECT_CLASS(areaWidget_parent_class)->finalize(obj); +} + +static void areaWidget_size_allocate(GtkWidget *w, GtkAllocation *allocation) +{ + struct areaPrivate *ap = areaWidget(w)->priv; + + // GtkDrawingArea has a size_allocate() implementation; we need to call it + // this will call gtk_widget_set_allocation() for us + GTK_WIDGET_CLASS(areaWidget_parent_class)->size_allocate(w, allocation); + ap->clientWidth = allocation->width; + ap->clientHeight = allocation->height; + updateScroll(areaWidget(w)); +} + +// TODO draw + +// TODO preferred height/width + +// TODO events + +enum { + pHAdjustment = 1, + pVAdjustment, + pHScrollPolicy, + pVScrollPolicy, + nProps, +}; + +static void onValueChanged(GtkAdjustment *a, gpointer data) +{ + gtk_widget_queue_draw(GTK_WIDGET(data)); +} + +static void replaceAdjustment(areaWidget *a, GtkAdjustment **adj, const GValue *value) +{ + if (*adj != NULL) { + g_signal_handlers_disconnect_by_func(*adj, G_CALLBACK(onValueChanged), cc); + g_object_unref(*adj); + } + *adj = GTK_ADJUSTMENT(g_value_get_object(value)); + if (*adj != NULL) + g_object_ref_sink(*adj); + else + *adj = gtk_adjustment_new(0, 0, 0, 0, 0, 0); + g_signal_connect(*adj, "value-changed", G_CALLBACK(onValueChanged), a); + updateScroll(a); +} + +static void areaWidget_set_property(GObject *obj, guint prop, const GValue *value, GParamSpec *pspec) +{ + areaWidget *a = areaWidget(obj); + struct areaPrivate *ap = a->priv; + + switch (prop) { + case pHAdjustment: + replaceAdjustment(a, &(ap->ha), value); + return; + case pVAdjustment: + replaceAdjustment(a, &(ap->va), value); + return; + case pHScrollPolicy: + ap->hpolicy = g_value_get_enum(value); + return; + case pVScrollPolicy: + ap->vpolicy = g_value_get_enum(value); + return; + } + G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, prop, pspec); +} + +static void areaWidget_get_property(GObject *obj, guint prop, GValue *value, GParamSpec *pspec) +{ + areaWidget *a = areaWidget(obj); + struct areaPrivate *ap = a->priv; + + switch (prop) { + case pHAdjustment: + g_value_set_object(value, ap->ha); + return; + case pVAdjustment: + g_value_set_object(value, ap->va); + return; + case pHScrollPolicy: + g_value_set_enum(value, ap->hpolicy); + return; + case pVScrollPolicy: + g_value_set_enum(value, ap->vpolicy); + return; + } + G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, prop, pspec); +} + +static void areaWidget_class_init(areaWidgetClass *class) +{ + G_OBJECT_CLASS(class)->dispose = areaWidget_dispose; + G_OBJECT_CLASS(class)->finalize = areaWidget_finalize; + G_OBJECT_CLASS(class)->set_property = areaWidget_set_property; + G_OBJECT_CLASS(class)->get_property = areaWidget_get_property; + + GTK_WIDGET_CLASS(class)->size_allocate = areaWidget_size_allocate; +// GTK_WIDGET_CLASS(class)->draw = areaWidget_draw; +// GTK_WIDGET_CLASS(class)->get_preferred_height = areaWidget_get_preferred_height; +// GTK_WIDGET_CLASS(class)->get_preferred_width = areaWidget_get_preferred_width; +// GTK_WIDGET_CLASS(class)->button_press_event = areaWidget_button_press_event; +// GTK_WIDGET_CLASS(class)->button_release_event = areaWidget_button_release_event; +// GTK_WIDGET_CLASS(class)->motion_notify_event = areaWidget_motion_notify_event; +// GTK_WIDGET_CLASS(class)->key_press_event = areaWidget_key_press_event; + + // this is the actual interface implementation + g_object_class_override_property(G_OBJECT_CLASS(class), pHAdjustment, "hadjustment"); + g_object_class_override_property(G_OBJECT_CLASS(class), pVAdjustment, "vadjustment"); + g_object_class_override_property(G_OBJECT_CLASS(class), pHScrollPolicy, "hscroll-policy"); + g_object_class_override_property(G_OBJECT_CLASS(class), pVScrollPolicy, "vscroll-policy"); +} + +static void areaWidget_scrollable_init(GtkScrollable *iface) +{ + // no need to do anything; the interface only has properties +} diff --git a/gtkarea/area.h b/gtkarea/area.h new file mode 100644 index 00000000..7a0a3931 --- /dev/null +++ b/gtkarea/area.h @@ -0,0 +1,29 @@ +// 4 september 2015 +#define GLIB_VERSION_MIN_REQUIRED GLIB_VERSION_2_32 +#define GLIB_VERSION_MAX_ALLOWED GLIB_VERSION_2_32 +#define GDK_VERSION_MIN_REQUIRED GDK_VERSION_3_4 +#define GDK_VERSION_MAX_ALLOWED GDK_VERSION_3_4 +#include + +#define areaWidgetType (areaWidget_get_type()) +#define areaWidget(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), areaWidgetType, areaWidget)) +#define isAreaWidget(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), areaWidgetType)) +#define areaWidgetClass(class) (G_TYPE_CHECK_CLASS_CAST((class), areaWidgetType, areaWidgetClass)) +#define isAreaWidgetClass(class) (G_TYPE_CHECK_CLASS_TYPE((class), areaWidget)) +#define getAreaWidgetClass(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), areaWidgetType, areaWidgetClass)) + +typedef struct areaWidget areaWidget; +typedef struct areaWidgetClass areaWidgetClass; + +struct areaWidget { + GtkDrawingArea parent_instance; + struct areaPrivate *priv; +}; + +struct areaWidgetClass { + GtkDrawingAreaClass parent_class; +}; + +extern GType areaWidget_get_type(void); + +#include "ui.h" diff --git a/gtkarea/ui.h b/gtkarea/ui.h new file mode 100644 index 00000000..923f2f2d --- /dev/null +++ b/gtkarea/ui.h @@ -0,0 +1,13 @@ +// 4 september 2015 + +typedef struct uiArea uiArea; +typedef struct uiAreaHandler uiAreaHandler; +typedef struct uiAreaDrawParams uiAreaDrawParams; + +struct uiAreaHandler { + // TODO draw + void (*HScrollConfig)(uiAreaHandler *, uiArea *, uintmax_t *, uintmax_t *); + void (*VScrollConfig)(uiAreaHandler *, uiArea *, uintmax_t *, uintmx_t *); +}; + +// TODO uiAreaDrawParams