4 days ago
add new imageviewer implementation (GTK)
application/main.c | file | annotate | diff | comparison | revisions | |
ui/gtk/image.c | file | annotate | diff | comparison | revisions | |
ui/gtk/image.h | file | annotate | diff | comparison | revisions | |
ui/ui/image.h | file | annotate | diff | comparison | revisions |
--- a/application/main.c Sat Mar 29 07:57:16 2025 +0100 +++ b/application/main.c Sat Mar 29 18:17:01 2025 +0100 @@ -480,7 +480,7 @@ } ui_tab(obj, "Tab 5") { ui_button(obj, .label = "Test Button", .icon = "application-x-generic", .onclick = action_button); - ui_imageviewer(obj, .varname = "image", .style_class = "imageviewer", .contextmenu = menubuilder); + ui_imageviewer(obj, .varname = "image", .style_class = "imageviewer", .contextmenu = menubuilder, .scrollarea = TRUE); } ui_tab(obj, "Tab 6") {
--- a/ui/gtk/image.c Sat Mar 29 07:57:16 2025 +0100 +++ b/ui/gtk/image.c Sat Mar 29 18:17:01 2025 +0100 @@ -33,38 +33,88 @@ #include "../common/context.h" #include "../common/object.h" +static void imageviewer_destroy(UiImageViewer *iv) { + if(iv->pixbuf) { + g_object_unref(iv->pixbuf); + } + free(iv); +} + +#if GTK_MAJOR_VERSION >= 4 + +static void imageviewer_draw( + GtkDrawingArea *drawingarea, + cairo_t *cr, + int width, + int height, + gpointer userdata) +{ + ui_cairo_draw_image(userdata, cr, width, height); +} + +#else + +static gboolean imageviewer_draw(GtkWidget *widget, cairo_t *cr, gpointer userdata) { + int width = gtk_widget_get_allocated_width(widget); + int height = gtk_widget_get_allocated_height(widget); + ui_cairo_draw_image(userdata, cr, width, height); + return FALSE; +} + +#endif UIWIDGET ui_imageviewer_create(UiObject *obj, UiImageViewerArgs args) { UiObject *current = uic_current_obj(obj); - GtkWidget *scrolledwindow = SCROLLEDWINDOW_NEW(); -#if GTK_CHECK_VERSION(4, 0, 0) - GtkWidget *image = gtk_picture_new(); -#else - GtkWidget *image = gtk_image_new(); -#endif + GtkWidget *drawingarea = gtk_drawing_area_new(); + GtkWidget *toplevel; + GtkWidget *widget = drawingarea; - ui_set_name_and_style(image, args.name, args.style_class); + gtk_widget_set_size_request(drawingarea, 100, 100); #if GTK_MAJOR_VERSION < 4 GtkWidget *eventbox = gtk_event_box_new(); - SCROLLEDWINDOW_SET_CHILD(scrolledwindow, eventbox); - gtk_container_add(GTK_CONTAINER(eventbox), image); -#else - SCROLLEDWINDOW_SET_CHILD(scrolledwindow, image); - GtkWidget *eventbox = image; + gtk_container_add(GTK_CONTAINER(eventbox), drawingarea); + widget = eventbox; #endif - UI_APPLY_LAYOUT1(current, args); - current->container->add(current->container, scrolledwindow, TRUE); + if(args.scrollarea) { + toplevel = SCROLLEDWINDOW_NEW(); + SCROLLEDWINDOW_SET_CHILD(toplevel, widget); + args.adjustsize = TRUE; + } else { + toplevel = widget; + } + + UiImageViewer *imgviewer = malloc(sizeof(UiImageViewer)); + memset(imgviewer, 0, sizeof(UiImageViewer)); + if(args.image_padding > 0) { + imgviewer->padding_left = args.image_padding; + imgviewer->padding_right = args.image_padding; + imgviewer->padding_top = args.image_padding; + imgviewer->padding_bottom = args.image_padding; + } else { + imgviewer->padding_left = args.image_padding_left; + imgviewer->padding_right = args.image_padding_right; + imgviewer->padding_top = args.image_padding_top; + imgviewer->padding_bottom = args.image_padding_bottom; + } + imgviewer->adjustsize = args.adjustsize; + imgviewer->autoscale = args.autoscale; + imgviewer->useradjustable = args.useradjustable; + + g_object_set_data_full(G_OBJECT(drawingarea), "uiimageviewer", imgviewer, (GDestroyNotify)imageviewer_destroy); UiVar *var = uic_widget_var(obj->ctx, current->ctx, args.value, args.varname, UI_VAR_GENERIC); + imgviewer->var = var; + imgviewer->widget = drawingarea; + if(var) { UiGeneric *value = var->value; value->get = ui_imageviewer_get; value->get_type = ui_imageviewer_get_type; value->set = ui_imageviewer_set; - value->obj = image; + value->obj = imgviewer; if(value->value && value->type && !strcmp(value->type, UI_IMAGE_OBJECT_TYPE)) { GdkPixbuf *pixbuf = value->value; value->value = NULL; @@ -72,20 +122,80 @@ } } +#if GTK_MAJOR_VERSION >= 4 + gtk_drawing_area_set_draw_func( + GTK_DRAWING_AREA(drawingarea), + imageviewer_draw, + imgviewer, + NULL); +#elif GTK_MAJOR_VERSION == 3 + g_signal_connect( + drawingarea, + "draw", + G_CALLBACK(imageviewer_draw), + imgviewer); + +#endif + if(args.contextmenu) { - UIMENU menu = ui_contextmenu_create(args.contextmenu, obj, eventbox); - ui_widget_set_contextmenu(eventbox, menu); + UIMENU menu = ui_contextmenu_create(args.contextmenu, obj, widget); + ui_widget_set_contextmenu(widget, menu); + } + + UI_APPLY_LAYOUT1(current, args); + current->container->add(current->container, toplevel, TRUE); + + return toplevel; +} + + +void ui_cairo_draw_image(UiImageViewer *imgviewer, cairo_t *cr, int width, int height) { + if(!imgviewer->pixbuf) { + return; } - return scrolledwindow; + GdkPixbuf *pixbuf = imgviewer->pixbuf; + double dpixwidth = (double)gdk_pixbuf_get_width(pixbuf); + double dpixheight = (double)gdk_pixbuf_get_height(pixbuf); + + double dwidth = width; + double dheight = height; + if(imgviewer->autoscale) { + double scale = dwidth / dpixwidth; + if(dpixheight * scale > dheight) { + scale = dheight / dpixheight; + } + + dpixwidth *= scale; + dpixheight *= scale; + double x = (dwidth - dpixwidth) / 2; + double y = (dheight - dpixheight) / 2; + cairo_translate(cr, x, y); + cairo_scale(cr, scale, scale); + } else { + double x = (dwidth - dpixwidth) / 2; + double y = (dheight - dpixheight) / 2; + if(x < 0) { + x = 0; + } + if(y < 0) { + y = 0; + } + cairo_translate(cr, (int)x, (int)y); + } + + gdk_cairo_set_source_pixbuf(cr, pixbuf, 0, 0); + cairo_paint(cr); } void* ui_imageviewer_get(UiGeneric *g) { + UiImageViewer *imgviewer = g->obj; + g->value = imgviewer->pixbuf; return g->value; } const char* ui_imageviewer_get_type(UiGeneric *g) { - + return UI_IMAGE_OBJECT_TYPE; } int ui_imageviewer_set(UiGeneric *g, void *value, const char *type) { @@ -93,25 +203,22 @@ return 1; } - // TODO: do we need to free the previous value here? - - g->value = value; - g->type = type; GdkPixbuf *pixbuf = value; - if(pixbuf) { + UiImageViewer *imgviewer = g->obj; + g->value = pixbuf; + + if(imgviewer->pixbuf) { + g_object_unref(imgviewer->pixbuf); + } + imgviewer->pixbuf = pixbuf; + + if(imgviewer->adjustsize) { int width = gdk_pixbuf_get_width(pixbuf); int height = gdk_pixbuf_get_height(pixbuf); - -#if GTK_CHECK_VERSION(4, 0, 0) - GdkTexture *texture = gdk_texture_new_for_pixbuf(pixbuf); - gtk_picture_set_paintable(GTK_PICTURE(g->obj), GDK_PAINTABLE(texture)); -#else - gtk_image_set_from_pixbuf(GTK_IMAGE(g->obj), pixbuf); -#endif - gtk_widget_set_size_request(g->obj, width, height); + gtk_widget_set_size_request(imgviewer->widget, width, height); } - + gtk_widget_queue_draw(imgviewer->widget); return 0; } @@ -130,6 +237,6 @@ } else { obj->value = pixbuf; } - + return 0; }
--- a/ui/gtk/image.h Sat Mar 29 07:57:16 2025 +0100 +++ b/ui/gtk/image.h Sat Mar 29 18:17:01 2025 +0100 @@ -36,6 +36,20 @@ extern "C" { #endif +typedef struct UiImageViewer { + GtkWidget *widget; + UiVar *var; + int padding_left; + int padding_right; + int padding_top; + int padding_bottom; + UiBool autoscale; + UiBool adjustsize; + UiBool useradjustable; + GdkPixbuf *pixbuf; +} UiImageViewer; + +void ui_cairo_draw_image(UiImageViewer *imgviewer, cairo_t *cr, int width, int height); void* ui_imageviewer_get(UiGeneric *g); const char* ui_imageviewer_get_type(UiGeneric *g);
--- a/ui/ui/image.h Sat Mar 29 07:57:16 2025 +0100 +++ b/ui/ui/image.h Sat Mar 29 18:17:01 2025 +0100 @@ -51,6 +51,13 @@ UiBool scrollarea; UiBool autoscale; + UiBool adjustsize; + UiBool useradjustable; + int image_padding; + int image_padding_left; + int image_padding_right; + int image_padding_top; + int image_padding_bottom; UiGeneric *value; const char *varname; UiMenuBuilder *contextmenu;