ui/gtk/image.c

changeset 103
6606616eca9f
parent 88
e27526429d85
child 108
77254bd6dccb
equal deleted inserted replaced
102:64ded9f6a6c6 103:6606616eca9f
31 #include "container.h" 31 #include "container.h"
32 #include "menu.h" 32 #include "menu.h"
33 #include "../common/context.h" 33 #include "../common/context.h"
34 #include "../common/object.h" 34 #include "../common/object.h"
35 35
36 static void imageviewer_destroy(UiImageViewer *iv) {
37 if(iv->pixbuf) {
38 g_object_unref(iv->pixbuf);
39 }
40 free(iv);
41 }
42
43 #if GTK_MAJOR_VERSION >= 4
44
45 static void imageviewer_draw(
46 GtkDrawingArea *drawingarea,
47 cairo_t *cr,
48 int width,
49 int height,
50 gpointer userdata)
51 {
52 ui_cairo_draw_image(userdata, cr, width, height);
53 }
54
55 #else
56
57 static gboolean imageviewer_draw(GtkWidget *widget, cairo_t *cr, gpointer userdata) {
58 int width = gtk_widget_get_allocated_width(widget);
59 int height = gtk_widget_get_allocated_height(widget);
60 ui_cairo_draw_image(userdata, cr, width, height);
61 return FALSE;
62 }
63
64 #endif
36 65
37 UIWIDGET ui_imageviewer_create(UiObject *obj, UiImageViewerArgs args) { 66 UIWIDGET ui_imageviewer_create(UiObject *obj, UiImageViewerArgs args) {
38 UiObject *current = uic_current_obj(obj); 67 UiObject *current = uic_current_obj(obj);
39 68
40 GtkWidget *scrolledwindow = SCROLLEDWINDOW_NEW(); 69 GtkWidget *drawingarea = gtk_drawing_area_new();
41 #if GTK_CHECK_VERSION(4, 0, 0) 70 GtkWidget *toplevel;
42 GtkWidget *image = gtk_picture_new(); 71 GtkWidget *widget = drawingarea;
43 #else 72
44 GtkWidget *image = gtk_image_new(); 73 gtk_widget_set_size_request(drawingarea, 100, 100);
45 #endif
46
47 ui_set_name_and_style(image, args.name, args.style_class);
48 74
49 #if GTK_MAJOR_VERSION < 4 75 #if GTK_MAJOR_VERSION < 4
50 GtkWidget *eventbox = gtk_event_box_new(); 76 GtkWidget *eventbox = gtk_event_box_new();
51 SCROLLEDWINDOW_SET_CHILD(scrolledwindow, eventbox); 77 gtk_container_add(GTK_CONTAINER(eventbox), drawingarea);
52 gtk_container_add(GTK_CONTAINER(eventbox), image); 78 widget = eventbox;
53 #else
54 SCROLLEDWINDOW_SET_CHILD(scrolledwindow, image);
55 GtkWidget *eventbox = image;
56 #endif 79 #endif
57 80
58 UI_APPLY_LAYOUT1(current, args); 81 if(args.scrollarea) {
59 current->container->add(current->container, scrolledwindow, TRUE); 82 toplevel = SCROLLEDWINDOW_NEW();
83 SCROLLEDWINDOW_SET_CHILD(toplevel, widget);
84 args.adjustwidgetsize = TRUE;
85 } else {
86 toplevel = widget;
87 }
88
89 UiImageViewer *imgviewer = malloc(sizeof(UiImageViewer));
90 memset(imgviewer, 0, sizeof(UiImageViewer));
91 imgviewer->obj = obj;
92 imgviewer->onbuttonpress = args.onbuttonpress;
93 imgviewer->onbuttonpressdata = args.onbuttonpressdata;
94 imgviewer->onbuttonrelease = args.onbuttonrelease;
95 imgviewer->onbuttonreleasedata = args.onbuttonreleasedata;
96 if(args.image_padding > 0) {
97 imgviewer->padding_left = args.image_padding;
98 imgviewer->padding_right = args.image_padding;
99 imgviewer->padding_top = args.image_padding;
100 imgviewer->padding_bottom = args.image_padding;
101 } else {
102 imgviewer->padding_left = args.image_padding_left;
103 imgviewer->padding_right = args.image_padding_right;
104 imgviewer->padding_top = args.image_padding_top;
105 imgviewer->padding_bottom = args.image_padding_bottom;
106 }
107 imgviewer->adjustwidgetsize = args.adjustwidgetsize;
108 imgviewer->autoscale = args.autoscale;
109 imgviewer->useradjustable = args.useradjustable;
110 imgviewer->zoom_scale = 20;
111
112 g_object_set_data_full(G_OBJECT(drawingarea), "uiimageviewer", imgviewer, (GDestroyNotify)imageviewer_destroy);
60 113
61 UiVar *var = uic_widget_var(obj->ctx, current->ctx, args.value, args.varname, UI_VAR_GENERIC); 114 UiVar *var = uic_widget_var(obj->ctx, current->ctx, args.value, args.varname, UI_VAR_GENERIC);
115 imgviewer->var = var;
116 imgviewer->widget = drawingarea;
117
62 if(var) { 118 if(var) {
63 UiGeneric *value = var->value; 119 UiGeneric *value = var->value;
64 value->get = ui_imageviewer_get; 120 value->get = ui_imageviewer_get;
65 value->get_type = ui_imageviewer_get_type; 121 value->get_type = ui_imageviewer_get_type;
66 value->set = ui_imageviewer_set; 122 value->set = ui_imageviewer_set;
67 value->obj = image; 123 value->obj = imgviewer;
68 if(value->value && value->type && !strcmp(value->type, UI_IMAGE_OBJECT_TYPE)) { 124 if(value->value && value->type && !strcmp(value->type, UI_IMAGE_OBJECT_TYPE)) {
69 GdkPixbuf *pixbuf = value->value; 125 GdkPixbuf *pixbuf = value->value;
70 value->value = NULL; 126 value->value = NULL;
71 ui_imageviewer_set(value, pixbuf, UI_IMAGE_OBJECT_TYPE); 127 ui_imageviewer_set(value, pixbuf, UI_IMAGE_OBJECT_TYPE);
128 g_object_unref(pixbuf);
72 } 129 }
73 } 130 }
74 131
132 #if GTK_MAJOR_VERSION >= 4
133 gtk_drawing_area_set_draw_func(
134 GTK_DRAWING_AREA(drawingarea),
135 imageviewer_draw,
136 imgviewer,
137 NULL);
138
139 if(args.useradjustable) {
140 gtk_widget_set_focusable(drawingarea, TRUE);
141 }
142
143 GtkEventController *scrollcontroller = gtk_event_controller_scroll_new(GTK_EVENT_CONTROLLER_SCROLL_VERTICAL);
144 g_signal_connect(scrollcontroller, "scroll", G_CALLBACK(ui_imageviewer_scroll), imgviewer);
145 gtk_widget_add_controller(GTK_WIDGET(drawingarea), GTK_EVENT_CONTROLLER(scrollcontroller));
146
147 GtkGesture *drag = gtk_gesture_drag_new();
148 g_signal_connect(drag, "drag-begin", G_CALLBACK(ui_imageviewer_drag_begin_cb), imgviewer);
149 g_signal_connect(drag, "drag-end", G_CALLBACK(ui_imageviewer_drag_end_cb), imgviewer);
150 g_signal_connect(drag, "drag-update", G_CALLBACK(ui_imageviewer_drag_update_cb), imgviewer);
151 gtk_widget_add_controller(GTK_WIDGET(drawingarea), GTK_EVENT_CONTROLLER(drag));
152
153 GtkGesture *click = gtk_gesture_click_new();
154 g_signal_connect(click, "pressed", G_CALLBACK(ui_imageviewer_pressed_cb), imgviewer);
155 g_signal_connect(click, "released", G_CALLBACK(ui_imageviewer_released_cb), imgviewer);
156 gtk_widget_add_controller(GTK_WIDGET(drawingarea), GTK_EVENT_CONTROLLER(click));
157
158 #elif GTK_MAJOR_VERSION == 3
159 g_signal_connect(
160 drawingarea,
161 "draw",
162 G_CALLBACK(imageviewer_draw),
163 imgviewer);
164
165 gtk_widget_add_events(eventbox, GDK_SCROLL_MASK);
166
167 g_signal_connect(
168 eventbox,
169 "scroll-event",
170 G_CALLBACK(ui_imageviewer_scroll_event),
171 imgviewer);
172 g_signal_connect(
173 eventbox,
174 "button-press-event",
175 G_CALLBACK(ui_imageviewer_button_press_event),
176 imgviewer);
177 g_signal_connect(
178 eventbox,
179 "button-release-event",
180 G_CALLBACK(ui_imageviewer_button_release_event),
181 imgviewer);
182
183 #endif
184
75 if(args.contextmenu) { 185 if(args.contextmenu) {
76 UIMENU menu = ui_contextmenu_create(args.contextmenu, obj, eventbox); 186 UIMENU menu = ui_contextmenu_create(args.contextmenu, obj, widget);
77 ui_widget_set_contextmenu(eventbox, menu); 187 ui_widget_set_contextmenu(widget, menu);
78 } 188 }
79 189
80 return scrolledwindow; 190 UI_APPLY_LAYOUT1(current, args);
191 current->container->add(current->container, toplevel, TRUE);
192
193 return toplevel;
194 }
195
196 static void imageviewer_reset(UiImageViewer *imgviewer) {
197 imgviewer->isautoscaled = FALSE;
198 imgviewer->transx = 0;
199 imgviewer->transy;
200 imgviewer->begin_transx = 0;
201 imgviewer->begin_transy = 0;
202 imgviewer->scale = 1;
203 imgviewer->user_scale = 1;
204 }
205
206 UIWIDGET ui_imageviewer_reset(UIWIDGET w) {
207 UiImageViewer *imgviewer = g_object_get_data(G_OBJECT(w), "uiimageviewer");
208 if(imgviewer) {
209 imageviewer_reset(imgviewer);
210 gtk_widget_queue_draw(w);
211 }
212 }
213
214 UIWIDGET ui_imageviewer_set_autoscale(UIWIDGET w, UiBool set) {
215 UiImageViewer *imgviewer = g_object_get_data(G_OBJECT(w), "uiimageviewer");
216 if(imgviewer) {
217 imgviewer->autoscale = set;
218 }
219 }
220
221 UIWIDGET ui_imageviewer_set_adjustwidgetsize(UIWIDGET w, UiBool set) {
222 UiImageViewer *imgviewer = g_object_get_data(G_OBJECT(w), "uiimageviewer");
223 if(imgviewer) {
224 imgviewer->adjustwidgetsize = set;
225 }
226 }
227
228 UIWIDGET ui_imageviewer_set_useradjustable(UIWIDGET w, UiBool set) {
229 UiImageViewer *imgviewer = g_object_get_data(G_OBJECT(w), "uiimageviewer");
230 if(imgviewer) {
231 imgviewer->useradjustable = set;
232 }
233 }
234
235 void ui_cairo_draw_image(UiImageViewer *imgviewer, cairo_t *cr, int width, int height) {
236 if(!imgviewer->pixbuf) {
237 return;
238 }
239
240 GdkPixbuf *pixbuf = imgviewer->pixbuf;
241 double dpixwidth = (double)gdk_pixbuf_get_width(pixbuf);
242 double dpixheight = (double)gdk_pixbuf_get_height(pixbuf);
243
244 double dwidth = width;
245 double dheight = height;
246 double scale = 1;
247 // if autoscale is enabled, scale the image to fill available space
248 // if useradjustable is also enabled, the autoscaling is only done once
249 if(imgviewer->autoscale && imgviewer->scale != 0) {
250 if(!imgviewer->isautoscaled) {
251 scale = dwidth / dpixwidth;
252 if(dpixheight * scale > dheight) {
253 scale = dheight / dpixheight;
254 }
255
256 if(imgviewer->useradjustable) {
257 imgviewer->isautoscaled = TRUE;
258 }
259
260 imgviewer->scale = scale;
261 } else {
262 scale = imgviewer->scale;
263 }
264
265 imgviewer->user_scale = scale;
266 } else {
267 // user-adjusted scaling
268 //scale = 1 + ((double)imgviewer->zoom / (double)imgviewer->zoom_scale);
269 scale = imgviewer->user_scale;
270 }
271
272 dpixwidth *= scale;
273 dpixheight *= scale;
274 double x = (dwidth - dpixwidth) / 2;
275 double y = (dheight - dpixheight) / 2;
276
277 x += imgviewer->transx;
278 y += imgviewer->transy;
279
280 cairo_translate(cr, x, y);
281 cairo_scale(cr, scale, scale);
282
283 gdk_cairo_set_source_pixbuf(cr, pixbuf, 0, 0);
284 cairo_paint(cr);
81 } 285 }
82 286
83 void* ui_imageviewer_get(UiGeneric *g) { 287 void* ui_imageviewer_get(UiGeneric *g) {
288 UiImageViewer *imgviewer = g->obj;
289 g->value = imgviewer->pixbuf;
84 return g->value; 290 return g->value;
85 } 291 }
86 292
87 const char* ui_imageviewer_get_type(UiGeneric *g) { 293 const char* ui_imageviewer_get_type(UiGeneric *g) {
88 294 return UI_IMAGE_OBJECT_TYPE;
89 } 295 }
90 296
91 int ui_imageviewer_set(UiGeneric *g, void *value, const char *type) { 297 int ui_imageviewer_set(UiGeneric *g, void *value, const char *type) {
92 if(!type || strcmp(type, UI_IMAGE_OBJECT_TYPE)) { 298 if(!type || strcmp(type, UI_IMAGE_OBJECT_TYPE)) {
93 return 1; 299 return 1;
94 } 300 }
95 301
96 // TODO: do we need to free the previous value here?
97
98 g->value = value;
99 g->type = type;
100 GdkPixbuf *pixbuf = value; 302 GdkPixbuf *pixbuf = value;
101 303 g_object_ref(pixbuf);
102 if(pixbuf) { 304
305 UiImageViewer *imgviewer = g->obj;
306 g->value = pixbuf;
307
308 imageviewer_reset(imgviewer);
309
310 if(imgviewer->pixbuf) {
311 g_object_unref(imgviewer->pixbuf);
312 }
313 imgviewer->pixbuf = pixbuf;
314
315 if(imgviewer->adjustwidgetsize && !imgviewer->autoscale) {
103 int width = gdk_pixbuf_get_width(pixbuf); 316 int width = gdk_pixbuf_get_width(pixbuf);
104 int height = gdk_pixbuf_get_height(pixbuf); 317 int height = gdk_pixbuf_get_height(pixbuf);
105 318 gtk_widget_set_size_request(imgviewer->widget, width, height);
106 #if GTK_CHECK_VERSION(4, 0, 0) 319 }
107 GdkTexture *texture = gdk_texture_new_for_pixbuf(pixbuf); 320 gtk_widget_queue_draw(imgviewer->widget);
108 gtk_picture_set_paintable(GTK_PICTURE(g->obj), GDK_PAINTABLE(texture));
109 #else
110 gtk_image_set_from_pixbuf(GTK_IMAGE(g->obj), pixbuf);
111 #endif
112 gtk_widget_set_size_request(g->obj, width, height);
113 }
114
115 321
116 return 0; 322 return 0;
117 } 323 }
118 324
119 325
125 return 1; 331 return 1;
126 } 332 }
127 333
128 if(obj->set) { 334 if(obj->set) {
129 obj->set(obj, pixbuf, UI_IMAGE_OBJECT_TYPE); 335 obj->set(obj, pixbuf, UI_IMAGE_OBJECT_TYPE);
336 g_object_unref(pixbuf);
130 } else { 337 } else {
131 obj->value = pixbuf; 338 obj->value = pixbuf;
132 } 339 }
133 340
134 return 0; 341 return 0;
135 } 342 }
343
344 void ui_image_ref(UIIMAGE img) {
345 g_object_ref(img);
346 }
347
348 void ui_image_unref(UIIMAGE img) {
349 g_object_unref(img);
350 }
351
352 #if GTK_MAJOR_VERSION >= 4
353
354 gboolean ui_imageviewer_scroll(
355 GtkEventControllerScroll *widget,
356 gdouble dx,
357 gdouble dy,
358 gpointer userdata)
359 {
360 UiImageViewer *imgviewer = userdata;
361 if(imgviewer->useradjustable) {
362 double step = dy / imgviewer->zoom_scale;
363 if(imgviewer->user_scale - step > 0) {
364 imgviewer->user_scale -= step;
365 }
366
367 imgviewer->scale = 0; // disable autoscale
368 gtk_widget_queue_draw(imgviewer->widget);
369 return TRUE;
370 }
371 return FALSE;
372 }
373
374 void ui_imageviewer_drag_begin_cb(
375 GtkGestureDrag *self,
376 gdouble start_x,
377 gdouble start_y,
378 gpointer userdata)
379 {
380 UiImageViewer *imgviewer = userdata;
381 imgviewer->begin_transx = imgviewer->transx;
382 imgviewer->begin_transy = imgviewer->transy;
383 }
384
385 void ui_imageviewer_drag_end_cb(
386 GtkGestureDrag* self,
387 gdouble x,
388 gdouble y,
389 gpointer userdata)
390 {
391
392 }
393
394 void ui_imageviewer_drag_update_cb(
395 GtkGestureDrag *self,
396 gdouble x,
397 gdouble y,
398 gpointer userdata)
399 {
400 UiImageViewer *imgviewer = userdata;
401 if(imgviewer->useradjustable) {
402 imgviewer->transx = imgviewer->begin_transx + x;
403 imgviewer->transy = imgviewer->begin_transy + y;
404 gtk_widget_queue_draw(imgviewer->widget);
405 }
406 }
407
408 static void imgviewer_button_event(
409 GtkGestureClick *gesture,
410 UiImageViewer *imgviewer,
411 ui_callback callback,
412 void *userdata)
413 {
414 UiEvent event;
415 event.obj = imgviewer->obj;
416 event.window = event.obj->window;
417 event.document = event.obj->ctx->document;
418 event.eventdata = NULL;
419 event.intval = gtk_gesture_single_get_current_button(GTK_GESTURE_SINGLE(gesture));
420 event.set = 0;
421 callback(&event, userdata);
422 }
423
424 void ui_imageviewer_pressed_cb(
425 GtkGestureClick *self,
426 gint n_press,
427 gdouble x,
428 gdouble y,
429 gpointer userdata)
430 {
431 UiImageViewer *imgviewer = userdata;
432 if(imgviewer->onbuttonpress) {
433 imgviewer_button_event(self, imgviewer, imgviewer->onbuttonpress, imgviewer->onbuttonpressdata);
434 }
435 }
436
437 void ui_imageviewer_released_cb(
438 GtkGestureClick *self,
439 gint n_press,
440 gdouble x,
441 gdouble y,
442 gpointer userdata)
443 {
444 UiImageViewer *imgviewer = userdata;
445 if(imgviewer->onbuttonrelease) {
446 imgviewer_button_event(self, imgviewer, imgviewer->onbuttonrelease, imgviewer->onbuttonreleasedata);
447 }
448 }
449
450 #else
451
452 gboolean ui_imageviewer_scroll_event(
453 GtkWidget *widget,
454 GdkEventScroll event,
455 gpointer userdata)
456 {
457 // TODO
458 return FALSE;
459 }
460
461 gboolean ui_imageviewer_button_press_event(
462 GtkWidget *widget,
463 GdkEventButton event,
464 gpointer userdata)
465 {
466 // TODO
467 return FALSE;
468 }
469
470 gboolean ui_imageviewer_button_release_event(
471 GtkWidget *widget,
472 GdkEventButton event,
473 gpointer userdata)
474 {
475 // TODO
476 return FALSE;
477 }
478
479 #endif

mercurial