messenger-gtk

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

commit 8ac4536fbfe1bbcc6da753e3bea2944d0c937323
parent 43019c37a2a12147c401d75327e19e2ac383d6f4
Author: TheJackiMonster <thejackimonster@gmail.com>
Date:   Mon, 14 Mar 2022 14:33:21 +0100

Added interface to record audio messages

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

Diffstat:
Mresources/ui/chat.ui | 212++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------
Msrc/ui/chat.c | 210+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------
Msrc/ui/chat.h | 12++++++++++++
3 files changed, 370 insertions(+), 64 deletions(-)

diff --git a/resources/ui/chat.ui b/resources/ui/chat.ui @@ -311,69 +311,181 @@ Author: Tobias Frisch <property name="border-width">4</property> <property name="spacing">4</property> <child> - <object class="GtkButton" id="attach_file_button"> + <object class="GtkStack" id="send_stack"> <property name="visible">True</property> - <property name="sensitive">False</property> - <property name="can-focus">True</property> - <property name="receives-default">True</property> - <property name="valign">center</property> - <property name="relief">none</property> + <property name="can-focus">False</property> + <property name="transition-type">slide-up</property> <child> - <object class="GtkImage"> + <object class="GtkBox" id="send_text_box"> <property name="visible">True</property> <property name="can-focus">False</property> - <property name="icon-name">mail-attachment-symbolic</property> + <property name="spacing">4</property> + <child> + <object class="GtkButton" id="attach_file_button"> + <property name="visible">True</property> + <property name="sensitive">False</property> + <property name="can-focus">True</property> + <property name="receives-default">True</property> + <property name="valign">center</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">mail-attachment-symbolic</property> + </object> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkTextView" id="send_text_view"> + <property name="width-request">210</property> + <property name="height-request">48</property> + <property name="visible">True</property> + <property name="sensitive">False</property> + <property name="can-focus">True</property> + <property name="valign">end</property> + <property name="editable">False</property> + <property name="wrap-mode">word-char</property> + <property name="left-margin">8</property> + <property name="right-margin">8</property> + <property name="top-margin">8</property> + <property name="bottom-margin">8</property> + <property name="input-hints">GTK_INPUT_HINT_SPELLCHECK | GTK_INPUT_HINT_WORD_COMPLETION | GTK_INPUT_HINT_INHIBIT_OSK | GTK_INPUT_HINT_EMOJI | GTK_INPUT_HINT_NONE</property> + </object> + <packing> + <property name="expand">True</property> + <property name="fill">True</property> + <property name="position">1</property> + </packing> + </child> + <child> + <object class="GtkButton" id="emoji_button"> + <property name="visible">True</property> + <property name="sensitive">False</property> + <property name="can-focus">True</property> + <property name="receives-default">True</property> + <property name="valign">center</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">face-smile-symbolic</property> + </object> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">2</property> + </packing> + </child> </object> + <packing> + <property name="name">page_send_text</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="GtkTextView" id="send_text_view"> - <property name="width-request">210</property> - <property name="height-request">48</property> - <property name="visible">True</property> - <property name="sensitive">False</property> - <property name="can-focus">True</property> - <property name="valign">end</property> - <property name="editable">False</property> - <property name="wrap-mode">word-char</property> - <property name="left-margin">8</property> - <property name="right-margin">8</property> - <property name="top-margin">8</property> - <property name="bottom-margin">8</property> - <property name="input-hints">GTK_INPUT_HINT_SPELLCHECK | GTK_INPUT_HINT_WORD_COMPLETION | GTK_INPUT_HINT_INHIBIT_OSK | GTK_INPUT_HINT_EMOJI | GTK_INPUT_HINT_NONE</property> - </object> - <packing> - <property name="expand">True</property> - <property name="fill">True</property> - <property name="position">1</property> - </packing> - </child> - <child> - <object class="GtkButton" id="emoji_button"> - <property name="visible">True</property> - <property name="sensitive">False</property> - <property name="can-focus">True</property> - <property name="receives-default">True</property> - <property name="valign">center</property> - <property name="relief">none</property> <child> - <object class="GtkImage"> + <object class="GtkBox" id="send_recording_box"> <property name="visible">True</property> <property name="can-focus">False</property> - <property name="icon-name">face-smile-symbolic</property> + <property name="spacing">4</property> + <child> + <object class="GtkButton" id="recording_close_button"> + <property name="visible">True</property> + <property name="can-focus">True</property> + <property name="receives-default">True</property> + <property name="valign">center</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-symbolic</property> + </object> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkButton" id="recording_play_button"> + <property name="visible">True</property> + <property name="can-focus">True</property> + <property name="receives-default">True</property> + <property name="valign">center</property> + <property name="relief">none</property> + <child> + <object class="GtkImage" id="play_pause_symbol"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="icon-name">media-playback-start-symbolic</property> + </object> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">1</property> + </packing> + </child> + <child> + <object class="GtkBox"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="valign">center</property> + <property name="orientation">vertical</property> + <property name="spacing">2</property> + <child> + <object class="GtkLabel" id="recording_label"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="label" translatable="yes">00:00</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkProgressBar" id="recording_progress_bar"> + <property name="visible">True</property> + <property name="can-focus">False</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">1</property> + </packing> + </child> + </object> + <packing> + <property name="expand">True</property> + <property name="fill">True</property> + <property name="position">2</property> + </packing> + </child> </object> + <packing> + <property name="name">page_send_recording</property> + <property name="position">1</property> + </packing> </child> </object> <packing> - <property name="expand">False</property> + <property name="expand">True</property> <property name="fill">True</property> - <property name="position">2</property> + <property name="position">0</property> </packing> </child> <child> @@ -395,7 +507,7 @@ Author: Tobias Frisch <packing> <property name="expand">False</property> <property name="fill">True</property> - <property name="position">3</property> + <property name="position">1</property> </packing> </child> </object> diff --git a/src/ui/chat.c b/src/ui/chat.c @@ -328,11 +328,10 @@ close_dialog: } static void -handle_send_text_buffer_changed(GtkTextBuffer *buffer, - gpointer user_data) +_update_send_record_symbol(GtkTextBuffer *buffer, + GtkImage *symbol, + gboolean picker_revealed) { - GtkImage *symbol = GTK_IMAGE(user_data); - GtkTextIter start, end; gtk_text_buffer_get_start_iter(buffer, &start); gtk_text_buffer_get_end_iter(buffer, &end); @@ -341,13 +340,26 @@ handle_send_text_buffer_changed(GtkTextBuffer *buffer, gtk_image_set_from_icon_name( symbol, - 0 < strlen(text)? + (0 < strlen(text)) || (picker_revealed)? "mail-send-symbolic" : "audio-input-microphone-symbolic", GTK_ICON_SIZE_BUTTON ); } +static void +handle_send_text_buffer_changed(GtkTextBuffer *buffer, + gpointer user_data) +{ + UI_CHAT_Handle *handle = (UI_CHAT_Handle*) user_data; + + _update_send_record_symbol( + buffer, + handle->send_record_symbol, + gtk_revealer_get_child_revealed(handle->picker_revealer) + ); +} + static gboolean _send_text_from_view(MESSENGER_Application *app, GtkTextView *text_view) @@ -375,19 +387,112 @@ _send_text_from_view(MESSENGER_Application *app, } static void +_drop_any_recording(UI_CHAT_Handle *handle) +{ + _update_send_record_symbol( + gtk_text_view_get_buffer(handle->send_text_view), + handle->send_record_symbol, + FALSE + ); + + gtk_stack_set_visible_child(handle->send_stack, handle->send_text_box); + + handle->recorded = FALSE; +} + +static void handle_send_record_button_click(GtkButton *button, gpointer user_data) { MESSENGER_Application *app = (MESSENGER_Application*) user_data; - GtkTextView *text_view = GTK_TEXT_VIEW( - g_object_get_qdata(G_OBJECT(button), app->quarks.widget) + UI_CHAT_Handle *handle = (UI_CHAT_Handle*) ( + g_object_get_qdata(G_OBJECT(button), app->quarks.ui) ); - if (!_send_text_from_view(app, text_view)) + if ((handle->recorded) && + (!gtk_revealer_get_child_revealed(handle->picker_revealer)) && + (gtk_stack_get_visible_child(handle->send_stack) == + handle->send_recording_box)) { - // TODO: record audio and attach as file? + // TODO: send audio as file! + + _drop_any_recording(handle); + return; } + + GtkTextView *text_view = GTK_TEXT_VIEW( + g_object_get_qdata(G_OBJECT(button), app->quarks.widget) + ); + + _send_text_from_view(app, text_view); +} + +static gboolean +handle_send_record_button_pressed(GtkWidget *widget, + UNUSED GdkEvent *event, + gpointer user_data) +{ + MESSENGER_Application *app = (MESSENGER_Application*) user_data; + + UI_CHAT_Handle *handle = (UI_CHAT_Handle*) ( + g_object_get_qdata(G_OBJECT(widget), app->quarks.ui) + ); + + if ((handle->recorded) || + (gtk_revealer_get_child_revealed(handle->picker_revealer)) || + (handle->send_text_box != gtk_stack_get_visible_child(handle->send_stack))) + return FALSE; + + gtk_image_set_from_icon_name( + handle->play_pause_symbol, + "media-playback-start-symbolic", + GTK_ICON_SIZE_BUTTON + ); + + gtk_image_set_from_icon_name( + handle->send_record_symbol, + "media-record-symbolic", + GTK_ICON_SIZE_BUTTON + ); + + gtk_widget_set_sensitive(GTK_WIDGET(handle->recording_play_button), FALSE); + + gtk_stack_set_visible_child(handle->send_stack, handle->send_recording_box); + + return TRUE; +} + +static gboolean +handle_send_record_button_released(GtkWidget *widget, + UNUSED GdkEvent *event, + gpointer user_data) +{ + MESSENGER_Application *app = (MESSENGER_Application*) user_data; + + UI_CHAT_Handle *handle = (UI_CHAT_Handle*) ( + g_object_get_qdata(G_OBJECT(widget), app->quarks.ui) + ); + + if ((handle->recorded) || + (gtk_revealer_get_child_revealed(handle->picker_revealer)) || + (handle->send_recording_box != gtk_stack_get_visible_child( + handle->send_stack))) + return FALSE; + + gtk_widget_set_sensitive(GTK_WIDGET(handle->recording_play_button), TRUE); + + gtk_revealer_set_reveal_child(handle->picker_revealer, FALSE); + + handle->recorded = TRUE; + + gtk_image_set_from_icon_name( + handle->send_record_symbol, + "mail-send-symbolic", + GTK_ICON_SIZE_BUTTON + ); + + return TRUE; } static gboolean @@ -407,13 +512,29 @@ handle_send_text_key_press (GtkWidget *widget, } static void +handle_recording_close_button_click(UNUSED GtkButton *button, + gpointer user_data) +{ + UI_CHAT_Handle *handle = (UI_CHAT_Handle*) user_data; + + _drop_any_recording(handle); +} + +static void handle_picker_button_click(UNUSED GtkButton *button, gpointer user_data) { - GtkRevealer *revealer = GTK_REVEALER(user_data); - gboolean reveal = !gtk_revealer_get_child_revealed(revealer); + UI_CHAT_Handle *handle = (UI_CHAT_Handle*) user_data; + + gboolean reveal = !gtk_revealer_get_child_revealed(handle->picker_revealer); - gtk_revealer_set_reveal_child(revealer, reveal); + gtk_revealer_set_reveal_child(handle->picker_revealer, reveal); + + _update_send_record_symbol( + gtk_text_view_get_buffer(handle->send_text_view), + handle->send_record_symbol, + reveal + ); } UI_CHAT_Handle* @@ -424,6 +545,8 @@ ui_chat_new(MESSENGER_Application *app) UI_CHAT_Handle *handle = g_malloc(sizeof(UI_CHAT_Handle)); UI_MESSENGER_Handle *messenger = &(app->ui.messenger); + handle->recorded = FALSE; + handle->app = app; handle->messages = NULL; @@ -605,6 +728,18 @@ ui_chat_new(MESSENGER_Application *app) handle ); + handle->send_stack = GTK_STACK( + gtk_builder_get_object(handle->builder, "send_stack") + ); + + handle->send_text_box = GTK_WIDGET( + gtk_builder_get_object(handle->builder, "send_text_box") + ); + + handle->send_recording_box = GTK_WIDGET( + gtk_builder_get_object(handle->builder, "send_recording_box") + ); + handle->attach_file_button = GTK_BUTTON( gtk_builder_get_object(handle->builder, "attach_file_button") ); @@ -640,7 +775,7 @@ ui_chat_new(MESSENGER_Application *app) send_text_buffer, "changed", G_CALLBACK(handle_send_text_buffer_changed), - handle->send_record_symbol + handle ); g_signal_connect( @@ -651,6 +786,20 @@ ui_chat_new(MESSENGER_Application *app) ); g_signal_connect( + handle->send_record_button, + "button-press-event", + G_CALLBACK(handle_send_record_button_pressed), + app + ); + + g_signal_connect( + handle->send_record_button, + "button-release-event", + G_CALLBACK(handle_send_record_button_released), + app + ); + + g_signal_connect( handle->send_text_view, "key-press-event", G_CALLBACK(handle_send_text_key_press), @@ -675,6 +824,39 @@ ui_chat_new(MESSENGER_Application *app) handle->send_text_view ); + g_object_set_qdata( + G_OBJECT(handle->send_record_button), + app->quarks.ui, + handle + ); + + handle->recording_close_button = GTK_BUTTON( + gtk_builder_get_object(handle->builder, "recording_close_button") + ); + + g_signal_connect( + handle->recording_close_button, + "clicked", + G_CALLBACK(handle_recording_close_button_click), + handle + ); + + handle->recording_play_button = GTK_BUTTON( + gtk_builder_get_object(handle->builder, "recording_play_button") + ); + + handle->play_pause_symbol = GTK_IMAGE( + gtk_builder_get_object(handle->builder, "play_pause_symbol") + ); + + handle->recording_label = GTK_LABEL( + gtk_builder_get_object(handle->builder, "recording_label") + ); + + handle->recording_progress_bar = GTK_PROGRESS_BAR( + gtk_builder_get_object(handle->builder, "recording_progress_bar") + ); + handle->picker_revealer = GTK_REVEALER( gtk_builder_get_object(handle->builder, "picker_revealer") ); @@ -690,7 +872,7 @@ ui_chat_new(MESSENGER_Application *app) handle->emoji_button, "clicked", G_CALLBACK(handle_picker_button_click), - handle->picker_revealer + handle ); return handle; diff --git a/src/ui/chat.h b/src/ui/chat.h @@ -38,6 +38,8 @@ typedef struct UI_FILE_LOAD_ENTRY_Handle UI_FILE_LOAD_ENTRY_Handle; typedef struct UI_CHAT_Handle { + gboolean recorded; + MESSENGER_Application *app; GList *messages; @@ -79,12 +81,22 @@ typedef struct UI_CHAT_Handle GtkListBox *chat_files_listbox; GtkListBox *messages_listbox; + GtkStack *send_stack; + GtkWidget *send_text_box; + GtkWidget *send_recording_box; + GtkButton *attach_file_button; GtkTextView *send_text_view; GtkButton *emoji_button; GtkButton *send_record_button; GtkImage *send_record_symbol; + GtkButton *recording_close_button; + GtkButton *recording_play_button; + GtkImage *play_pause_symbol; + GtkLabel *recording_label; + GtkProgressBar *recording_progress_bar; + GtkRevealer *picker_revealer; UI_PICKER_Handle *picker;