messenger-gtk

Gtk+3 graphical user interfaces for GNUnet Messenger
Log | Files | Refs | Submodules | README | LICENSE

commit 1049e99951c69db162f66eb6288450c2eacbbe15
parent 69e3d367f8e0d1168d5c6d6daebed1d2722b265c
Author: TheJackiMonster <thejackimonster@gmail.com>
Date:   Mon, 10 Jan 2022 21:56:48 +0100

Added visual previews of files

Signed-off-by: TheJackiMonster <thejackimonster@gmail.com>

Diffstat:
Mresources/ui/message_content.ui | 6+++---
Msrc/ui/message.c | 217++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
Msrc/ui/message.h | 16+++++++++++++++-
Msrc/ui/send_file.c | 12++++++++----
4 files changed, 242 insertions(+), 9 deletions(-)

diff --git a/resources/ui/message_content.ui b/resources/ui/message_content.ui @@ -1,7 +1,7 @@ <?xml version="1.0" encoding="UTF-8"?> <!-- Generated with glade 3.38.2 -Copyright (C) 2021 GNUnet e.V. +Copyright (C) 2021‑‑2022 GNUnet e.V. GNUnet is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published @@ -115,7 +115,7 @@ Author: Tobias Frisch </packing> </child> <child> - <object class="GtkProgressBar"> + <object class="GtkProgressBar" id="file_progress_bar"> <property name="visible">True</property> <property name="can-focus">False</property> </object> @@ -141,7 +141,7 @@ Author: Tobias Frisch <property name="valign">center</property> <property name="relief">none</property> <child> - <object class="GtkImage"> + <object class="GtkImage" id="file_status_image"> <property name="visible">True</property> <property name="can-focus">False</property> <property name="icon-name">folder-download-symbolic</property> diff --git a/src/ui/message.c b/src/ui/message.c @@ -1,6 +1,6 @@ /* This file is part of GNUnet. - Copyright (C) 2021 GNUnet e.V. + Copyright (C) 2021--2022 GNUnet e.V. GNUnet is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published @@ -24,8 +24,143 @@ #include "message.h" +#include <gnunet/gnunet_chat_lib.h> + #include "../application.h" +static int +handle_message_redraw_animation(gpointer user_data) +{ + UI_MESSAGE_Handle *handle = (UI_MESSAGE_Handle*) user_data; + + handle->redraw_animation = 0; + + if ((handle->preview_drawing_area) && + ((handle->preview_image) || + (handle->preview_animation) || + (handle->preview_animation_iter))) + gtk_widget_queue_draw(GTK_WIDGET(handle->preview_drawing_area)); + + return FALSE; +} + +static gboolean +handle_preview_drawing_area_draw(GtkWidget* drawing_area, + cairo_t* cairo, + gpointer user_data) +{ + UI_MESSAGE_Handle *handle = (UI_MESSAGE_Handle*) user_data; + + GtkStyleContext* context = gtk_widget_get_style_context(drawing_area); + + const guint width = gtk_widget_get_allocated_width(drawing_area); + const guint height = gtk_widget_get_allocated_height(drawing_area); + + gtk_render_background(context, cairo, 0, 0, width, height); + + GdkPixbuf *image = handle->preview_image; + + if (!(handle->preview_animation)) + goto render_image; + + if (handle->preview_animation_iter) + gdk_pixbuf_animation_iter_advance(handle->preview_animation_iter, NULL); + else + handle->preview_animation_iter = gdk_pixbuf_animation_get_iter( + handle->preview_animation, NULL + ); + + image = gdk_pixbuf_animation_iter_get_pixbuf(handle->preview_animation_iter); + + const int delay = gdk_pixbuf_animation_iter_get_delay_time( + handle->preview_animation_iter + ); + + handle->redraw_animation = g_timeout_add( + delay, handle_message_redraw_animation, handle + ); + +render_image: + if (!image) + return FALSE; + + int dwidth = gdk_pixbuf_get_width(image); + int dheight = gdk_pixbuf_get_height(image); + + gint optimal_height = width * dheight / dwidth; + + gtk_widget_set_size_request( + GTK_WIDGET(drawing_area), + width, + optimal_height + ); + + double ratio_width = 1.0 * width / dwidth; + double ratio_height = 1.0 * height / dheight; + + const double ratio = ratio_width < ratio_height? ratio_width : ratio_height; + + dwidth = (int) (dwidth * ratio); + dheight = (int) (dheight * ratio); + + double dx = (width - dwidth) * 0.5; + double dy = (height - dheight) * 0.5; + + const int interp_type = (ratio >= 1.0? + GDK_INTERP_NEAREST : + GDK_INTERP_BILINEAR + ); + + GdkPixbuf* scaled = gdk_pixbuf_scale_simple( + image, + dwidth, + dheight, + interp_type + ); + + gtk_render_icon(context, cairo, scaled, dx, dy); + + cairo_fill(cairo); + + g_object_unref(scaled); + return FALSE; +} + +static void +_clear_message_preview_data(UI_MESSAGE_Handle *handle) +{ + if (handle->preview_image) + { + g_object_unref(handle->preview_image); + handle->preview_image = NULL; + } + + if (handle->redraw_animation) + { + g_source_remove(handle->redraw_animation); + handle->redraw_animation = 0; + } + + if (handle->preview_animation_iter) + { + g_object_unref(handle->preview_animation_iter); + handle->preview_animation_iter = NULL; + } + + if (handle->preview_animation) + { + g_object_unref(handle->preview_animation); + handle->preview_animation = NULL; + } + + if (handle->preview_drawing_area) + gtk_widget_set_size_request( + GTK_WIDGET(handle->preview_drawing_area), + -1, + -1 + ); +} + UI_MESSAGE_Handle* ui_message_new(MESSENGER_Application *app, UI_MESSAGE_Type type) @@ -120,10 +255,33 @@ ui_message_new(MESSENGER_Application *app, gtk_builder_get_object(builder, "file_revealer") ); + handle->filename_label = GTK_LABEL( + gtk_builder_get_object(builder, "filename_label") + ); + + handle->file_progress_bar = GTK_PROGRESS_BAR( + gtk_builder_get_object(builder, "file_progress_bar") + ); + + handle->file_button = GTK_BUTTON( + gtk_builder_get_object(builder, "file_button") + ); + + handle->file_status_image = GTK_IMAGE( + gtk_builder_get_object(builder, "file_status_image") + ); + handle->preview_drawing_area = GTK_DRAWING_AREA( gtk_builder_get_object(builder, "preview_drawing_area") ); + handle->preview_draw_signal = g_signal_connect( + handle->preview_drawing_area, + "draw", + G_CALLBACK(handle_preview_drawing_area_draw), + handle + ); + switch (handle->type) { case UI_MESSAGE_STATUS: @@ -138,6 +296,13 @@ ui_message_new(MESSENGER_Application *app, )); g_object_unref(builder); + + handle->preview_image = NULL; + handle->preview_animation = NULL; + handle->preview_animation_iter = NULL; + + handle->redraw_animation = 0; + return handle; } @@ -150,6 +315,49 @@ ui_message_update(UI_MESSAGE_Handle *handle, if (!file) return; + if (GNUNET_YES != GNUNET_CHAT_file_is_local(file)) + goto file_content; + + if (!(handle->preview_drawing_area)) + goto file_progress; + + const char *preview = GNUNET_CHAT_file_open_preview(file); + + if (!preview) + goto file_progress; + + handle->preview_animation = gdk_pixbuf_animation_new_from_file( + preview, NULL + ); + + if (!(handle->preview_animation)) + handle->preview_image = gdk_pixbuf_new_from_file(preview, NULL); + + if ((handle->preview_animation) || (handle->preview_animation)) + { + gtk_widget_set_size_request( + GTK_WIDGET(handle->preview_drawing_area), + 250, + -1 + ); + + gtk_stack_set_visible_child( + handle->content_stack, + GTK_WIDGET(handle->preview_drawing_area) + ); + + gtk_widget_queue_draw(GTK_WIDGET(handle->preview_drawing_area)); + return; + } + + GNUNET_CHAT_file_close_preview(file); + +file_progress: + gtk_progress_bar_set_fraction(handle->file_progress_bar, 1.0); + +file_content: + gtk_label_set_text(handle->filename_label, GNUNET_CHAT_file_get_name(file)); + gtk_stack_set_visible_child( handle->content_stack, GTK_WIDGET(handle->file_revealer) @@ -161,6 +369,13 @@ ui_message_update(UI_MESSAGE_Handle *handle, void ui_message_delete(UI_MESSAGE_Handle *handle) { + _clear_message_preview_data(handle); + + g_signal_handler_disconnect( + handle->preview_drawing_area, + handle->preview_draw_signal + ); + g_object_unref(handle->builder); g_free(handle); diff --git a/src/ui/message.h b/src/ui/message.h @@ -1,6 +1,6 @@ /* This file is part of GNUnet. - Copyright (C) 2021 GNUnet e.V. + Copyright (C) 2021--2022 GNUnet e.V. GNUnet is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published @@ -63,8 +63,22 @@ typedef struct UI_MESSAGE_Handle GtkStack *content_stack; GtkLabel *text_label; + GtkRevealer *file_revealer; + GtkLabel *filename_label; + GtkProgressBar *file_progress_bar; + GtkButton *file_button; + GtkImage *file_status_image; + GtkDrawingArea *preview_drawing_area; + + gulong preview_draw_signal; + + GdkPixbuf *preview_image; + GdkPixbufAnimation *preview_animation; + GdkPixbufAnimationIter *preview_animation_iter; + + guint redraw_animation; } UI_MESSAGE_Handle; UI_MESSAGE_Handle* diff --git a/src/ui/send_file.c b/src/ui/send_file.c @@ -85,17 +85,16 @@ handle_send_button_click(GtkButton *button, UI_CHAT_ENTRY_Handle *entry = GNUNET_CHAT_context_get_user_pointer(context); UI_CHAT_Handle *handle = entry? entry->chat : NULL; + UI_FILE_LOAD_ENTRY_Handle *file_load = NULL; struct GNUNET_CHAT_File *file = NULL; if ((context) && (handle)) { - UI_FILE_LOAD_ENTRY_Handle *file_load = ui_file_load_entry_new(app); + file_load = ui_file_load_entry_new(app); gtk_label_set_text(file_load->file_label, filename); gtk_progress_bar_set_fraction(file_load->load_progress_bar, 0.0); - ui_chat_add_file_load(handle, file_load); - file = GNUNET_CHAT_context_send_file( context, filename, @@ -109,11 +108,16 @@ handle_send_button_click(GtkButton *button, gtk_window_close(GTK_WINDOW(app->ui.send_file.dialog)); if (!file) + { + if (file_load) + ui_file_load_entry_delete(file_load); + return; + } file_create_info(file); - // TODO: create UI component? + ui_chat_add_file_load(handle, file_load); } static void