add new imageviewer implementation (GTK)

3 days ago

author
Olaf Wintermann <olaf.wintermann@gmail.com>
date
Sat, 29 Mar 2025 18:17:01 +0100 (3 days ago)
changeset 529
0a4a6b0d1c82
parent 528
fb1dca7d83da
child 530
7992a44fe719

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;

mercurial