messenger-gtk

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

commit 2830cd336ab3db18af4905896a76806502b9ef92
parent b2d0576fb5eb5a855e61dc5678fe04ef2036ef04
Author: Jacki <jacki@thejackimonster.de>
Date:   Mon, 17 Jun 2024 21:08:57 +0200

Adjust discourse dialog and fix avatar flickering

Signed-off-by: Jacki <jacki@thejackimonster.de>

Diffstat:
Mresources/css/style.css | 14++++++++++++++
Mresources/ui/discourse.ui | 103++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------------------
Mresources/ui/discourse_panel.ui | 2+-
Msrc/ui.c | 58++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/ui.h | 26++++++++++++++++++++++++++
Msrc/ui/discourse.c | 246++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------------------
Msrc/ui/discourse.h | 5++---
7 files changed, 349 insertions(+), 105 deletions(-)

diff --git a/resources/css/style.css b/resources/css/style.css @@ -51,6 +51,20 @@ padding: 4px; } +.discourse-action { + background-color: @theme_selected_bg_color; + background-image: none; + color: @theme_selected_fg_color; +} + +.success-action { + background-color: @success_color; +} + +.error-action { + background-color: @error_color; +} + .message-avatar { margin: 4px; } diff --git a/resources/ui/discourse.ui b/resources/ui/discourse.ui @@ -99,15 +99,30 @@ Author: Tobias Frisch <property name="can-focus">False</property> <property name="orientation">vertical</property> <child> - <object class="GtkFlowBox" id="members_flowbox"> + <object class="GtkScrolledWindow"> <property name="width-request">280</property> <property name="height-request">180</property> <property name="visible">True</property> - <property name="can-focus">False</property> - <property name="border-width">16</property> - <property name="column-spacing">2</property> - <property name="row-spacing">2</property> - <property name="selection-mode">none</property> + <property name="can-focus">True</property> + <child> + <object class="GtkViewport"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <child> + <object class="GtkFlowBox" id="members_flowbox"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="halign">center</property> + <property name="valign">center</property> + <property name="border-width">16</property> + <property name="column-spacing">2</property> + <property name="row-spacing">2</property> + <property name="min-children-per-line">2</property> + <property name="selection-mode">none</property> + </object> + </child> + </object> + </child> </object> <packing> <property name="expand">True</property> @@ -133,28 +148,31 @@ Author: Tobias Frisch <property name="visible">True</property> <property name="can-focus">False</property> <child> - <object class="GtkImage" id="microphone_on_icon"> + <object class="GtkImage" id="microphone_off_icon"> <property name="visible">True</property> <property name="can-focus">False</property> - <property name="icon-name">microphone-sensitivity-high-symbolic</property> + <property name="icon-name">microphone-sensitivity-muted-symbolic</property> </object> <packing> - <property name="name">on_page</property> + <property name="name">off_page</property> </packing> </child> <child> - <object class="GtkImage" id="microphone_off_icon"> + <object class="GtkImage" id="microphone_on_icon"> <property name="visible">True</property> <property name="can-focus">False</property> - <property name="icon-name">microphone-sensitivity-muted-symbolic</property> + <property name="icon-name">microphone-sensitivity-high-symbolic</property> </object> <packing> - <property name="name">off_page</property> + <property name="name">on_page</property> <property name="position">1</property> </packing> </child> </object> </child> + <style> + <class name="discourse-action"/> + </style> </object> <packing> <property name="expand">False</property> @@ -175,6 +193,9 @@ Author: Tobias Frisch <property name="icon-name">camera-web-symbolic</property> </object> </child> + <style> + <class name="discourse-action"/> + </style> </object> <packing> <property name="expand">False</property> @@ -195,6 +216,9 @@ Author: Tobias Frisch <property name="icon-name">video-display-symbolic</property> </object> </child> + <style> + <class name="discourse-action"/> + </style> </object> <packing> <property name="expand">False</property> @@ -232,6 +256,9 @@ audio-volume-medium-symbolic</property> <property name="relief">none</property> </object> </child> + <style> + <class name="discourse-action"/> + </style> </object> <packing> <property name="expand">False</property> @@ -240,43 +267,59 @@ audio-volume-medium-symbolic</property> </packing> </child> <child> - <object class="GtkButton" id="call_button"> + <object class="GtkStack" id="call_stack"> <property name="visible">True</property> - <property name="can-focus">True</property> - <property name="receives-default">True</property> - <property name="relief">none</property> + <property name="can-focus">False</property> <child> - <object class="GtkStack" id="call_stack"> + <object class="GtkButton" id="call_stop_button"> <property name="visible">True</property> - <property name="can-focus">False</property> + <property name="can-focus">True</property> + <property name="receives-default">True</property> + <property name="relief">none</property> <child> - <object class="GtkImage" id="call_start_icon"> + <object class="GtkImage"> <property name="visible">True</property> <property name="can-focus">False</property> - <property name="icon-name">call-start-symbolic</property> + <property name="icon-name">call-stop-symbolic</property> </object> - <packing> - <property name="name">start_page</property> - </packing> </child> + <style> + <class name="discourse-action"/> + <class name="error-action"/> + </style> + </object> + <packing> + <property name="name">stop_page</property> + </packing> + </child> + <child> + <object class="GtkButton" id="call_start_button"> + <property name="visible">True</property> + <property name="can-focus">True</property> + <property name="receives-default">True</property> <child> - <object class="GtkImage" id="call_stop_icon"> + <object class="GtkImage"> <property name="visible">True</property> <property name="can-focus">False</property> - <property name="icon-name">call-stop-symbolic</property> + <property name="icon-name">call-start-symbolic</property> </object> - <packing> - <property name="name">stop_page</property> - <property name="position">1</property> - </packing> </child> + <style> + <class name="discourse-action"/> + <class name="success-action"/> + </style> </object> + <packing> + <property name="name">page0</property> + <property name="title" translatable="yes">page0</property> + <property name="position">1</property> + </packing> </child> </object> <packing> <property name="expand">False</property> <property name="fill">True</property> - <property name="position">5</property> + <property name="position">4</property> </packing> </child> </object> diff --git a/resources/ui/discourse_panel.ui b/resources/ui/discourse_panel.ui @@ -33,7 +33,7 @@ Author: Tobias Frisch <property name="can-focus">False</property> <child> <object class="GtkBox" id="avatar_box"> - <property name="width-request">140</property> + <property name="width-request">124</property> <property name="visible">True</property> <property name="can-focus">False</property> <property name="border-width">8</property> diff --git a/src/ui.c b/src/ui.c @@ -245,3 +245,61 @@ ui_avatar_set_icon(HdyAvatar *avatar, else hdy_avatar_set_loadable_icon(avatar, G_LOADABLE_ICON(icon)); } + +gboolean +ui_find_qdata_in_container(GtkContainer *container, + GQuark quark, + const gpointer data) +{ + g_assert((container) && (data)); + + GList* children = gtk_container_get_children(container); + GList *item = children; + + while (item) + { + GtkWidget *widget = GTK_WIDGET(item->data); + + if (data == g_object_get_qdata(G_OBJECT(widget), quark)) + break; + + item = item->next; + } + + if (children) + g_list_free(children); + + return item? TRUE : FALSE; +} + +void +ui_clear_container_of_missing_qdata(GtkContainer *container, + GQuark quark, + const GList *list) +{ + g_assert(container); + + GList* children = gtk_container_get_children(container); + GList *item = children; + + while (item) { + GtkWidget *widget = GTK_WIDGET(item->data); + const GList *data = list; + + while (data) + { + if (data->data == g_object_get_qdata(G_OBJECT(widget), quark)) + break; + + data = data->next; + } + + if (!data) + gtk_container_remove(container, widget); + + item = item->next; + } + + if (children) + g_list_free(children); +} diff --git a/src/ui.h b/src/ui.h @@ -114,4 +114,30 @@ void ui_avatar_set_icon(HdyAvatar *avatar, GIcon *icon); +/** + * Searches for a specific data set as qdata inside a + * container. + * + * @param container Container + * @param quark Quark + * @param data Data + */ +gboolean +ui_find_qdata_in_container(GtkContainer *container, + GQuark quark, + const gpointer data); + +/** + * Removes children from container which qdata is missing + * inside a list of data. + * + * @param container Container + * @param quark Quark + * @param list List of data + */ +void +ui_clear_container_of_missing_qdata(GtkContainer *container, + GQuark quark, + const GList *list); + #endif /* UI_H_ */ diff --git a/src/ui/discourse.c b/src/ui/discourse.c @@ -34,10 +34,24 @@ #include "../application.h" #include "../ui.h" #include "../util.h" -#include <gnunet/gnunet_chat_lib.h> -#include <gnunet/gnunet_common.h> + #include <string.h> +static const struct GNUNET_ShortHashCode* +get_voice_discourse_id() +{ + static enum GNUNET_GenericReturnValue init = GNUNET_NO; + static struct GNUNET_ShortHashCode id; + + if (GNUNET_YES != init) + { + memset(&id, 0, sizeof(id)); + init = GNUNET_YES; + } + + return &id; +} + static void handle_back_button_click(UNUSED GtkButton *button, gpointer user_data) @@ -101,8 +115,20 @@ handle_microphone_button_click(UNUSED GtkButton *button, } static void -handle_call_button_click(UNUSED GtkButton *button, - gpointer user_data) +_update_call_button(UI_DISCOURSE_Handle *handle) +{ + g_assert(handle); + + if ((handle->voice_discourse) && + (GNUNET_YES ==GNUNET_CHAT_discourse_is_open(handle->voice_discourse))) + gtk_stack_set_visible_child(handle->call_stack, handle->call_stop_button); + else + gtk_stack_set_visible_child(handle->call_stack, handle->call_start_button); +} + +static void +handle_call_start_button_click(UNUSED GtkButton *button, + gpointer user_data) { g_assert(user_data); @@ -111,25 +137,28 @@ handle_call_button_click(UNUSED GtkButton *button, if (!(handle->context)) return; - const gboolean calling = (handle->voice_discourse? TRUE : FALSE); + handle->voice_discourse = GNUNET_CHAT_context_open_discourse( + handle->context, get_voice_discourse_id() + ); - struct GNUNET_ShortHashCode voice_id; - memset(&voice_id, 0, sizeof(voice_id)); + _update_call_button(handle); +} - if (calling) - { - GNUNET_CHAT_discourse_close(handle->voice_discourse); - handle->voice_discourse = NULL; - } - else - handle->voice_discourse = GNUNET_CHAT_context_open_discourse( - handle->context, &voice_id - ); +static void +handle_call_stop_button_click(UNUSED GtkButton *button, + gpointer user_data) +{ + g_assert(user_data); - if (handle->voice_discourse) - gtk_stack_set_visible_child(handle->call_stack, handle->call_stop_icon); - else - gtk_stack_set_visible_child(handle->call_stack, handle->call_start_icon); + UI_DISCOURSE_Handle *handle = (UI_DISCOURSE_Handle*) user_data; + + if ((!(handle->context)) || (!(handle->voice_discourse))) + return; + + GNUNET_CHAT_discourse_close(handle->voice_discourse); + handle->voice_discourse = NULL; + + _update_call_button(handle); } static void @@ -237,17 +266,6 @@ ui_discourse_window_init(MESSENGER_Application *app, gtk_builder_get_object(handle->builder, "speakers_button") ); - handle->call_button = GTK_BUTTON( - gtk_builder_get_object(handle->builder, "call_button") - ); - - g_signal_connect( - handle->call_button, - "clicked", - G_CALLBACK(handle_call_button_click), - handle - ); - handle->microphone_stack = GTK_STACK( gtk_builder_get_object(handle->builder, "microphone_stack") ); @@ -264,12 +282,26 @@ ui_discourse_window_init(MESSENGER_Application *app, gtk_builder_get_object(handle->builder, "call_stack") ); - handle->call_start_icon = GTK_WIDGET( - gtk_builder_get_object(handle->builder, "call_start_icon") + handle->call_start_button = GTK_WIDGET( + gtk_builder_get_object(handle->builder, "call_start_button") + ); + + g_signal_connect( + handle->call_start_button, + "clicked", + G_CALLBACK(handle_call_start_button_click), + handle ); - handle->call_stop_icon = GTK_WIDGET( - gtk_builder_get_object(handle->builder, "call_stop_icon") + handle->call_stop_button = GTK_WIDGET( + gtk_builder_get_object(handle->builder, "call_stop_button") + ); + + g_signal_connect( + handle->call_stop_button, + "clicked", + G_CALLBACK(handle_call_stop_button_click), + handle ); handle->close_details_button = GTK_BUTTON( @@ -297,6 +329,46 @@ ui_discourse_window_init(MESSENGER_Application *app, gtk_widget_show_all(GTK_WIDGET(handle->window)); } +static enum GNUNET_GenericReturnValue +append_discourse_members_to_list(void *cls, + UNUSED const struct GNUNET_CHAT_Discourse *discourse, + struct GNUNET_CHAT_Contact *contact) +{ + g_assert((cls) && (contact)); + + GList **list = (GList**) cls; + *list = g_list_append(*list, contact); + return GNUNET_YES; +} + +static enum GNUNET_GenericReturnValue +append_discourses_members(void *cls, + UNUSED struct GNUNET_CHAT_Context *context, + struct GNUNET_CHAT_Discourse *discourse) +{ + g_assert((cls) && (discourse)); + + GNUNET_CHAT_discourse_iterate_contacts( + discourse, + append_discourse_members_to_list, + cls + ); + + return GNUNET_YES; +} + +static enum GNUNET_GenericReturnValue +append_group_contacts(void *cls, + UNUSED const struct GNUNET_CHAT_Group *group, + struct GNUNET_CHAT_Contact *contact) +{ + g_assert((cls) && (contact)); + + GList **list = (GList**) cls; + *list = g_list_append(*list, contact); + return GNUNET_YES; +} + struct IterateDiscourseClosure { MESSENGER_Application *app; GtkContainer *container; @@ -311,9 +383,12 @@ iterate_ui_discourse_update_discourse_members(void *cls, (struct IterateDiscourseClosure*) cls ); + if (ui_find_qdata_in_container(closure->container, closure->app->quarks.data, contact)) + return GNUNET_YES; + GtkFlowBox *flowbox = GTK_FLOW_BOX(closure->container); - UI_DISCOURSE_PANEL_Handle* panel = ui_discourse_panel_new(closure->app); + UI_DISCOURSE_PANEL_Handle* panel = ui_discourse_panel_new(closure->app); ui_discourse_panel_set_contact(panel, contact); gtk_flow_box_insert(flowbox, panel->panel_box, -1); @@ -354,23 +429,21 @@ _discourse_update_members(UI_DISCOURSE_Handle *handle) { g_assert(handle); - GList* children = gtk_container_get_children( - GTK_CONTAINER(handle->members_flowbox) + GList *list = NULL; + GNUNET_CHAT_context_iterate_discourses( + handle->context, + append_discourses_members, + &list + ); + + ui_clear_container_of_missing_qdata( + GTK_CONTAINER(handle->members_flowbox), + handle->app->quarks.data, + list ); - GList *item = children; - while ((item) && (item->next)) { - GtkWidget *widget = GTK_WIDGET(item->data); - item = item->next; - - gtk_container_remove( - GTK_CONTAINER(handle->members_flowbox), - widget - ); - } - - if (children) - g_list_free(children); + if (list) + g_list_free(list); if (!(handle->context)) return; @@ -395,6 +468,9 @@ iterate_ui_discourse_update_group_contacts(void *cls, (struct IterateDiscourseClosure*) cls ); + if (ui_find_qdata_in_container(closure->container, closure->app->quarks.data, contact)) + return GNUNET_YES; + GtkListBox *listbox = GTK_LIST_BOX(closure->container); UI_ACCOUNT_ENTRY_Handle* entry = ui_account_entry_new(closure->app); @@ -423,23 +499,22 @@ _discourse_update_contacts(UI_DISCOURSE_Handle *handle, { g_assert((handle) && (handle->app)); - GList* children = gtk_container_get_children( - GTK_CONTAINER(handle->contacts_listbox) - ); - - GList *item = children; - while ((item) && (item->next)) { - GtkWidget *widget = GTK_WIDGET(item->data); - item = item->next; - - gtk_container_remove( - GTK_CONTAINER(handle->contacts_listbox), - widget + GList *list = NULL; + if (group) + GNUNET_CHAT_group_iterate_contacts( + group, + append_group_contacts, + &list ); - } + + ui_clear_container_of_missing_qdata( + GTK_CONTAINER(handle->contacts_listbox), + handle->app->quarks.data, + list + ); - if (children) - g_list_free(children); + if (list) + g_list_free(list); if (group) { @@ -460,19 +535,48 @@ _discourse_update_contacts(UI_DISCOURSE_Handle *handle, ); } +static enum GNUNET_GenericReturnValue +iterate_ui_discourse_search_context_discourses(void *cls, + struct GNUNET_CHAT_Context *context, + struct GNUNET_CHAT_Discourse *discourse) +{ + g_assert((cls) && (context) && (discourse)); + + UI_DISCOURSE_Handle *handle = (UI_DISCOURSE_Handle*) cls; + + if (0 == GNUNET_memcmp(GNUNET_CHAT_discourse_get_id(discourse), get_voice_discourse_id())) + handle->voice_discourse = discourse; + + return GNUNET_YES; +} + +static void +_update_discourse_via_context(UI_DISCOURSE_Handle *handle) +{ + g_assert(handle); + + handle->voice_discourse = NULL; + + if (!(handle->context)) + return; + + GNUNET_CHAT_context_iterate_discourses( + handle->context, + iterate_ui_discourse_search_context_discourses, + handle + ); +} + void ui_discourse_window_update(UI_DISCOURSE_Handle *handle, struct GNUNET_CHAT_Context *context) { g_assert(handle); - if (handle->context) - { - // TODO - } - handle->context = context; + _update_discourse_via_context(handle); + _update_call_button(handle); _update_microphone_icon(handle); _discourse_update_members(handle); diff --git a/src/ui/discourse.h b/src/ui/discourse.h @@ -59,15 +59,14 @@ typedef struct UI_DISCOURSE_Handle GtkButton *camera_button; GtkButton *screen_button; GtkVolumeButton *speakers_button; - GtkButton *call_button; GtkStack *microphone_stack; GtkWidget *microphone_on_icon; GtkWidget *microphone_off_icon; GtkStack *call_stack; - GtkWidget *call_start_icon; - GtkWidget *call_stop_icon; + GtkWidget *call_start_button; + GtkWidget *call_stop_button; GtkButton *close_details_button; GtkListBox *contacts_listbox;