messenger-gtk

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

commit 11bef15928c414e2a26e8e4b63104dd684bf60f2
parent 3b7003ea0cf2f53311e268a773f5f3b23d70e23e
Author: Jacki <jacki@thejackimonster.de>
Date:   Sat, 15 Jun 2024 20:26:53 +0200

Implement discourse dialog opening via button from chat details

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

Diffstat:
Mresources/ui/discourse.ui | 108++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------------------
Msrc/application.h | 2++
Msrc/ui/chat.c | 24++++++++++++++++++++++++
Msrc/ui/chat_title.c | 6+-----
Asrc/ui/discourse.c | 307+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/ui/discourse.h | 93+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/ui/meson.build | 1+
7 files changed, 505 insertions(+), 36 deletions(-)

diff --git a/resources/ui/discourse.ui b/resources/ui/discourse.ui @@ -99,7 +99,7 @@ Author: Tobias Frisch <property name="can-focus">False</property> <property name="orientation">vertical</property> <child> - <object class="GtkFlowBox"> + <object class="GtkFlowBox" id="members_flowbox"> <property name="width-request">280</property> <property name="height-request">180</property> <property name="visible">True</property> @@ -205,38 +205,33 @@ Author: Tobias Frisch </packing> </child> <child> - <object class="GtkButton" id="speakers_button"> + <object class="GtkVolumeButton" id="speakers_button"> <property name="visible">True</property> <property name="can-focus">True</property> + <property name="focus-on-click">False</property> <property name="receives-default">True</property> <property name="relief">none</property> - <child> - <object class="GtkStack"> - <property name="visible">True</property> - <property name="can-focus">False</property> - <child> - <object class="GtkImage" id="speakers_on_icon"> - <property name="visible">True</property> - <property name="can-focus">False</property> - <property name="icon-name">audio-volume-high-symbolic</property> - </object> - <packing> - <property name="name">page0</property> - <property name="title" translatable="yes">page0</property> - </packing> - </child> - <child> - <object class="GtkImage" id="speakers_off_icon"> - <property name="visible">True</property> - <property name="can-focus">False</property> - <property name="icon-name">audio-volume-muted-symbolic</property> - </object> - <packing> - <property name="name">page1</property> - <property name="title" translatable="yes">page1</property> - <property name="position">1</property> - </packing> - </child> + <property name="orientation">vertical</property> + <property name="icons">audio-volume-muted-symbolic +audio-volume-high-symbolic +audio-volume-low-symbolic +audio-volume-medium-symbolic</property> + <child internal-child="plus_button"> + <object class="GtkButton"> + <property name="can-focus">True</property> + <property name="receives-default">True</property> + <property name="halign">center</property> + <property name="valign">center</property> + <property name="relief">none</property> + </object> + </child> + <child internal-child="minus_button"> + <object class="GtkButton"> + <property name="can-focus">True</property> + <property name="receives-default">True</property> + <property name="halign">center</property> + <property name="valign">center</property> + <property name="relief">none</property> </object> </child> </object> @@ -285,7 +280,7 @@ Author: Tobias Frisch <packing> <property name="expand">False</property> <property name="fill">True</property> - <property name="position">4</property> + <property name="position">5</property> </packing> </child> </object> @@ -311,6 +306,56 @@ Author: Tobias Frisch <property name="can-focus">False</property> <property name="orientation">vertical</property> <child> + <object class="GtkBox"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="border-width">8</property> + <property name="spacing">8</property> + <child> + <object class="GtkLabel"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="label" translatable="yes">Contacts</property> + <property name="xalign">0</property> + <attributes> + <attribute name="weight" value="bold"/> + </attributes> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkButton" id="close_details_button"> + <property name="visible">True</property> + <property name="can-focus">True</property> + <property name="receives-default">True</property> + <property name="relief">none</property> + <child> + <object class="GtkImage"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="icon-name">window-close</property> + </object> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="pack-type">end</property> + <property name="position">1</property> + </packing> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + <child> <object class="GtkScrolledWindow"> <property name="width-request">240</property> <property name="visible">True</property> @@ -324,6 +369,7 @@ Author: Tobias Frisch <object class="GtkListBox" id="contacts_listbox"> <property name="visible">True</property> <property name="can-focus">False</property> + <property name="selection-mode">none</property> </object> </child> </object> @@ -332,7 +378,7 @@ Author: Tobias Frisch <packing> <property name="expand">True</property> <property name="fill">True</property> - <property name="position">0</property> + <property name="position">1</property> </packing> </child> </object> diff --git a/src/application.h b/src/application.h @@ -41,6 +41,7 @@ #include "ui/contact_info.h" #include "ui/contacts.h" #include "ui/delete_messages.h" +#include "ui/discourse.h" #include "ui/files.h" #include "ui/invite_contact.h" #include "ui/messenger.h" @@ -122,6 +123,7 @@ typedef struct MESSENGER_Application UI_CONTACT_INFO_Handle contact_info; UI_DELETE_MESSAGES_Handle delete_messages; + UI_DISCOURSE_Handle discourse; UI_INVITE_CONTACT_Handle invite_contact; UI_SEND_FILE_Handle send_file; UI_PLAY_MEDIA_Handle play_media; diff --git a/src/ui/chat.c b/src/ui/chat.c @@ -221,6 +221,23 @@ handle_reveal_identity_button_click(GtkButton *button, } static void +handle_discourse_button_click(GtkButton *button, + gpointer user_data) +{ + g_assert((button) && (user_data)); + + UI_CHAT_Handle *handle = (UI_CHAT_Handle*) user_data; + MESSENGER_Application *app = handle->app; + + hdy_flap_set_reveal_flap(handle->flap_chat_details, FALSE); + + ui_discourse_window_init(app, &(app->ui.discourse)); + ui_discourse_window_update(&(app->ui.discourse), handle->context); + + gtk_widget_show(GTK_WIDGET(app->ui.discourse.window)); +} + +static void handle_block_button_click(UNUSED GtkButton *button, gpointer user_data) { @@ -1285,6 +1302,13 @@ ui_chat_new(MESSENGER_Application *app, gtk_builder_get_object(handle->builder, "discourse_button") ); + g_signal_connect( + handle->discourse_button, + "clicked", + G_CALLBACK(handle_discourse_button_click), + handle + ); + handle->block_stack = GTK_STACK( gtk_builder_get_object(handle->builder, "block_stack") ); diff --git a/src/ui/chat_title.c b/src/ui/chat_title.c @@ -56,11 +56,7 @@ _flap_chat_details_reveal_switch(gpointer user_data) UI_CHAT_Handle *handle = (UI_CHAT_Handle*) user_data; HdyFlap* flap = handle->flap_chat_details; - if (TRUE == hdy_flap_get_reveal_flap(flap)) { - hdy_flap_set_reveal_flap(flap, FALSE); - } else { - hdy_flap_set_reveal_flap(flap, TRUE); - } + hdy_flap_set_reveal_flap(flap, !hdy_flap_get_reveal_flap(flap)); gtk_widget_set_sensitive(GTK_WIDGET(handle->messages_listbox), TRUE); return FALSE; diff --git a/src/ui/discourse.c b/src/ui/discourse.c @@ -0,0 +1,307 @@ +/* + This file is part of GNUnet. + Copyright (C) 2024 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 + by the Free Software Foundation, either version 3 of the License, + or (at your option) any later version. + + GNUnet is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + + SPDX-License-Identifier: AGPL3.0-or-later + */ +/* + * @author Tobias Frisch + * @file ui/discourse.c + */ + +#include "discourse.h" + +#include "account_entry.h" + +#include "../application.h" +#include "../ui.h" +#include "../util.h" + +static void +handle_back_button_click(UNUSED GtkButton *button, + gpointer user_data) +{ + g_assert(user_data); + + GtkWindow *window = GTK_WINDOW(user_data); + gtk_window_close(window); +} + +static void +handle_details_button_click(UNUSED GtkButton *button, + gpointer user_data) +{ + g_assert(user_data); + + HdyFlap *flap = HDY_FLAP(user_data); + + hdy_flap_set_reveal_flap(flap, !hdy_flap_get_reveal_flap(flap)); +} + +static void +handle_details_folded(GObject* object, + GParamSpec* pspec, + gpointer user_data) +{ + g_assert((object) && (pspec) && (user_data)); + + HdyFlap* flap = HDY_FLAP(object); + UI_DISCOURSE_Handle *handle = (UI_DISCOURSE_Handle*) user_data; + + const gboolean revealed = hdy_flap_get_reveal_flap(flap); + + gtk_widget_set_sensitive( + GTK_WIDGET(handle->back_button), + !revealed + ); +} + +static void +handle_window_destroy(UNUSED GtkWidget *window, + gpointer user_data) +{ + g_assert(user_data); + + ui_discourse_window_cleanup((UI_DISCOURSE_Handle*) user_data); +} + +void +ui_discourse_window_init(MESSENGER_Application *app, + UI_DISCOURSE_Handle *handle) +{ + g_assert((app) && (handle)); + + handle->app = app; + handle->context = NULL; + + handle->parent = GTK_WINDOW(app->ui.messenger.main_window); + + handle->builder = ui_builder_from_resource( + application_get_resource_path(app, "ui/discourse.ui") + ); + + handle->window = HDY_WINDOW( + gtk_builder_get_object(handle->builder, "discourse_window") + ); + + gtk_window_set_position( + GTK_WINDOW(handle->window), + GTK_WIN_POS_CENTER_ON_PARENT + ); + + gtk_window_set_transient_for( + GTK_WINDOW(handle->window), + handle->parent + ); + + handle->title_bar = HDY_HEADER_BAR( + gtk_builder_get_object(handle->builder, "title_bar") + ); + + handle->back_button = GTK_BUTTON( + gtk_builder_get_object(handle->builder, "back_button") + ); + + g_signal_connect( + handle->back_button, + "clicked", + G_CALLBACK(handle_back_button_click), + handle->window + ); + + handle->details_button = GTK_BUTTON( + gtk_builder_get_object(handle->builder, "details_button") + ); + + handle->details_flap = HDY_FLAP( + gtk_builder_get_object(handle->builder, "details_flap") + ); + + g_signal_connect( + handle->details_button, + "clicked", + G_CALLBACK(handle_details_button_click), + handle->details_flap + ); + + g_signal_connect( + handle->details_flap, + "notify::reveal-flap", + G_CALLBACK(handle_details_folded), + handle + ); + + handle->members_flowbox = GTK_FLOW_BOX( + gtk_builder_get_object(handle->builder, "members_flowbox") + ); + + handle->microphone_button = GTK_BUTTON( + gtk_builder_get_object(handle->builder, "microphone_button") + ); + + handle->camera_button = GTK_BUTTON( + gtk_builder_get_object(handle->builder, "camera_button") + ); + + handle->screen_button = GTK_BUTTON( + gtk_builder_get_object(handle->builder, "screen_button") + ); + + handle->speakers_button = GTK_VOLUME_BUTTON( + gtk_builder_get_object(handle->builder, "speakers_button") + ); + + handle->call_button = GTK_BUTTON( + gtk_builder_get_object(handle->builder, "call_button") + ); + + handle->close_details_button = GTK_BUTTON( + gtk_builder_get_object(handle->builder, "close_details_button") + ); + + g_signal_connect( + handle->close_details_button, + "clicked", + G_CALLBACK(handle_details_button_click), + handle->details_flap + ); + + handle->contacts_listbox = GTK_LIST_BOX( + gtk_builder_get_object(handle->builder, "contacts_listbox") + ); + + g_signal_connect( + handle->window, + "destroy", + G_CALLBACK(handle_window_destroy), + handle + ); + + gtk_widget_show_all(GTK_WIDGET(handle->window)); +} + +struct IterateChatClosure { + MESSENGER_Application *app; + GtkContainer *container; +}; + +static enum GNUNET_GenericReturnValue +iterate_ui_discourse_update_group_contacts(void *cls, + UNUSED const struct GNUNET_CHAT_Group *group, + struct GNUNET_CHAT_Contact *contact) +{ + struct IterateChatClosure *closure = ( + (struct IterateChatClosure*) cls + ); + + GtkListBox *listbox = GTK_LIST_BOX(closure->container); + UI_ACCOUNT_ENTRY_Handle* entry = ui_account_entry_new(closure->app); + + ui_account_entry_set_contact(entry, contact); + + gtk_list_box_prepend(listbox, entry->entry_box); + + GtkListBoxRow *row = GTK_LIST_BOX_ROW( + gtk_widget_get_parent(entry->entry_box) + ); + + g_object_set_qdata(G_OBJECT(row), closure->app->quarks.data, contact); + g_object_set_qdata_full( + G_OBJECT(row), + closure->app->quarks.ui, + entry, + (GDestroyNotify) ui_account_entry_delete + ); + + return GNUNET_YES; +} + +static void +_discourse_update_contacts(UI_DISCOURSE_Handle *handle, + struct GNUNET_CHAT_Group* group) +{ + 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 + ); + } + + if (children) + g_list_free(children); + + if (group) + { + struct IterateChatClosure closure; + closure.app = handle->app; + closure.container = GTK_CONTAINER(handle->contacts_listbox); + + GNUNET_CHAT_group_iterate_contacts( + group, + iterate_ui_discourse_update_group_contacts, + &closure + ); + } + + gtk_widget_set_visible( + GTK_WIDGET(handle->details_button), + group? TRUE : FALSE + ); +} + +void +ui_discourse_window_update(UI_DISCOURSE_Handle *handle, + struct GNUNET_CHAT_Context *context) +{ + g_assert(handle); + + if (handle->context) + { + // TODO + + + } + + handle->context = context; + + struct GNUNET_CHAT_Group* group = GNUNET_CHAT_context_get_group( + handle->context + ); + + _discourse_update_contacts(handle, group); +} + +void +ui_discourse_window_cleanup(UI_DISCOURSE_Handle *handle) +{ + g_assert(handle); + + ui_discourse_window_update(handle, NULL); + + g_object_unref(handle->builder); + + memset(handle, 0, sizeof(*handle)); +} diff --git a/src/ui/discourse.h b/src/ui/discourse.h @@ -0,0 +1,93 @@ +/* + This file is part of GNUnet. + Copyright (C) 2024 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 + by the Free Software Foundation, either version 3 of the License, + or (at your option) any later version. + + GNUnet is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + + SPDX-License-Identifier: AGPL3.0-or-later + */ +/* + * @author Tobias Frisch + * @file ui/discourse.h + */ + +#ifndef UI_DISCOURSE_H_ +#define UI_DISCOURSE_H_ + +#include "messenger.h" + +#include <glib-2.0/glib.h> +#include <gstreamer-1.0/gst/gst.h> +#include <pthread.h> + +typedef struct UI_DISCOURSE_Handle +{ + MESSENGER_Application *app; + struct GNUNET_CHAT_Context *context; + + GtkWindow *parent; + + GtkBuilder *builder; + HdyWindow *window; + + HdyHeaderBar *title_bar; + GtkButton *back_button; + GtkButton *details_button; + + HdyFlap *details_flap; + + GtkFlowBox *members_flowbox; + + GtkButton *microphone_button; + GtkButton *camera_button; + GtkButton *screen_button; + GtkVolumeButton *speakers_button; + GtkButton *call_button; + + GtkButton *close_details_button; + GtkListBox *contacts_listbox; +} UI_DISCOURSE_Handle; + +/** + * Initializes a handle for the discourse window + * of a given messenger application. + * + * @param app Messenger application + * @param handle Discourse window handle + */ +void +ui_discourse_window_init(MESSENGER_Application *app, + UI_DISCOURSE_Handle *handle); + +/** + * Updates a handle for the discourse window with + * a given chat context. + * + * @param handle Discourse window handle + * @param context Chat context + */ +void +ui_discourse_window_update(UI_DISCOURSE_Handle *handle, + struct GNUNET_CHAT_Context *context); + +/** + * Cleans up the allocated resources and resets the + * state of a given discourse window handle. + * + * @param handle Discourse window handle + */ +void +ui_discourse_window_cleanup(UI_DISCOURSE_Handle *handle); + +#endif /* UI_DISCOURSE_H_ */ diff --git a/src/ui/meson.build b/src/ui/meson.build @@ -29,6 +29,7 @@ messenger_gtk_ui_sources = files([ 'contact_info.c', 'contact_info.h', 'contacts.c', 'contacts.h', 'delete_messages.c', 'delete_messages.h', + 'discourse.c', 'discourse.h', 'files.c', 'files.h', 'file_entry.c', 'file_entry.h', 'file_load_entry.c', 'file_load_entry.h',