8 days ago
implement imageviewer useradjustable setting in gtk4 implementation
/* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * * Copyright 2024 Olaf Wintermann. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include "image.h" #include "container.h" #include "menu.h" #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 *drawingarea = gtk_drawing_area_new(); GtkWidget *toplevel; GtkWidget *widget = drawingarea; gtk_widget_set_size_request(drawingarea, 100, 100); #if GTK_MAJOR_VERSION < 4 GtkWidget *eventbox = gtk_event_box_new(); gtk_container_add(GTK_CONTAINER(eventbox), drawingarea); widget = eventbox; #endif 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; imgviewer->zoom_scale = 20; 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 = imgviewer; if(value->value && value->type && !strcmp(value->type, UI_IMAGE_OBJECT_TYPE)) { GdkPixbuf *pixbuf = value->value; value->value = NULL; ui_imageviewer_set(value, pixbuf, UI_IMAGE_OBJECT_TYPE); } } #if GTK_MAJOR_VERSION >= 4 gtk_drawing_area_set_draw_func( GTK_DRAWING_AREA(drawingarea), imageviewer_draw, imgviewer, NULL); if(args.useradjustable) { gtk_widget_set_focusable(drawingarea, TRUE); } GtkEventController *scrollcontroller = gtk_event_controller_scroll_new(GTK_EVENT_CONTROLLER_SCROLL_VERTICAL); g_signal_connect(scrollcontroller, "scroll", G_CALLBACK(ui_imageviewer_scroll), imgviewer); gtk_widget_add_controller(GTK_WIDGET(drawingarea), GTK_EVENT_CONTROLLER(scrollcontroller)); GtkGesture *drag = gtk_gesture_drag_new(); g_signal_connect(drag, "drag-begin", G_CALLBACK(ui_imageviewer_drag_begin_cb), imgviewer); g_signal_connect(drag, "drag-end", G_CALLBACK(ui_imageviewer_drag_end_cb), imgviewer); g_signal_connect(drag, "drag-update", G_CALLBACK(ui_imageviewer_drag_update_cb), imgviewer); gtk_widget_add_controller(GTK_WIDGET(drawingarea), GTK_EVENT_CONTROLLER(drag)); #elif GTK_MAJOR_VERSION == 3 g_signal_connect( drawingarea, "draw", G_CALLBACK(imageviewer_draw), imgviewer); gtk_widget_add_events(eventbox, GDK_SCROLL_MASK); g_signal_connect( eventbox, "scroll-event", G_CALLBACK(ui_imageviewer_scroll_event), imgviewer); g_signal_connect( eventbox, "button-press-event", G_CALLBACK(ui_imageviewer_button_press_event), imgviewer); g_signal_connect( eventbox, "button-release-event", G_CALLBACK(ui_imageviewer_button_release_event), imgviewer); #endif if(args.contextmenu) { 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; } 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; double scale = 1; if(imgviewer->autoscale) { scale = dwidth / dpixwidth; if(dpixheight * scale > dheight) { scale = dheight / dpixheight; } } else { scale = 1 + ((double)imgviewer->zoom / (double)imgviewer->zoom_scale); } dpixwidth *= scale; dpixheight *= scale; double x = (dwidth - dpixwidth) / 2; double y = (dheight - dpixheight) / 2; x += imgviewer->transx; y += imgviewer->transy; cairo_translate(cr, x, y); cairo_scale(cr, scale, scale); gdk_cairo_set_source_pixbuf(cr, pixbuf, 0, 0); cairo_paint(cr); imgviewer->prev_scale = scale; } 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) { if(!type || strcmp(type, UI_IMAGE_OBJECT_TYPE)) { return 1; } GdkPixbuf *pixbuf = value; 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); gtk_widget_set_size_request(imgviewer->widget, width, height); } gtk_widget_queue_draw(imgviewer->widget); return 0; } int ui_image_load_file(UiGeneric *obj, const char *path) { GError *error = NULL; GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file(path, &error); if(!pixbuf) { return 1; } if(obj->set) { obj->set(obj, pixbuf, UI_IMAGE_OBJECT_TYPE); } else { obj->value = pixbuf; } return 0; } #if GTK_MAJOR_VERSION >= 4 gboolean ui_imageviewer_scroll( GtkEventControllerScroll *widget, gdouble dx, gdouble dy, gpointer userdata) { UiImageViewer *imgviewer = userdata; if(imgviewer->useradjustable) { imgviewer->zoom -= dy; if(imgviewer->zoom < -imgviewer->zoom_scale) { imgviewer->zoom = -imgviewer->zoom_scale; } gtk_widget_queue_draw(imgviewer->widget); return TRUE; } return FALSE; } void ui_imageviewer_drag_begin_cb( GtkGestureDrag* self, gdouble start_x, gdouble start_y, gpointer userdata) { UiImageViewer *imgviewer = userdata; imgviewer->begin_transx = imgviewer->transx; imgviewer->begin_transy = imgviewer->transy; } void ui_imageviewer_drag_end_cb( GtkGestureDrag* self, gdouble x, gdouble y, gpointer userdata) { } void ui_imageviewer_drag_update_cb( GtkGestureDrag* self, gdouble x, gdouble y, gpointer userdata) { UiImageViewer *imgviewer = userdata; if(imgviewer->useradjustable) { imgviewer->transx = imgviewer->begin_transx + x; imgviewer->transy = imgviewer->begin_transy + y; gtk_widget_queue_draw(imgviewer->widget); } } #else gboolean ui_imageviewer_scroll_event( GtkWidget *widget, GdkEventScroll event, gpointer userdata) { printf("scroll event\n"); return FALSE; } gboolean ui_imageviewer_button_press_event( GtkWidget *widget, GdkEventButton event, gpointer userdata) { printf("button pressed\n"); } gboolean ui_imageviewer_button_release_event( GtkWidget *widget, GdkEventButton event, gpointer userdata) { printf("button released\n"); } #endif