diff --git a/meson.build b/meson.build index cdcdd56..63e4f96 100644 --- a/meson.build +++ b/meson.build @@ -57,6 +57,8 @@ src = [ 'src/foreign.c', 'src/session.c', 'src/widgets/cairo-widget.c', + 'src/widgets/cairo-box.c', + 'src/widgets/cairo-window.c', 'src/main.c', wayland_targets] diff --git a/src/base.h b/src/base.h index dd90c11..d701ed8 100644 --- a/src/base.h +++ b/src/base.h @@ -3,14 +3,6 @@ #include -typedef struct -{ - gint x; - gint y; - gint width; - gint height; -} diya_rect_t; - /** * Base class object * @@ -23,9 +15,8 @@ struct _DiyaObjectClass GObjectClass parent_class; const gchar *(*to_string)(DiyaObject *self); /** - * @brief reserve for futur use to + * @brief reserve for future use to * define common API for descendants - * */ }; diff --git a/src/session.c b/src/session.c index 2b23e6e..e84d6c7 100644 --- a/src/session.c +++ b/src/session.c @@ -10,6 +10,7 @@ #include #include "session.h" #include "wayland.h" +#include "widgets/cairo-window.h" #define DEF_SURF_W 600 #define DEF_SURF_H 400 @@ -30,6 +31,8 @@ struct _DiyaLockSession guint32 raw_shared_buffer_size; cairo_surface_t *cairo_surface; + DiyaCairoWindow * window; + guint surface_width; guint surface_height; @@ -59,6 +62,11 @@ static void diya_lock_session_dispose(GObject *object) cairo_surface_destroy(self->cairo_surface); self->cairo_surface = NULL; } + if(self->window) + { + g_object_unref(self->window); + self->window = NULL; + } G_OBJECT_CLASS(diya_lock_session_parent_class)->dispose(object); } @@ -95,9 +103,7 @@ static void handle_session_lock_finished(void *data, struct ext_session_lock_v1 } static void randname(char *buf) { - struct timespec ts; - clock_gettime(CLOCK_REALTIME, &ts); - long r = ts.tv_nsec; + long r = g_get_real_time() / 1000000; for (int i = 0; i < 6; ++i) { buf[i] = 'A' + (r & 15) + (r & 16) * 2; @@ -122,7 +128,7 @@ static int create_shm_file(void) return -1; } -static int allocate_shm_file(size_t size) +static int allocate_shm_file(guint size) { int fd = create_shm_file(); if (fd < 0) @@ -176,7 +182,7 @@ static void session_lock_realloc_surface(DiyaLockSession *self) self->cairo_surface = NULL; } int stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, self->surface_width); - int size = stride * self->surface_height; + guint size = stride * self->surface_height; int fd = allocate_shm_file(size); if (fd == -1) @@ -214,32 +220,7 @@ static void lock_session_draw_frame(DiyaLockSession *self) { return; } - cairo_t *cr = cairo_create(self->cairo_surface); - // Start of example - double xc = 128.0; - double yc = 128.0; - double radius = 100.0; - double angle1 = (double)(self->last_frame_time % 180) * (M_PI / 180.0); /* angles are specified */ - double angle2 = 180.0 * (M_PI / 180.0); /* in radians */ - - cairo_set_line_width(cr, 10.0); - cairo_arc(cr, xc, yc, radius, angle1, angle2); - cairo_stroke(cr); - - /* draw helping lines */ - cairo_set_source_rgba(cr, 1, 0.2, 0.2, 0.6); - cairo_set_line_width(cr, 6.0); - - cairo_arc(cr, xc, yc, 10.0, 0, 2 * M_PI); - cairo_fill(cr); - - cairo_arc(cr, xc, yc, radius, angle1, angle1); - cairo_line_to(cr, xc, yc); - cairo_arc(cr, xc, yc, radius, angle2, angle2); - cairo_line_to(cr, xc, yc); - cairo_stroke(cr); - // End of example - cairo_destroy(cr); + diya_cairo_window_render(self->window, self->cairo_surface); wl_surface_attach(self->wl_surface, self->wl_buffer, 0, 0); wl_surface_damage_buffer(self->wl_surface, 0, 0, self->surface_width, self->surface_height); @@ -257,6 +238,8 @@ static void lock_surface_configure(void *data, self->surface_width = width; self->surface_height = height; session_lock_realloc_surface(self); + diya_cairo_widget_set_size(self->window, width, height); + diya_cairo_window_update(self->window); } ext_session_lock_surface_v1_ack_configure(lock_surface, serial); @@ -273,7 +256,7 @@ static void lock_surface_frame_done(void *data, struct wl_callback *cb, uint32_t cb = wl_surface_frame(lock->wl_surface); wl_callback_add_listener(cb, &lock->frame_listener, data); - if (!lock->cairo_surface || !lock->wl_buffer) + if (!lock->cairo_surface || !lock->wl_buffer || lock->window) { return; } @@ -299,6 +282,7 @@ static void diya_lock_session_init(DiyaLockSession *self) self->wl_surface = NULL; self->wl_buffer = NULL; + self->window = g_object_new(DIYA_TYPE_CAIRO_WINDOW,NULL); self->cairo_surface = NULL; self->raw_shared_buffer = NULL; diff --git a/src/widgets/cairo-box.c b/src/widgets/cairo-box.c new file mode 100644 index 0000000..e5f0098 --- /dev/null +++ b/src/widgets/cairo-box.c @@ -0,0 +1,67 @@ +#include "cairo-box.h" + +enum +{ + NO_PROP, + BOX_MODE, + N_PROPERTIES +}; +static GParamSpec *g_prop[N_PROPERTIES] = {0}; + +struct _DiyaCairoBox +{ + DiyaCairoContainer parent_object; + guint mode; +}; + +G_DEFINE_FINAL_TYPE(DiyaCairoBox, diya_cairo_box, DIYA_TYPE_CAIRO_CONTAINER) + +static void diya_cairo_box_dispose(GObject * object) +{ + g_debug("diya_cairo_box_dispose: %s", diya_object_to_string(object)); + G_OBJECT_CLASS(diya_cairo_box_parent_class)->dispose(object); +} + +static void diya_cairo_box_init(DiyaCairoBox* self) +{ + self->mode = DIYA_CAIRO_HORIZONTAL_BOX; +} + +static void diya_cairo_box_set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) +{ + DiyaCairoBox * self = DIYA_CAIRO_BOX(object); + switch (property_id) + { + case BOX_MODE: + self->mode = g_value_get_uint(value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void diya_cairo_box_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec) +{ + DiyaCairoBox * self = DIYA_CAIRO_BOX(object); + switch (property_id) + { + case BOX_MODE: + g_value_set_uint(value, self->mode); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void diya_cairo_box_class_init(DiyaCairoBoxClass * class) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS(class); + DIYA_CAIRO_WIDGET_SET_NAME(class, "DiyaCairoBox"); + gobject_class->dispose = diya_cairo_box_dispose; + gobject_class->set_property = diya_cairo_box_set_property; + gobject_class->get_property = diya_cairo_box_get_property; + g_prop[BOX_MODE] = g_param_spec_pointer("mode", NULL, "Box mode", G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); + g_object_class_install_properties (gobject_class, N_PROPERTIES, g_prop); +} \ No newline at end of file diff --git a/src/widgets/cairo-box.h b/src/widgets/cairo-box.h new file mode 100644 index 0000000..cdf098e --- /dev/null +++ b/src/widgets/cairo-box.h @@ -0,0 +1,14 @@ +#ifndef CAIRO_BOX_H +#define CAIRO_BOX_H + +#include "cairo-widget.h" + +enum { + DIYA_CAIRO_HORIZONTAL_BOX, + DIYA_CAIRO_VERTICAL_BOX +}; + +#define DIYA_TYPE_CAIRO_BOX (diya_cairo_box_get_type ()) +G_DECLARE_FINAL_TYPE (DiyaCairoBox, diya_cairo_box, DIYA, CAIRO_BOX, DiyaCairoContainer) + +#endif \ No newline at end of file diff --git a/src/widgets/cairo-widget.c b/src/widgets/cairo-widget.c index 282b71e..e8e67d5 100644 --- a/src/widgets/cairo-widget.c +++ b/src/widgets/cairo-widget.c @@ -1,13 +1,55 @@ #include #include #include +#include #include "cairo-widget.h" +/* +#include +int main() +{ + cairo_surface_t *s = cairo_image_surface_create(CAIRO_FORMAT_RGB24, 150, 50); + cairo_t *cr = cairo_create(s); + + cairo_set_source_rgb(cr, 1, 0, 0); + cairo_paint(cr); + + cairo_rectangle(cr, 0, 0, 100, 100); + cairo_clip(cr); + cairo_move_to(cr, 50, 25); + cairo_set_source_rgb(cr, 0, 0, 0); + cairo_show_text(cr, "pretend that this string is > 100px wide"); + + cairo_destroy(cr); + cairo_surface_write_to_png(s, "out.png"); + cairo_surface_destroy(s); + + return 0; +} +*/ enum { CW_NO_PROP, + CW_EXTENT_DEF, CW_EXTENT, CW_PARENT, + CW_VISIBILITY, + CW_HALIGN, + CW_VALIGN, + CW_HSPACE, + CW_VSPACE, + CW_PADDING, + CW_PADDING_LEFT, + CW_PADDING_TOP, + CW_PADDING_BOTTOM, + CW_PADDING_RIGHT, + CW_MARGIN_LEFT, + CW_MARGIN_RIGHT, + CW_MARGIN_TOP, + CW_MARGIN_BOTTOM, + CW_MARGIN, + CW_FILL, + CW_STROKE, CW_N_PROPERTIES }; static GParamSpec *g_cw_prop[CW_N_PROPERTIES] = {0}; @@ -16,9 +58,25 @@ typedef struct _DiyaCairoWidgetPrivate { DiyaObject * parent_object; diya_rect_t extent; + diya_rect_t default_extent; DiyaCairoWidget * parent_widget; - // TODO list of children + gchar string[64]; + + guint16 visibility; + guint16 halign; + guint16 valign; + guint16 hspace; + guint16 vspace; + + diya_offset_t margin; + diya_offset_t padding; + + diya_color_t fill; + + diya_stroke_t stroke; + + gboolean damaged; } DiyaCairoWidgetPrivate; G_DEFINE_TYPE_WITH_PRIVATE(DiyaCairoWidget, diya_cairo_widget, DIYA_TYPE_OBJECT); @@ -26,7 +84,7 @@ G_DEFINE_TYPE_WITH_PRIVATE(DiyaCairoWidget, diya_cairo_widget, DIYA_TYPE_OBJECT) static void diya_cairo_widget_dispose(GObject* object) { - g_debug("diya_cairo_widget_dispose"); + g_debug("diya_cairo_widget_dispose: %s", diya_object_to_string(object)); G_OBJECT_CLASS(diya_cairo_widget_parent_class)->dispose(object); } @@ -34,16 +92,78 @@ static void diya_cairo_widget_set_property(GObject *object, guint property_id, c { DiyaCairoWidget * self = DIYA_CAIRO_WIDGET(object); DiyaCairoWidgetPrivate* priv = diya_cairo_widget_get_instance_private(self); - diya_rect_t * rect; + gpointer ptr; switch (property_id) { case CW_PARENT: priv->parent_widget = g_value_get_pointer(value); + assert(DIYA_IS_CAIRO_WIDGET(priv->parent_widget)); + break; + case CW_EXTENT_DEF: + ptr = g_value_get_pointer(value); + assert(ptr); + priv->default_extent = *(diya_rect_t*)ptr; break; case CW_EXTENT: - rect = g_value_get_pointer(value); - assert(rect); - memcpy(&priv->extent,rect, sizeof(priv->extent)); + break; + case CW_VISIBILITY: + priv->visibility = (guint16) g_value_get_uint(value); + break; + case CW_HALIGN: + priv->halign = (guint16)g_value_get_uint(value); + break; + case CW_VALIGN: + priv->valign = (guint16)g_value_get_uint(value); + break; + case CW_HSPACE: + priv->hspace = (guint16)g_value_get_uint(value); + break; + case CW_VSPACE: + priv->vspace = (guint16)g_value_get_uint(value); + break; + case CW_PADDING: + ptr = g_value_get_pointer(value); + assert(ptr); + priv->padding = *(diya_offset_t*)ptr; + break; + case CW_PADDING_LEFT: + priv->padding.left = g_value_get_int(value); + break; + case CW_PADDING_RIGHT: + priv->padding.right = g_value_get_int(value); + break; + case CW_PADDING_BOTTOM: + priv->padding.bottom = g_value_get_int(value); + break; + case CW_PADDING_TOP: + priv->padding.top = g_value_get_int(value); + break; + case CW_MARGIN: + ptr = g_value_get_pointer(value); + assert(ptr); + priv->margin = *(diya_offset_t*)ptr; + break; + case CW_MARGIN_LEFT: + priv->margin.left = g_value_get_int(value); + break; + case CW_MARGIN_RIGHT: + priv->margin.right = g_value_get_int(value); + break; + case CW_MARGIN_BOTTOM: + priv->margin.bottom = g_value_get_int(value); + break; + case CW_MARGIN_TOP: + priv->margin.top = g_value_get_int(value); + break; + case CW_FILL: + ptr = g_value_get_pointer(value); + assert(ptr); + priv->fill = *(diya_color_t*)ptr; + break; + case CW_STROKE: + ptr = g_value_get_pointer(value); + assert(ptr); + priv->stroke = *(diya_stroke_t*) ptr; break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); @@ -60,22 +180,66 @@ static void diya_cairo_widget_get_property(GObject *object, guint property_id, G case CW_PARENT: g_value_set_pointer(value, priv->parent_widget); break; + case CW_EXTENT_DEF: + g_value_set_pointer(value, &priv->default_extent); + break; case CW_EXTENT: g_value_set_pointer(value, &priv->extent); break; + case CW_VISIBILITY: + g_value_set_uint(value, priv->visibility); + break; + case CW_HALIGN: + g_value_set_uint(value, priv->halign); + break; + case CW_VALIGN: + g_value_set_uint(value, priv->valign); + break; + case CW_HSPACE: + g_value_set_uint(value, priv->hspace); + break; + case CW_VSPACE: + g_value_set_uint(value, priv->vspace); + break; + case CW_PADDING: + g_value_set_pointer(value, &priv->padding); + break; + case CW_MARGIN: + g_value_set_pointer(value, &priv->margin); + break; + case CW_FILL: + g_value_set_pointer(value, &priv->fill); + break; + case CW_STROKE: + g_value_set_pointer(value, &priv->stroke); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } - static void diya_cairo_widget_init(DiyaCairoWidget *self) { DiyaCairoWidgetPrivate* priv = diya_cairo_widget_get_instance_private(self); memset(&priv->extent,0,sizeof(priv->extent)); + memset(&priv->extent,0,sizeof(priv->default_extent)); priv->parent_widget = NULL; priv->string[0] = '\0'; + + priv->visibility = DIYA_CAIRO_WIDGET_VISIBLE_ON; + + priv->hspace = DIYA_CAIRO_WIDGET_FILL_EXTEND; + priv->vspace = DIYA_CAIRO_WIDGET_FILL_EXTEND; + + priv->halign = DIYA_CAIRO_WIDGET_ALIGN_MIDDLE; + priv->valign = DIYA_CAIRO_WIDGET_ALIGN_MIDDLE; + + memset(&priv->padding,0,sizeof(priv->padding)); + memset(&priv->margin,0,sizeof(priv->margin)); + memset(&priv->fill, 0, sizeof(priv->fill)); + + priv->damaged = TRUE; } static const gchar *diya_cairo_widget_to_string(DiyaObject *object) @@ -93,18 +257,519 @@ static const gchar *diya_cairo_widget_to_string(DiyaObject *object) return priv->string; } +static diya_rect_t diya_cairo_widget_get_view_port(gpointer object) +{ + DiyaCairoWidget * self = DIYA_CAIRO_WIDGET(object); + DiyaCairoWidgetPrivate* priv = diya_cairo_widget_get_instance_private(self); + + diya_point_t gpos = diya_cairo_widget_get_global_position(object); + diya_rect_t view = {gpos.x, gpos.y, priv->extent.width, priv->extent.height}; + if(priv->parent_widget) + { + diya_rect_t parent_view = diya_cairo_widget_get_view_port(priv->parent_widget); + + if(!diya_rect_overlap(&view, &parent_view)) + { + view.x = view.y = view.width = view.height = 0; + return view; + } + + view.width = MIN(view.x + view.width, parent_view.x + parent_view.width); + view.height = MIN(view.y + view.height, parent_view.y + parent_view.height); + + view.x = MAX(view.x, parent_view.x); + view.y = MAX(view.y, parent_view.y); + + view.width -= view.x; + view.height -= view.y; + + } + return view; +} + +static void diya_cairo_widget_draw(gpointer object, cairo_t* context) +{ + (void) context; + + DiyaCairoWidget * self = DIYA_CAIRO_WIDGET(object); + DiyaCairoWidgetPrivate* priv = diya_cairo_widget_get_instance_private(self); + + if(!priv->damaged) + { + return; + } + if(priv->visibility != DIYA_CAIRO_WIDGET_VISIBLE_ON) + { + return; + } + diya_rect_t view_port = diya_cairo_widget_get_view_port(object); + if(diya_rect_is_empty(&view_port)) + { + return; + } + cairo_save(context); + cairo_rectangle(context, view_port.x, view_port.y, view_port.width, view_port.height); + cairo_clip(context); + diya_point_t gpos = diya_cairo_widget_get_global_position(object); + cairo_move_to(context, (double)gpos.x, (double)gpos.y); + if(priv->fill.a > 0) + { + cairo_set_source_rgba(context, priv->fill.r, priv->fill.g, priv->fill.b, priv->fill.a); + cairo_fill(context); + } + if(priv->stroke.size > 0 && priv->stroke.color.a != 0) + { + cairo_set_line_width(context,priv->stroke.size); + cairo_stroke(context); + } + cairo_restore(context); + priv->damaged = FALSE; +} + +static void diya_cairo_widget_content_size(gpointer object, guint* width, guint* height) +{ + DiyaCairoWidget * self = DIYA_CAIRO_WIDGET(object); + DiyaCairoWidgetPrivate* priv = diya_cairo_widget_get_instance_private(self); + *width = priv->extent.width; + *height = priv->extent.height; + // take into account margin +} +static void diya_cairo_widget_resize(gpointer object) +{ + // calculate effective size + DiyaCairoWidget * self = DIYA_CAIRO_WIDGET(object); + DiyaCairoWidgetPrivate* priv = diya_cairo_widget_get_instance_private(self); + if(priv->visibility == DIYA_CAIRO_WIDGET_VISIBLE_HIDDEN) + { + /** + * Item is hidden, set extent to empty + */ + if(!diya_rect_is_empty(&priv->extent)) + { + priv->damaged = TRUE; + } + priv->extent.width = 0; + priv->extent.height = 0; + priv->extent.x = 0; + priv->extent.y = 0; + return; + } + diya_rect_t space = { + priv->margin.left, + priv->margin.top, + priv->extent.width - priv->margin.left - priv->margin.right, + priv->extent.height - priv->margin.top - priv->margin.bottom}; + if(priv->parent_widget) + { + space = DIYA_CAIRO_WIDGET_GET_CLASS(self)->alloc_space(priv->parent_object, object); + } + guint width,height,cwidth = 0,cheight = 0; + if(priv->hspace == DIYA_CAIRO_WIDGET_FILL_CONTENT || + priv->vspace == DIYA_CAIRO_WIDGET_FILL_CONTENT) + { + DIYA_CAIRO_WIDGET_GET_CLASS(self)->content_size(object, &cwidth, &cheight); + } + switch(priv->hspace) + { + case DIYA_CAIRO_WIDGET_FILL_NONE: + width = priv->default_extent.width; + break; + case DIYA_CAIRO_WIDGET_FILL_EXTEND: + width = space.width; + break; + case DIYA_CAIRO_WIDGET_FILL_CONTENT: + width = cwidth; + break; + default: + width = priv->extent.width; + break; + } + + switch(priv->vspace) + { + case DIYA_CAIRO_WIDGET_FILL_NONE: + height = priv->default_extent.height; + break; + case DIYA_CAIRO_WIDGET_FILL_EXTEND: + height = space.height; + break; + case DIYA_CAIRO_WIDGET_FILL_CONTENT: + height = cheight; + break; + default: + height = priv->extent.height; + break; + } + + if(width != priv->extent.width || height != priv->extent.height) + { + priv->damaged = TRUE; + } + priv->extent.width = width; + priv->extent.height = height; + /*if(space.x != priv->extent.x || space.y != priv->extent.y) + { + priv->damaged = TRUE; + }*/ + priv->extent.x = space.x; + priv->extent.y = space.y; +} +static void diya_cairo_widget_align(gpointer object) +{ + guint cwidth, cheight; + // calculate widget position + DiyaCairoWidget * self = DIYA_CAIRO_WIDGET(object); + DiyaCairoWidgetPrivate* priv = diya_cairo_widget_get_instance_private(self); + DIYA_CAIRO_WIDGET_GET_CLASS(self)->content_size(object, &cwidth, &cheight); + if(diya_rect_is_empty(&priv->extent)) + { + return; + } + int x = priv->extent.x, y = priv->extent.y; + switch(priv->halign) + { + case DIYA_CAIRO_WIDGET_ALIGN_MIDDLE: + x += (priv->extent.width - cwidth ) / 2; + break; + case DIYA_CAIRO_WIDGET_ALIGN_END: + x += priv->extent.width - cwidth; + break; + default: + break; + } + + switch(priv->valign) + { + case DIYA_CAIRO_WIDGET_ALIGN_MIDDLE: + y += (priv->extent.height - cheight ) / 2; + break; + case DIYA_CAIRO_WIDGET_ALIGN_END: + y += priv->extent.height - cheight; + break; + default: + break; + } + if(x != priv->extent.x || y != priv->extent.y) + { + priv->damaged = TRUE; + } + priv->extent.x = x; + priv->extent.y = y; +} +static void diya_cairo_widget_update(gpointer object) +{ + + DIYA_CAIRO_WIDGET_GET_CLASS(DIYA_CAIRO_WIDGET(object))->resize(object); + DIYA_CAIRO_WIDGET_GET_CLASS(DIYA_CAIRO_WIDGET(object))->align(object); +} + +static diya_rect_t diya_cairo_widget_alloc_space(gpointer object, gpointer child) +{ + assert(object); + DiyaCairoWidget * self = DIYA_CAIRO_WIDGET(object); + DiyaCairoWidgetPrivate* priv = diya_cairo_widget_get_instance_private(self); + diya_rect_t space = {0,0,priv->extent.width, priv->extent.height}; + diya_offset_t * margin = NULL; + g_object_get(child,"margin", &margin, NULL); + + space.x += priv->padding.left + margin->left; + space.y += priv->padding.top + margin->top; + space.width -= (priv->padding.right + priv->padding.left + margin->right + margin->left); + space.height -= (priv->padding.bottom + priv->padding.top + margin->bottom + margin->top); + + return space; +} + static void diya_cairo_widget_class_init(DiyaCairoWidgetClass *class) { GObjectClass *gobject_class = G_OBJECT_CLASS(class); DiyaObjectClass *base_class = DIYA_OBJECT_CLASS(class); - DIYA_CAIRO_WIDGET_SET_NAME(class, ""); + DIYA_CAIRO_WIDGET_SET_NAME(class, "DiyaCairoWidget"); gobject_class->dispose = diya_cairo_widget_dispose; gobject_class->set_property = diya_cairo_widget_set_property; gobject_class->get_property = diya_cairo_widget_get_property; - g_cw_prop[CW_EXTENT] = g_param_spec_pointer("extent", NULL, "Widget extent relative to its parent", G_PARAM_READWRITE); // + class->draw = diya_cairo_widget_draw; + class->update = diya_cairo_widget_update; + class->alloc_space = diya_cairo_widget_alloc_space; + class->resize = diya_cairo_widget_resize; + class->align = diya_cairo_widget_align; + class->content_size = diya_cairo_widget_content_size; + + g_cw_prop[CW_EXTENT_DEF] = g_param_spec_pointer("default-extent", NULL, "Widget actual extent relative to its parent", G_PARAM_READWRITE); // + g_cw_prop[CW_EXTENT] = g_param_spec_pointer("extent", NULL, "Widget default extent", G_PARAM_READABLE); g_cw_prop[CW_PARENT] = g_param_spec_pointer("parent", NULL, "Parent widget", G_PARAM_READWRITE); // + g_cw_prop[CW_FILL] = g_param_spec_pointer("fill", NULL, "Fill color", G_PARAM_READWRITE); + g_cw_prop[CW_STROKE] = g_param_spec_pointer("stroke", NULL, "Border size and color", G_PARAM_READWRITE); + g_cw_prop[CW_VISIBILITY] = g_param_spec_uint( + "visibility", NULL, "Widget visibility", + DIYA_CAIRO_WIDGET_VISIBLE_ON, + DIYA_CAIRO_WIDGET_VISIBLE_MAX, + DIYA_CAIRO_WIDGET_VISIBLE_ON, + G_PARAM_READWRITE); + + g_cw_prop[CW_HALIGN] = g_param_spec_uint( + "halign", NULL, "Widget halign", + DIYA_CAIRO_WIDGET_ALIGN_START, + DIYA_CAIRO_WIDGET_ALIGN_MAX, + DIYA_CAIRO_WIDGET_ALIGN_MIDDLE, + G_PARAM_READWRITE); + + g_cw_prop[CW_VALIGN] = g_param_spec_uint( + "valign", NULL, "Widget valign", + DIYA_CAIRO_WIDGET_ALIGN_START, + DIYA_CAIRO_WIDGET_ALIGN_MAX, + DIYA_CAIRO_WIDGET_ALIGN_MIDDLE, + G_PARAM_READWRITE); + + g_cw_prop[CW_HSPACE] = g_param_spec_uint( + "hspace", NULL, "Widget hspace", + DIYA_CAIRO_WIDGET_FILL_NONE, + DIYA_CAIRO_WIDGET_FILL_MAX, + DIYA_CAIRO_WIDGET_FILL_EXTEND, + G_PARAM_READWRITE); + + g_cw_prop[CW_VSPACE] = g_param_spec_uint( + "vpsace", NULL, "Widget vspace", + DIYA_CAIRO_WIDGET_FILL_NONE, + DIYA_CAIRO_WIDGET_FILL_MAX, + DIYA_CAIRO_WIDGET_FILL_EXTEND, + G_PARAM_READWRITE); + + g_cw_prop[CW_PADDING] = g_param_spec_pointer("padding", NULL, "Widget padding", G_PARAM_READWRITE); + g_cw_prop[CW_MARGIN] = g_param_spec_pointer("margin", NULL, "Widget margin", G_PARAM_READWRITE); + + g_cw_prop[CW_PADDING_LEFT] = g_param_spec_int( + "padding-left", NULL, "Widget padding-left", + G_MININT, + G_MAXINT, + 0, + G_PARAM_WRITABLE); + g_cw_prop[CW_PADDING_RIGHT] = g_param_spec_int( + "padding-right", NULL, "Widget padding-right", + INT_MIN, + INT_MAX, + 0, + G_PARAM_WRITABLE); + g_cw_prop[CW_PADDING_TOP] = g_param_spec_int( + "padding-top", NULL, "Widget padding-top", + INT_MIN, + INT_MAX, + 0, + G_PARAM_WRITABLE); + g_cw_prop[CW_PADDING_BOTTOM] = g_param_spec_int( + "padding-bottom", NULL, "Widget padding-bottom", + INT_MIN, + INT_MAX, + 0, + G_PARAM_WRITABLE); + + g_cw_prop[CW_MARGIN_LEFT] = g_param_spec_int( + "margin-left", NULL, "Widget margin-left", + INT_MIN, + INT_MAX, + 0, + G_PARAM_WRITABLE); + g_cw_prop[CW_MARGIN_RIGHT] = g_param_spec_int( + "margin-right", NULL, "Widget margin-right", + INT_MIN, + INT_MAX, + 0, + G_PARAM_WRITABLE); + g_cw_prop[CW_MARGIN_TOP] = g_param_spec_int( + "margin-top", NULL, "Widget margin-top", + INT_MIN, + INT_MAX, + 0, + G_PARAM_WRITABLE); + g_cw_prop[CW_MARGIN_BOTTOM] = g_param_spec_int( + "margin-bottom", NULL, "Widget margin-bottom", + INT_MIN, + INT_MAX, + 0, + G_PARAM_WRITABLE); base_class->to_string = diya_cairo_widget_to_string; g_object_class_install_properties (gobject_class, CW_N_PROPERTIES, g_cw_prop); } + +diya_point_t diya_cairo_widget_get_position(gpointer object) +{ + diya_point_t p = {0}; + assert(DIYA_IS_CAIRO_WIDGET(object)); + DiyaCairoWidget * self = DIYA_CAIRO_WIDGET(object); + DiyaCairoWidgetPrivate* priv = diya_cairo_widget_get_instance_private(self); + p.x = priv->extent.x; + p.y = priv->extent.y; + return p; +} +diya_point_t diya_cairo_widget_get_global_position(gpointer object) +{ + assert(DIYA_IS_CAIRO_WIDGET(object)); + DiyaCairoWidget * self = DIYA_CAIRO_WIDGET(object); + DiyaCairoWidgetPrivate* priv = diya_cairo_widget_get_instance_private(self); + diya_point_t p = {priv->extent.x, priv->extent.y}; + if(priv->parent_widget) + { + + diya_point_t parent_pos = diya_cairo_widget_get_global_position(priv->parent_widget); + p.x += parent_pos.x; + p.y += parent_pos.y; + } + return p; +} +DiyaCairoWidget* diya_cairo_widget_get_parent(gpointer object) +{ + assert(DIYA_IS_CAIRO_WIDGET(object)); + DiyaCairoWidget * self = DIYA_CAIRO_WIDGET(object); + DiyaCairoWidgetPrivate* priv = diya_cairo_widget_get_instance_private(self); + return priv->parent_widget; +} +diya_rect_t diya_cairo_widget_get_extent(gpointer object) +{ + assert(DIYA_IS_CAIRO_WIDGET(object)); + DiyaCairoWidget * self = DIYA_CAIRO_WIDGET(object); + DiyaCairoWidgetPrivate* priv = diya_cairo_widget_get_instance_private(self); + + return priv->extent; +} +gboolean diya_cairo_widget_is_visible(gpointer object) +{ + assert(DIYA_IS_CAIRO_WIDGET(object)); + DiyaCairoWidget * self = DIYA_CAIRO_WIDGET(object); + DiyaCairoWidgetPrivate* priv = diya_cairo_widget_get_instance_private(self); + return priv->visibility == DIYA_CAIRO_WIDGET_VISIBLE_ON; +} +gboolean diya_cairo_widget_is_hidden(gpointer object) +{ + assert(DIYA_IS_CAIRO_WIDGET(object)); + DiyaCairoWidget * self = DIYA_CAIRO_WIDGET(object); + DiyaCairoWidgetPrivate* priv = diya_cairo_widget_get_instance_private(self); + return priv->visibility == DIYA_CAIRO_WIDGET_VISIBLE_HIDDEN; +} + +void diya_cairo_widget_set_size(gpointer object, guint width, guint height) +{ + assert(DIYA_IS_CAIRO_WIDGET(object)); + DiyaCairoWidget * self = DIYA_CAIRO_WIDGET(object); + DiyaCairoWidgetPrivate* priv = diya_cairo_widget_get_instance_private(self); + + priv->default_extent.width = width; + priv->default_extent.height = height; + +} +void diya_cairo_widget_set_position(gpointer object, gint x, gint y) +{ + assert(DIYA_IS_CAIRO_WIDGET(object)); + DiyaCairoWidget * self = DIYA_CAIRO_WIDGET(object); + DiyaCairoWidgetPrivate* priv = diya_cairo_widget_get_instance_private(self); + + priv->default_extent.x = x; + priv->default_extent.y = y; + +} + +void diya_cairo_widget_fill(gpointer object, diya_color_t * color) +{ + assert(DIYA_IS_CAIRO_WIDGET(object)); + DiyaCairoWidget * self = DIYA_CAIRO_WIDGET(object); + DiyaCairoWidgetPrivate* priv = diya_cairo_widget_get_instance_private(self); + priv->fill = *color; +} +void diya_cairo_widget_set_stroke_size(gpointer object, gdouble size) +{ + assert(DIYA_IS_CAIRO_WIDGET(object)); + DiyaCairoWidget * self = DIYA_CAIRO_WIDGET(object); + DiyaCairoWidgetPrivate* priv = diya_cairo_widget_get_instance_private(self); + + priv->stroke.size = size; +} +void diya_cairo_widget_set_stroke_color(gpointer object, diya_color_t * color) +{ + assert(DIYA_IS_CAIRO_WIDGET(object)); + DiyaCairoWidget * self = DIYA_CAIRO_WIDGET(object); + DiyaCairoWidgetPrivate* priv = diya_cairo_widget_get_instance_private(self); + + priv->stroke.color = *color; +} + +gboolean diya_cairo_widget_is_damaged(gpointer object) +{ + assert(DIYA_IS_CAIRO_WIDGET(object)); + DiyaCairoWidget * self = DIYA_CAIRO_WIDGET(object); + DiyaCairoWidgetPrivate* priv = diya_cairo_widget_get_instance_private(self); + + return priv->damaged; +} + +void diya_cairo_widget_damage(gpointer object) +{ + assert(DIYA_IS_CAIRO_WIDGET(object)); + DiyaCairoWidget * self = DIYA_CAIRO_WIDGET(object); + DiyaCairoWidgetPrivate* priv = diya_cairo_widget_get_instance_private(self); + + priv->damaged = TRUE; +} + +/*Container API*/ + +typedef struct _DiyaCairoContainerPrivate +{ + DiyaCairoWidget * parent_object; + GList* children; + +} DiyaCairoContainerPrivate; + +G_DEFINE_TYPE_WITH_PRIVATE(DiyaCairoContainer, diya_cairo_container, DIYA_TYPE_CAIRO_WIDGET) + +static void diya_cairo_container_dispose(GObject* object) +{ + DiyaCairoContainer * self = DIYA_CAIRO_CONTAINER(object); + DiyaCairoContainerPrivate* priv = diya_cairo_container_get_instance_private(self); + g_debug("diya_cairo_container_dispose: %s", diya_object_to_string(object)); + g_list_free_full(priv->children, g_object_unref); + G_OBJECT_CLASS(diya_cairo_widget_parent_class)->dispose(object); +} + +static void diya_cairo_container_init(DiyaCairoContainer *self) +{ + DiyaCairoContainerPrivate* priv = diya_cairo_container_get_instance_private(self); + priv->children = NULL; +} +static void diya_cairo_container_class_init(DiyaCairoContainerClass *class) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS(class); + //DiyaObjectClass *base_class = DIYA_OBJECT_CLASS(class); + DIYA_CAIRO_WIDGET_SET_NAME(class, "DiyaCairoContainer"); + gobject_class->dispose = diya_cairo_container_dispose; +} + +void diya_cairo_container_add(gpointer object, gpointer child) +{ + assert(DIYA_IS_CAIRO_WIDGET(child)); + assert(DIYA_IS_CAIRO_CONTAINER(object)); + DiyaCairoContainerPrivate* priv = diya_cairo_container_get_instance_private(DIYA_CAIRO_CONTAINER(object)); + g_object_set(child,"parent", object, NULL); + if(g_list_find (priv->children,child)) + { + return; + } + priv->children = g_list_append(priv->children, child); +} + +void diya_offset_normalize(diya_offset_t * offset) +{ + gint tmp; + if(offset->left > offset->right) + { + tmp = offset->left; + offset->left = offset->right; + offset->right = tmp; + } + if(offset->top > offset->bottom) + { + tmp = offset->top; + offset->top = offset->bottom; + offset->bottom = tmp; + } +} \ No newline at end of file diff --git a/src/widgets/cairo-widget.h b/src/widgets/cairo-widget.h index e6a0138..f0ffa18 100644 --- a/src/widgets/cairo-widget.h +++ b/src/widgets/cairo-widget.h @@ -1,20 +1,124 @@ #ifndef CAIRO_WIDGET_H #define CAIRO_WIDGET_H + +#include #include "../base.h" -#define DIYA_CAIRO_WIDGET_SET_NAME(c, n) (DIYA_CAIRO_WIDGET_CLASS(c)->name = n) +// basic type +typedef struct +{ + gint x; + gint y; + guint width; + guint height; +} diya_rect_t; + +typedef struct +{ + gint x; + gint y; +} +diya_point_t; + +typedef struct +{ + gint left; + gint top; + gint bottom; + gint right; +} +diya_offset_t; + +typedef struct +{ + gdouble r; + gdouble g; + gdouble b; + gdouble a; +} +diya_color_t; + +typedef struct +{ + gdouble size; + diya_color_t color; +} diya_stroke_t; + +#define BETWEEN(value,min,max) ((value > min) && (value < max)) + +#define diya_rect_is_empty(rect) (((rect)->width <= 0) && ((rect)->height <= 0)) +#define diya_rect_to_offset(rect) ({(rect)->x,(rect)->y,(rect)->y+(rect)->height, (rect)->x + (rect)->width}) +#define diya_rect_overlap(a,b) ( \ + (BETWEEN((a)->x, (b)->x, (b)->x + (gint)(b)->width) || BETWEEN((b)->x, (a)->x, (a)->x + (gint)(a)->width)) && \ + (BETWEEN((a)->y, (b)->y, (b)->y + (gint)(b)->height) || BETWEEN((b)->y, (a)->y, (a)->y + (gint)(a)->height))) + +void diya_offset_normalize(diya_offset_t * offset); + +#define DIYA_CAIRO_WIDGET_SET_NAME(c, n) (DIYA_CAIRO_WIDGET_CLASS(c)->name = n) #define DIYA_TYPE_CAIRO_WIDGET (diya_cairo_widget_get_type ()) G_DECLARE_DERIVABLE_TYPE (DiyaCairoWidget, diya_cairo_widget, DIYA, CAIRO_WIDGET, DiyaObject) +enum +{ + /*Align enum*/ + DIYA_CAIRO_WIDGET_ALIGN_START, + DIYA_CAIRO_WIDGET_ALIGN_MIDDLE, + DIYA_CAIRO_WIDGET_ALIGN_END, + DIYA_CAIRO_WIDGET_ALIGN_MAX, // not used + /*fill enum*/ + DIYA_CAIRO_WIDGET_FILL_NONE, // using extent size + DIYA_CAIRO_WIDGET_FILL_CONTENT, // extent calculated by its content + DIYA_CAIRO_WIDGET_FILL_EXTEND, // extend to the entire free space of the parent + DIYA_CAIRO_WIDGET_FILL_MAX, //not used + + /* Visibility enum*/ + DIYA_CAIRO_WIDGET_VISIBLE_ON, + DIYA_CAIRO_WIDGET_VISIBLE_OFF, // not show but take space + DIYA_CAIRO_WIDGET_VISIBLE_HIDDEN, // not show and does not take space + DIYA_CAIRO_WIDGET_VISIBLE_MAX, // not used + +}; + struct _DiyaCairoWidgetClass { DiyaObjectClass parent_class; + void (*draw)(gpointer, cairo_t*); + void (*update)(gpointer); + void (*resize)(gpointer); + void (*align)(gpointer); + void (*content_size)(gpointer, guint*, guint*); + diya_rect_t (*alloc_space)(gpointer, gpointer); + const gchar* name; - /** - * @brief TODO define more API function - * - */ }; + +diya_point_t diya_cairo_widget_get_position(gpointer object); +diya_point_t diya_cairo_widget_get_global_position(gpointer object); +DiyaCairoWidget* diya_cairo_widget_get_parent(gpointer object); +diya_rect_t diya_cairo_widget_get_extent(gpointer object); +gboolean diya_cairo_widget_is_visible(gpointer object); +gboolean diya_cairo_widget_is_hidden(gpointer object); +gboolean diya_cairo_widget_is_damaged(gpointer object); + +void diya_cairo_widget_damage(gpointer object); + +void diya_cairo_widget_set_size(gpointer object, guint width, guint height); +void diya_cairo_widget_set_position(gpointer object, gint x, gint y); +void diya_cairo_widget_fill(gpointer object, diya_color_t * color); +void diya_cairo_widget_set_stroke_size(gpointer object, gdouble size); +void diya_cairo_widget_set_stroke_color(gpointer object, diya_color_t * color); + +#define DIYA_TYPE_CAIRO_CONTAINER (diya_cairo_container_get_type ()) +G_DECLARE_DERIVABLE_TYPE (DiyaCairoContainer, diya_cairo_container, DIYA, CAIRO_CONTAINER, DiyaCairoWidget) + + +struct _DiyaCairoContainerClass +{ + DiyaCairoWidget parent_class; +}; + +void diya_cairo_container_add(gpointer object, gpointer child); + #endif \ No newline at end of file diff --git a/src/widgets/cairo-window.c b/src/widgets/cairo-window.c new file mode 100644 index 0000000..9bd9d0a --- /dev/null +++ b/src/widgets/cairo-window.c @@ -0,0 +1,97 @@ +#include +#include "cairo-window.h" + +/* +enum +{ + NO_PROP, + BOX_MODE, + N_PROPERTIES +}; +static GParamSpec *g_prop[N_PROPERTIES] = {0}; +*/ +struct _DiyaCairoWindow +{ + DiyaCairoWidget * parent_object; + DiyaCairoWidget * root; +}; + +G_DEFINE_FINAL_TYPE(DiyaCairoWindow, diya_cairo_window, DIYA_TYPE_CAIRO_WIDGET) + +static void diya_cairo_window_dispose(GObject * object) +{ + g_debug("diya_cairo_window_dispose: %s", diya_object_to_string(object)); + DiyaCairoWindow * window = DIYA_CAIRO_WINDOW(object); + if(window->root) + { + g_object_unref(window->root); + } + G_OBJECT_CLASS(diya_cairo_window_parent_class)->dispose(object); +} + +static void diya_cairo_window_init(DiyaCairoWindow * window) +{ + window->root = NULL; +} + +static void cairo_window_draw(gpointer object, cairo_t * cairo) +{ + DiyaCairoWindow * window = DIYA_CAIRO_WINDOW(object); + DIYA_CAIRO_WIDGET_CLASS(diya_cairo_window_parent_class)->draw(object, cairo); + if(window->root) + { + DIYA_CAIRO_WIDGET_GET_CLASS(DIYA_CAIRO_WIDGET(window->root))->draw(object, cairo); + } +} + +static void cairo_window_update(gpointer object) +{ + DiyaCairoWindow * self = DIYA_CAIRO_WINDOW(object); + DIYA_CAIRO_WIDGET_CLASS(diya_cairo_window_parent_class)->update(object); + + if(self->root) + { + DIYA_CAIRO_WIDGET_CLASS(DIYA_CAIRO_WIDGET(self->root))->update(self->root); + } +} + +static void diya_cairo_window_class_init(DiyaCairoWindowClass* class) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS(class); + DiyaCairoWidgetClass *wclass = DIYA_CAIRO_WIDGET_CLASS(class); + wclass->draw = cairo_window_draw; + wclass->update = cairo_window_update; + DIYA_CAIRO_WIDGET_SET_NAME(class, "DiyaCairoWindow"); + gobject_class->dispose = diya_cairo_window_dispose; +} + +void diya_cairo_window_set_root(gpointer object, gpointer root) +{ + assert(DIYA_IS_CAIRO_WINDOW(object)); + assert(DIYA_IS_CAIRO_WIDGET(root)); + DiyaCairoWindow * window = DIYA_CAIRO_WINDOW(object); + window->root = DIYA_CAIRO_WIDGET(root); + diya_rect_t extent = diya_cairo_widget_get_extent(window); + g_object_set(root, + "parent", window, + "default-extent", &extent, + "vspace", DIYA_CAIRO_WIDGET_FILL_EXTEND, + "hspace", DIYA_CAIRO_WIDGET_FILL_EXTEND, + NULL + ); +} + +void diya_cairo_window_render(gpointer object, cairo_surface_t * surface) +{ + assert(DIYA_IS_CAIRO_WINDOW(object)); + DiyaCairoWidget * widget = DIYA_CAIRO_WIDGET(object); + cairo_t *cr = cairo_create(surface); + DIYA_CAIRO_WIDGET_GET_CLASS(widget)->draw(object,cr); + cairo_destroy(cr); +} + +void diya_cairo_window_update(gpointer object) +{ + assert(DIYA_IS_CAIRO_WINDOW(object)); + DIYA_CAIRO_WIDGET_CLASS(DIYA_CAIRO_WIDGET(object))->update(object); +} \ No newline at end of file diff --git a/src/widgets/cairo-window.h b/src/widgets/cairo-window.h new file mode 100644 index 0000000..fe0d6ec --- /dev/null +++ b/src/widgets/cairo-window.h @@ -0,0 +1,13 @@ +#ifndef DIYA_CAIRO_WINDOW_H +#define DIYA_CAIRO_WINDOW_H + +#include "cairo-widget.h" + +#define DIYA_TYPE_CAIRO_WINDOW (diya_cairo_window_get_type ()) +G_DECLARE_FINAL_TYPE (DiyaCairoWindow, diya_cairo_window, DIYA, CAIRO_WINDOW, DiyaCairoWidget) + + +void diya_cairo_window_set_root(gpointer object, gpointer root); +void diya_cairo_window_render(gpointer object, cairo_surface_t * surface); +void diya_cairo_window_update(gpointer object); +#endif \ No newline at end of file