aboutsummaryrefslogtreecommitdiff
path: root/main/src
diff options
context:
space:
mode:
authorfiaxh <git@lightrise.org>2022-02-14 14:55:59 +0100
committerfiaxh <git@lightrise.org>2022-07-27 20:34:20 +0200
commit7e7dcedaf31ee35499875491c9f569c575d28435 (patch)
tree0c5fee2b28baf320775fbc92b3c252e97d9d054f /main/src
parentf25bfb00969a7e09996da2d5500e6718f4cc0148 (diff)
downloaddino-7e7dcedaf31ee35499875491c9f569c575d28435.tar.gz
dino-7e7dcedaf31ee35499875491c9f569c575d28435.zip
Port from GTK3 to GTK4
Diffstat (limited to 'main/src')
-rw-r--r--main/src/emojichooser.c823
-rw-r--r--main/src/emojichooser.h36
-rw-r--r--main/src/main.vala2
-rw-r--r--main/src/ui/add_conversation/add_conference_dialog.vala71
-rw-r--r--main/src/ui/add_conversation/add_groupchat_dialog.vala11
-rw-r--r--main/src/ui/add_conversation/conference_details_fragment.vala48
-rw-r--r--main/src/ui/add_conversation/conference_list.vala37
-rw-r--r--main/src/ui/add_conversation/list_row.vala21
-rw-r--r--main/src/ui/add_conversation/roster_list.vala40
-rw-r--r--main/src/ui/add_conversation/select_contact_dialog.vala20
-rw-r--r--main/src/ui/add_conversation/select_jid_fragment.vala74
-rw-r--r--main/src/ui/application.vala24
-rw-r--r--main/src/ui/avatar_image.vala23
-rw-r--r--main/src/ui/call_window/audio_settings_popover.vala56
-rw-r--r--main/src/ui/call_window/call_bottom_bar.vala64
-rw-r--r--main/src/ui/call_window/call_connection_details_window.vala8
-rw-r--r--main/src/ui/call_window/call_encryption_button.vala50
-rw-r--r--main/src/ui/call_window/call_window.vala58
-rw-r--r--main/src/ui/call_window/call_window_controller.vala43
-rw-r--r--main/src/ui/call_window/participant_widget.vala71
-rw-r--r--main/src/ui/call_window/video_settings_popover.vala30
-rw-r--r--main/src/ui/chat_input/chat_input_controller.vala15
-rw-r--r--main/src/ui/chat_input/chat_text_view.vala55
-rw-r--r--main/src/ui/chat_input/edit_history.vala77
-rw-r--r--main/src/ui/chat_input/encryption_button.vala43
-rw-r--r--main/src/ui/chat_input/occupants_tab_completer.vala14
-rw-r--r--main/src/ui/chat_input/smiley_converter.vala8
-rw-r--r--main/src/ui/chat_input/view.vala30
-rw-r--r--main/src/ui/contact_details/blocking_provider.vala2
-rw-r--r--main/src/ui/contact_details/dialog.vala43
-rw-r--r--main/src/ui/contact_details/muc_config_form_provider.vala2
-rw-r--r--main/src/ui/contact_details/permissions_provider.vala4
-rw-r--r--main/src/ui/contact_details/settings_provider.vala2
-rw-r--r--main/src/ui/conversation_content/conversation_item_factory.vala36
-rw-r--r--main/src/ui/conversation_content_view/call_widget.vala37
-rw-r--r--main/src/ui/conversation_content_view/chat_state_populator.vala6
-rw-r--r--main/src/ui/conversation_content_view/conversation_item_skeleton.vala265
-rw-r--r--main/src/ui/conversation_content_view/conversation_view.vala133
-rw-r--r--main/src/ui/conversation_content_view/date_separator_populator.vala8
-rw-r--r--main/src/ui/conversation_content_view/file_default_widget.vala78
-rw-r--r--main/src/ui/conversation_content_view/file_image_widget.vala25
-rw-r--r--main/src/ui/conversation_content_view/file_widget.vala45
-rw-r--r--main/src/ui/conversation_content_view/message_item_widget.vala229
-rw-r--r--main/src/ui/conversation_content_view/message_widget.vala217
-rw-r--r--main/src/ui/conversation_content_view/subscription_notification.vala10
-rw-r--r--main/src/ui/conversation_list/conversation_list_item_factory.vala245
-rw-r--r--main/src/ui/conversation_list/conversation_list_model.vala141
-rw-r--r--main/src/ui/conversation_list/conversation_list_row.vala41
-rw-r--r--main/src/ui/conversation_list_titlebar.vala18
-rw-r--r--main/src/ui/conversation_selector/conversation_selector.vala54
-rw-r--r--main/src/ui/conversation_selector/conversation_selector_row.vala6
-rw-r--r--main/src/ui/conversation_titlebar/call_entry.vala69
-rw-r--r--main/src/ui/conversation_titlebar/conversation_titlebar.vala69
-rw-r--r--main/src/ui/conversation_titlebar/menu_entry.vala44
-rw-r--r--main/src/ui/conversation_titlebar/occupants_entry.vala42
-rw-r--r--main/src/ui/conversation_titlebar/search_entry.vala20
-rw-r--r--main/src/ui/conversation_view.vala37
-rw-r--r--main/src/ui/conversation_view_controller.vala184
-rw-r--r--main/src/ui/file_send_overlay.vala46
-rw-r--r--main/src/ui/global_search.vala140
-rw-r--r--main/src/ui/main_window.vala107
-rw-r--r--main/src/ui/main_window_controller.vala97
-rw-r--r--main/src/ui/manage_accounts/add_account_dialog.vala65
-rw-r--r--main/src/ui/manage_accounts/dialog.vala48
-rw-r--r--main/src/ui/occupant_menu/list.vala53
-rw-r--r--main/src/ui/occupant_menu/list_row.vala19
-rw-r--r--main/src/ui/occupant_menu/view.vala44
-rw-r--r--main/src/ui/util/data_forms.vala10
-rw-r--r--main/src/ui/util/helper.vala45
-rw-r--r--main/src/ui/util/label_hybrid.vala66
-rw-r--r--main/src/ui/util/scaling_image.vala89
-rw-r--r--main/src/ui/util/size_request_box.vala6
-rw-r--r--main/src/ui/util/sizing_bin.vala38
73 files changed, 2313 insertions, 2524 deletions
diff --git a/main/src/emojichooser.c b/main/src/emojichooser.c
deleted file mode 100644
index ba401ddc..00000000
--- a/main/src/emojichooser.c
+++ /dev/null
@@ -1,823 +0,0 @@
-/* gtkemojichooser.c: An Emoji chooser widget
- * Copyright 2017, Red Hat, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library 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
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include "emojichooser.h"
-
-#define BOX_SPACE 6
-
-typedef struct {
- GtkWidget *box;
- GtkWidget *heading;
- GtkWidget *button;
- const char *first;
- gunichar label;
- gboolean empty;
-} EmojiSection;
-
-struct _DinoEmojiChooser
-{
- GtkPopover parent_instance;
-
- GtkWidget *search_entry;
- GtkWidget *stack;
- GtkWidget *scrolled_window;
-
- int emoji_max_width;
-
- EmojiSection recent;
- EmojiSection people;
- EmojiSection body;
- EmojiSection nature;
- EmojiSection food;
- EmojiSection travel;
- EmojiSection activities;
- EmojiSection objects;
- EmojiSection symbols;
- EmojiSection flags;
-
- GtkGesture *recent_long_press;
- GtkGesture *recent_multi_press;
- GtkGesture *people_long_press;
- GtkGesture *people_multi_press;
- GtkGesture *body_long_press;
- GtkGesture *body_multi_press;
-
- GVariant *data;
- GtkWidget *box;
- GVariantIter *iter;
- guint populate_idle;
-
- GSettings *settings;
-};
-
-struct _DinoEmojiChooserClass {
- GtkPopoverClass parent_class;
- gboolean (* popover_button_release_event) (GtkWidget *widget,
- GdkEventButton *event);
-};
-
-enum {
- EMOJI_PICKED,
- LAST_SIGNAL
-};
-
-static int signals[LAST_SIGNAL];
-
-G_DEFINE_TYPE (DinoEmojiChooser, dino_emoji_chooser, GTK_TYPE_POPOVER)
-
-static void
-dino_emoji_chooser_finalize (GObject *object)
-{
- DinoEmojiChooser *chooser = DINO_EMOJI_CHOOSER (object);
-
- if (chooser->populate_idle)
- g_source_remove (chooser->populate_idle);
-
- g_variant_unref (chooser->data);
- g_object_unref (chooser->settings);
-
- g_clear_object (&chooser->recent_long_press);
- g_clear_object (&chooser->recent_multi_press);
- g_clear_object (&chooser->people_long_press);
- g_clear_object (&chooser->people_multi_press);
- g_clear_object (&chooser->body_long_press);
- g_clear_object (&chooser->body_multi_press);
-
- G_OBJECT_CLASS (dino_emoji_chooser_parent_class)->finalize (object);
-}
-
-static void
-scroll_to_section (GtkButton *button,
- gpointer data)
-{
- EmojiSection *section = data;
- DinoEmojiChooser *chooser;
- GtkAdjustment *adj;
- GtkAllocation alloc = { 0, 0, 0, 0 };
-
- chooser = DINO_EMOJI_CHOOSER (gtk_widget_get_ancestor (GTK_WIDGET (button), GTK_TYPE_EMOJI_CHOOSER));
-
- adj = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (chooser->scrolled_window));
-
- if (section->heading)
- gtk_widget_get_allocation (section->heading, &alloc);
-
- gtk_adjustment_set_value (adj, alloc.y - BOX_SPACE);
-}
-
-static void
-add_emoji (GtkWidget *box,
- gboolean prepend,
- GVariant *item,
- gunichar modifier,
- DinoEmojiChooser *chooser);
-
-#define MAX_RECENT (7*3)
-
-static void
-populate_recent_section (DinoEmojiChooser *chooser)
-{
- GVariant *variant;
- GVariant *item;
- GVariantIter iter;
- gboolean empty = FALSE;
-
- variant = g_settings_get_value (chooser->settings, "recent-emoji");
- g_variant_iter_init (&iter, variant);
- while ((item = g_variant_iter_next_value (&iter)))
- {
- GVariant *emoji_data;
- gunichar modifier;
-
- emoji_data = g_variant_get_child_value (item, 0);
- g_variant_get_child (item, 1, "u", &modifier);
- add_emoji (chooser->recent.box, FALSE, emoji_data, modifier, chooser);
- g_variant_unref (emoji_data);
- g_variant_unref (item);
- empty = FALSE;
- }
-
- if (!empty)
- {
- gtk_widget_show (chooser->recent.box);
- gtk_widget_set_sensitive (chooser->recent.button, TRUE);
- }
- g_variant_unref (variant);
-}
-
-static void
-add_recent_item (DinoEmojiChooser *chooser,
- GVariant *item,
- gunichar modifier)
-{
- GList *children, *l;
- int i;
- GVariantBuilder builder;
-
- g_variant_ref (item);
-
- g_variant_builder_init (&builder, G_VARIANT_TYPE ("a((auss)u)"));
- g_variant_builder_add (&builder, "(@(auss)u)", item, modifier);
-
- children = gtk_container_get_children (GTK_CONTAINER (chooser->recent.box));
- for (l = children, i = 1; l; l = l->next, i++)
- {
- GVariant *item2 = g_object_get_data (G_OBJECT (l->data), "emoji-data");
- gunichar modifier2 = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (l->data), "modifier"));
-
- if (modifier == modifier2 && g_variant_equal (item, item2))
- {
- gtk_widget_destroy (GTK_WIDGET (l->data));
- i--;
- continue;
- }
- if (i >= MAX_RECENT)
- {
- gtk_widget_destroy (GTK_WIDGET (l->data));
- continue;
- }
-
- g_variant_builder_add (&builder, "(@(auss)u)", item2, modifier2);
- }
- g_list_free (children);
-
- add_emoji (chooser->recent.box, TRUE, item, modifier, chooser);
-
- /* Enable recent */
- gtk_widget_show (chooser->recent.box);
- gtk_widget_set_sensitive (chooser->recent.button, TRUE);
-
- g_settings_set_value (chooser->settings, "recent-emoji", g_variant_builder_end (&builder));
-
- g_variant_unref (item);
-}
-
-static void
-emoji_activated (GtkFlowBox *box,
- GtkFlowBoxChild *child,
- gpointer data)
-{
- DinoEmojiChooser *chooser = data;
- char *text;
- GtkWidget *ebox;
- GtkWidget *label;
- GVariant *item;
- gunichar modifier;
-
- gtk_popover_popdown (GTK_POPOVER (chooser));
-
- ebox = gtk_bin_get_child (GTK_BIN (child));
- label = gtk_bin_get_child (GTK_BIN (ebox));
- text = g_strdup (gtk_label_get_label (GTK_LABEL (label)));
-
- item = (GVariant*) g_object_get_data (G_OBJECT (child), "emoji-data");
- modifier = (gunichar) GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (child), "modifier"));
- add_recent_item (chooser, item, modifier);
-
- g_signal_emit (data, signals[EMOJI_PICKED], 0, text);
- g_free (text);
-}
-
-static gboolean
-has_variations (GVariant *emoji_data)
-{
- GVariant *codes;
- gsize i;
- gboolean has_variations;
-
- has_variations = FALSE;
- codes = g_variant_get_child_value (emoji_data, 0);
- for (i = 0; i < g_variant_n_children (codes); i++)
- {
- gunichar code;
- g_variant_get_child (codes, i, "u", &code);
- if (code == 0)
- {
- has_variations = TRUE;
- break;
- }
- }
- g_variant_unref (codes);
-
- return has_variations;
-}
-
-static void
-show_variations (DinoEmojiChooser *chooser,
- GtkWidget *child)
-{
- GtkWidget *popover;
- GtkWidget *view;
- GtkWidget *box;
- GVariant *emoji_data;
- GtkWidget *parent_popover;
- gunichar modifier;
-
- if (!child)
- return;
-
- emoji_data = (GVariant*) g_object_get_data (G_OBJECT (child), "emoji-data");
- if (!emoji_data)
- return;
-
- if (!has_variations (emoji_data))
- return;
-
- parent_popover = gtk_widget_get_ancestor (child, GTK_TYPE_POPOVER);
- popover = gtk_popover_new (child);
- view = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
- gtk_style_context_add_class (gtk_widget_get_style_context (view), "view");
- box = gtk_flow_box_new ();
- gtk_flow_box_set_homogeneous (GTK_FLOW_BOX (box), TRUE);
- gtk_flow_box_set_min_children_per_line (GTK_FLOW_BOX (box), 6);
- gtk_flow_box_set_max_children_per_line (GTK_FLOW_BOX (box), 6);
- gtk_flow_box_set_activate_on_single_click (GTK_FLOW_BOX (box), TRUE);
- gtk_flow_box_set_selection_mode (GTK_FLOW_BOX (box), GTK_SELECTION_NONE);
- gtk_container_add (GTK_CONTAINER (popover), view);
- gtk_container_add (GTK_CONTAINER (view), box);
-
- g_signal_connect (box, "child-activated", G_CALLBACK (emoji_activated), parent_popover);
-
- add_emoji (box, FALSE, emoji_data, 0, chooser);
- for (modifier = 0x1f3fb; modifier <= 0x1f3ff; modifier++)
- add_emoji (box, FALSE, emoji_data, modifier, chooser);
-
- gtk_widget_show_all (view);
- gtk_popover_popup (GTK_POPOVER (popover));
-}
-
-static void
-update_hover (GtkWidget *widget,
- GdkEvent *event,
- gpointer data)
-{
- if (event->type == GDK_ENTER_NOTIFY)
- gtk_widget_set_state_flags (widget, GTK_STATE_FLAG_PRELIGHT, FALSE);
- else
- gtk_widget_unset_state_flags (widget, GTK_STATE_FLAG_PRELIGHT);
-}
-
-static void
-long_pressed_cb (GtkGesture *gesture,
- double x,
- double y,
- gpointer data)
-{
- DinoEmojiChooser *chooser = data;
- GtkWidget *box;
- GtkWidget *child;
-
- box = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (gesture));
- child = GTK_WIDGET (gtk_flow_box_get_child_at_pos (GTK_FLOW_BOX (box), x, y));
- show_variations (chooser, child);
-}
-
-static void
-pressed_cb (GtkGesture *gesture,
- int n_press,
- double x,
- double y,
- gpointer data)
-{
- DinoEmojiChooser *chooser = data;
- GtkWidget *box;
- GtkWidget *child;
-
- box = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (gesture));
- child = GTK_WIDGET (gtk_flow_box_get_child_at_pos (GTK_FLOW_BOX (box), x, y));
- show_variations (chooser, child);
-}
-
-static gboolean
-popup_menu (GtkWidget *widget,
- gpointer data)
-{
- DinoEmojiChooser *chooser = data;
-
- show_variations (chooser, widget);
- return TRUE;
-}
-
-static void
-add_emoji (GtkWidget *box,
- gboolean prepend,
- GVariant *item,
- gunichar modifier,
- DinoEmojiChooser *chooser)
-{
- GtkWidget *child;
- GtkWidget *ebox;
- GtkWidget *label;
- PangoAttrList *attrs;
- GVariant *codes;
- char text[64];
- char *p = text;
- gsize i;
- PangoLayout *layout;
- PangoRectangle rect;
-
- codes = g_variant_get_child_value (item, 0);
- for (i = 0; i < g_variant_n_children (codes); i++)
- {
- gunichar code;
-
- g_variant_get_child (codes, i, "u", &code);
- if (code == 0)
- code = modifier;
- if (code != 0)
- p += g_unichar_to_utf8 (code, p);
- }
- g_variant_unref (codes);
- p += g_unichar_to_utf8 (0xFE0F, p); /* U+FE0F is the Emoji variation selector */
- p[0] = 0;
-
- label = gtk_label_new (text);
- attrs = pango_attr_list_new ();
- pango_attr_list_insert (attrs, pango_attr_scale_new (PANGO_SCALE_X_LARGE));
- gtk_label_set_attributes (GTK_LABEL (label), attrs);
- pango_attr_list_unref (attrs);
-
- layout = gtk_label_get_layout (GTK_LABEL (label));
- pango_layout_get_extents (layout, &rect, NULL);
-
- /* Check for fallback rendering that generates too wide items */
- if (pango_layout_get_unknown_glyphs_count (layout) > 0 ||
- rect.width >= 1.5 * chooser->emoji_max_width)
- {
- gtk_widget_destroy (label);
- return;
- }
-
- child = gtk_flow_box_child_new ();
- gtk_style_context_add_class (gtk_widget_get_style_context (child), "emoji");
- g_object_set_data_full (G_OBJECT (child), "emoji-data",
- g_variant_ref (item),
- (GDestroyNotify)g_variant_unref);
- if (modifier != 0)
- g_object_set_data (G_OBJECT (child), "modifier", GUINT_TO_POINTER (modifier));
-
- ebox = gtk_event_box_new ();
- gtk_widget_add_events (ebox, GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK);
- g_signal_connect (ebox, "enter-notify-event", G_CALLBACK (update_hover), FALSE);
- g_signal_connect (ebox, "leave-notify-event", G_CALLBACK (update_hover), FALSE);
- gtk_container_add (GTK_CONTAINER (child), ebox);
- gtk_container_add (GTK_CONTAINER (ebox), label);
- gtk_widget_show_all (child);
-
- if (chooser)
- g_signal_connect (child, "popup-menu", G_CALLBACK (popup_menu), chooser);
-
- gtk_flow_box_insert (GTK_FLOW_BOX (box), child, prepend ? 0 : -1);
-}
-
-static gboolean
-populate_emoji_chooser (gpointer data)
-{
- DinoEmojiChooser *chooser = data;
- GBytes *bytes = NULL;
- GVariant *item;
- guint64 start, now;
-
- start = g_get_monotonic_time ();
-
- if (!chooser->data)
- {
- bytes = g_resources_lookup_data ("/org/gtk/libgtk/emoji/emoji.data", 0, NULL);
- if (bytes == NULL) {
- bytes = g_resources_lookup_data ("/org/gtk/libgtk/emoji/en.data", 0, NULL);
- }
- chooser->data = g_variant_ref_sink (g_variant_new_from_bytes (G_VARIANT_TYPE ("a(auss)"), bytes, TRUE));
- }
-
- if (!chooser->iter)
- {
- chooser->iter = g_variant_iter_new (chooser->data);
- chooser->box = chooser->people.box;
- }
- while ((item = g_variant_iter_next_value (chooser->iter)))
- {
- const char *name;
-
- g_variant_get_child (item, 1, "&s", &name);
-
- if (g_strcmp0 (name, chooser->body.first) == 0)
- chooser->box = chooser->body.box;
- else if (g_strcmp0 (name, chooser->nature.first) == 0)
- chooser->box = chooser->nature.box;
- else if (g_strcmp0 (name, chooser->food.first) == 0)
- chooser->box = chooser->food.box;
- else if (g_strcmp0 (name, chooser->travel.first) == 0)
- chooser->box = chooser->travel.box;
- else if (g_strcmp0 (name, chooser->activities.first) == 0)
- chooser->box = chooser->activities.box;
- else if (g_strcmp0 (name, chooser->objects.first) == 0)
- chooser->box = chooser->objects.box;
- else if (g_strcmp0 (name, chooser->symbols.first) == 0)
- chooser->box = chooser->symbols.box;
- else if (g_strcmp0 (name, chooser->flags.first) == 0)
- chooser->box = chooser->flags.box;
-
- add_emoji (chooser->box, FALSE, item, 0, chooser);
- g_variant_unref (item);
-
- now = g_get_monotonic_time ();
- if (now > start + 8000)
- return G_SOURCE_CONTINUE;
- }
-
- /* We scroll to the top on show, so check the right button for the 1st time */
- gtk_widget_set_state_flags (chooser->recent.button, GTK_STATE_FLAG_CHECKED, FALSE);
-
- g_variant_iter_free (chooser->iter);
- chooser->iter = NULL;
- chooser->box = NULL;
- chooser->populate_idle = 0;
-
- return G_SOURCE_REMOVE;
-}
-
-static void
-adj_value_changed (GtkAdjustment *adj,
- gpointer data)
-{
- DinoEmojiChooser *chooser = data;
- double value = gtk_adjustment_get_value (adj);
- EmojiSection const *sections[] = {
- &chooser->recent,
- &chooser->people,
- &chooser->body,
- &chooser->nature,
- &chooser->food,
- &chooser->travel,
- &chooser->activities,
- &chooser->objects,
- &chooser->symbols,
- &chooser->flags,
- };
- EmojiSection const *select_section = sections[0];
- gsize i;
-
- /* Figure out which section the current scroll position is within */
- for (i = 0; i < G_N_ELEMENTS (sections); ++i)
- {
- EmojiSection const *section = sections[i];
- GtkAllocation alloc;
-
- if (section->heading)
- gtk_widget_get_allocation (section->heading, &alloc);
- else
- gtk_widget_get_allocation (section->box, &alloc);
-
- if (value < alloc.y - BOX_SPACE)
- break;
-
- select_section = section;
- }
-
- /* Un/Check the section buttons accordingly */
- for (i = 0; i < G_N_ELEMENTS (sections); ++i)
- {
- EmojiSection const *section = sections[i];
-
- if (section == select_section)
- gtk_widget_set_state_flags (section->button, GTK_STATE_FLAG_CHECKED, FALSE);
- else
- gtk_widget_unset_state_flags (section->button, GTK_STATE_FLAG_CHECKED);
- }
-}
-
-static gboolean
-filter_func (GtkFlowBoxChild *child,
- gpointer data)
-{
- EmojiSection *section = data;
- DinoEmojiChooser *chooser;
- GVariant *emoji_data;
- const char *text;
- const char *name;
- gboolean res;
-
- res = TRUE;
-
- chooser = DINO_EMOJI_CHOOSER (gtk_widget_get_ancestor (GTK_WIDGET (child), GTK_TYPE_EMOJI_CHOOSER));
- text = gtk_entry_get_text (GTK_ENTRY (chooser->search_entry));
- emoji_data = (GVariant *) g_object_get_data (G_OBJECT (child), "emoji-data");
-
- if (text[0] == 0)
- goto out;
-
- if (!emoji_data)
- goto out;
-
- g_variant_get_child (emoji_data, 1, "&s", &name);
- res = g_str_match_string (text, name, TRUE);
-
-out:
- if (res)
- section->empty = FALSE;
-
- return res;
-}
-
-static void
-invalidate_section (EmojiSection *section)
-{
- section->empty = TRUE;
- gtk_flow_box_invalidate_filter (GTK_FLOW_BOX (section->box));
-}
-
-static void
-update_headings (DinoEmojiChooser *chooser)
-{
- gtk_widget_set_visible (chooser->people.heading, !chooser->people.empty);
- gtk_widget_set_visible (chooser->people.box, !chooser->people.empty);
- gtk_widget_set_visible (chooser->body.heading, !chooser->body.empty);
- gtk_widget_set_visible (chooser->body.box, !chooser->body.empty);
- gtk_widget_set_visible (chooser->nature.heading, !chooser->nature.empty);
- gtk_widget_set_visible (chooser->nature.box, !chooser->nature.empty);
- gtk_widget_set_visible (chooser->food.heading, !chooser->food.empty);
- gtk_widget_set_visible (chooser->food.box, !chooser->food.empty);
- gtk_widget_set_visible (chooser->travel.heading, !chooser->travel.empty);
- gtk_widget_set_visible (chooser->travel.box, !chooser->travel.empty);
- gtk_widget_set_visible (chooser->activities.heading, !chooser->activities.empty);
- gtk_widget_set_visible (chooser->activities.box, !chooser->activities.empty);
- gtk_widget_set_visible (chooser->objects.heading, !chooser->objects.empty);
- gtk_widget_set_visible (chooser->objects.box, !chooser->objects.empty);
- gtk_widget_set_visible (chooser->symbols.heading, !chooser->symbols.empty);
- gtk_widget_set_visible (chooser->symbols.box, !chooser->symbols.empty);
- gtk_widget_set_visible (chooser->flags.heading, !chooser->flags.empty);
- gtk_widget_set_visible (chooser->flags.box, !chooser->flags.empty);
-
- if (chooser->recent.empty && chooser->people.empty &&
- chooser->body.empty && chooser->nature.empty &&
- chooser->food.empty && chooser->travel.empty &&
- chooser->activities.empty && chooser->objects.empty &&
- chooser->symbols.empty && chooser->flags.empty)
- gtk_stack_set_visible_child_name (GTK_STACK (chooser->stack), "empty");
- else
- gtk_stack_set_visible_child_name (GTK_STACK (chooser->stack), "list");
-}
-
-static void
-search_changed (GtkEntry *entry,
- gpointer data)
-{
- DinoEmojiChooser *chooser = data;
-
- invalidate_section (&chooser->recent);
- invalidate_section (&chooser->people);
- invalidate_section (&chooser->body);
- invalidate_section (&chooser->nature);
- invalidate_section (&chooser->food);
- invalidate_section (&chooser->travel);
- invalidate_section (&chooser->activities);
- invalidate_section (&chooser->objects);
- invalidate_section (&chooser->symbols);
- invalidate_section (&chooser->flags);
-
- update_headings (chooser);
-}
-
-static void
-setup_section (DinoEmojiChooser *chooser,
- EmojiSection *section,
- const char *first,
- const char *icon)
-{
- GtkAdjustment *adj;
- GtkWidget *image;
-
- section->first = first;
-
- image = gtk_bin_get_child (GTK_BIN (section->button));
- gtk_image_set_from_icon_name (GTK_IMAGE (image), icon, GTK_ICON_SIZE_BUTTON);
-
- adj = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (chooser->scrolled_window));
-
- gtk_container_set_focus_vadjustment (GTK_CONTAINER (section->box), adj);
- gtk_flow_box_set_filter_func (GTK_FLOW_BOX (section->box), filter_func, section, NULL);
- g_signal_connect (section->button, "clicked", G_CALLBACK (scroll_to_section), section);
-}
-
-static void
-dino_emoji_chooser_init (DinoEmojiChooser *chooser)
-{
- GtkAdjustment *adj;
-
- chooser->settings = g_settings_new ("org.gtk.Settings.EmojiChooser");
-
- gtk_widget_init_template (GTK_WIDGET (chooser));
-
- /* Get a reasonable maximum width for an emoji. We do this to
- * skip overly wide fallback rendering for certain emojis the
- * font does not contain and therefore end up being rendered
- * as multiply glyphs.
- */
- {
- PangoLayout *layout = gtk_widget_create_pango_layout (GTK_WIDGET (chooser), "🙂");
- PangoAttrList *attrs;
- PangoRectangle rect;
-
- attrs = pango_attr_list_new ();
- pango_attr_list_insert (attrs, pango_attr_scale_new (PANGO_SCALE_X_LARGE));
- pango_layout_set_attributes (layout, attrs);
- pango_attr_list_unref (attrs);
-
- pango_layout_get_extents (layout, &rect, NULL);
- chooser->emoji_max_width = rect.width;
-
- g_object_unref (layout);
- }
-
- chooser->recent_long_press = gtk_gesture_long_press_new (chooser->recent.box);
- g_signal_connect (chooser->recent_long_press, "pressed", G_CALLBACK (long_pressed_cb), chooser);
- chooser->recent_multi_press = gtk_gesture_multi_press_new (chooser->recent.box);
- gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (chooser->recent_multi_press), GDK_BUTTON_SECONDARY);
- g_signal_connect (chooser->recent_multi_press, "pressed", G_CALLBACK (pressed_cb), chooser);
-
- chooser->people_long_press = gtk_gesture_long_press_new (chooser->people.box);
- g_signal_connect (chooser->people_long_press, "pressed", G_CALLBACK (long_pressed_cb), chooser);
- chooser->people_multi_press = gtk_gesture_multi_press_new (chooser->people.box);
- gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (chooser->people_multi_press), GDK_BUTTON_SECONDARY);
- g_signal_connect (chooser->people_multi_press, "pressed", G_CALLBACK (pressed_cb), chooser);
-
- chooser->body_long_press = gtk_gesture_long_press_new (chooser->body.box);
- g_signal_connect (chooser->body_long_press, "pressed", G_CALLBACK (long_pressed_cb), chooser);
- chooser->body_multi_press = gtk_gesture_multi_press_new (chooser->body.box);
- gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (chooser->body_multi_press), GDK_BUTTON_SECONDARY);
- g_signal_connect (chooser->body_multi_press, "pressed", G_CALLBACK (pressed_cb), chooser);
-
- adj = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (chooser->scrolled_window));
- g_signal_connect (adj, "value-changed", G_CALLBACK (adj_value_changed), chooser);
-
- setup_section (chooser, &chooser->recent, NULL, "emoji-recent-symbolic");
- setup_section (chooser, &chooser->people, "grinning face", "emoji-people-symbolic");
- setup_section (chooser, &chooser->body, "selfie", "emoji-body-symbolic");
- setup_section (chooser, &chooser->nature, "monkey face", "emoji-nature-symbolic");
- setup_section (chooser, &chooser->food, "grapes", "emoji-food-symbolic");
- setup_section (chooser, &chooser->travel, "globe showing Europe-Africa", "emoji-travel-symbolic");
- setup_section (chooser, &chooser->activities, "jack-o-lantern", "emoji-activities-symbolic");
- setup_section (chooser, &chooser->objects, "muted speaker", "emoji-objects-symbolic");
- setup_section (chooser, &chooser->symbols, "ATM sign", "emoji-symbols-symbolic");
- setup_section (chooser, &chooser->flags, "chequered flag", "emoji-flags-symbolic");
-
- populate_recent_section (chooser);
-
- chooser->populate_idle = g_idle_add (populate_emoji_chooser, chooser);
- g_source_set_name_by_id (chooser->populate_idle, "[gtk] populate_emoji_chooser");
-}
-
-static void
-dino_emoji_chooser_show (GtkWidget *widget)
-{
- DinoEmojiChooser *chooser = DINO_EMOJI_CHOOSER (widget);
- GtkAdjustment *adj;
-
- GTK_WIDGET_CLASS (dino_emoji_chooser_parent_class)->show (widget);
-
- adj = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (chooser->scrolled_window));
- gtk_adjustment_set_value (adj, 0);
-
- gtk_entry_set_text (GTK_ENTRY (chooser->search_entry), "");
-}
-
-static gboolean
-dino_emoji_chooser_button_release (GtkWidget *widget,
- GdkEventButton *event)
-{
- DinoEmojiChooserClass *klass = DINO_EMOJI_CHOOSER_GET_CLASS(widget);
- GtkWidget *event_widget = gtk_get_event_widget ((GdkEvent *) event);
- if (!event_widget && event->window != gtk_widget_get_window (widget))
- {
- return GDK_EVENT_PROPAGATE;
- }
- return klass->popover_button_release_event (widget, event);
-}
-
-static void
-dino_emoji_chooser_class_init (DinoEmojiChooserClass *klass)
-{
- GObjectClass *object_class = G_OBJECT_CLASS (klass);
- GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
-
- object_class->finalize = dino_emoji_chooser_finalize;
- widget_class->show = dino_emoji_chooser_show;
- klass->popover_button_release_event = widget_class->button_release_event;
- widget_class->button_release_event = dino_emoji_chooser_button_release;
-
- signals[EMOJI_PICKED] = g_signal_new ("emoji-picked",
- G_OBJECT_CLASS_TYPE (object_class),
- G_SIGNAL_RUN_LAST,
- 0,
- NULL, NULL,
- NULL,
- G_TYPE_NONE, 1, G_TYPE_STRING|G_SIGNAL_TYPE_STATIC_SCOPE);
-
- gtk_widget_class_set_template_from_resource (widget_class, "/im/dino/Dino/emojichooser.ui");
-
- gtk_widget_class_bind_template_child (widget_class, DinoEmojiChooser, search_entry);
- gtk_widget_class_bind_template_child (widget_class, DinoEmojiChooser, stack);
- gtk_widget_class_bind_template_child (widget_class, DinoEmojiChooser, scrolled_window);
-
- gtk_widget_class_bind_template_child (widget_class, DinoEmojiChooser, recent.box);
- gtk_widget_class_bind_template_child (widget_class, DinoEmojiChooser, recent.button);
-
- gtk_widget_class_bind_template_child (widget_class, DinoEmojiChooser, people.box);
- gtk_widget_class_bind_template_child (widget_class, DinoEmojiChooser, people.heading);
- gtk_widget_class_bind_template_child (widget_class, DinoEmojiChooser, people.button);
-
- gtk_widget_class_bind_template_child (widget_class, DinoEmojiChooser, body.box);
- gtk_widget_class_bind_template_child (widget_class, DinoEmojiChooser, body.heading);
- gtk_widget_class_bind_template_child (widget_class, DinoEmojiChooser, body.button);
-
- gtk_widget_class_bind_template_child (widget_class, DinoEmojiChooser, nature.box);
- gtk_widget_class_bind_template_child (widget_class, DinoEmojiChooser, nature.heading);
- gtk_widget_class_bind_template_child (widget_class, DinoEmojiChooser, nature.button);
-
- gtk_widget_class_bind_template_child (widget_class, DinoEmojiChooser, food.box);
- gtk_widget_class_bind_template_child (widget_class, DinoEmojiChooser, food.heading);
- gtk_widget_class_bind_template_child (widget_class, DinoEmojiChooser, food.button);
-
- gtk_widget_class_bind_template_child (widget_class, DinoEmojiChooser, travel.box);
- gtk_widget_class_bind_template_child (widget_class, DinoEmojiChooser, travel.heading);
- gtk_widget_class_bind_template_child (widget_class, DinoEmojiChooser, travel.button);
-
- gtk_widget_class_bind_template_child (widget_class, DinoEmojiChooser, activities.box);
- gtk_widget_class_bind_template_child (widget_class, DinoEmojiChooser, activities.heading);
- gtk_widget_class_bind_template_child (widget_class, DinoEmojiChooser, activities.button);
-
- gtk_widget_class_bind_template_child (widget_class, DinoEmojiChooser, objects.box);
- gtk_widget_class_bind_template_child (widget_class, DinoEmojiChooser, objects.heading);
- gtk_widget_class_bind_template_child (widget_class, DinoEmojiChooser, objects.button);
-
- gtk_widget_class_bind_template_child (widget_class, DinoEmojiChooser, symbols.box);
- gtk_widget_class_bind_template_child (widget_class, DinoEmojiChooser, symbols.heading);
- gtk_widget_class_bind_template_child (widget_class, DinoEmojiChooser, symbols.button);
-
- gtk_widget_class_bind_template_child (widget_class, DinoEmojiChooser, flags.box);
- gtk_widget_class_bind_template_child (widget_class, DinoEmojiChooser, flags.heading);
- gtk_widget_class_bind_template_child (widget_class, DinoEmojiChooser, flags.button);
-
- gtk_widget_class_bind_template_callback (widget_class, emoji_activated);
- gtk_widget_class_bind_template_callback (widget_class, search_changed);
-}
-
-DinoEmojiChooser *
-dino_emoji_chooser_new (void)
-{
- return DINO_EMOJI_CHOOSER (g_object_new (GTK_TYPE_EMOJI_CHOOSER, NULL));
-}
diff --git a/main/src/emojichooser.h b/main/src/emojichooser.h
deleted file mode 100644
index e7dc8660..00000000
--- a/main/src/emojichooser.h
+++ /dev/null
@@ -1,36 +0,0 @@
-/* gtkemojichooser.h: An Emoji chooser widget
- * Copyright 2017, Red Hat, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library 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
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#pragma once
-#include <gtk/gtk.h>
-
-G_BEGIN_DECLS
-
-#define GTK_TYPE_EMOJI_CHOOSER (dino_emoji_chooser_get_type ())
-#define DINO_EMOJI_CHOOSER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_EMOJI_CHOOSER, DinoEmojiChooser))
-#define DINO_EMOJI_CHOOSER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_EMOJI_CHOOSER, DinoEmojiChooserClass))
-#define GTK_IS_EMOJI_CHOOSER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_EMOJI_CHOOSER))
-#define GTK_IS_EMOJI_CHOOSER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_EMOJI_CHOOSER))
-#define DINO_EMOJI_CHOOSER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_EMOJI_CHOOSER, DinoEmojiChooserClass))
-
-typedef struct _DinoEmojiChooser DinoEmojiChooser;
-typedef struct _DinoEmojiChooserClass DinoEmojiChooserClass;
-
-GType dino_emoji_chooser_get_type (void) G_GNUC_CONST;
-DinoEmojiChooser *dino_emoji_chooser_new (void);
-
-G_END_DECLS
diff --git a/main/src/main.vala b/main/src/main.vala
index afa1f52b..bf8759b6 100644
--- a/main/src/main.vala
+++ b/main/src/main.vala
@@ -14,7 +14,7 @@ void main(string[] args) {
Intl.textdomain(GETTEXT_PACKAGE);
internationalize(GETTEXT_PACKAGE, search_path_generator.get_locale_path(GETTEXT_PACKAGE, LOCALE_INSTALL_DIR));
- Gtk.init(ref args);
+ Gtk.init();
Dino.Ui.Application app = new Dino.Ui.Application() { search_path_generator=search_path_generator };
Plugins.Loader loader = new Plugins.Loader(app);
loader.load_all();
diff --git a/main/src/ui/add_conversation/add_conference_dialog.vala b/main/src/ui/add_conversation/add_conference_dialog.vala
index a03f3db6..e078bc62 100644
--- a/main/src/ui/add_conversation/add_conference_dialog.vala
+++ b/main/src/ui/add_conversation/add_conference_dialog.vala
@@ -10,14 +10,13 @@ namespace Dino.Ui {
public class AddConferenceDialog : Gtk.Dialog {
private Stack stack = new Stack();
- private Button cancel_button;
+ private Button cancel_button = new Button() { visible=true };
private Button ok_button;
- private Label cancel_label = new Label(_("Cancel")) {visible=true};
- private Image cancel_image = new Image.from_icon_name("go-previous-symbolic", IconSize.MENU) {visible=true};
private SelectJidFragment select_fragment;
private ConferenceDetailsFragment details_fragment;
private ConferenceList conference_list;
+ private ListBox conference_list_box;
private StreamInteractor stream_interactor;
@@ -29,7 +28,7 @@ public class AddConferenceDialog : Gtk.Dialog {
stack.visible = true;
stack.vhomogeneous = false;
- get_content_area().add(stack);
+ get_content_area().append(stack);
setup_headerbar();
setup_jid_add_view();
@@ -40,8 +39,7 @@ public class AddConferenceDialog : Gtk.Dialog {
private void show_jid_add_view() {
// Rewire headerbar (if CSD)
if (Util.use_csd()) {
- if (cancel_image.get_parent() != null) cancel_button.remove(cancel_image);
- cancel_button.add(cancel_label);
+ cancel_button.set_label(_("Cancel"));
cancel_button.clicked.disconnect(show_jid_add_view);
cancel_button.clicked.connect(on_cancel);
ok_button.label = _("Next");
@@ -59,8 +57,7 @@ public class AddConferenceDialog : Gtk.Dialog {
private void show_conference_details_view() {
// Rewire headerbar (if CSD)
if (Util.use_csd()) {
- if (cancel_label.get_parent() != null) cancel_button.remove(cancel_label);
- cancel_button.add(cancel_image);
+ cancel_button.set_icon_name("go-previous-symbolic");
cancel_button.clicked.disconnect(on_cancel);
cancel_button.clicked.connect(show_jid_add_view);
ok_button.label = _("Join");
@@ -73,31 +70,30 @@ public class AddConferenceDialog : Gtk.Dialog {
stack.transition_type = StackTransitionType.SLIDE_LEFT;
stack.set_visible_child_name("details");
- animate_window_resize();
+// animate_window_resize();
}
private void setup_headerbar() {
- cancel_button = new Button() { visible=true };
-
- ok_button = new Button() { can_focus=true, can_default=true, visible=true };
+ ok_button = new Button() { can_focus=true, visible=true };
ok_button.get_style_context().add_class("suggested-action");
if (Util.use_csd()) {
HeaderBar header_bar = get_header_bar() as HeaderBar;
- header_bar.show_close_button = false;
+ header_bar.show_title_buttons = false;
header_bar.pack_start(cancel_button);
header_bar.pack_end(ok_button);
- ok_button.has_default = true;
+// ok_button.has_default = true;
}
}
private void setup_jid_add_view() {
conference_list = new ConferenceList(stream_interactor);
- conference_list.row_activated.connect(() => { ok_button.clicked(); });
+ conference_list_box = conference_list.get_list_box();
+ conference_list_box.row_activated.connect(() => { ok_button.clicked(); });
- select_fragment = new SelectJidFragment(stream_interactor, conference_list, stream_interactor.get_accounts());
+ select_fragment = new SelectJidFragment(stream_interactor, conference_list_box, stream_interactor.get_accounts());
select_fragment.add_jid.connect((row) => {
AddGroupchatDialog dialog = new AddGroupchatDialog(stream_interactor);
dialog.set_transient_for(this);
@@ -109,23 +105,23 @@ public class AddConferenceDialog : Gtk.Dialog {
});
Box wrap_box = new Box(Orientation.VERTICAL, 0) { visible=true };
- wrap_box.add(select_fragment);
+ wrap_box.append(select_fragment);
stack.add_named(wrap_box, "select");
if (!Util.use_csd()) {
Box box = new Box(Orientation.HORIZONTAL, 5) { halign=Align.END, margin_bottom=15, margin_start=80, margin_end=80, visible=true };
- Button ok_button = new Button() { label=_("Next"), sensitive=false, halign = Align.END, can_focus=true, can_default=true, visible=true };
+ Button ok_button = new Button.with_label(_("Next")) { sensitive=false, halign = Align.END, can_focus=true, visible=true };
ok_button.get_style_context().add_class("suggested-action");
ok_button.clicked.connect(on_next_button_clicked);
select_fragment.notify["done"].connect(() => { ok_button.sensitive = select_fragment.done; });
- Button cancel_button = new Button() { label=_("Cancel"), halign=Align.START, visible=true };
+ Button cancel_button = new Button.with_label(_("Cancel")) { halign=Align.START, visible=true };
cancel_button.clicked.connect(on_cancel);
- box.add(cancel_button);
- box.add(ok_button);
- wrap_box.add(box);
+ box.append(cancel_button);
+ box.append(ok_button);
+ wrap_box.append(box);
- ok_button.has_default = true;
+// ok_button.has_default = true;
}
}
@@ -134,22 +130,22 @@ public class AddConferenceDialog : Gtk.Dialog {
details_fragment.joined.connect(() => this.close());
Box wrap_box = new Box(Orientation.VERTICAL, 0) { visible=true };
- wrap_box.add(details_fragment);
+ wrap_box.append(details_fragment);
if (!Util.use_csd()) {
Box box = new Box(Orientation.HORIZONTAL, 5) { halign=Align.END, margin_bottom=15, margin_start=80, margin_end=80, visible=true };
- Button ok_button = new Button() { label=_("Join"), halign = Align.END, can_focus=true, can_default=true, visible=true };
+ Button ok_button = new Button.with_label(_("Join")) { halign = Align.END, can_focus=true, visible=true };
ok_button.get_style_context().add_class("suggested-action");
details_fragment.notify["done"].connect(() => { ok_button.sensitive = select_fragment.done; });
details_fragment.ok_button = ok_button;
- Button cancel_button = new Button() { label=_("Back"), halign=Align.START, visible=true };
+ Button cancel_button = new Button.with_label(_("Back")) { halign=Align.START, visible=true };
cancel_button.clicked.connect(show_jid_add_view);
- box.add(cancel_button);
- box.add(ok_button);
+ box.append(cancel_button);
+ box.append(ok_button);
- wrap_box.add(box);
+ wrap_box.append(box);
}
stack.add_named(wrap_box, "details");
}
@@ -164,8 +160,9 @@ public class AddConferenceDialog : Gtk.Dialog {
private void on_next_button_clicked() {
details_fragment.clear();
- ListRow? row = conference_list.get_selected_row() as ListRow;
- ConferenceListRow? conference_row = conference_list.get_selected_row() as ConferenceListRow;
+
+ ListRow? row = conference_list_box.get_selected_row() != null ? conference_list_box.get_selected_row().get_child() as ListRow : null;
+ ConferenceListRow? conference_row = conference_list_box.get_selected_row() != null ? conference_list_box.get_selected_row() as ConferenceListRow : null;
if (conference_row != null) {
details_fragment.account = conference_row.account;
details_fragment.jid = conference_row.bookmark.jid.to_string();
@@ -184,10 +181,11 @@ public class AddConferenceDialog : Gtk.Dialog {
}
private void animate_window_resize() {
- int def_height, curr_width, curr_height;
- get_size(out curr_width, out curr_height);
- stack.get_preferred_height(null, out def_height);
- int difference = def_height - curr_height;
+ int curr_height = get_size(Orientation.VERTICAL);
+ int curr_width = get_size(Orientation.HORIZONTAL);
+ var natural_size = new Requisition();
+ stack.get_preferred_size(null, out natural_size);
+ int difference = natural_size.height - curr_height;
Timer timer = new Timer();
Timeout.add((int) (stack.transition_duration / 30),
() => {
@@ -195,7 +193,8 @@ public class AddConferenceDialog : Gtk.Dialog {
timer.elapsed(out microsec);
ulong millisec = microsec / 1000;
double partial = double.min(1, (double) millisec / stack.transition_duration);
- resize(curr_width, (int) (curr_height + difference * partial));
+ var a = this.list_toplevels().nth_data(0);
+ set_size_request(curr_width, (int) (curr_height + difference * partial));
return millisec < stack.transition_duration;
});
}
diff --git a/main/src/ui/add_conversation/add_groupchat_dialog.vala b/main/src/ui/add_conversation/add_groupchat_dialog.vala
index a71c0a15..786c1a0b 100644
--- a/main/src/ui/add_conversation/add_groupchat_dialog.vala
+++ b/main/src/ui/add_conversation/add_groupchat_dialog.vala
@@ -30,11 +30,12 @@ protected class AddGroupchatDialog : Gtk.Dialog {
cancel_button.clicked.connect(() => { close(); });
ok_button.clicked.connect(on_ok_button_clicked);
- jid_entry.key_release_event.connect(on_jid_key_release);
- nick_entry.key_release_event.connect(check_ok);
+
+ jid_entry.changed.connect(on_jid_key_release);
+ nick_entry.changed.connect(check_ok);
}
- private bool on_jid_key_release() {
+ private void on_jid_key_release() {
check_ok();
if (!alias_entry_changed) {
try {
@@ -44,17 +45,15 @@ protected class AddGroupchatDialog : Gtk.Dialog {
alias_entry.text = jid_entry.text;
}
}
- return false;
}
- private bool check_ok() {
+ private void check_ok() {
try {
Jid parsed_jid = new Jid(jid_entry.text);
ok_button.sensitive = parsed_jid != null && parsed_jid.localpart != null && parsed_jid.resourcepart == null;
} catch (InvalidJidError e) {
ok_button.sensitive = false;
}
- return false;
}
private void on_ok_button_clicked() {
diff --git a/main/src/ui/add_conversation/conference_details_fragment.vala b/main/src/ui/add_conversation/conference_details_fragment.vala
index 88f9cc73..721c660e 100644
--- a/main/src/ui/add_conversation/conference_details_fragment.vala
+++ b/main/src/ui/add_conversation/conference_details_fragment.vala
@@ -114,12 +114,12 @@ protected class ConferenceDetailsFragment : Box {
account_combobox.changed.connect(() => { accounts_label.label = account_combobox.selected.bare_jid.to_string(); });
accounts_label.label = account_combobox.selected.bare_jid.to_string();
- jid_entry.key_release_event.connect(on_jid_key_release_event);
- nick_entry.key_release_event.connect(on_nick_key_release_event);
- password_entry.key_release_event.connect(on_password_key_release_event);
+// jid_entry.key_release_event.connect(on_jid_key_release_event);
+// nick_entry.key_release_event.connect(on_nick_key_release_event);
+// password_entry.key_release_event.connect(on_password_key_release_event);
- jid_entry.key_release_event.connect(() => { done = true; return false; }); // just for notifying
- nick_entry.key_release_event.connect(() => { done = true; return false; });
+// jid_entry.key_release_event.connect(() => { done = true; return false; }); // just for notifying
+// nick_entry.key_release_event.connect(() => { done = true; return false; });
notification_button.clicked.connect(() => { notification_revealer.set_reveal_child(false); });
@@ -195,25 +195,25 @@ protected class ConferenceDetailsFragment : Box {
notification_revealer.set_reveal_child(true);
}
- private bool on_jid_key_release_event(EventKey event) {
- jid_label.label = jid_entry.text;
- if (event.keyval == Key.Return) jid_stack.set_visible_child_name("label");
- return false;
- }
-
- private bool on_nick_key_release_event(EventKey event) {
- nick_label.label = nick_entry.text;
- if (event.keyval == Key.Return) nick_stack.set_visible_child_name("label");
- return false;
- }
-
- private bool on_password_key_release_event(EventKey event) {
- string filler = "";
- for (int i = 0; i < password_entry.text.length; i++) filler += password_entry.get_invisible_char().to_string();
- password_label.label = filler;
- if (event.keyval == Key.Return) password_stack.set_visible_child_name("label");
- return false;
- }
+// private bool on_jid_key_release_event(EventKey event) {
+// jid_label.label = jid_entry.text;
+// if (event.keyval == Key.Return) jid_stack.set_visible_child_name("label");
+// return false;
+// }
+//
+// private bool on_nick_key_release_event(EventKey event) {
+// nick_label.label = nick_entry.text;
+// if (event.keyval == Key.Return) nick_stack.set_visible_child_name("label");
+// return false;
+// }
+//
+// private bool on_password_key_release_event(EventKey event) {
+// string filler = "";
+// for (int i = 0; i < password_entry.text.length; i++) filler += password_entry.get_invisible_char().to_string();
+// password_label.label = filler;
+// if (event.keyval == Key.Return) password_stack.set_visible_child_name("label");
+// return false;
+// }
private void set_active_stack(Stack stack) {
stack.set_visible_child_name("entry");
diff --git a/main/src/ui/add_conversation/conference_list.vala b/main/src/ui/add_conversation/conference_list.vala
index fa71f98f..a19630e4 100644
--- a/main/src/ui/add_conversation/conference_list.vala
+++ b/main/src/ui/add_conversation/conference_list.vala
@@ -7,20 +7,22 @@ using Dino.Entities;
namespace Dino.Ui {
-protected class ConferenceList : FilterableList {
+protected class ConferenceList {
public signal void conversation_selected(Conversation? conversation);
private StreamInteractor stream_interactor;
+
+ private ListBox list_box = new ListBox();
private HashMap<Account, Set<Conference>> lists = new HashMap<Account, Set<Conference>>(Account.hash_func, Account.equals_func);
private HashMap<Account, HashMap<Jid, Widget>> widgets = new HashMap<Account, HashMap<Jid, Widget>>(Account.hash_func, Account.equals_func);
public ConferenceList(StreamInteractor stream_interactor) {
this.stream_interactor = stream_interactor;
- set_filter_func(filter);
- set_header_func(header);
- set_sort_func(sort);
+// list_box.set_filter_func(filter);
+ list_box.set_header_func(header);
+// list_box.set_sort_func(sort);
stream_interactor.get_module(MucManager.IDENTITY).bookmarks_updated.connect((account, conferences) => {
lists[account] = conferences;
@@ -44,18 +46,18 @@ protected class ConferenceList : FilterableList {
}
var widget = new ConferenceListRow(stream_interactor, conference, account);
widgets[account][conference.jid] = widget;
- add(widget);
+ list_box.append(widget);
}
private void remove_conference(Account account, Jid jid) {
if (widgets.has_key(account) && widgets[account].has_key(jid)) {
- remove(widgets[account][jid]);
+ list_box.remove(widgets[account][jid]);
widgets[account].unset(jid);
}
}
public void refresh_conferences() {
- @foreach((widget) => { remove(widget); });
+// @foreach((widget) => { remove(widget); });
foreach (Account account in lists.keys) {
foreach (Conference conference in lists[account]) {
add_conference(account, conference);
@@ -78,25 +80,8 @@ protected class ConferenceList : FilterableList {
}
}
- private bool filter(ListBoxRow r) {
- if (r.get_type().is_a(typeof(ListRow))) {
- ListRow row = r as ListRow;
- if (filter_values != null) {
- foreach (string filter in filter_values) {
- if (!(row.name_label.label.down().contains(filter.down()) ||
- row.jid.to_string().down().contains(filter.down()))) {
- return false;
- }
- }
- }
- }
- return true;
- }
-
- public override int sort(ListBoxRow row1, ListBoxRow row2) {
- ListRow c1 = (row1 as ListRow);
- ListRow c2 = (row2 as ListRow);
- return c1.name_label.label.collate(c2.name_label.label);
+ public ListBox get_list_box() {
+ return list_box;
}
}
diff --git a/main/src/ui/add_conversation/list_row.vala b/main/src/ui/add_conversation/list_row.vala
index 97c2b2fe..326dba98 100644
--- a/main/src/ui/add_conversation/list_row.vala
+++ b/main/src/ui/add_conversation/list_row.vala
@@ -6,16 +6,27 @@ using Xmpp;
namespace Dino.Ui {
-[GtkTemplate (ui = "/im/dino/Dino/add_conversation/list_row.ui")]
-public class ListRow : ListBoxRow {
+public class ListRow : Widget {
- [GtkChild] public unowned AvatarImage image;
- [GtkChild] public unowned Label name_label;
- [GtkChild] public unowned Label via_label;
+ public Grid outer_grid;
+ public AvatarImage image;
+ public Label name_label;
+ public Label via_label;
public Jid? jid;
public Account? account;
+ construct {
+ Builder builder = new Builder.from_resource("/im/dino/Dino/add_conversation/list_row.ui");
+ outer_grid = (Grid) builder.get_object("outer_grid");
+ image = (AvatarImage) builder.get_object("image");
+ name_label = (Label) builder.get_object("name_label");
+ via_label = (Label) builder.get_object("via_label");
+
+ this.layout_manager = new BinLayout();
+ outer_grid.insert_after(this, null);
+ }
+
public ListRow() {}
public ListRow.from_jid(StreamInteractor stream_interactor, Jid jid, Account account, bool show_account) {
diff --git a/main/src/ui/add_conversation/roster_list.vala b/main/src/ui/add_conversation/roster_list.vala
index fd4d4ade..a89d24e8 100644
--- a/main/src/ui/add_conversation/roster_list.vala
+++ b/main/src/ui/add_conversation/roster_list.vala
@@ -6,22 +6,23 @@ using Xmpp;
namespace Dino.Ui {
-protected class RosterList : FilterableList {
+protected class RosterList {
public signal void conversation_selected(Conversation? conversation);
private StreamInteractor stream_interactor;
private Gee.List<Account> accounts;
private ulong[] handler_ids = new ulong[0];
+ private ListBox list_box = new ListBox();
private HashMap<Account, HashMap<Jid, ListRow>> rows = new HashMap<Account, HashMap<Jid, ListRow>>(Account.hash_func, Account.equals_func);
public RosterList(StreamInteractor stream_interactor, Gee.List<Account> accounts) {
this.stream_interactor = stream_interactor;
this.accounts = accounts;
- set_filter_func(filter);
- set_header_func(header);
- set_sort_func(sort);
+// set_filter_func(filter);
+ list_box.set_header_func(header);
+// set_sort_func(sort);
handler_ids += stream_interactor.get_module(RosterManager.IDENTITY).removed_roster_item.connect( (account, jid, roster_item) => {
if (accounts.contains(account)) {
@@ -33,7 +34,7 @@ protected class RosterList : FilterableList {
on_updated_roster_item(account, jid, roster_item);
}
});
- destroy.connect(() => {
+ list_box.destroy.connect(() => {
foreach (ulong handler_id in handler_ids) stream_interactor.get_module(RosterManager.IDENTITY).disconnect(handler_id);
});
@@ -42,7 +43,7 @@ protected class RosterList : FilterableList {
private void on_removed_roster_item(Account account, Jid jid, Roster.Item roster_item) {
if (rows.has_key(account) && rows[account].has_key(jid)) {
- remove(rows[account][jid]);
+ list_box.remove(rows[account][jid]);
rows[account].unset(jid);
}
}
@@ -51,9 +52,9 @@ protected class RosterList : FilterableList {
on_removed_roster_item(account, jid, roster_item);
ListRow row = new ListRow.from_jid(stream_interactor, roster_item.jid, account, accounts.size > 1);
rows[account][jid] = row;
- add(row);
- invalidate_sort();
- invalidate_filter();
+ list_box.append(row);
+ list_box.invalidate_sort();
+ list_box.invalidate_filter();
}
private void fetch_roster_items(Account account) {
@@ -69,25 +70,8 @@ protected class RosterList : FilterableList {
}
}
- private bool filter(ListBoxRow r) {
- if (r.get_type().is_a(typeof(ListRow))) {
- ListRow row = r as ListRow;
- if (filter_values != null) {
- foreach (string filter in filter_values) {
- if (!(row.name_label.label.down().contains(filter.down()) ||
- row.jid.to_string().down().contains(filter.down()))) {
- return false;
- }
- }
- }
- }
- return true;
- }
-
- public override int sort(ListBoxRow row1, ListBoxRow row2) {
- ListRow c1 = (row1 as ListRow);
- ListRow c2 = (row2 as ListRow);
- return c1.name_label.label.collate(c2.name_label.label);
+ public ListBox get_list_box() {
+ return list_box;
}
}
diff --git a/main/src/ui/add_conversation/select_contact_dialog.vala b/main/src/ui/add_conversation/select_contact_dialog.vala
index d78a17c1..4bf5b193 100644
--- a/main/src/ui/add_conversation/select_contact_dialog.vala
+++ b/main/src/ui/add_conversation/select_contact_dialog.vala
@@ -14,6 +14,7 @@ public class SelectContactDialog : Gtk.Dialog {
public Button ok_button;
private RosterList roster_list;
+ private ListBox roster_list_box;
private SelectJidFragment select_jid_fragment;
private StreamInteractor stream_interactor;
private Gee.List<Account> accounts;
@@ -45,7 +46,7 @@ public class SelectContactDialog : Gtk.Dialog {
if (Util.use_csd()) {
HeaderBar header_bar = get_header_bar() as HeaderBar;
- header_bar.show_close_button = false;
+ header_bar.show_title_buttons = false;
header_bar.pack_start(cancel_button);
header_bar.pack_end(ok_button);
@@ -54,15 +55,15 @@ public class SelectContactDialog : Gtk.Dialog {
cancel_button.halign = Align.START;
ok_button.halign = Align.END;
- box.add(cancel_button);
- box.add(ok_button);
+ box.append(cancel_button);
+ box.append(ok_button);
- get_content_area().add(box);
+ get_content_area().append(box);
}
cancel_button.clicked.connect(() => { close(); });
ok_button.clicked.connect(() => {
- ListRow? selected_row = roster_list.get_selected_row() as ListRow;
+ ListRow? selected_row = roster_list_box.get_selected_row() != null ? roster_list_box.get_selected_row().get_child() as ListRow : null;
if (selected_row != null) selected(selected_row.account, selected_row.jid);
close();
});
@@ -70,21 +71,22 @@ public class SelectContactDialog : Gtk.Dialog {
private void setup_view() {
roster_list = new RosterList(stream_interactor, accounts);
- roster_list.row_activated.connect(() => { ok_button.clicked(); });
- select_jid_fragment = new SelectJidFragment(stream_interactor, roster_list, accounts);
+ roster_list_box = roster_list.get_list_box();
+ roster_list_box.row_activated.connect(() => { ok_button.clicked(); });
+ select_jid_fragment = new SelectJidFragment(stream_interactor, roster_list_box, accounts);
select_jid_fragment.add_jid.connect((row) => {
AddContactDialog add_contact_dialog = new AddContactDialog(stream_interactor);
add_contact_dialog.set_transient_for(this);
add_contact_dialog.present();
});
select_jid_fragment.remove_jid.connect((row) => {
- ListRow list_row = roster_list.get_selected_row() as ListRow;
+ ListRow list_row = roster_list_box.get_selected_row() as ListRow;
stream_interactor.get_module(RosterManager.IDENTITY).remove_jid(list_row.account, list_row.jid);
});
select_jid_fragment.notify["done"].connect(() => {
ok_button.sensitive = select_jid_fragment.done;
});
- get_content_area().add(select_jid_fragment);
+ get_content_area().append(select_jid_fragment);
}
}
diff --git a/main/src/ui/add_conversation/select_jid_fragment.vala b/main/src/ui/add_conversation/select_jid_fragment.vala
index bc349b6c..58f019c1 100644
--- a/main/src/ui/add_conversation/select_jid_fragment.vala
+++ b/main/src/ui/add_conversation/select_jid_fragment.vala
@@ -12,7 +12,7 @@ public class SelectJidFragment : Gtk.Box {
public signal void add_jid();
public signal void remove_jid(ListRow row);
public bool done {
- get { return filterable_list.get_selected_row() != null; }
+ get { return list.get_selected_row() != null; }
private set {}
}
@@ -22,43 +22,45 @@ public class SelectJidFragment : Gtk.Box {
[GtkChild] private unowned Button remove_button;
private StreamInteractor stream_interactor;
- private FilterableList filterable_list;
private Gee.List<Account> accounts;
-
private ArrayList<AddListRow> added_rows = new ArrayList<AddListRow>();
- public SelectJidFragment(StreamInteractor stream_interactor, FilterableList filterable_list, Gee.List<Account> accounts) {
+ private ListBox list;
+ private string[]? filter_values;
+
+ public SelectJidFragment(StreamInteractor stream_interactor, ListBox list, Gee.List<Account> accounts) {
this.stream_interactor = stream_interactor;
- this.filterable_list = filterable_list;
+ this.list = list;
this.accounts = accounts;
- filterable_list.visible = true;
- filterable_list.activate_on_single_click = false;
- filterable_list.vexpand = true;
- box.add(filterable_list);
+ list.activate_on_single_click = false;
+ list.vexpand = true;
+ box.append(list);
- filterable_list.set_sort_func(sort);
- filterable_list.row_selected.connect(check_buttons_active);
- filterable_list.row_selected.connect(() => { done = true; }); // just for notifying
+ list.set_sort_func(sort);
+ list.set_filter_func(filter);
+ list.row_selected.connect(check_buttons_active);
+ list.row_selected.connect(() => { done = true; }); // just for notifying
entry.changed.connect(() => { set_filter(entry.text); });
add_button.clicked.connect(() => { add_jid(); });
- remove_button.clicked.connect(() => { remove_jid(filterable_list.get_selected_row() as ListRow); });
+ remove_button.clicked.connect(() => { remove_jid(list.get_selected_row() as ListRow); });
}
public void set_filter(string str) {
if (entry.text != str) entry.text = str;
- foreach (AddListRow row in added_rows) filterable_list.remove(row);
+ foreach (AddListRow row in added_rows) list.remove(row);
added_rows.clear();
- string[] ? values = str == "" ? null : str.split(" ");
- filterable_list.set_filter_values(values);
+ filter_values = str == "" ? null : str.split(" ");
+ list.invalidate_filter();
+
try {
Jid parsed_jid = new Jid(str);
if (parsed_jid != null && parsed_jid.localpart != null) {
foreach (Account account in accounts) {
AddListRow row = new AddListRow(stream_interactor, parsed_jid, account);
- filterable_list.add(row);
+ list.append(row);
added_rows.add(row);
}
}
@@ -68,7 +70,7 @@ public class SelectJidFragment : Gtk.Box {
}
private void check_buttons_active() {
- ListBoxRow? row = filterable_list.get_selected_row();
+ ListBoxRow? row = list.get_selected_row();
bool active = row != null && !row.get_type().is_a(typeof(AddListRow));
remove_button.sensitive = active;
}
@@ -81,7 +83,29 @@ public class SelectJidFragment : Gtk.Box {
} else if (al2 != null && al1 == null) {
return 1;
}
- return filterable_list.sort(row1, row2);
+
+ ListRow? c1 = (row1.child as ListRow);
+ ListRow? c2 = (row2.child as ListRow);
+ if (c1 != null && c2 != null) {
+ return c1.name_label.label.collate(c2.name_label.label);
+ }
+
+ return 0;
+ }
+
+ private bool filter(ListBoxRow r) {
+ ListRow? row = (r.child as ListRow);
+ if (row == null) return true;
+
+ if (filter_values != null) {
+ foreach (string filter in filter_values) {
+ if (!(row.name_label.label.down().contains(filter.down()) ||
+ row.jid.to_string().down().contains(filter.down()))) {
+ return false;
+ }
+ }
+ }
+ return true;
}
private class AddListRow : ListRow {
@@ -101,16 +125,4 @@ public class SelectJidFragment : Gtk.Box {
}
}
-public abstract class FilterableList : Gtk.ListBox {
- public string[]? filter_values;
-
- public void set_filter_values(string[] values) {
- if (filter_values == values) return;
- filter_values = values;
- invalidate_filter();
- }
-
- public abstract int sort(ListBoxRow row1, ListBoxRow row2);
-}
-
}
diff --git a/main/src/ui/application.vala b/main/src/ui/application.vala
index 6c02087f..f83e403b 100644
--- a/main/src/ui/application.vala
+++ b/main/src/ui/application.vala
@@ -35,7 +35,7 @@ public class Dino.Ui.Application : Gtk.Application, Dino.Application {
CssProvider provider = new CssProvider();
provider.load_from_resource("/im/dino/Dino/theme.css");
- StyleContext.add_provider_for_screen(Gdk.Screen.get_default(), provider, STYLE_PROVIDER_PRIORITY_APPLICATION);
+ StyleContext.add_provider_for_display(Gdk.Display.get_default(), provider, STYLE_PROVIDER_PRIORITY_APPLICATION);
create_actions();
add_main_option_entries(options);
@@ -68,7 +68,7 @@ public class Dino.Ui.Application : Gtk.Application, Dino.Application {
var desktop_env = Environment.get_variable("XDG_CURRENT_DESKTOP");
if (desktop_env == null || !desktop_env.down().contains("gnome")) {
if (this.active_window != null) {
- this.active_window.urgency_hint = true;
+// this.active_window.urgency_hint = true;
}
}
});
@@ -80,7 +80,7 @@ public class Dino.Ui.Application : Gtk.Application, Dino.Application {
config = new Config(db);
window = new MainWindow(this, stream_interactor, db, config);
controller.set_window(window);
- if ((get_flags() & ApplicationFlags.IS_SERVICE) == ApplicationFlags.IS_SERVICE) window.delete_event.connect(window.hide_on_delete);
+ if ((get_flags() & ApplicationFlags.IS_SERVICE) == ApplicationFlags.IS_SERVICE) window.hide_on_close = true;
}
window.present();
});
@@ -205,9 +205,9 @@ public class Dino.Ui.Application : Gtk.Application, Dino.Application {
if (!use_csd()) {
// Hack to prevent CRITICAL in Gtk when trying to destroy non-existant headerbar
Widget shortcuts_hack = dialog.get_titlebar();
- dialog.destroy.connect_after(() => {
- shortcuts_hack = null;
- });
+// dialog.destroy.connect_after(() => {
+// shortcuts_hack = null;
+// });
dialog.set_titlebar(null);
}
dialog.title = _("Keyboard Shortcuts");
@@ -292,11 +292,11 @@ public class Dino.Ui.Application : Gtk.Application, Dino.Application {
dialog.copyright = "Copyright © 2016-2022 - Dino Team";
dialog.license_type = License.GPL_3_0;
- dialog.response.connect((response_id) => {
- if (response_id == Gtk.ResponseType.CANCEL || response_id == Gtk.ResponseType.DELETE_EVENT) {
- dialog.destroy();
- }
- });
+// dialog.response.connect((response_id) => {
+// if (response_id == Gtk.ResponseType.CANCEL || response_id == Gtk.ResponseType.DELETE_EVENT) {
+// dialog.destroy();
+// }
+// });
if (!use_csd()) {
dialog.set_titlebar(null);
@@ -315,7 +315,7 @@ public class Dino.Ui.Application : Gtk.Application, Dino.Application {
conference_fragment.account = account;
}
Box content_area = dialog.get_content_area();
- content_area.add(conference_fragment);
+ content_area.append(conference_fragment);
conference_fragment.joined.connect(() => {
dialog.destroy();
});
diff --git a/main/src/ui/avatar_image.vala b/main/src/ui/avatar_image.vala
index f7731373..a304f5a2 100644
--- a/main/src/ui/avatar_image.vala
+++ b/main/src/ui/avatar_image.vala
@@ -5,7 +5,7 @@ using Xmpp.Util;
namespace Dino.Ui {
-public class AvatarImage : Misc {
+public class AvatarImage : Widget {
public int height { get; set; default = 35; }
public int width { get; set; default = 35; }
public bool allow_gray { get; set; default = true; }
@@ -34,17 +34,24 @@ public class AvatarImage : Misc {
disconnect_stream_interactor();
}
- public override void get_preferred_width(out int minimum_width, out int natural_width) {
- minimum_width = width;
- natural_width = width;
+ public override void measure(Orientation orientation, int for_size, out int minimum, out int natural, out int minimum_baseline, out int natural_baseline) {
+ if (orientation == Orientation.HORIZONTAL) {
+ minimum = width;
+ natural = width;
+ } else {
+ minimum = height;
+ natural = height;
+ }
+ minimum_baseline = natural_baseline = -1;
}
- public override void get_preferred_height(out int minimum_height, out int natural_height) {
- minimum_height = height;
- natural_height = height;
+ public override void snapshot(Snapshot snapshot) {
+ Cairo.Context context = snapshot.append_cairo(Graphene.Rect.alloc().init(0, 0, width, height));
+ draw(context);
}
- public override bool draw(Cairo.Context ctx_in) {
+ public bool draw(Cairo.Context ctx_in) {
+ if (conversation == null || jids == null) return false;
Cairo.Context ctx = ctx_in;
int width = this.width, height = this.height, base_factor = 1;
if (use_image_surface == -1) {
diff --git a/main/src/ui/call_window/audio_settings_popover.vala b/main/src/ui/call_window/audio_settings_popover.vala
index f7e490cd..f5af90ff 100644
--- a/main/src/ui/call_window/audio_settings_popover.vala
+++ b/main/src/ui/call_window/audio_settings_popover.vala
@@ -14,11 +14,11 @@ public class Dino.Ui.AudioSettingsPopover : Gtk.Popover {
private HashMap<ListBoxRow, Plugins.MediaDevice> row_speaker_device = new HashMap<ListBoxRow, Plugins.MediaDevice>();
public AudioSettingsPopover() {
- Box box = new Box(Orientation.VERTICAL, 15) { margin=18, visible=true };
- box.add(create_microphone_box());
- box.add(create_speaker_box());
+ Box box = new Box(Orientation.VERTICAL, 15) { visible=true };
+ box.append(create_microphone_box());
+ box.append(create_speaker_box());
- this.add(box);
+ this.set_child(box);
}
private Widget create_microphone_box() {
@@ -26,18 +26,18 @@ public class Dino.Ui.AudioSettingsPopover : Gtk.Popover {
Gee.List<Plugins.MediaDevice> devices = call_plugin.get_devices("audio", false);
Box micro_box = new Box(Orientation.VERTICAL, 10) { visible=true };
- micro_box.add(new Label("<b>" + _("Microphones") + "</b>") { use_markup=true, xalign=0, visible=true, can_focus=true /* grab initial focus*/ });
+ micro_box.append(new Label("<b>" + _("Microphones") + "</b>") { use_markup=true, xalign=0, visible=true, can_focus=true /* grab initial focus*/ });
if (devices.size == 0) {
- micro_box.add(new Label(_("No microphone found.")));
+ micro_box.append(new Label(_("No microphone found.")));
} else {
ListBox micro_list_box = new ListBox() { activate_on_single_click=true, selection_mode=SelectionMode.SINGLE, visible=true };
micro_list_box.set_header_func(listbox_header_func);
Frame micro_frame = new Frame(null) { visible=true };
- micro_frame.add(micro_list_box);
+ micro_frame.set_child(micro_list_box);
foreach (Plugins.MediaDevice device in devices) {
Label display_name_label = new Label(device.display_name) { xalign=0, visible=true };
- Image image = new Image.from_icon_name("object-select-symbolic", IconSize.BUTTON) { visible=true };
+ Image image = new Image.from_icon_name("object-select-symbolic") { visible=true };
if (current_microphone_device == null || current_microphone_device.id != device.id) {
image.opacity = 0;
}
@@ -48,21 +48,21 @@ public class Dino.Ui.AudioSettingsPopover : Gtk.Popover {
image.opacity = 1;
}
});
- Box device_box = new Box(Orientation.HORIZONTAL, 0) { spacing=7, margin=7, visible=true };
- device_box.add(image);
+ Box device_box = new Box(Orientation.HORIZONTAL, 0) { spacing=7, visible=true };
+ device_box.append(image);
Box label_box = new Box(Orientation.VERTICAL, 0) { visible = true };
- label_box.add(display_name_label);
+ label_box.append(display_name_label);
if (device.detail_name != null) {
Label detail_name_label = new Label(device.detail_name) { xalign=0, visible=true };
detail_name_label.get_style_context().add_class("dim-label");
detail_name_label.attributes = new Pango.AttrList();
detail_name_label.attributes.insert(Pango.attr_scale_new(0.8));
- label_box.add(detail_name_label);
+ label_box.append(detail_name_label);
}
- device_box.add(label_box);
+ device_box.append(label_box);
ListBoxRow list_box_row = new ListBoxRow() { visible=true };
- list_box_row.add(device_box);
- micro_list_box.add(list_box_row);
+ list_box_row.set_child(device_box);
+ micro_list_box.append(list_box_row);
row_microphone_device[list_box_row] = device;
}
@@ -71,7 +71,7 @@ public class Dino.Ui.AudioSettingsPopover : Gtk.Popover {
microphone_selected(row_microphone_device[row]);
micro_list_box.unselect_row(row);
});
- micro_box.add(micro_frame);
+ micro_box.append(micro_frame);
}
return micro_box;
@@ -82,10 +82,10 @@ public class Dino.Ui.AudioSettingsPopover : Gtk.Popover {
Gee.List<Plugins.MediaDevice> devices = call_plugin.get_devices("audio", true);
Box speaker_box = new Box(Orientation.VERTICAL, 10) { visible=true };
- speaker_box.add(new Label("<b>" + _("Speakers") +"</b>") { use_markup=true, xalign=0, visible=true });
+ speaker_box.append(new Label("<b>" + _("Speakers") +"</b>") { use_markup=true, xalign=0, visible=true });
if (devices.size == 0) {
- speaker_box.add(new Label(_("No speaker found.")));
+ speaker_box.append(new Label(_("No speaker found.")));
} else {
ListBox speaker_list_box = new ListBox() { activate_on_single_click=true, selection_mode=SelectionMode.SINGLE, visible=true };
speaker_list_box.set_header_func(listbox_header_func);
@@ -93,10 +93,10 @@ public class Dino.Ui.AudioSettingsPopover : Gtk.Popover {
});
Frame speaker_frame = new Frame(null) { visible=true };
- speaker_frame.add(speaker_list_box);
+ speaker_frame.set_child(speaker_list_box);
foreach (Plugins.MediaDevice device in devices) {
Label display_name_label = new Label(device.display_name) { xalign=0, visible=true };
- Image image = new Image.from_icon_name("object-select-symbolic", IconSize.BUTTON) { visible=true };
+ Image image = new Image.from_icon_name("object-select-symbolic") { visible=true };
if (current_speaker_device == null || current_speaker_device.id != device.id) {
image.opacity = 0;
}
@@ -107,21 +107,21 @@ public class Dino.Ui.AudioSettingsPopover : Gtk.Popover {
image.opacity = 1;
}
});
- Box device_box = new Box(Orientation.HORIZONTAL, 0) { spacing=7, margin=7, visible=true };
- device_box.add(image);
+ Box device_box = new Box(Orientation.HORIZONTAL, 0) { spacing=7, visible=true };
+ device_box.append(image);
Box label_box = new Box(Orientation.VERTICAL, 0) { visible = true };
- label_box.add(display_name_label);
+ label_box.append(display_name_label);
if (device.detail_name != null) {
Label detail_name_label = new Label(device.detail_name) { xalign=0, visible=true };
detail_name_label.get_style_context().add_class("dim-label");
detail_name_label.attributes = new Pango.AttrList();
detail_name_label.attributes.insert(Pango.attr_scale_new(0.8));
- label_box.add(detail_name_label);
+ label_box.append(detail_name_label);
}
- device_box.add(label_box);
+ device_box.append(label_box);
ListBoxRow list_box_row = new ListBoxRow() { visible=true };
- list_box_row.add(device_box);
- speaker_list_box.add(list_box_row);
+ list_box_row.set_child(device_box);
+ speaker_list_box.append(list_box_row);
row_speaker_device[list_box_row] = device;
}
@@ -130,7 +130,7 @@ public class Dino.Ui.AudioSettingsPopover : Gtk.Popover {
speaker_selected(row_speaker_device[row]);
speaker_list_box.unselect_row(row);
});
- speaker_box.add(speaker_frame);
+ speaker_box.append(speaker_frame);
}
return speaker_box;
diff --git a/main/src/ui/call_window/call_bottom_bar.vala b/main/src/ui/call_window/call_bottom_bar.vala
index b3fa2093..ddc196f2 100644
--- a/main/src/ui/call_window/call_bottom_bar.vala
+++ b/main/src/ui/call_window/call_bottom_bar.vala
@@ -9,63 +9,58 @@ public class Dino.Ui.CallBottomBar : Gtk.Box {
public bool audio_enabled { get; set; }
public bool video_enabled { get; set; }
- public static IconSize ICON_SIZE_MEDIADEVICE_BUTTON = Gtk.icon_size_register("im.dino.Dino.CALL_MEDIADEVICE_BUTTON", 10, 10);
-
public string counterpart_display_name { get; set; }
private Button audio_button = new Button() { height_request=45, width_request=45, halign=Align.START, valign=Align.START, visible=true };
private Overlay audio_button_overlay = new Overlay() { visible=true };
- private Image audio_image = new Image() { visible=true };
- private MenuButton audio_settings_button = new MenuButton() { halign=Align.END, valign=Align.END };
+ private Image audio_image = new Image() { pixel_size=22 };
+ private MenuButton audio_settings_button = new MenuButton() { icon_name="go-up-symbolic", halign=Align.END, valign=Align.END };
public AudioSettingsPopover? audio_settings_popover;
private Button video_button = new Button() { height_request=45, width_request=45, halign=Align.START, valign=Align.START, visible=true };
private Overlay video_button_overlay = new Overlay() { visible=true };
- private Image video_image = new Image() { visible=true };
- private MenuButton video_settings_button = new MenuButton() { halign=Align.END, valign=Align.END };
+ private Image video_image = new Image() { pixel_size=22 };
+ private MenuButton video_settings_button = new MenuButton() { icon_name="go-up-symbolic", halign=Align.END, valign=Align.END };
public VideoSettingsPopover? video_settings_popover;
- private Label label = new Label("") { margin=20, halign=Align.CENTER, valign=Align.CENTER, wrap=true, wrap_mode=Pango.WrapMode.WORD_CHAR, hexpand=true, visible=true };
+ private Label label = new Label("") { halign=Align.CENTER, valign=Align.CENTER, wrap=true, wrap_mode=Pango.WrapMode.WORD_CHAR, hexpand=true, visible=true };
private Stack stack = new Stack() { visible=true };
public CallBottomBar() {
Object(orientation:Orientation.HORIZONTAL, spacing:0);
- Box main_buttons = new Box(Orientation.HORIZONTAL, 20) { margin_start=40, margin_end=40, margin=20, halign=Align.CENTER, hexpand=true, visible=true };
+ Box main_buttons = new Box(Orientation.HORIZONTAL, 20) { margin_start=40, margin_end=40, margin_bottom=20, margin_top=20, halign=Align.CENTER, hexpand=true, visible=true };
- audio_button.add(audio_image);
+ audio_button.set_child(audio_image);
audio_button.get_style_context().add_class("call-button");
audio_button.clicked.connect(() => { audio_enabled = !audio_enabled; });
audio_button.margin_end = audio_button.margin_bottom = 5; // space for the small settings button
- audio_button_overlay.add(audio_button);
+ audio_button_overlay.set_child(audio_button);
audio_button_overlay.add_overlay(audio_settings_button);
- audio_settings_button.set_image(new Image.from_icon_name("go-up-symbolic", ICON_SIZE_MEDIADEVICE_BUTTON) { visible=true });
audio_settings_button.get_style_context().add_class("call-mediadevice-settings-button");
- audio_settings_button.use_popover = true;
- main_buttons.add(audio_button_overlay);
+ main_buttons.append(audio_button_overlay);
- video_button.add(video_image);
+ video_button.set_child(video_image);
video_button.get_style_context().add_class("call-button");
video_button.clicked.connect(() => { video_enabled = !video_enabled; });
video_button.margin_end = video_button.margin_bottom = 5;
- video_button_overlay.add(video_button);
+ video_button_overlay.set_child(video_button);
video_button_overlay.add_overlay(video_settings_button);
- video_settings_button.set_image(new Image.from_icon_name("go-up-symbolic", ICON_SIZE_MEDIADEVICE_BUTTON) { visible=true });
video_settings_button.get_style_context().add_class("call-mediadevice-settings-button");
- video_settings_button.use_popover = true;
- main_buttons.add(video_button_overlay);
+ main_buttons.append(video_button_overlay);
- Button button_hang = new Button.from_icon_name("dino-phone-hangup-symbolic", IconSize.LARGE_TOOLBAR) { height_request=45, width_request=45, halign=Align.START, valign=Align.START, visible=true };
+ Button button_hang = new Button() { height_request=45, width_request=45, halign=Align.START, valign=Align.START, visible=true };
+ button_hang.set_child(new Image() { icon_name="dino-phone-hangup-symbolic", pixel_size=22 });
button_hang.get_style_context().add_class("call-button");
button_hang.get_style_context().add_class("destructive-action");
button_hang.clicked.connect(() => hang_up());
- main_buttons.add(button_hang);
+ main_buttons.append(button_hang);
label.get_style_context().add_class("text-no-controls");
stack.add_named(main_buttons, "control-buttons");
stack.add_named(label, "label");
- this.add(stack);
+ this.append(stack);
this.notify["audio-enabled"].connect(on_audio_enabled_changed);
this.notify["video-enabled"].connect(on_video_enabled_changed);
@@ -85,18 +80,15 @@ public class Dino.Ui.CallBottomBar : Gtk.Box {
if (!show) return null;
audio_settings_popover = new AudioSettingsPopover();
-
audio_settings_button.popover = audio_settings_popover;
-
- audio_settings_popover.set_relative_to(audio_settings_button);
- audio_settings_popover.microphone_selected.connect(() => { audio_settings_button.active = false; });
- audio_settings_popover.speaker_selected.connect(() => { audio_settings_button.active = false; });
+ audio_settings_popover.microphone_selected.connect(() => { audio_settings_button.popdown(); });
+ audio_settings_popover.speaker_selected.connect(() => { audio_settings_button.popdown(); });
return audio_settings_popover;
}
public void show_audio_device_error() {
- audio_settings_button.set_image(new Image.from_icon_name("dialog-warning-symbolic", IconSize.BUTTON) { visible=true });
+ audio_settings_button.set_icon_name("dialog-warning-symbolic");
Util.force_error_color(audio_settings_button);
}
@@ -106,28 +98,24 @@ public class Dino.Ui.CallBottomBar : Gtk.Box {
if (!show) return null;
video_settings_popover = new VideoSettingsPopover();
-
-
video_settings_button.popover = video_settings_popover;
-
- video_settings_popover.set_relative_to(video_settings_button);
- video_settings_popover.camera_selected.connect(() => { video_settings_button.active = false; });
+ video_settings_popover.camera_selected.connect(() => { video_settings_button.popdown(); });
return video_settings_popover;
}
public void show_video_device_error() {
- video_settings_button.set_image(new Image.from_icon_name("dialog-warning-symbolic", IconSize.BUTTON) { visible=true });
+ video_settings_button.set_icon_name("dialog-warning-symbolic");
Util.force_error_color(video_settings_button);
}
public void on_audio_enabled_changed() {
if (audio_enabled) {
- audio_image.set_from_icon_name("dino-microphone-symbolic", IconSize.LARGE_TOOLBAR);
+ audio_image.icon_name = "dino-microphone-symbolic";
audio_button.get_style_context().add_class("white-button");
audio_button.get_style_context().remove_class("transparent-white-button");
} else {
- audio_image.set_from_icon_name("dino-microphone-off-symbolic", IconSize.LARGE_TOOLBAR);
+ audio_image.icon_name = "dino-microphone-off-symbolic";
audio_button.get_style_context().remove_class("white-button");
audio_button.get_style_context().add_class("transparent-white-button");
}
@@ -135,12 +123,12 @@ public class Dino.Ui.CallBottomBar : Gtk.Box {
public void on_video_enabled_changed() {
if (video_enabled) {
- video_image.set_from_icon_name("dino-video-symbolic", IconSize.LARGE_TOOLBAR);
+ video_image.icon_name = "dino-video-symbolic";
video_button.get_style_context().add_class("white-button");
video_button.get_style_context().remove_class("transparent-white-button");
} else {
- video_image.set_from_icon_name("dino-video-off-symbolic", IconSize.LARGE_TOOLBAR);
+ video_image.icon_name = "dino-video-off-symbolic";
video_button.get_style_context().remove_class("white-button");
video_button.get_style_context().add_class("transparent-white-button");
}
@@ -152,6 +140,6 @@ public class Dino.Ui.CallBottomBar : Gtk.Box {
}
public bool is_menu_active() {
- return video_settings_button.active || audio_settings_button.active;
+ return video_settings_button.popover.visible || audio_settings_button.popover.visible; // TODO gtk4 does this work? check for null?
}
} \ No newline at end of file
diff --git a/main/src/ui/call_window/call_connection_details_window.vala b/main/src/ui/call_window/call_connection_details_window.vala
index 1d5265c9..0905908c 100644
--- a/main/src/ui/call_window/call_connection_details_window.vala
+++ b/main/src/ui/call_window/call_connection_details_window.vala
@@ -4,16 +4,16 @@ namespace Dino.Ui {
public class CallConnectionDetailsWindow : Gtk.Window {
- public Box box = new Box(Orientation.VERTICAL, 15) { margin=10, halign=Align.CENTER, valign=Align.CENTER, visible=true };
+ public Box box = new Box(Orientation.VERTICAL, 15) { halign=Align.CENTER, valign=Align.CENTER, visible=true };
private bool video_added = false;
private CallContentDetails audio_details = new CallContentDetails("Audio") { visible=true };
private CallContentDetails video_details = new CallContentDetails("Video");
public CallConnectionDetailsWindow() {
- box.add(audio_details);
- box.add(video_details);
- add(box);
+ box.append(audio_details);
+ box.append(video_details);
+ set_child(box);
}
public void update_content(PeerInfo peer_info) {
diff --git a/main/src/ui/call_window/call_encryption_button.vala b/main/src/ui/call_window/call_encryption_button.vala
index a7081954..095db2b4 100644
--- a/main/src/ui/call_window/call_encryption_button.vala
+++ b/main/src/ui/call_window/call_encryption_button.vala
@@ -2,61 +2,63 @@ using Dino.Entities;
using Gtk;
using Pango;
-public class Dino.Ui.CallEncryptionButton : MenuButton {
+public class Dino.Ui.CallEncryptionButtonController {
- private Image encryption_image = new Image.from_icon_name("", IconSize.BUTTON) { visible=true };
private bool has_been_set = false;
public bool controls_active { get; set; default=false; }
- public CallEncryptionButton() {
- this.opacity = 0;
- add(encryption_image);
- this.set_popover(popover);
+ public MenuButton button;
- this.notify["controls-active"].connect(update_opacity);
+ public CallEncryptionButtonController(MenuButton button) {
+ this.button = button;
+
+ button.opacity = 0;
+// button.set_popover(popover);
+
+ button.notify["controls-active"].connect(update_opacity);
}
public void set_icon(bool encrypted, string? icon_name) {
if (encrypted) {
- encryption_image.set_from_icon_name(icon_name ?? "changes-prevent-symbolic", IconSize.BUTTON);
- get_style_context().remove_class("unencrypted");
+ button.icon_name = icon_name ?? "changes-prevent-symbolic";
+ button.get_style_context().remove_class("unencrypted");
} else {
- encryption_image.set_from_icon_name(icon_name ?? "changes-allow-symbolic", IconSize.BUTTON);
- get_style_context().add_class("unencrypted");
+ button.icon_name = icon_name ?? "changes-allow-symbolic";
+ button.get_style_context().add_class("unencrypted");
}
has_been_set = true;
update_opacity();
}
public void set_info(string? title, bool show_keys, Xmpp.Xep.Jingle.ContentEncryption? audio_encryption, Xmpp.Xep.Jingle.ContentEncryption? video_encryption) {
- Popover popover = new Popover(this);
- this.set_popover(popover);
+ Popover popover = new Popover();
+ button.set_popover(popover);
if (audio_encryption == null) {
- popover.add(new Label("This call is unencrypted.") { margin=10, visible=true } );
+ popover.set_child(new Label("This call is unencrypted.") { visible=true } );
return;
}
if (title != null && !show_keys) {
- popover.add(new Label(title) { use_markup=true, margin=10, visible=true } );
+ popover.set_child(new Label(title) { use_markup=true, visible=true } );
return;
}
- Box box = new Box(Orientation.VERTICAL, 10) { margin=10, visible=true };
- box.add(new Label("<b>%s</b>".printf(title ?? "This call is end-to-end encrypted.")) { use_markup=true, xalign=0, visible=true });
+ Box box = new Box(Orientation.VERTICAL, 10) { visible=true };
+ box.append(new Label("<b>%s</b>".printf(title ?? "This call is end-to-end encrypted.")) { use_markup=true, xalign=0, visible=true });
if (video_encryption == null) {
- box.add(create_media_encryption_grid(audio_encryption));
+ box.append(create_media_encryption_grid(audio_encryption));
} else {
- box.add(new Label("<b>Audio</b>") { use_markup=true, xalign=0, visible=true });
- box.add(create_media_encryption_grid(audio_encryption));
- box.add(new Label("<b>Video</b>") { use_markup=true, xalign=0, visible=true });
- box.add(create_media_encryption_grid(video_encryption));
+ box.append(new Label("<b>Audio</b>") { use_markup=true, xalign=0, visible=true });
+ box.append(create_media_encryption_grid(audio_encryption));
+ box.append(new Label("<b>Video</b>") { use_markup=true, xalign=0, visible=true });
+ box.append(create_media_encryption_grid(video_encryption));
}
- popover.add(box);
+ popover.set_child(box);
}
public void update_opacity() {
- this.opacity = controls_active && has_been_set ? 1 : 0;
+ button.opacity = controls_active && has_been_set ? 1 : 0;
}
private Grid create_media_encryption_grid(Xmpp.Xep.Jingle.ContentEncryption? encryption) {
diff --git a/main/src/ui/call_window/call_window.vala b/main/src/ui/call_window/call_window.vala
index ab969597..5facd574 100644
--- a/main/src/ui/call_window/call_window.vala
+++ b/main/src/ui/call_window/call_window.vala
@@ -15,13 +15,15 @@ namespace Dino.Ui {
public Grid grid = new Grid() { visible=true };
public CallBottomBar bottom_bar = new CallBottomBar() { visible=true };
public Revealer bottom_bar_revealer = new Revealer() { valign=Align.END, transition_type=RevealerTransitionType.CROSSFADE, transition_duration=200, visible=true };
- public HeaderBar header_bar = new HeaderBar() { valign=Align.START, halign=Align.END, show_close_button=true, visible=true, opacity=0.0 };
+ public HeaderBar header_bar = new HeaderBar() { valign=Align.START, halign=Align.END, show_title_buttons=true, visible=true, opacity=0.0 };
public Revealer header_bar_revealer = new Revealer() { halign=Align.END, valign=Align.START, transition_type=RevealerTransitionType.SLIDE_LEFT, transition_duration=200, visible=true, reveal_child=false };
public Box own_video_box = new Box(Orientation.HORIZONTAL, 0) { halign=Align.END, valign=Align.END, visible=true };
private Widget? own_video = null;
private HashMap<string, ParticipantWidget> participant_widgets = new HashMap<string, ParticipantWidget>();
private ArrayList<string> participants = new ArrayList<string>();
+ private EventControllerMotion this_motion_events = new EventControllerMotion();
+
private int own_video_width = 150;
private int own_video_height = 100;
@@ -31,32 +33,36 @@ namespace Dino.Ui {
construct {
header_bar.get_style_context().add_class("call-header-bar");
- header_bar.custom_title = new Box(Orientation.VERTICAL, 0);
- header_bar.spacing = 0;
- header_bar_revealer.add(header_bar);
- bottom_bar_revealer.add(bottom_bar);
+ header_bar.title_widget = new Box(Orientation.VERTICAL, 0);
+// header_bar.spacing = 0;
+ header_bar_revealer.set_child(header_bar);
+ bottom_bar_revealer.set_child(bottom_bar);
own_video_box.get_style_context().add_class("own-video");
this.get_style_context().add_class("dino-call-window");
- overlay.add(grid);
+ overlay.set_child(grid);
overlay.add_overlay(own_video_box);
overlay.add_overlay(bottom_bar_revealer);
overlay.add_overlay(header_bar_revealer);
overlay.get_child_position.connect(on_get_child_position);
- add(overlay);
+ set_child(overlay);
}
public CallWindow() {
this.bind_property("controls-active", bottom_bar_revealer, "reveal-child", BindingFlags.SYNC_CREATE);
- this.motion_notify_event.connect(reveal_control_elements);
- this.enter_notify_event.connect(reveal_control_elements);
- this.leave_notify_event.connect(reveal_control_elements);
- this.configure_event.connect(reveal_control_elements); // upon resizing
+ ((Widget) this).add_controller(this_motion_events);
+ this_motion_events.motion.connect(reveal_control_elements);
+ this_motion_events.enter.connect(reveal_control_elements);
+ this_motion_events.leave.connect(reveal_control_elements);
+
+ this.notify["default-width"].connect(reveal_control_elements);
+ this.notify["default-height"].connect(reveal_control_elements);
- this.configure_event.connect(reposition_participant_widgets);
+ this.notify["default-width"].connect(reposition_participant_widgets);
+ this.notify["default-height"].connect(reposition_participant_widgets);
this.set_titlebar(new OutsideHeaderBar(this.header_bar) { visible=true });
@@ -103,11 +109,10 @@ namespace Dino.Ui {
}
}
- private bool reposition_participant_widgets() {
- int width, height;
- this.get_size(out width,out height);
+ private void reposition_participant_widgets() {
+ int width = get_size(Orientation.HORIZONTAL);
+ int height = get_size(Orientation.VERTICAL);
reposition_participant_widgets_rec(participants, width, height, 0, 0, 0, 0);
- return false;
}
private void reposition_participant_widgets_rec(ArrayList<string> participants, int width, int height, int margin_top, int margin_right, int margin_bottom, int margin_left) {
@@ -144,14 +149,14 @@ namespace Dino.Ui {
}
public void set_own_video(Widget? widget_) {
- own_video_box.foreach((widget) => { own_video_box.remove(widget); });
+// own_video_box.foreach((widget) => { own_video_box.remove(widget); });
own_video = widget_;
if (own_video == null) {
- own_video = new Box(Orientation.HORIZONTAL, 0) { expand=true };
+ own_video = new Box(Orientation.HORIZONTAL, 0) { hexpand=true, vexpand=true };
}
own_video.visible = true;
- own_video_box.add(own_video);
+ own_video_box.append(own_video);
}
public void set_own_video_ratio(int width, int height) {
@@ -165,7 +170,7 @@ namespace Dino.Ui {
}
public void unset_own_video() {
- own_video_box.foreach((widget) => { own_video_box.remove(widget); });
+// own_video_box.foreach((widget) => { own_video_box.remove(widget); });
}
public void set_status(string participant_id, string state) {
@@ -192,13 +197,12 @@ namespace Dino.Ui {
bottom_bar.show_counterpart_ended(text);
}
- private bool reveal_control_elements() {
+ private void reveal_control_elements() {
if (!bottom_bar_revealer.child_revealed) {
controls_active = true;
}
timeout_hide_control_elements();
- return false;
}
private void timeout_hide_control_elements() {
@@ -229,8 +233,8 @@ namespace Dino.Ui {
private bool on_get_child_position(Widget widget, out Gdk.Rectangle allocation) {
if (widget == own_video_box) {
- int width, height;
- this.get_size(out width,out height);
+ int width = get_size(Orientation.HORIZONTAL);
+ int height = get_size(Orientation.VERTICAL);
allocation = Gdk.Rectangle();
allocation.width = own_video_width;
@@ -252,8 +256,8 @@ namespace Dino.Ui {
public OutsideHeaderBar(HeaderBar header_bar) {
this.header_bar = header_bar;
- size_allocate.connect_after(on_header_bar_size_allocate);
- header_bar.size_allocate.connect(on_header_bar_size_allocate);
+// size_allocate.connect_after(on_header_bar_size_allocate);
+// header_bar.size_allocate.connect(on_header_bar_size_allocate);
}
public void on_header_bar_size_allocate() {
@@ -263,7 +267,7 @@ namespace Dino.Ui {
Allocation alloc;
get_allocation(out alloc);
alloc.height = header_bar_alloc.height;
- set_allocation(alloc);
+// set_allocation(alloc);
}
}
} \ No newline at end of file
diff --git a/main/src/ui/call_window/call_window_controller.vala b/main/src/ui/call_window/call_window_controller.vala
index 276fff98..6ff02964 100644
--- a/main/src/ui/call_window/call_window_controller.vala
+++ b/main/src/ui/call_window/call_window_controller.vala
@@ -31,7 +31,7 @@ public class Dino.Ui.CallWindowController : Object {
this.stream_interactor = stream_interactor;
this.calls = stream_interactor.get_module(Calls.IDENTITY);
- this.own_video = call_plugin.create_widget(Plugins.WidgetType.GTK);
+ this.own_video = call_plugin.create_widget(Plugins.WidgetType.GTK4);
call_window.set_default_size(704, 528); // 640x480 * 1.1
@@ -70,9 +70,10 @@ public class Dino.Ui.CallWindowController : Object {
call_window.destroy();
this.dispose();
});
- call_window_handler_ids += call_window.destroy.connect(() => {
+ call_window_handler_ids += call_window.close_request.connect(() => {
call_state.end();
this.dispose();
+ return false;
});
bottom_bar_handler_ids += call_window.bottom_bar.notify["audio-enabled"].connect(() => {
call_state.mute_own_audio(!call_window.bottom_bar.audio_enabled);
@@ -81,17 +82,23 @@ public class Dino.Ui.CallWindowController : Object {
call_state.mute_own_video(!call_window.bottom_bar.video_enabled);
update_own_video();
});
- call_window_handler_ids += call_window.configure_event.connect((event) => {
- if (window_width == -1 || window_height == -1) return false;
- int current_height = this.call_window.get_allocated_height();
+ call_window_handler_ids += call_window.notify["default-width"].connect((event) => {
+ if (call_window.default_width == -1) return;
int current_width = this.call_window.get_allocated_width();
- if (window_width != current_width || window_height != current_height) {
- debug("Call window size changed by user. Disabling auto window-to-video size adaptation. %i->%i x %i->%i", window_width, current_width, window_height, current_height);
+ if (window_width != current_width) {
+ debug("Call window size changed by user. Disabling auto window-to-video size adaptation. Width %i->%i", window_width, current_width);
+ window_size_changed = true;
+ }
+ });
+ call_window_handler_ids += call_window.notify["default-height"].connect((event) => {
+ if (call_window.default_height == -1) return;
+ int current_height = this.call_window.get_allocated_height();
+ if (window_height != current_height) {
+ debug("Call window size changed by user. Disabling auto window-to-video size adaptation. Height %i->%i", window_height, current_height);
window_size_changed = true;
}
- return false;
});
- call_window_handler_ids += call_window.realize.connect(() => {
+ call_window_handler_ids += ((Widget)call_window).realize.connect(() => {
capture_window_size();
});
@@ -138,7 +145,7 @@ public class Dino.Ui.CallWindowController : Object {
Gee.List<Account> acc_list = new ArrayList<Account>(Account.equals_func);
acc_list.add(call.account);
SelectContactDialog add_chat_dialog = new SelectContactDialog(stream_interactor, acc_list);
- add_chat_dialog.set_transient_for((Window) call_window.get_toplevel());
+ add_chat_dialog.set_transient_for((Window) call_window.get_root());
add_chat_dialog.title = _("Invite to Call");
add_chat_dialog.ok_button.label = _("Invite");
add_chat_dialog.selected.connect((account, jid) => {
@@ -192,11 +199,11 @@ public class Dino.Ui.CallWindowController : Object {
}
});
peer_state.encryption_updated.connect((audio_encryption, video_encryption, same) => {
- update_encryption_indicator(participant_widgets[peer_id].encryption_button, audio_encryption, video_encryption, same);
+ update_encryption_indicator(participant_widgets[peer_id].encryption_button_controller, audio_encryption, video_encryption, same);
});
}
- private void update_encryption_indicator(CallEncryptionButton encryption_button, Xep.Jingle.ContentEncryption? audio_encryption, Xep.Jingle.ContentEncryption? video_encryption, bool same) {
+ private void update_encryption_indicator(CallEncryptionButtonController encryption_button, Xep.Jingle.ContentEncryption? audio_encryption, Xep.Jingle.ContentEncryption? video_encryption, bool same) {
string? title = null;
string? icon_name = null;
bool show_keys = true;
@@ -235,24 +242,26 @@ public class Dino.Ui.CallWindowController : Object {
return true;
});
conn_details_window.set_transient_for(call_window);
- conn_details_window.destroy.connect(() => Source.remove(timeout_handle_id));
+ conn_details_window.close_request.connect(() => { Source.remove(timeout_handle_id); return false; });
conn_details_window.present();
- this.call_window.destroy.connect(() => conn_details_window.close() );
+ this.call_window.close_request.connect(() => { conn_details_window.close(); return false; });
});
invite_handler_ids[participant_id] += participant_widget.invite_button_clicked.connect(() => invite_button_clicked());
participant_widgets[participant_id] = participant_widget;
call_window.add_participant(participant_id, participant_widget);
- participant_videos[participant_id] = call_plugin.create_widget(Plugins.WidgetType.GTK);
+ participant_videos[participant_id] = call_plugin.create_widget(Plugins.WidgetType.GTK4);
participant_videos[participant_id].resolution_changed.connect((width, height) => {
if (window_size_changed || participant_widgets.size > 1) return;
if (width == 0 || height == 0) return;
if (width > height) {
- call_window.resize(704, (int) (height * 704 / width));
+ call_window.default_width = 704;
+ call_window.default_height = (int) (height * 704 / width);
} else {
- call_window.resize((int) (width * 704 / height), 704);
+ call_window.default_width = (int) (width * 704 / height);
+ call_window.default_height = 704;
}
capture_window_size();
});
diff --git a/main/src/ui/call_window/participant_widget.vala b/main/src/ui/call_window/participant_widget.vala
index f18ae242..ecd6cbb3 100644
--- a/main/src/ui/call_window/participant_widget.vala
+++ b/main/src/ui/call_window/participant_widget.vala
@@ -6,15 +6,19 @@ using Gtk;
namespace Dino.Ui {
- public class ParticipantWidget : Gtk.Overlay {
+ public class ParticipantWidget : Box {
+ public Overlay overlay = new Overlay();
public Widget main_widget;
public HeaderBar header_bar = new HeaderBar() { valign=Align.START, visible=true };
+ public Label title_label = new Label("");
+ public Label subtitle_label = new Label("");
public Box inner_box = new Box(Orientation.HORIZONTAL, 0) { margin_start=5, margin_top=5, hexpand=true, visible=true };
public Box title_box = new Box(Orientation.VERTICAL, 0) { valign=Align.CENTER, hexpand=true, visible=true };
- public CallEncryptionButton encryption_button = new CallEncryptionButton() { opacity=0, relief=ReliefStyle.NONE, height_request=30, width_request=30, margin_end=5, visible=true };
- public MenuButton menu_button = new MenuButton() { relief=ReliefStyle.NONE, visible=true };
- public Button invite_button = new Button.from_icon_name("dino-account-plus") { relief=ReliefStyle.NONE, visible=true };
+ public MenuButton encryption_button = new MenuButton() { opacity=0, has_frame=false, height_request=30, width_request=30, margin_end=5, visible=true };
+ public CallEncryptionButtonController encryption_button_controller;
+ public MenuButton menu_button = new MenuButton() { icon_name="open-menu-symbolic", has_frame=false, visible=true };
+ public Button invite_button = new Button.from_icon_name("dino-account-plus") { has_frame=false, visible=true };
public bool shows_video = false;
public string? participant_name;
@@ -26,19 +30,38 @@ namespace Dino.Ui {
public signal void debug_information_clicked();
public signal void invite_button_clicked();
+ class construct {
+ install_action("menu.debuginfo", null, (widget, action_name) => { ((ParticipantWidget) widget).debug_information_clicked(); });
+ }
+
public ParticipantWidget(string participant_name) {
+ encryption_button_controller = new CallEncryptionButtonController(encryption_button);
+
this.participant_name = participant_name;
- header_bar.title = participant_name;
+
+ Box titles_box = new Box(Orientation.VERTICAL, 0) { valign=Align.CENTER };
+ title_label.attributes = new AttrList();
+ title_label.attributes.insert(Pango.attr_weight_new(Weight.BOLD));
+ titles_box.append(title_label);
+ subtitle_label.attributes = new AttrList();
+ subtitle_label.attributes.insert(Pango.attr_scale_new(Pango.Scale.SMALL));
+ subtitle_label.get_style_context().add_class("dim-label");
+ titles_box.append(subtitle_label);
+
+ header_bar.set_title_widget(titles_box);
+ title_label.label = participant_name;
+
header_bar.get_style_context().add_class("participant-header-bar");
header_bar.pack_start(invite_button);
header_bar.pack_start(encryption_button);
header_bar.pack_end(menu_button);
- menu_button.image = new Image.from_icon_name("open-menu-symbolic", IconSize.MENU);
- menu_button.set_popover(create_menu());
+ create_menu();
+
invite_button.clicked.connect(() => invite_button_clicked());
- this.add_overlay(header_bar);
+ this.append(overlay);
+ overlay.add_overlay(header_bar);
this.notify["controls-active"].connect(reveal_or_hide_controls);
this.notify["may-show-invite-button"].connect(reveal_or_hide_controls);
@@ -48,7 +71,7 @@ namespace Dino.Ui {
this.is_highest_row = is_highest;
this.is_start_row = is_start;
- header_bar.show_close_button = is_highest_row;
+ header_bar.show_title_buttons = is_highest_row;
if (is_highest_row) {
header_bar.get_style_context().add_class("call-header-background");
Gtk.Settings? gtk_settings = Gtk.Settings.get_default();
@@ -78,37 +101,35 @@ namespace Dino.Ui {
} else {
avatar.set_text("?", false);
}
- box.add(avatar);
+ box.append(avatar);
set_participant_widget(box);
}
private void set_participant_widget(Widget widget) {
- widget.expand = true;
- if (main_widget != null) this.remove(main_widget);
+ widget.hexpand = widget.vexpand = true;
main_widget = widget;
- this.add(main_widget);
+ overlay.set_child(main_widget);
}
- private PopoverMenu create_menu() {
- PopoverMenu menu = new PopoverMenu();
- Box box = new Box(Orientation.VERTICAL, 0) { margin=10, visible=true };
- ModelButton debug_information_button = new ModelButton() { text=_("Debug information"), visible=true };
- debug_information_button.clicked.connect(() => debug_information_clicked());
- box.add(debug_information_button);
- menu.add(box);
- return menu;
+ private void create_menu() {
+ Menu menu_model = new Menu();
+ menu_model.append(_("Debug information"), "menu.debuginfo");
+ Gtk.PopoverMenu popover_menu = new Gtk.PopoverMenu.from_model(menu_model);
+ menu_button.popover = popover_menu;
}
public void set_status(string state) {
+ subtitle_label.visible = true;
+
if (state == "requested") {
- header_bar.subtitle = _("Calling…");
+ subtitle_label.label = _("Calling…");
} else if (state == "ringing") {
- header_bar.subtitle = _("Ringing…");
+ subtitle_label.label = _("Ringing…");
} else if (state == "establishing") {
- header_bar.subtitle = _("Connecting…");
+ subtitle_label.label = _("Connecting…");
} else {
- header_bar.subtitle = "";
+ subtitle_label.visible = false;
}
}
diff --git a/main/src/ui/call_window/video_settings_popover.vala b/main/src/ui/call_window/video_settings_popover.vala
index 7dd5ec9f..c931c466 100644
--- a/main/src/ui/call_window/video_settings_popover.vala
+++ b/main/src/ui/call_window/video_settings_popover.vala
@@ -11,10 +11,10 @@ public class Dino.Ui.VideoSettingsPopover : Gtk.Popover {
private HashMap<ListBoxRow, Plugins.MediaDevice> row_device = new HashMap<ListBoxRow, Plugins.MediaDevice>();
public VideoSettingsPopover() {
- Box box = new Box(Orientation.VERTICAL, 15) { margin=18, visible=true };
- box.add(create_camera_box());
+ Box box = new Box(Orientation.VERTICAL, 15) { visible=true };
+ box.append(create_camera_box());
- this.add(box);
+ this.set_child(box);
}
private Widget create_camera_box() {
@@ -22,18 +22,18 @@ public class Dino.Ui.VideoSettingsPopover : Gtk.Popover {
Gee.List<Plugins.MediaDevice> devices = call_plugin.get_devices("video", false);
Box camera_box = new Box(Orientation.VERTICAL, 10) { visible=true };
- camera_box.add(new Label("<b>" + _("Cameras") + "</b>") { use_markup=true, xalign=0, visible=true, can_focus=true /* grab initial focus*/ });
+ camera_box.append(new Label("<b>" + _("Cameras") + "</b>") { use_markup=true, xalign=0, visible=true, can_focus=true /* grab initial focus*/ });
if (devices.size == 0) {
- camera_box.add(new Label(_("No camera found.")) { visible=true });
+ camera_box.append(new Label(_("No camera found.")) { visible=true });
} else {
ListBox list_box = new ListBox() { activate_on_single_click=true, selection_mode=SelectionMode.SINGLE, visible=true };
list_box.set_header_func(listbox_header_func);
Frame frame = new Frame(null) { visible=true };
- frame.add(list_box);
+ frame.set_child(list_box);
foreach (Plugins.MediaDevice device in devices) {
Label display_name_label = new Label(device.display_name) { xalign=0, visible=true };
- Image image = new Image.from_icon_name("object-select-symbolic", IconSize.BUTTON) { visible=true };
+ Image image = new Image.from_icon_name("object-select-symbolic") { visible=true };
if (current_device == null || current_device.id != device.id) {
image.opacity = 0;
}
@@ -44,21 +44,21 @@ public class Dino.Ui.VideoSettingsPopover : Gtk.Popover {
image.opacity = 1;
}
});
- Box device_box = new Box(Orientation.HORIZONTAL, 0) { spacing=7, margin=7, visible=true };
- device_box.add(image);
+ Box device_box = new Box(Orientation.HORIZONTAL, 0) { spacing=7, visible=true };
+ device_box.append(image);
Box label_box = new Box(Orientation.VERTICAL, 0) { visible = true };
- label_box.add(display_name_label);
+ label_box.append(display_name_label);
if (device.detail_name != null) {
Label detail_name_label = new Label(device.detail_name) { xalign=0, visible=true };
detail_name_label.get_style_context().add_class("dim-label");
detail_name_label.attributes = new Pango.AttrList();
detail_name_label.attributes.insert(Pango.attr_scale_new(0.8));
- label_box.add(detail_name_label);
+ label_box.append(detail_name_label);
}
- device_box.add(label_box);
+ device_box.append(label_box);
ListBoxRow list_box_row = new ListBoxRow() { visible=true };
- list_box_row.add(device_box);
- list_box.add(list_box_row);
+ list_box_row.set_child(device_box);
+ list_box.append(list_box_row);
row_device[list_box_row] = device;
}
@@ -67,7 +67,7 @@ public class Dino.Ui.VideoSettingsPopover : Gtk.Popover {
camera_selected(row_device[row]);
list_box.unselect_row(row);
});
- camera_box.add(frame);
+ camera_box.append(frame);
}
return camera_box;
diff --git a/main/src/ui/chat_input/chat_input_controller.vala b/main/src/ui/chat_input/chat_input_controller.vala
index d7a69c3d..41891519 100644
--- a/main/src/ui/chat_input/chat_input_controller.vala
+++ b/main/src/ui/chat_input/chat_input_controller.vala
@@ -34,9 +34,12 @@ public class ChatInputController : Object {
reset_input_field_status();
- chat_input.chat_text_view.text_view.buffer.changed.connect(on_text_input_changed);
- chat_input.chat_text_view.text_view.key_press_event.connect(on_text_input_key_press);
+ var text_input_key_events = new EventControllerKey();
+ text_input_key_events.key_pressed.connect(on_text_input_key_press);
+ chat_input.chat_text_view.text_view.add_controller(text_input_key_events);
+
chat_input.chat_text_view.text_view.paste_clipboard.connect(() => clipboard_pasted());
+ chat_input.chat_text_view.text_view.buffer.changed.connect(on_text_input_changed);
chat_text_view_controller.send_text.connect(send_text);
@@ -50,7 +53,7 @@ public class ChatInputController : Object {
status_description_label.activate_link.connect((uri) => {
if (uri == OPEN_CONVERSATION_DETAILS_URI){
ContactDetails.Dialog contact_details_dialog = new ContactDetails.Dialog(stream_interactor, conversation);
- contact_details_dialog.set_transient_for((Gtk.Window) chat_input.get_toplevel());
+ contact_details_dialog.set_transient_for((Gtk.Window) chat_input.get_root());
contact_details_dialog.present();
}
return true;
@@ -136,7 +139,7 @@ public class ChatInputController : Object {
case "/ping":
Xmpp.XmppStream? stream = stream_interactor.get_stream(conversation.account);
try {
- stream.get_module(Xmpp.Xep.Ping.Module.IDENTITY).send_ping.begin(stream, conversation.counterpart.with_resource(token[1]), null);
+ stream.get_module(Xmpp.Xep.Ping.Module.IDENTITY).send_ping.begin(stream, conversation.counterpart.with_resource(token[1]));
} catch (Xmpp.InvalidJidError e) {
warning("Could not ping invalid Jid: %s", e.message);
}
@@ -184,8 +187,8 @@ public class ChatInputController : Object {
}
}
- private bool on_text_input_key_press(EventKey event) {
- if (event.keyval == Gdk.Key.Up && chat_input.chat_text_view.text_view.buffer.text == "") {
+ private bool on_text_input_key_press(uint keyval, uint keycode, Gdk.ModifierType state) {
+ if (keyval == Gdk.Key.Up && chat_input.chat_text_view.text_view.buffer.text == "") {
activate_last_message_correction();
return true;
} else {
diff --git a/main/src/ui/chat_input/chat_text_view.vala b/main/src/ui/chat_input/chat_text_view.vala
index 2f8393d2..b1f719b6 100644
--- a/main/src/ui/chat_input/chat_text_view.vala
+++ b/main/src/ui/chat_input/chat_text_view.vala
@@ -30,61 +30,64 @@ public class ChatTextViewController : Object {
}
}
-public class ChatTextView : ScrolledWindow {
+public class ChatTextView : Box {
public signal void send_text();
public signal void cancel_input();
- public TextView text_view = new TextView() { can_focus=true, hexpand=true, margin=8, wrap_mode=Gtk.WrapMode.WORD_CHAR, valign=Align.CENTER, visible=true };
+ public ScrolledWindow scrolled_window = new ScrolledWindow() { propagate_natural_height=true, max_content_height=300 };
+ public TextView text_view = new TextView() { hexpand=true, wrap_mode=Gtk.WrapMode.WORD_CHAR, valign=Align.CENTER, margin_top=7, margin_bottom=7 };
private int vscrollbar_min_height;
private SmileyConverter smiley_converter;
- public EditHistory edit_history;
- private SpellChecker spell_checker;
+// private SpellChecker spell_checker;
construct {
- max_content_height = 300;
- propagate_natural_height = true;
- this.add(text_view);
+ scrolled_window.set_child(text_view);
+ this.append(scrolled_window);
smiley_converter = new SmileyConverter(text_view);
- edit_history = new EditHistory(text_view);
- spell_checker = new SpellChecker(text_view);
- this.get_vscrollbar().get_preferred_height(out vscrollbar_min_height, null);
- this.vadjustment.notify["upper"].connect_after(on_upper_notify);
- text_view.key_press_event.connect(on_text_input_key_press);
+// scrolled_window.get_vscrollbar().get_preferred_size(out vscrollbar_min_size, null);
+ scrolled_window.vadjustment.notify["upper"].connect(on_upper_notify);
- Gtk.drag_dest_unset(text_view);
+ var text_input_key_events = new EventControllerKey();
+ text_input_key_events.key_pressed.connect(on_text_input_key_press);
+ text_view.add_controller(text_input_key_events);
+
+ text_view.realize.connect(() => {
+ var minimum_size = new Requisition();
+ scrolled_window.get_preferred_size(out minimum_size, null);
+ vscrollbar_min_height = minimum_size.height;
+ });
+// Gtk.drag_dest_unset(text_view);
}
public void initialize_for_conversation(Conversation conversation) {
- edit_history.initialize_for_conversation(conversation);
- spell_checker.initialize_for_conversation(conversation);
+// spell_checker.initialize_for_conversation(conversation);
}
- public override void get_preferred_height(out int min_height, out int nat_height) {
- base.get_preferred_height(out min_height, out nat_height);
- min_height = nat_height;
- }
+// public override void get_preferred_size(out Gtk.Requisition minimum_size, out Gtk.Requisition natural_size) {
+// base.get_preferred_height(out min_height, out nat_height);
+// min_height = nat_height;
+// }
private void on_upper_notify() {
- this.vadjustment.value = this.vadjustment.upper - this.vadjustment.page_size;
+ scrolled_window.vadjustment.value = scrolled_window.vadjustment.upper - scrolled_window.vadjustment.page_size;
// hack for vscrollbar not requiring space and making textview higher //TODO doesn't resize immediately
- this.get_vscrollbar().visible = (this.vadjustment.upper > this.max_content_height - 2 * this.vscrollbar_min_height);
+ scrolled_window.get_vscrollbar().visible = (scrolled_window.vadjustment.upper > scrolled_window.max_content_height - 2 * this.vscrollbar_min_height);
}
- private bool on_text_input_key_press(EventKey event) {
- if (event.keyval in new uint[]{Key.Return, Key.KP_Enter}) {
- if ((event.state & ModifierType.SHIFT_MASK) > 0) {
+ private bool on_text_input_key_press(uint keyval, uint keycode, Gdk.ModifierType state) {
+ if (keyval in new uint[]{ Key.Return, Key.KP_Enter }) {
+ if ((state & ModifierType.SHIFT_MASK) > 0) {
text_view.buffer.insert_at_cursor("\n", 1);
} else if (text_view.buffer.text.strip() != "") {
send_text();
- edit_history.reset_history();
}
return true;
}
- if (event.keyval == Key.Escape) {
+ if (keyval == Key.Escape) {
cancel_input();
}
return false;
diff --git a/main/src/ui/chat_input/edit_history.vala b/main/src/ui/chat_input/edit_history.vala
deleted file mode 100644
index 70f6d400..00000000
--- a/main/src/ui/chat_input/edit_history.vala
+++ /dev/null
@@ -1,77 +0,0 @@
-using Gdk;
-using Gee;
-using Gtk;
-
-using Dino.Entities;
-
-namespace Dino.Ui {
-
-public class EditHistory {
-
- private Conversation? conversation;
- private TextView text_input;
-
- private HashMap<Conversation, Gee.List<string>> histories = new HashMap<Conversation, Gee.List<string>>(Conversation.hash_func, Conversation.equals_func);
- private HashMap<Conversation, int> indices = new HashMap<Conversation, int>(Conversation.hash_func, Conversation.equals_func);
-
- public EditHistory(TextView text_input) {
- this.text_input = text_input;
-
- text_input.key_press_event.connect(on_text_input_key_press);
- text_input.cut_clipboard.connect_after(save_state);
- text_input.paste_clipboard.connect_after(save_state);
- text_input.move_cursor.connect_after(save_state);
- text_input.button_release_event.connect_after(() => { save_state(); return false; });
- }
-
- public void initialize_for_conversation(Conversation conversation) {
- this.conversation = conversation;
- if (!histories.has_key(conversation)) {
- reset_history();
- }
- }
-
- public bool on_text_input_key_press(EventKey event) {
- bool ctrl_pressed = (event.state & ModifierType.CONTROL_MASK) > 0;
- if (ctrl_pressed && event.keyval == Key.z) {
- undo();
- } else if (ctrl_pressed && (event.keyval in new uint[]{ Key.Z, Key.y } )) {
- redo();
- } else if (event.keyval in new uint[]{ Key.space, Key.Tab, Key.ISO_Left_Tab }) {
- save_state();
- }
- return false;
- }
-
- private void undo() {
- save_state();
- if (indices[conversation] > 0) {
- indices[conversation] = indices[conversation] - 1;
- text_input.buffer.text = histories[conversation][indices[conversation]];
- }
- }
-
- private void redo() {
- if (indices[conversation] < histories[conversation].size - 1) {
- indices[conversation] = indices[conversation] + 1;
- text_input.buffer.text = histories[conversation][indices[conversation]];
- }
- }
-
- private void save_state() {
- if (histories[conversation][indices[conversation]] == text_input.buffer.text) return;
- if (indices[conversation] < histories[conversation].size - 1) {
- histories[conversation] = histories[conversation].slice(0, indices[conversation] + 1);
- }
- histories[conversation].add(text_input.buffer.text);
- indices[conversation] = indices[conversation] + 1;
- }
-
- public void reset_history() {
- histories[conversation] = new ArrayList<string>();
- histories[conversation].add("");
- indices[conversation] = 0;
- }
-}
-
-}
diff --git a/main/src/ui/chat_input/encryption_button.vala b/main/src/ui/chat_input/encryption_button.vala
index 11466931..e5831802 100644
--- a/main/src/ui/chat_input/encryption_button.vala
+++ b/main/src/ui/chat_input/encryption_button.vala
@@ -5,27 +5,25 @@ using Dino.Entities;
namespace Dino.Ui {
-public class EncryptionButton : MenuButton {
+public class EncryptionButton {
public signal void encryption_changed(Plugins.EncryptionListEntry? encryption_entry);
+ private MenuButton menu_button;
private Conversation? conversation;
- private RadioButton? button_unencrypted;
- private Map<RadioButton, Plugins.EncryptionListEntry> encryption_radios = new HashMap<RadioButton, Plugins.EncryptionListEntry>();
+ private CheckButton? button_unencrypted;
+ private Map<CheckButton, Plugins.EncryptionListEntry> encryption_radios = new HashMap<CheckButton, Plugins.EncryptionListEntry>();
private string? current_icon;
private StreamInteractor stream_interactor;
- public EncryptionButton(StreamInteractor stream_interactor) {
+ public EncryptionButton(StreamInteractor stream_interactor, MenuButton menu_button) {
this.stream_interactor = stream_interactor;
-
- use_popover = true;
- image = new Image.from_icon_name("changes-allow-symbolic", IconSize.BUTTON);
- get_style_context().add_class("flat");
+ this.menu_button = menu_button;
Builder builder = new Builder.from_resource("/im/dino/Dino/menu_encryption.ui");
- popover = builder.get_object("menu_encryption") as PopoverMenu;
+ menu_button.popover = builder.get_object("menu_encryption") as PopoverMenu;
Box encryption_box = builder.get_object("encryption_box") as Box;
- button_unencrypted = builder.get_object("button_unencrypted") as RadioButton;
+ button_unencrypted = builder.get_object("button_unencrypted") as CheckButton;
button_unencrypted.toggled.connect(encryption_button_toggled);
stream_interactor.get_module(MucManager.IDENTITY).room_info_updated.connect((account, muc_jid) => {
@@ -36,17 +34,18 @@ public class EncryptionButton : MenuButton {
Application app = GLib.Application.get_default() as Application;
foreach (var e in app.plugin_registry.encryption_list_entries) {
- RadioButton btn = new RadioButton.with_label(button_unencrypted.get_group(), e.name);
+ CheckButton btn = new CheckButton.with_label(e.name);
+ btn.set_group(button_unencrypted);
encryption_radios[btn] = e;
btn.toggled.connect(encryption_button_toggled);
btn.visible = true;
- encryption_box.pack_end(btn, false);
+ encryption_box.prepend(btn);
}
- clicked.connect(update_encryption_menu_state);
+ menu_button.activate.connect(update_encryption_menu_state);
}
private void encryption_button_toggled() {
- foreach (RadioButton e in encryption_radios.keys) {
+ foreach (CheckButton e in encryption_radios.keys) {
if (e.get_active()) {
conversation.encryption = encryption_radios[e].encryption;
encryption_changed(encryption_radios[e]);
@@ -62,7 +61,7 @@ public class EncryptionButton : MenuButton {
}
private void update_encryption_menu_state() {
- foreach (RadioButton e in encryption_radios.keys) {
+ foreach (CheckButton e in encryption_radios.keys) {
if (conversation.encryption == encryption_radios[e].encryption) {
e.set_active(true);
encryption_changed(encryption_radios[e]);
@@ -76,7 +75,7 @@ public class EncryptionButton : MenuButton {
private void set_icon(string icon) {
if (icon != current_icon) {
- image = new Image.from_icon_name(icon, IconSize.BUTTON);
+ menu_button.set_icon_name(icon);
current_icon = icon;
}
}
@@ -87,23 +86,23 @@ public class EncryptionButton : MenuButton {
private void update_visibility() {
if (conversation.encryption != Encryption.NONE) {
- visible = true;
+ menu_button.visible = true;
return;
}
switch (conversation.type_) {
case Conversation.Type.CHAT:
- visible = true;
+ menu_button.visible = true;
break;
case Conversation.Type.GROUPCHAT_PM:
- visible = false;
+ menu_button.visible = false;
break;
case Conversation.Type.GROUPCHAT:
- visible = stream_interactor.get_module(MucManager.IDENTITY).is_private_room(conversation.account, conversation.counterpart);
+ menu_button.visible = stream_interactor.get_module(MucManager.IDENTITY).is_private_room(conversation.account, conversation.counterpart);
break;
}
}
- public new void set_conversation(Conversation conversation) {
+ public void set_conversation(Conversation conversation) {
this.conversation = conversation;
update_encryption_menu_state();
update_encryption_menu_icon();
@@ -111,4 +110,4 @@ public class EncryptionButton : MenuButton {
}
}
-}
+} \ No newline at end of file
diff --git a/main/src/ui/chat_input/occupants_tab_completer.vala b/main/src/ui/chat_input/occupants_tab_completer.vala
index 6d2a7434..e50fe831 100644
--- a/main/src/ui/chat_input/occupants_tab_completer.vala
+++ b/main/src/ui/chat_input/occupants_tab_completer.vala
@@ -27,16 +27,18 @@ public class OccupantsTabCompletor {
this.stream_interactor = stream_interactor;
this.text_input = text_input;
- text_input.key_press_event.connect(on_text_input_key_press);
+ var text_input_key_events = new EventControllerKey();
+ text_input_key_events.key_pressed.connect(on_text_input_key_press);
+ text_input.add_controller(text_input_key_events);
}
public void initialize_for_conversation(Conversation conversation) {
this.conversation = conversation;
}
- public bool on_text_input_key_press(EventKey event) {
+ public bool on_text_input_key_press(uint keyval, uint keycode, Gdk.ModifierType state) {
if (conversation.type_ == Conversation.Type.GROUPCHAT) {
- if (event.keyval == Key.Tab || event.keyval == Key.ISO_Left_Tab) {
+ if (keyval == Key.Tab || keyval == Key.ISO_Left_Tab) {
string text = text_input.buffer.text;
int start_index = int.max(text.last_index_of(" "), text.last_index_of("\n")) + 1;
string word = text.substring(start_index);
@@ -51,11 +53,11 @@ public class OccupantsTabCompletor {
index = -1;
}
}
- if (event.keyval != Key.ISO_Group_Shift && active) {
- text_input.buffer.text = next_completion(event.keyval == Key.ISO_Left_Tab);
+ if (keyval != Key.ISO_Group_Shift && active) {
+ text_input.buffer.text = next_completion(keyval == Key.ISO_Left_Tab);
return true;
}
- } else if (event.keyval != Key.Shift_L && active) {
+ } else if (keyval != Key.Shift_L && active) {
active = false;
}
}
diff --git a/main/src/ui/chat_input/smiley_converter.vala b/main/src/ui/chat_input/smiley_converter.vala
index 8f4dee9a..fe280d99 100644
--- a/main/src/ui/chat_input/smiley_converter.vala
+++ b/main/src/ui/chat_input/smiley_converter.vala
@@ -33,11 +33,13 @@ class SmileyConverter {
public SmileyConverter(TextView text_input) {
this.text_input = text_input;
- text_input.key_press_event.connect(on_text_input_key_press);
+ var text_input_key_events = new EventControllerKey();
+ text_input_key_events.key_pressed.connect(on_text_input_key_press);
+ text_input.add_controller(text_input_key_events);
}
- public bool on_text_input_key_press(EventKey event) {
- if (event.keyval == Key.space || event.keyval == Key.Return) {
+ public bool on_text_input_key_press(uint keyval, uint keycode, Gdk.ModifierType state) {
+ if (keyval == Key.space || keyval == Key.Return) {
check_convert();
}
return false;
diff --git a/main/src/ui/chat_input/view.vala b/main/src/ui/chat_input/view.vala
index 5142eb10..6524a825 100644
--- a/main/src/ui/chat_input/view.vala
+++ b/main/src/ui/chat_input/view.vala
@@ -23,6 +23,8 @@ public class View : Box {
[GtkChild] public unowned ChatTextView chat_text_view;
[GtkChild] public unowned Box outer_box;
[GtkChild] public unowned Button file_button;
+ [GtkChild] public unowned MenuButton emoji_button;
+ [GtkChild] public unowned MenuButton encryption_button;
[GtkChild] public unowned Separator file_separator;
[GtkChild] public unowned Label chat_input_status;
@@ -31,29 +33,13 @@ public class View : Box {
public View init(StreamInteractor stream_interactor) {
this.stream_interactor = stream_interactor;
- encryption_widget = new EncryptionButton(stream_interactor) { relief=ReliefStyle.NONE, margin_top=3, valign=Align.START, visible=true };
+ encryption_widget = new EncryptionButton(stream_interactor, encryption_button);
- file_button.get_style_context().add_class("dino-attach-button");
-
- encryption_widget.get_style_context().add_class("dino-chatinput-button");
-
- // Emoji button for emoji picker (recents don't work < 3.22.19, category icons don't work <3.23.2)
- if (Gtk.get_major_version() >= 3 && Gtk.get_minor_version() >= 24) {
- MenuButton emoji_button = new MenuButton() { relief=ReliefStyle.NONE, margin_top=3, valign=Align.START, visible=true };
- emoji_button.get_style_context().add_class("flat");
- emoji_button.get_style_context().add_class("dino-chatinput-button");
- emoji_button.image = new Image.from_icon_name("dino-emoticon-symbolic", IconSize.BUTTON) { visible=true };
-
- EmojiChooser chooser = new EmojiChooser();
- chooser.emoji_picked.connect((emoji) => {
- chat_text_view.text_view.buffer.insert_at_cursor(emoji, emoji.data.length);
- });
- emoji_button.set_popover(chooser);
-
- outer_box.add(emoji_button);
- }
-
- outer_box.add(encryption_widget);
+ EmojiChooser chooser = new EmojiChooser();
+ chooser.emoji_picked.connect((emoji) => {
+ chat_text_view.text_view.buffer.insert_at_cursor(emoji, emoji.data.length);
+ });
+ emoji_button.set_popover(chooser);
Util.force_css(frame, "* { border-radius: 3px; }");
diff --git a/main/src/ui/contact_details/blocking_provider.vala b/main/src/ui/contact_details/blocking_provider.vala
index bf59a083..76e5d000 100644
--- a/main/src/ui/contact_details/blocking_provider.vala
+++ b/main/src/ui/contact_details/blocking_provider.vala
@@ -14,7 +14,7 @@ public class BlockingProvider : Plugins.ContactDetailsProvider, Object {
}
public void populate(Conversation conversation, Plugins.ContactDetails contact_details, Plugins.WidgetType type) {
- if (type != Plugins.WidgetType.GTK) return;
+ if (type != Plugins.WidgetType.GTK4) return;
if (conversation.type_ != Conversation.Type.CHAT) return;
if (stream_interactor.get_module(BlockingManager.IDENTITY).is_supported(conversation.account)) {
diff --git a/main/src/ui/contact_details/dialog.vala b/main/src/ui/contact_details/dialog.vala
index f29d068e..b07ab9c9 100644
--- a/main/src/ui/contact_details/dialog.vala
+++ b/main/src/ui/contact_details/dialog.vala
@@ -37,7 +37,7 @@ public class Dialog : Gtk.Dialog {
title = conversation.type_ == Conversation.Type.GROUPCHAT ? _("Conference Details") : _("Contact Details");
if (Util.use_csd()) {
// TODO get_header_bar directly returns a HeaderBar in vala > 0.48
- ((HeaderBar) get_header_bar()).set_subtitle(Util.get_conversation_display_name(stream_interactor, conversation));
+// ((HeaderBar) get_header_bar()).set_subtitle(Util.get_conversation_display_name(stream_interactor, conversation));
}
setup_top();
@@ -50,23 +50,24 @@ public class Dialog : Gtk.Dialog {
app.plugin_registry.register_contact_details_entry(new PermissionsProvider(stream_interactor));
foreach (Plugins.ContactDetailsProvider provider in app.plugin_registry.contact_details_entries) {
- provider.populate(conversation, contact_details, Plugins.WidgetType.GTK);
+ provider.populate(conversation, contact_details, Plugins.WidgetType.GTK4);
}
- destroy.connect(() => {
- contact_details.save();
- });
+// destroy.connect(() => {
+// contact_details.save();
+// });
}
private void setup_top() {
if (conversation.type_ == Conversation.Type.CHAT) {
name_label.visible = false;
- jid_label.margin_start = new Button().get_style_context().get_padding(StateFlags.NORMAL).left + 1;
+ jid_label.margin_start = new Button().get_style_context().get_padding().left + 1;
name_hybrid.text = Util.get_conversation_display_name(stream_interactor, conversation);
- destroy.connect(() => {
+ close_request.connect(() => {
if (name_hybrid.text != Util.get_conversation_display_name(stream_interactor, conversation)) {
stream_interactor.get_module(RosterManager.IDENTITY).set_jid_handle(conversation.account, conversation.counterpart, name_hybrid.text);
}
+ return false;
});
} else {
name_hybrid.visible = false;
@@ -84,18 +85,18 @@ public class Dialog : Gtk.Dialog {
ListBoxRow list_row = new ListBoxRow() { activatable=false, visible=true };
Box row = new Box(Orientation.HORIZONTAL, 20) { margin_start=15, margin_end=15, margin_top=3, margin_bottom=3, visible=true };
- list_row.add(row);
+ list_row.set_child(row);
Label label_label = new Label(label) { xalign=0, yalign=0.5f, hexpand=true, visible=true };
if (description != null && description != "") {
Box box = new Box(Orientation.VERTICAL, 0) { visible=true };
- box.add(label_label);
+ box.append(label_label);
Label desc_label = new Label("") { xalign=0, yalign=0.5f, hexpand=true, visible=true };
desc_label.set_markup("<span size='small'>%s</span>".printf(Markup.escape_text(description)));
desc_label.get_style_context().add_class("dim-label");
- box.add(desc_label);
- row.add(box);
+ box.append(desc_label);
+ row.append(box);
} else {
- row.add(label_label);
+ row.append(label_label);
}
Widget widget = w;
@@ -112,13 +113,13 @@ public class Dialog : Gtk.Dialog {
widget.margin_top = 5;
- row.add(widget);
- categories[category].add(list_row);
+ row.append(widget);
+ categories[category].append(list_row);
int pref_height, pref_width;
- get_content_area().get_preferred_height(null, out pref_height);
- get_preferred_width(out pref_width, null);
- resize(pref_width, int.min(500, pref_height));
+// get_content_area().get_preferred_height(null, out pref_height);
+// get_preferred_width(out pref_width, null);
+// resize(pref_width, int.min(500, pref_height));
}
private void add_category(string category) {
@@ -133,11 +134,11 @@ public class Dialog : Gtk.Dialog {
Box box = new Box(Orientation.VERTICAL, 5) { margin_top=12, margin_bottom=12, visible=true };
Label category_label = new Label("") { xalign=0, visible=true };
category_label.set_markup(@"<b>$(Markup.escape_text(category))</b>");
- box.add(category_label);
+ box.append(category_label);
Frame frame = new Frame(null) { visible=true };
- frame.add(list_box);
- box.add(frame);
- main_box.add(box);
+ frame.set_child(list_box);
+ box.append(frame);
+ main_box.append(box);
}
}
}
diff --git a/main/src/ui/contact_details/muc_config_form_provider.vala b/main/src/ui/contact_details/muc_config_form_provider.vala
index 5b4184c5..1244a759 100644
--- a/main/src/ui/contact_details/muc_config_form_provider.vala
+++ b/main/src/ui/contact_details/muc_config_form_provider.vala
@@ -15,7 +15,7 @@ public class MucConfigFormProvider : Plugins.ContactDetailsProvider, Object {
}
public void populate(Conversation conversation, Plugins.ContactDetails contact_details, Plugins.WidgetType type) {
- if (type != Plugins.WidgetType.GTK) return;
+ if (type != Plugins.WidgetType.GTK4) return;
if (conversation.type_ == Conversation.Type.GROUPCHAT) {
Xmpp.XmppStream? stream = stream_interactor.get_stream(conversation.account);
if (stream == null) return;
diff --git a/main/src/ui/contact_details/permissions_provider.vala b/main/src/ui/contact_details/permissions_provider.vala
index 1fd460ad..d87658ff 100644
--- a/main/src/ui/contact_details/permissions_provider.vala
+++ b/main/src/ui/contact_details/permissions_provider.vala
@@ -14,13 +14,13 @@ public class PermissionsProvider : Plugins.ContactDetailsProvider, Object {
}
public void populate(Conversation conversation, Plugins.ContactDetails contact_details, Plugins.WidgetType type) {
- if (type != Plugins.WidgetType.GTK) return;
+ if (type != Plugins.WidgetType.GTK4) return;
Xmpp.Jid? own_jid = stream_interactor.get_module(MucManager.IDENTITY).get_own_jid(conversation.counterpart, conversation.account);
if (own_jid == null) return;
if (stream_interactor.get_module(MucManager.IDENTITY).get_role(own_jid, conversation.account) == Xmpp.Xep.Muc.Role.VISITOR){
- Button voice_request = new Button() {visible=true, label=_("Request")};
+ Button voice_request = new Button.with_label(_("Request")) { visible=true };
voice_request.clicked.connect(()=>stream_interactor.get_module(MucManager.IDENTITY).request_voice(conversation.account, conversation.counterpart));
contact_details.add(_("Permissions"), _("Request permission to send messages"), "", voice_request);
}
diff --git a/main/src/ui/contact_details/settings_provider.vala b/main/src/ui/contact_details/settings_provider.vala
index 262029a2..42c690e5 100644
--- a/main/src/ui/contact_details/settings_provider.vala
+++ b/main/src/ui/contact_details/settings_provider.vala
@@ -17,7 +17,7 @@ public class SettingsProvider : Plugins.ContactDetailsProvider, Object {
}
public void populate(Conversation conversation, Plugins.ContactDetails contact_details, Plugins.WidgetType type) {
- if (type != Plugins.WidgetType.GTK) return;
+ if (type != Plugins.WidgetType.GTK4) return;
if (!stream_interactor.get<MucManager>().is_public_room(conversation.account, conversation.counterpart)) {
string details_headline = conversation.type_ == Conversation.Type.GROUPCHAT ? DETAILS_HEADLINE_ROOM : DETAILS_HEADLINE_CHAT;
diff --git a/main/src/ui/conversation_content/conversation_item_factory.vala b/main/src/ui/conversation_content/conversation_item_factory.vala
new file mode 100644
index 00000000..29a83785
--- /dev/null
+++ b/main/src/ui/conversation_content/conversation_item_factory.vala
@@ -0,0 +1,36 @@
+using Gtk;
+
+namespace Dino.Ui {
+
+ public static ListItemFactory get_item_factory() {
+ SignalListItemFactory item_factory = new SignalListItemFactory();
+ item_factory.setup.connect((list_item) => { on_setup(list_item); });
+ item_factory.bind.connect((list_item) => { on_bind(list_item); });
+ return item_factory;
+ }
+
+ public static void on_setup(ListItem listitem) {
+ listitem.child = new ConversationItemWidget();
+ }
+
+ public static void on_bind(ListItem listitem) {
+ MessageViewModel view_model = (MessageViewModel) listitem.get_item();
+ ConversationItemWidget view = (ConversationItemWidget) listitem.get_child();
+
+ view_model.bind_property("name", view.name_label, "label", BindingFlags.SYNC_CREATE);
+ view_model.bind_property("time", view.time_label, "label", BindingFlags.SYNC_CREATE);
+
+ Label? label = view.content_widget as Label;
+ if (label == null) {
+ label = new Label("") { use_markup=true, xalign=0, selectable=true, wrap=true, wrap_mode=Pango.WrapMode.WORD_CHAR, hexpand=true, vexpand=true };
+ view.set_content_widget(label);
+ }
+ view_model.bind_property("message", label, "label", BindingFlags.SYNC_CREATE);
+
+ view_model.bind_property("encryption-icon-name", view.encrypted_image, "icon-name", BindingFlags.SYNC_CREATE);
+ view_model.bind_property("encryption-icon-tooltip", view.encrypted_image, "tooltip-text", BindingFlags.SYNC_CREATE);
+
+ view_model.bind_property("marked-icon-name", view.marked_image, "icon-name", BindingFlags.SYNC_CREATE);
+ view_model.bind_property("marked-icon-tooltip", view.marked_image, "tooltip-text", BindingFlags.SYNC_CREATE);
+ }
+} \ No newline at end of file
diff --git a/main/src/ui/conversation_content_view/call_widget.vala b/main/src/ui/conversation_content_view/call_widget.vala
index e45792e2..645c31c1 100644
--- a/main/src/ui/conversation_content_view/call_widget.vala
+++ b/main/src/ui/conversation_content_view/call_widget.vala
@@ -17,7 +17,7 @@ namespace Dino.Ui {
this.stream_interactor = stream_interactor;
}
- public override Object? get_widget(Plugins.WidgetType type) {
+ public override Object? get_widget(Plugins.ConversationItemWidgetInterface outer, Plugins.WidgetType type) {
CallItem call_item = content_item as CallItem;
CallState? call_state = stream_interactor.get_module(Calls.IDENTITY).call_states[call_item.call];
return new CallWidget(stream_interactor, call_item.call, call_state, call_item.conversation) { visible=true };
@@ -45,6 +45,7 @@ namespace Dino.Ui {
private Conversation conversation;
public Call.State call_state { get; set; } // needs to be public for binding
private uint time_update_handler_id = 0;
+ private ArrayList<Widget> multiparty_peer_widgets = new ArrayList<Widget>();
construct {
margin_top = 4;
@@ -58,11 +59,11 @@ namespace Dino.Ui {
this.call = call;
this.conversation = conversation;
- size_allocate.connect((allocation) => {
- if (allocation.height > parent.get_allocated_height()) {
- Idle.add(() => { parent.queue_resize(); return false; });
- }
- });
+// size_allocate.connect((allocation) => {
+// if (allocation.height > parent.get_allocated_height()) {
+// Idle.add(() => { parent.queue_resize(); return false; });
+// }
+// });
call.bind_property("state", this, "call-state");
this.notify["call-state"].connect(update_call_state);
@@ -88,16 +89,20 @@ namespace Dino.Ui {
if (call.state != Call.State.IN_PROGRESS && call.state != Call.State.ENDED) return;
if (call.counterparts.size <= 1 && conversation.type_ == Conversation.Type.CHAT) return;
- multiparty_peer_box.foreach((widget) => { multiparty_peer_box.remove(widget); });
+ foreach (Widget peer_widget in multiparty_peer_widgets) {
+ multiparty_peer_box.remove(peer_widget);
+ }
foreach (Jid counterpart in call.counterparts) {
AvatarImage image = new AvatarImage() { force_gray=true, margin_top=2, visible=true };
image.set_conversation_participant(stream_interactor, conversation, counterpart.bare_jid);
- multiparty_peer_box.add(image);
+ multiparty_peer_box.append(image);
+ multiparty_peer_widgets.add(image);
}
AvatarImage image2 = new AvatarImage() { force_gray=true, margin_top=2, visible=true };
image2.set_conversation_participant(stream_interactor, conversation, call.account.bare_jid);
- multiparty_peer_box.add(image2);
+ multiparty_peer_box.append(image2);
+ multiparty_peer_widgets.add(image2);
outer_additional_box.get_style_context().add_class("multiparty-participants");
@@ -121,7 +126,7 @@ namespace Dino.Ui {
switch (relevant_state) {
case Call.State.RINGING:
- image.set_from_icon_name("dino-phone-ring-symbolic", IconSize.LARGE_TOOLBAR);
+ image.set_from_icon_name("dino-phone-ring-symbolic");
if (call.direction == Call.DIRECTION_INCOMING) {
bool video = call_manager.should_we_send_video();
@@ -146,7 +151,7 @@ namespace Dino.Ui {
break;
case Call.State.ESTABLISHING:
case Call.State.IN_PROGRESS:
- image.set_from_icon_name("dino-phone-in-talk-symbolic", IconSize.LARGE_TOOLBAR);
+ image.set_from_icon_name("dino-phone-in-talk-symbolic");
title_label.label = _("Call started");
string duration = get_duration_string((new DateTime.now_utc()).difference(call.local_time));
subtitle_label.label = _("Started %s ago").printf(duration);
@@ -162,13 +167,13 @@ namespace Dino.Ui {
break;
case Call.State.OTHER_DEVICE:
- image.set_from_icon_name("dino-phone-hangup-symbolic", IconSize.LARGE_TOOLBAR);
+ image.set_from_icon_name("dino-phone-hangup-symbolic");
title_label.label = call.direction == Call.DIRECTION_INCOMING ? _("Incoming call") : _("Outgoing call");
subtitle_label.label = _("You handled this call on another device");
break;
case Call.State.ENDED:
- image.set_from_icon_name("dino-phone-hangup-symbolic", IconSize.LARGE_TOOLBAR);
+ image.set_from_icon_name("dino-phone-hangup-symbolic");
title_label.label = _("Call ended");
string formated_end = Util.format_time(call.end_time, _("%H∶%M"), _("%l∶%M %p"));
string duration = get_duration_string(call.end_time.difference(call.local_time));
@@ -177,7 +182,7 @@ namespace Dino.Ui {
_("Lasted %s").printf(duration);
break;
case Call.State.MISSED:
- image.set_from_icon_name("dino-phone-missed-symbolic", IconSize.LARGE_TOOLBAR);
+ image.set_from_icon_name("dino-phone-missed-symbolic");
title_label.label = _("Call missed");
if (call.direction == Call.DIRECTION_INCOMING) {
subtitle_label.label = _("You missed this call");
@@ -187,7 +192,7 @@ namespace Dino.Ui {
}
break;
case Call.State.DECLINED:
- image.set_from_icon_name("dino-phone-hangup-symbolic", IconSize.LARGE_TOOLBAR);
+ image.set_from_icon_name("dino-phone-hangup-symbolic");
title_label.label = _("Call declined");
if (call.direction == Call.DIRECTION_INCOMING) {
subtitle_label.label = _("You declined this call");
@@ -197,7 +202,7 @@ namespace Dino.Ui {
}
break;
case Call.State.FAILED:
- image.set_from_icon_name("dino-phone-hangup-symbolic", IconSize.LARGE_TOOLBAR);
+ image.set_from_icon_name("dino-phone-hangup-symbolic");
title_label.label = _("Call failed");
subtitle_label.label = "Call failed to establish";
break;
diff --git a/main/src/ui/conversation_content_view/chat_state_populator.vala b/main/src/ui/conversation_content_view/chat_state_populator.vala
index 247c83fe..0665caac 100644
--- a/main/src/ui/conversation_content_view/chat_state_populator.vala
+++ b/main/src/ui/conversation_content_view/chat_state_populator.vala
@@ -76,14 +76,14 @@ private class MetaChatStateItem : Plugins.MetaConversationItem {
this.jids = jids;
}
- public override Object? get_widget(Plugins.WidgetType widget_type) {
+ public override Object? get_widget(Plugins.ConversationItemWidgetInterface outer, Plugins.WidgetType widget_type) {
label = new Label("") { xalign=0, vexpand=true, visible=true };
label.get_style_context().add_class("dim-label");
image = new AvatarImage() { margin_top=2, valign=Align.START, visible=true };
Box image_content_box = new Box(Orientation.HORIZONTAL, 8) { visible=true };
- image_content_box.add(image);
- image_content_box.add(label);
+ image_content_box.append(image);
+ image_content_box.append(label);
update();
return image_content_box;
diff --git a/main/src/ui/conversation_content_view/conversation_item_skeleton.vala b/main/src/ui/conversation_content_view/conversation_item_skeleton.vala
index 077440d6..3e4ce88b 100644
--- a/main/src/ui/conversation_content_view/conversation_item_skeleton.vala
+++ b/main/src/ui/conversation_content_view/conversation_item_skeleton.vala
@@ -7,7 +7,16 @@ using Dino.Entities;
namespace Dino.Ui.ConversationSummary {
-public class ConversationItemSkeleton : EventBox {
+public class ConversationItemSkeleton : Plugins.ConversationItemWidgetInterface, Object {
+
+ public Grid main_grid { get; set; }
+ public Label name_label { get; set; }
+ public Label time_label { get; set; }
+ public AvatarImage avatar_image { get; set; }
+ public Image encryption_image { get; set; }
+ public Image received_image { get; set; }
+
+ public Widget? content_widget = null;
public bool show_skeleton { get; set; default=false; }
public bool last_group_item { get; set; default=true; }
@@ -20,188 +29,130 @@ public class ConversationItemSkeleton : EventBox {
public ContentMetaItem? content_meta_item = null;
public Widget? widget = null;
- private Box image_content_box = new Box(Orientation.HORIZONTAL, 8) { visible=true };
- private Box header_content_box = new Box(Orientation.VERTICAL, 0) { visible=true };
- private ItemMetaDataHeader? metadata_header = null;
- private AvatarImage? image = null;
+ private uint time_update_timeout = 0;
+ private ulong updated_roster_handler_id = 0;
public ConversationItemSkeleton(StreamInteractor stream_interactor, Conversation conversation, Plugins.MetaConversationItem item, bool initial_item) {
this.stream_interactor = stream_interactor;
this.conversation = conversation;
this.item = item;
this.content_meta_item = item as ContentMetaItem;
- this.get_style_context().add_class("message-box");
-
- item.bind_property("in-edit-mode", this, "item-in-edit-mode");
- this.notify["item-in-edit-mode"].connect(update_edit_mode);
- item.bind_property("mark", this, "item-mark", BindingFlags.SYNC_CREATE);
- this.notify["item-mark"].connect(update_error_mode);
- update_error_mode();
+ Builder builder = new Builder.from_resource("/im/dino/Dino/conversation_item_widget.ui");
+ main_grid = (Grid) builder.get_object("main_grid");
+ main_grid.get_style_context().add_class("message-box");
+ name_label = (Label) builder.get_object("name_label");
+ time_label = (Label) builder.get_object("time_label");
+ avatar_image = (AvatarImage) builder.get_object("avatar_image");
+ encryption_image = (Image) builder.get_object("encrypted_image");
+ received_image = (Image) builder.get_object("marked_image");
- widget = item.get_widget(Plugins.WidgetType.GTK) as Widget;
+ widget = item.get_widget(this, Plugins.WidgetType.GTK4) as Widget;
if (widget != null) {
widget.valign = Align.END;
- header_content_box.add(widget);
+ set_widget(widget, Plugins.WidgetType.GTK4);
}
- image_content_box.add(header_content_box);
-
- if (initial_item) {
- this.add(image_content_box);
- } else {
- Revealer revealer = new Revealer() { transition_duration=200, transition_type=RevealerTransitionType.SLIDE_UP, reveal_child=false, visible=true };
- revealer.add_with_properties(image_content_box);
- this.add(revealer);
- revealer.reveal_child = true;
+ if (item.requires_header) {
+ avatar_image.set_conversation_participant(stream_interactor, conversation, item.jid);
}
-
this.notify["show-skeleton"].connect(update_margin);
this.notify["last-group-item"].connect(update_margin);
+ this.notify["show-skeleton"].connect(set_header);
update_margin();
}
- private void update_margin() {
- if (item.requires_header && show_skeleton && metadata_header == null) {
- metadata_header = new ItemMetaDataHeader(stream_interactor, conversation, item) { visible=true };
- header_content_box.add(metadata_header);
- header_content_box.reorder_child(metadata_header, 0);
- }
- if (item.requires_avatar && show_skeleton && image == null) {
- image = new AvatarImage() { margin_top=2, valign=Align.START, visible=true, allow_gray = false };
- image.set_conversation_participant(stream_interactor, conversation, item.jid);
- image_content_box.add(image);
- image_content_box.reorder_child(image, 0);
- }
+ private void set_header() {
+ if (!show_skeleton || !item.requires_header) return;
+
+ update_name_label();
+// name_label.style_updated.connect(update_name_label);
+ updated_roster_handler_id = stream_interactor.get_module(RosterManager.IDENTITY).updated_roster_item.connect((account, jid, roster_item) => {
+ if (this.conversation.account.equals(account) && this.conversation.counterpart.equals(jid)) {
+ update_name_label();
+ }
+ });
+
+ item.notify["encryption"].connect(update_encryption_icon);
+ update_encryption_icon();
- if (image != null) {
- image.visible = this.show_skeleton;
+ if (item.time != null) {
+ update_time();
}
- if (metadata_header != null) {
- metadata_header.visible = this.show_skeleton;
+
+ item.bind_property("mark", this, "item-mark", BindingFlags.SYNC_CREATE);
+ this.notify["item-mark"].connect_after(update_received_mark);
+ update_received_mark();
+ }
+
+ public void set_widget(Object object, Plugins.WidgetType type) {
+ if (content_widget != null) content_widget.unparent();
+
+ Widget widget = (Widget) object;
+ content_widget = widget;
+ main_grid.attach(widget, 1, 1, 4, 1);
+ }
+
+ private void update_margin() {
+ avatar_image.visible = show_skeleton;
+ name_label.visible = show_skeleton;
+ time_label.visible = show_skeleton;
+ encryption_image.visible = show_skeleton;
+ received_image.visible = show_skeleton;
+
+ if (show_skeleton) {
+ main_grid.get_style_context().add_class("has-skeleton");
}
- image_content_box.margin_start = this.show_skeleton ? 15 : 58;
- image_content_box.margin_end = 15;
- if (this.show_skeleton && this.last_group_item) {
- image_content_box.margin_top = 8;
- image_content_box.margin_bottom = 8;
- } else {
- image_content_box.margin_top = 4;
- image_content_box.margin_bottom = 4;
+ if (last_group_item) {
+ main_grid.get_style_context().add_class("last-group-item");
}
}
private void update_edit_mode() {
if (item.in_edit_mode) {
- this.get_style_context().add_class("edit-mode");
+ main_grid.get_style_context().add_class("edit-mode");
} else {
- this.get_style_context().remove_class("edit-mode");
+ main_grid.get_style_context().remove_class("edit-mode");
}
}
private void update_error_mode() {
if (item_mark == Message.Marked.ERROR) {
- this.get_style_context().add_class("error");
+ main_grid.get_style_context().add_class("error");
} else {
- this.get_style_context().remove_class("error");
- }
- }
-}
-
-[GtkTemplate (ui = "/im/dino/Dino/conversation_content_view/item_metadata_header.ui")]
-public class ItemMetaDataHeader : Box {
- [GtkChild] public unowned Label name_label;
- [GtkChild] public unowned Label time_label;
- public Image received_image = new Image() { opacity=0.4 };
- public Widget? encryption_image = null;
-
- public static IconSize ICON_SIZE_HEADER = Gtk.icon_size_register("im.dino.Dino.HEADER_ICON", 17, 12);
-
- private StreamInteractor stream_interactor;
- private Conversation conversation;
- private Plugins.MetaConversationItem item;
- public Entities.Message.Marked item_mark { get; set; }
- private ArrayList<Plugins.MetaConversationItem> items = new ArrayList<Plugins.MetaConversationItem>();
- private uint time_update_timeout = 0;
- private ulong updated_roster_handler_id = 0;
-
- public ItemMetaDataHeader(StreamInteractor stream_interactor, Conversation conversation, Plugins.MetaConversationItem item) {
- this.stream_interactor = stream_interactor;
- this.conversation = conversation;
- this.item = item;
- items.add(item);
-
- update_name_label();
- name_label.style_updated.connect(update_name_label);
- updated_roster_handler_id = stream_interactor.get_module(RosterManager.IDENTITY).updated_roster_item.connect((account, jid, roster_item) => {
- if (this.conversation.account.equals(account) && this.conversation.counterpart.equals(jid)) {
- update_name_label();
- }
- });
-
- conversation.notify["encryption"].connect(update_unencrypted_icon);
- item.notify["encryption"].connect(update_encryption_icon);
- update_encryption_icon();
-
- this.add(received_image);
-
- if (item.time != null) {
- update_time();
+ main_grid.get_style_context().remove_class("error");
}
-
- item.bind_property("mark", this, "item-mark");
- this.notify["item-mark"].connect_after(update_received_mark);
- update_received_mark();
}
private void update_encryption_icon() {
+ encryption_image.visible = true;
+
Application app = GLib.Application.get_default() as Application;
ContentMetaItem ci = item as ContentMetaItem;
if (item.encryption != Encryption.NONE && item.encryption != Encryption.UNKNOWN && ci != null) {
- Widget? widget = null;
+ string? icon_name = null;
foreach(var e in app.plugin_registry.encryption_list_entries) {
if (e.encryption == item.encryption) {
- widget = e.get_encryption_icon(conversation, ci.content_item) as Widget;
+ icon_name = e.get_encryption_icon_name(conversation, ci.content_item);
break;
}
}
- if (widget == null) {
- widget = new Image.from_icon_name("dino-changes-prevent-symbolic", ICON_SIZE_HEADER) { opacity=0.4, visible = true };
- }
- update_encryption_image(widget);
- }
- if (item.encryption == Encryption.NONE) {
- update_unencrypted_icon();
+ encryption_image.icon_name = icon_name ?? "dino-changes-prevent-symbolic";
}
- }
-
- private void update_unencrypted_icon() {
- if (item.encryption != Encryption.NONE) return;
-
- if (conversation.encryption != Encryption.NONE && encryption_image == null) {
- Image image = new Image() { opacity=0.4, visible = true };
- image.set_from_icon_name("dino-changes-allowed-symbolic", ICON_SIZE_HEADER);
- image.tooltip_text = _("Unencrypted");
- update_encryption_image(image);
- Util.force_error_color(image);
- } else if (conversation.encryption == Encryption.NONE && encryption_image != null) {
- update_encryption_image(null);
- }
- }
- private void update_encryption_image(Widget? widget) {
- if (encryption_image != null) {
- this.remove(encryption_image);
- encryption_image = null;
- }
- if (widget != null) {
- this.add(widget);
- this.reorder_child(widget, 3);
- encryption_image = widget;
+ if (item.encryption == Encryption.NONE) {
+ if (conversation.encryption != Encryption.NONE) {
+ encryption_image.icon_name = "dino-changes-allowed-symbolic";
+ encryption_image.tooltip_text = _("Unencrypted");
+ Util.force_error_color(encryption_image);
+ } else if (conversation.encryption == Encryption.NONE) {
+ encryption_image.icon_name = null;
+ encryption_image.visible = false;
+ }
}
}
@@ -209,7 +160,7 @@ public class ItemMetaDataHeader : Box {
time_label.label = get_relative_time(item.time.to_local()).to_string();
time_update_timeout = Timeout.add_seconds((int) get_next_time_change(), () => {
- if (this.parent == null) return false;
+ if (this.main_grid.parent == null) return false;
update_time();
return false;
});
@@ -220,41 +171,15 @@ public class ItemMetaDataHeader : Box {
}
private void update_received_mark() {
- bool all_received = true;
- bool all_read = true;
- bool all_sent = true;
- foreach (Plugins.MetaConversationItem item in items) {
- if (item.mark == Message.Marked.WONTSEND) {
- received_image.visible = true;
- received_image.set_from_icon_name("dialog-warning-symbolic", ICON_SIZE_HEADER);
- Util.force_error_color(received_image);
- Util.force_error_color(time_label);
- string error_text = _("Unable to send message");
- received_image.tooltip_text = error_text;
- time_label.tooltip_text = error_text;
- return;
- } else if (item.mark != Message.Marked.READ) {
- all_read = false;
- if (item.mark != Message.Marked.RECEIVED) {
- all_received = false;
- if (item.mark == Message.Marked.UNSENT) {
- all_sent = false;
- }
- }
- }
- }
- if (all_read) {
- received_image.visible = true;
- received_image.set_from_icon_name("dino-double-tick-symbolic", ICON_SIZE_HEADER);
- } else if (all_received) {
- received_image.visible = true;
- received_image.set_from_icon_name("dino-tick-symbolic", ICON_SIZE_HEADER);
- } else if (!all_sent) {
- received_image.visible = true;
- received_image.set_from_icon_name("image-loading-symbolic", ICON_SIZE_HEADER);
- } else if (received_image.visible) {
- received_image.set_from_icon_name("image-loading-symbolic", ICON_SIZE_HEADER);
-
+ switch (content_meta_item.mark) {
+ case Message.Marked.RECEIVED: received_image.icon_name = "dino-tick-symbolic"; break;
+ case Message.Marked.READ: received_image.icon_name = "dino-double-tick-symbolic"; break;
+ case Message.Marked.WONTSEND:
+ received_image.icon_name = "dialog-warning-symbolic";
+ received_image.icon_name = _("Unable to send message");
+ // TODO error color on marked icon and time
+ break;
+ default: received_image.icon_name = null; break;
}
}
@@ -311,6 +236,10 @@ public class ItemMetaDataHeader : Box {
}
}
+ public Widget get_widget() {
+ return main_grid;
+ }
+
public override void dispose() {
if (time_update_timeout != 0) {
Source.remove(time_update_timeout);
diff --git a/main/src/ui/conversation_content_view/conversation_view.vala b/main/src/ui/conversation_content_view/conversation_view.vala
index d6cbed62..8d46281f 100644
--- a/main/src/ui/conversation_content_view/conversation_view.vala
+++ b/main/src/ui/conversation_content_view/conversation_view.vala
@@ -8,7 +8,7 @@ using Dino.Entities;
namespace Dino.Ui.ConversationSummary {
[GtkTemplate (ui = "/im/dino/Dino/conversation_content_view/view.ui")]
-public class ConversationView : Box, Plugins.ConversationItemCollection, Plugins.NotificationCollection {
+public class ConversationView : Widget, Plugins.ConversationItemCollection, Plugins.NotificationCollection {
public Conversation? conversation { get; private set; }
@@ -19,8 +19,7 @@ public class ConversationView : Box, Plugins.ConversationItemCollection, Plugins
[GtkChild] private unowned Image button1_icon;
[GtkChild] private unowned Box notifications;
[GtkChild] private unowned Box main;
- [GtkChild] private unowned EventBox main_event_box;
- [GtkChild] private unowned EventBox main_wrap_event_box;
+ [GtkChild] private unowned Box main_wrap_box;
[GtkChild] private unowned Stack stack;
private StreamInteractor stream_interactor;
@@ -41,9 +40,13 @@ public class ConversationView : Box, Plugins.ConversationItemCollection, Plugins
private bool firstLoad = true;
private bool at_current_content = true;
private bool reload_messages = true;
- ConversationItemSkeleton currently_highlighted = null;
+ Widget currently_highlighted = null;
ContentMetaItem? current_meta_item = null;
- int last_y_root = -1;
+ double last_y = -1;
+
+ construct {
+ this.layout_manager = new BinLayout();
+ }
public ConversationView init(StreamInteractor stream_interactor) {
this.stream_interactor = stream_interactor;
@@ -64,20 +67,21 @@ public class ConversationView : Box, Plugins.ConversationItemCollection, Plugins
// we connect to the parent event box that also wraps the overlaying message_menu_box.
// This eliminates the unwanted leave events emitted on the main_event_box when hovering
// the overlaying menu buttons.
- main_wrap_event_box.events = EventMask.ENTER_NOTIFY_MASK;
- main_wrap_event_box.events = EventMask.LEAVE_NOTIFY_MASK;
- main_wrap_event_box.leave_notify_event.connect(on_leave_notify_event);
- main_wrap_event_box.enter_notify_event.connect(on_enter_notify_event);
+ EventControllerMotion main_wrap_motion_events = new EventControllerMotion();
+ main_wrap_box.add_controller(main_wrap_motion_events);
+ main_wrap_motion_events.leave.connect(on_leave_notify_event);
+ main_wrap_motion_events.enter.connect(update_highlight);
// The buttons of the overlaying message_menu_box may partially overlap the adjacent
// conversation items. We connect to the main_event_box directly to avoid emitting
// the pointer motion events as long as the pointer is above the message menu.
// This ensures that the currently highlighted item remains unchanged when the pointer
// reaches the overlapping part of a button.
- main_event_box.events = EventMask.POINTER_MOTION_MASK;
- main_event_box.motion_notify_event.connect(on_motion_notify_event);
+ EventControllerMotion main_motion_events = new EventControllerMotion();
+ main.add_controller(main_motion_events);
+ main_motion_events.motion.connect(update_highlight);
button1.clicked.connect(() => {
- current_meta_item.get_item_actions(Plugins.WidgetType.GTK)[0].callback(button1, current_meta_item, currently_highlighted.widget);
+ current_meta_item.get_item_actions(Plugins.WidgetType.GTK4)[0].callback(button1, current_meta_item, currently_highlighted);
update_message_menu();
});
@@ -102,66 +106,51 @@ public class ConversationView : Box, Plugins.ConversationItemCollection, Plugins
}
}
- private bool on_enter_notify_event(Gdk.EventCrossing event) {
- update_highlight((int)event.x_root, (int)event.y_root);
- return false;
- }
-
- private bool on_leave_notify_event(Gdk.EventCrossing event) {
+ private void on_leave_notify_event() {
if (currently_highlighted != null) {
- currently_highlighted.unset_state_flags(StateFlags.PRELIGHT);
+ currently_highlighted.get_style_context().remove_class("highlight");
currently_highlighted = null;
}
message_menu_box.visible = false;
- return false;
}
- private bool on_motion_notify_event(Gdk.EventMotion event) {
- update_highlight((int)event.x_root, (int)event.y_root);
- return false;
- }
-
- private void update_highlight(int x_root, int y_root) {
- if (currently_highlighted != null && (last_y_root - y_root).abs() <= 2) {
+ private void update_highlight(double x, double y) {
+ if (currently_highlighted != null && (last_y - y).abs() <= 2) {
return;
}
- last_y_root = y_root;
-
- int toplevel_window_pos_x, toplevel_window_pos_y, dest_x, dest_y;
- Widget toplevel_widget = this.get_toplevel();
- // Obtain the position of the main application window relative to the root window
- toplevel_widget.get_window().get_origin(out toplevel_window_pos_x, out toplevel_window_pos_y);
- // Get the pointer location relative to the `main` box
- toplevel_widget.translate_coordinates(main, x_root - toplevel_window_pos_x, y_root - toplevel_window_pos_y, out dest_x, out dest_y);
+ last_y = y;
// Get widget under pointer
int h = 0;
- ConversationItemSkeleton? w = null;
- foreach (Widget widget in main.get_children()) {
- h += widget.get_allocated_height();
- if (h >= dest_y) {
- w = widget as ConversationItemSkeleton;
+ Widget? w = null;
+ Plugins.MetaConversationItem? meta_item = null;
+ foreach (Plugins.MetaConversationItem item in meta_items) {
+ Widget widget = widgets[item];
+ h += widget.get_allocated_height() + widget.margin_top + widget.margin_bottom;
+ if (h >= y) {
+ w = widget;
break;
}
};
- if (currently_highlighted != null) currently_highlighted.unset_state_flags(StateFlags.PRELIGHT);
+ if (currently_highlighted != null) currently_highlighted.get_style_context().remove_class("highlight");
+
+ currently_highlighted = null;
+ current_meta_item = null;
if (w == null) {
- currently_highlighted = null;
- current_meta_item = null;
update_message_menu();
return;
}
// Get widget coordinates in main
- int widget_x, widget_y;
+ double widget_x, widget_y;
w.translate_coordinates(main, 0, 0, out widget_x, out widget_y);
// Get MessageItem
foreach (Plugins.MetaConversationItem item in item_item_skeletons.keys) {
- if (item_item_skeletons[item] == w) {
+ if (item_item_skeletons[item].get_widget() == w) {
current_meta_item = item as ContentMetaItem;
}
}
@@ -170,11 +159,11 @@ public class ConversationView : Box, Plugins.ConversationItemCollection, Plugins
if (current_meta_item != null) {
// Highlight widget
- w.set_state_flags(StateFlags.PRELIGHT, true);
currently_highlighted = w;
+ currently_highlighted.get_style_context().add_class("highlight");
// Move message menu
- message_menu_box.margin_top = widget_y - 10;
+ message_menu_box.margin_top = (int)(widget_y - 10);
}
}
@@ -184,11 +173,11 @@ public class ConversationView : Box, Plugins.ConversationItemCollection, Plugins
return;
}
- var actions = current_meta_item.get_item_actions(Plugins.WidgetType.GTK);
+ var actions = current_meta_item.get_item_actions(Plugins.WidgetType.GTK4);
message_menu_box.visible = actions != null && actions.size > 0;
if (actions != null && actions.size == 1) {
button1.visible = true;
- button1_icon.set_from_icon_name(actions[0].icon_name, IconSize.SMALL_TOOLBAR);
+ button1_icon.set_from_icon_name(actions[0].icon_name);
}
}
@@ -235,15 +224,14 @@ public class ConversationView : Box, Plugins.ConversationItemCollection, Plugins
reload_messages = false;
Timeout.add(700, () => {
int h = 0, i = 0;
- bool @break = false;
- main.@foreach((widget) => {
- if (widget == w || @break) {
- @break = true;
- return;
+ foreach (Plugins.MetaConversationItem item in meta_items) {
+ Widget widget = widgets[item];
+ if (widget == w) {
+ break;
}
h += widget.get_allocated_height();
i++;
- });
+ }
scrolled.vadjustment.value = h - scrolled.vadjustment.page_size * 1/3;
w.get_style_context().add_class("highlight-once");
reload_messages = true;
@@ -270,9 +258,9 @@ public class ConversationView : Box, Plugins.ConversationItemCollection, Plugins
// Init for new conversation
foreach (Plugins.ConversationItemPopulator populator in app.plugin_registry.conversation_addition_populators) {
- populator.init(conversation, this, Plugins.WidgetType.GTK);
+ populator.init(conversation, this, Plugins.WidgetType.GTK4);
}
- content_populator.init(this, conversation, Plugins.WidgetType.GTK);
+ content_populator.init(this, conversation, Plugins.WidgetType.GTK4);
subscription_notification.init(conversation, this);
animate = false;
@@ -286,7 +274,7 @@ public class ConversationView : Box, Plugins.ConversationItemCollection, Plugins
}
Application app = GLib.Application.get_default() as Application;
foreach (Plugins.NotificationPopulator populator in app.plugin_registry.notification_populators) {
- populator.init(conversation, this, Plugins.WidgetType.GTK);
+ populator.init(conversation, this, Plugins.WidgetType.GTK4);
}
Idle.add(() => { on_value_notify(); return false; });
}
@@ -318,7 +306,7 @@ public class ConversationView : Box, Plugins.ConversationItemCollection, Plugins
private void remove_item(Plugins.MetaConversationItem item) {
ConversationItemSkeleton? skeleton = item_item_skeletons[item];
if (skeleton != null) {
- main.remove(skeleton);
+ main.remove(skeleton.get_widget());
widgets.unset(item);
item_skeletons.remove(skeleton);
item_item_skeletons.unset(item);
@@ -331,21 +319,21 @@ public class ConversationView : Box, Plugins.ConversationItemCollection, Plugins
}
public void on_add_meta_notification(Plugins.MetaConversationNotification notification) {
- Widget? widget = (Widget) notification.get_widget(Plugins.WidgetType.GTK);
+ Widget? widget = (Widget) notification.get_widget(Plugins.WidgetType.GTK4);
if (widget != null) {
add_notification(widget);
}
}
public void on_remove_meta_notification(Plugins.MetaConversationNotification notification){
- Widget? widget = (Widget) notification.get_widget(Plugins.WidgetType.GTK);
+ Widget? widget = (Widget) notification.get_widget(Plugins.WidgetType.GTK4);
if (widget != null) {
remove_notification(widget);
}
}
public void add_notification(Widget widget) {
- notifications.add(widget);
+ notifications.append(widget);
Timeout.add(20, () => {
notification_revealer.transition_duration = 200;
notification_revealer.reveal_child = true;
@@ -362,15 +350,14 @@ public class ConversationView : Box, Plugins.ConversationItemCollection, Plugins
Plugins.MetaConversationItem? lower_item = meta_items.lower(item);
// Fill datastructure
- ConversationItemSkeleton item_skeleton = new ConversationItemSkeleton(stream_interactor, conversation, item, !animate) { visible=true };
+ ConversationItemSkeleton item_skeleton = new ConversationItemSkeleton(stream_interactor, conversation, item, !animate);
item_item_skeletons[item] = item_skeleton;
int index = lower_item != null ? item_skeletons.index_of(item_item_skeletons[lower_item]) + 1 : 0;
item_skeletons.insert(index, item_skeleton);
// Insert widget
- widgets[item] = item_skeleton;
- main.add(item_skeleton);
- main.reorder_child(item_skeleton, index);
+ widgets[item] = item_skeleton.get_widget();
+ widgets[item].insert_after(main, item_item_skeletons.has_key(lower_item) ? item_item_skeletons[lower_item].get_widget() : null);
if (lower_item != null) {
if (can_merge(item, lower_item)) {
@@ -405,7 +392,7 @@ public class ConversationView : Box, Plugins.ConversationItemCollection, Plugins
}
}
}
- return item_skeleton;
+ return item_skeleton.get_widget();
}
private bool can_merge(Plugins.MetaConversationItem upper_item /*more recent, displayed below*/, Plugins.MetaConversationItem lower_item /*less recent, displayed above*/) {
@@ -420,7 +407,11 @@ public class ConversationView : Box, Plugins.ConversationItemCollection, Plugins
private void on_upper_notify() {
if (was_upper == null || scrolled.vadjustment.value > was_upper - was_page_size - 1) { // scrolled down or content smaller than page size
if (at_current_content) {
- scrolled.vadjustment.value = scrolled.vadjustment.upper - scrolled.vadjustment.page_size; // scroll down
+ Idle.add(() => {
+ // If we do this directly without Idle.add, scrolling down doesn't work properly
+ scrolled.vadjustment.value = scrolled.vadjustment.upper - scrolled.vadjustment.page_size; // scroll down
+ return false;
+ });
}
} else if (scrolled.vadjustment.value < scrolled.vadjustment.upper - scrolled.vadjustment.page_size - 1) {
scrolled.vadjustment.value = scrolled.vadjustment.upper - was_upper + scrolled.vadjustment.value; // stay at same content
@@ -482,12 +473,14 @@ public class ConversationView : Box, Plugins.ConversationItemCollection, Plugins
meta_items.clear();
item_skeletons.clear();
item_item_skeletons.clear();
+ foreach (Widget widget in widgets.values) {
+ main.remove(widget);
+ }
widgets.clear();
- main.@foreach((widget) => { main.remove(widget); });
}
private void clear_notifications() {
- notifications.@foreach((widget) => { notifications.remove(widget); });
+// notifications.@foreach((widget) => { notifications.remove(widget); });
notification_revealer.transition_duration = 0;
notification_revealer.set_reveal_child(false);
}
diff --git a/main/src/ui/conversation_content_view/date_separator_populator.vala b/main/src/ui/conversation_content_view/date_separator_populator.vala
index 5f6838e5..40bf0693 100644
--- a/main/src/ui/conversation_content_view/date_separator_populator.vala
+++ b/main/src/ui/conversation_content_view/date_separator_populator.vala
@@ -61,7 +61,7 @@ public class MetaDateItem : Plugins.MetaConversationItem {
this.time = date;
}
- public override Object? get_widget(Plugins.WidgetType widget_type) {
+ public override Object? get_widget(Plugins.ConversationItemWidgetInterface outer, Plugins.WidgetType widget_type) {
return new DateSeparatorWidget(date);
}
@@ -86,9 +86,9 @@ public class DateSeparatorWidget : Box {
label = new Label("") { use_markup=true, halign=Align.CENTER, hexpand=false, visible=true };
label.get_style_context().add_class("dim-label");
- this.add(new Separator(Orientation.HORIZONTAL) { valign=Align.CENTER, hexpand=true, visible=true });
- this.add(label);
- this.add(new Separator(Orientation.HORIZONTAL) { valign=Align.CENTER, hexpand=true, visible=true });
+ this.append(new Separator(Orientation.HORIZONTAL) { valign=Align.CENTER, hexpand=true, visible=true });
+ this.append(label);
+ this.append(new Separator(Orientation.HORIZONTAL) { valign=Align.CENTER, hexpand=true, visible=true });
update_time();
}
diff --git a/main/src/ui/conversation_content_view/file_default_widget.vala b/main/src/ui/conversation_content_view/file_default_widget.vala
index 638dab15..3bd5842f 100644
--- a/main/src/ui/conversation_content_view/file_default_widget.vala
+++ b/main/src/ui/conversation_content_view/file_default_widget.vala
@@ -7,34 +7,49 @@ using Dino.Entities;
namespace Dino.Ui {
[GtkTemplate (ui = "/im/dino/Dino/file_default_widget.ui")]
-public class FileDefaultWidget : EventBox {
+public class FileDefaultWidget : Box {
+
+ public signal void open_file();
+ public signal void save_file_as();
+ public signal void cancel_download();
[GtkChild] public unowned Stack image_stack;
[GtkChild] public unowned Label name_label;
[GtkChild] public unowned Label mime_label;
[GtkChild] public unowned Image content_type_image;
[GtkChild] public unowned Spinner spinner;
- [GtkChild] public unowned EventBox stack_event_box;
[GtkChild] public unowned MenuButton file_menu;
- public ModelButton file_open_button;
- public ModelButton file_save_button;
- public ModelButton cancel_button;
-
private FileTransfer.State state;
+ class construct {
+ install_action("file.open", null, (widget, action_name) => { ((FileDefaultWidget) widget).open_file(); });
+ install_action("file.save_as", null, (widget, action_name) => { ((FileDefaultWidget) widget).save_file_as(); });
+ install_action("file.cancel", null, (widget, action_name) => { ((FileDefaultWidget) widget).cancel_download(); });
+ }
+
public FileDefaultWidget() {
- this.enter_notify_event.connect(on_pointer_entered_event);
- this.leave_notify_event.connect(on_pointer_left_event);
- file_open_button = new ModelButton() { text=_("Open"), visible=true };
- file_save_button = new ModelButton() { text=_("Save as…"), visible=true };
- cancel_button = new ModelButton() { text=_("Cancel"), visible=true };
+ EventControllerMotion this_motion_events = new EventControllerMotion();
+ this.add_controller(this_motion_events);
+ this_motion_events.enter.connect(on_pointer_entered_event);
+ this_motion_events.leave.connect(on_pointer_left_event);
+
+ GestureClick gesture_click_controller = new GestureClick();
+ this.add_controller(gesture_click_controller);
+ gesture_click_controller.pressed.connect((n_press, x, y) => {
+ // Check whether the click was inside the file menu. Otherwise, open the file.
+ double x_button, y_button;
+ this.translate_coordinates(file_menu, x, y, out x_button, out y_button);
+ if (file_menu.contains(x_button, y_button)) return;
+
+ this.open_file();
+ });
}
public void update_file_info(string? mime_type, FileTransfer.State state, long size) {
this.state = state;
- spinner.active = false; // A hidden spinning spinner still uses CPU. Deactivate asap
+ spinner.stop(); // A hidden spinning spinner still uses CPU. Deactivate asap
content_type_image.icon_name = get_file_icon_name(mime_type);
string? mime_description = mime_type != null ? ContentType.get_description(mime_type) : null;
@@ -45,33 +60,23 @@ public class FileDefaultWidget : EventBox {
image_stack.set_visible_child_name("content_type_image");
// Create a menu
- Gtk.PopoverMenu popover_menu = new Gtk.PopoverMenu();
- Box file_menu_box = new Box(Orientation.VERTICAL, 0) { margin=10, visible=true };
- file_menu_box.add(file_open_button);
- file_menu_box.add(file_save_button);
- popover_menu.add(file_menu_box);
+ Menu menu_model = new Menu();
+ menu_model.append(_("Open"), "file.open");
+ menu_model.append(_("Save as…"), "file.save_as");
+ Gtk.PopoverMenu popover_menu = new Gtk.PopoverMenu.from_model(menu_model);
file_menu.popover = popover_menu;
- file_menu.button_release_event.connect(() => {
- popover_menu.visible = true;
- return true;
- });
popover_menu.closed.connect(on_pointer_left);
break;
case FileTransfer.State.IN_PROGRESS:
mime_label.label = _("Downloading %s…").printf(get_size_string(size));
- spinner.active = true;
+ spinner.start();
image_stack.set_visible_child_name("spinner");
// Create a menu
- Gtk.PopoverMenu popover_menu = new Gtk.PopoverMenu();
- Box file_menu_box = new Box(Orientation.VERTICAL, 0) { margin=10, visible=true };
- file_menu_box.add(cancel_button);
- popover_menu.add(file_menu_box);
+ Menu menu_model = new Menu();
+ menu_model.append(_("Cancel"), "file.cancel_download");
+ Gtk.PopoverMenu popover_menu = new Gtk.PopoverMenu.from_model(menu_model);
file_menu.popover = popover_menu;
- file_menu.button_release_event.connect(() => {
- popover_menu.visible = true;
- return true;
- });
popover_menu.closed.connect(on_pointer_left);
break;
case FileTransfer.State.NOT_STARTED:
@@ -92,8 +97,8 @@ public class FileDefaultWidget : EventBox {
}
}
- private bool on_pointer_entered_event(Gdk.EventCrossing event) {
- event.get_window().set_cursor(new Cursor.for_display(Gdk.Display.get_default(), CursorType.HAND2));
+ private void on_pointer_entered_event() {
+ this.set_cursor_from_name("pointer");
content_type_image.opacity = 0.7;
if (state == FileTransfer.State.NOT_STARTED) {
image_stack.set_visible_child_name("download_image");
@@ -101,16 +106,13 @@ public class FileDefaultWidget : EventBox {
if (state == FileTransfer.State.COMPLETE || state == FileTransfer.State.IN_PROGRESS) {
file_menu.opacity = 1;
}
- return false;
}
- private bool on_pointer_left_event(Gdk.EventCrossing event) {
- if (event.detail == Gdk.NotifyType.INFERIOR) return false;
- if (file_menu.popover != null && file_menu.popover.visible) return false;
+ private void on_pointer_left_event() {
+ if (file_menu.popover != null && file_menu.popover.visible) return;
- event.get_window().set_cursor(new Cursor.for_display(Gdk.Display.get_default(), CursorType.XTERM));
+ this.set_cursor(null);
on_pointer_left();
- return false;
}
private void on_pointer_left() {
diff --git a/main/src/ui/conversation_content_view/file_image_widget.vala b/main/src/ui/conversation_content_view/file_image_widget.vala
index 91eddd93..aad220e8 100644
--- a/main/src/ui/conversation_content_view/file_image_widget.vala
+++ b/main/src/ui/conversation_content_view/file_image_widget.vala
@@ -6,7 +6,7 @@ using Dino.Entities;
namespace Dino.Ui {
-public class FileImageWidget : EventBox {
+public class FileImageWidget : Box {
private ScalingImage image;
FileDefaultWidget file_default_widget;
@@ -14,7 +14,6 @@ public class FileImageWidget : EventBox {
public FileImageWidget() {
this.halign = Align.START;
- this.events = EventMask.POINTER_MOTION_MASK;
this.get_style_context().add_class("file-image-widget");
}
@@ -36,6 +35,7 @@ public class FileImageWidget : EventBox {
pixbuf = pixbuf.apply_embedded_orientation();
image.load(pixbuf);
+ Picture picture = new Picture.for_pixbuf(pixbuf) { can_shrink=true, keep_aspect_ratio=true, halign=Align.START };
Idle.add(load_from_file.callback);
return image;
@@ -47,28 +47,29 @@ public class FileImageWidget : EventBox {
FileInfo file_info = file.query_info("*", FileQueryInfoFlags.NONE);
string? mime_type = file_info.get_content_type();
- file_default_widget = new FileDefaultWidget() { valign=Align.END, vexpand=false };
- file_default_widget.stack_event_box.visible = false;
+ file_default_widget = new FileDefaultWidget() { valign=Align.END, vexpand=false, visible=false };
+ file_default_widget.image_stack.visible = false;
file_default_widget_controller = new FileDefaultWidgetController(file_default_widget);
file_default_widget_controller.set_file(file, file_name, mime_type);
Overlay overlay = new Overlay() { visible=true };
- overlay.add(image);
+ overlay.set_child(image);
overlay.add_overlay(file_default_widget);
+ overlay.set_measure_overlay(image, true);
+ overlay.set_clip_overlay(file_default_widget, true);
- this.enter_notify_event.connect((event) => {
+ EventControllerMotion this_motion_events = new EventControllerMotion();
+ this.add_controller(this_motion_events);
+ this_motion_events.enter.connect(() => {
file_default_widget.visible = true;
- return false;
});
- this.leave_notify_event.connect((event) => {
- if (event.detail == Gdk.NotifyType.INFERIOR) return false;
- if (file_default_widget.file_menu.popover != null && file_default_widget.file_menu.popover.visible) return false;
+ this_motion_events.leave.connect(() => {
+ if (file_default_widget.file_menu.popover != null && file_default_widget.file_menu.popover.visible) return;
file_default_widget.visible = false;
- return false;
});
- this.add(overlay);
+ this.append(overlay);
}
}
diff --git a/main/src/ui/conversation_content_view/file_widget.vala b/main/src/ui/conversation_content_view/file_widget.vala
index b63195dc..6378c298 100644
--- a/main/src/ui/conversation_content_view/file_widget.vala
+++ b/main/src/ui/conversation_content_view/file_widget.vala
@@ -16,7 +16,7 @@ public class FileMetaItem : ConversationSummary.ContentMetaItem {
this.stream_interactor = stream_interactor;
}
- public override Object? get_widget(Plugins.WidgetType type) {
+ public override Object? get_widget(Plugins.ConversationItemWidgetInterface outer, Plugins.WidgetType type) {
FileItem file_item = content_item as FileItem;
FileTransfer transfer = file_item.file_transfer;
return new FileWidget(stream_interactor, transfer) { visible=true };
@@ -51,11 +51,11 @@ public class FileWidget : SizeRequestBox {
this.file_transfer = file_transfer;
update_widget.begin();
- size_allocate.connect((allocation) => {
- if (allocation.height > parent.get_allocated_height()) {
- Idle.add(() => { parent.queue_resize(); return false; });
- }
- });
+// size_allocate.connect((allocation) => {
+// if (allocation.height > parent.get_allocated_height()) {
+// Idle.add(() => { parent.queue_resize(); return false; });
+// }
+// });
file_transfer.bind_property("state", this, "file-transfer-state");
file_transfer.bind_property("mime-type", this, "file-transfer-mime-type");
@@ -79,7 +79,7 @@ public class FileWidget : SizeRequestBox {
if (content != null) this.remove(content);
content = file_image_widget;
state = State.IMAGE;
- this.add(content);
+ this.append(content);
return;
} catch (Error e) { }
}
@@ -91,7 +91,7 @@ public class FileWidget : SizeRequestBox {
default_widget_controller.set_file_transfer(file_transfer, stream_interactor);
content = default_file_widget;
this.state = State.DEFAULT;
- this.add(content);
+ this.append(content);
}
}
@@ -128,10 +128,15 @@ public class FileDefaultWidgetController : Object {
public FileDefaultWidgetController(FileDefaultWidget widget) {
this.widget = widget;
- widget.button_release_event.connect(on_clicked);
- widget.file_open_button.clicked.connect(open_file);
- widget.file_save_button.clicked.connect(save_file);
- widget.cancel_button.clicked.connect(cancel_download);
+
+ widget.open_file.connect(open_file);
+ widget.save_file_as.connect(save_file);
+ widget.cancel_download.connect(cancel_download);
+
+ var gesture_controller = new GestureClick();
+ gesture_controller.set_button(1); // listen for left clicks
+ gesture_controller.released.connect(on_clicked);
+ widget.add_controller(gesture_controller);
}
public void set_file_transfer(FileTransfer file_transfer, StreamInteractor stream_interactor) {
@@ -173,30 +178,29 @@ public class FileDefaultWidgetController : Object {
}
private void save_file() {
- var save_dialog = new FileChooserNative(_("Save as…"), widget.get_toplevel() as Gtk.Window, FileChooserAction.SAVE, null, null);
- save_dialog.set_do_overwrite_confirmation(true);
+ var save_dialog = new FileChooserNative(_("Save as…"), widget.get_root() as Gtk.Window, FileChooserAction.SAVE, null, null);
save_dialog.set_modal(true);
save_dialog.set_current_name(file_name);
- if (save_dialog.run() == Gtk.ResponseType.ACCEPT) {
+ save_dialog.response.connect(() => {
try{
GLib.File.new_for_uri(file_uri).copy(save_dialog.get_file(), GLib.FileCopyFlags.OVERWRITE, null);
} catch (Error err) {
warning("Failed copy file %s - %s", file_uri, err.message);
}
- }
+ });
+
+ save_dialog.show();
}
private void cancel_download() {
file_transfer.cancellable.cancel();
}
- private bool on_clicked(EventButton event_button) {
+ private void on_clicked() {
switch (state) {
case FileTransfer.State.COMPLETE:
- if (event_button.button == 1) {
- open_file();
- }
+ open_file();
break;
case FileTransfer.State.NOT_STARTED:
assert(stream_interactor != null && file_transfer != null);
@@ -206,7 +210,6 @@ public class FileDefaultWidgetController : Object {
// Clicking doesn't do anything in FAILED and IN_PROGRESS states
break;
}
- return false;
}
}
diff --git a/main/src/ui/conversation_content_view/message_item_widget.vala b/main/src/ui/conversation_content_view/message_item_widget.vala
new file mode 100644
index 00000000..23a499d9
--- /dev/null
+++ b/main/src/ui/conversation_content_view/message_item_widget.vala
@@ -0,0 +1,229 @@
+using Dino.Entities;
+using Gtk;
+
+namespace Dino.Ui {
+ public class MessageItemWidget : SizeRequestBin {
+
+ public signal void edit_cancelled();
+ public signal void edit_sent(string text);
+
+ enum AdditionalInfo {
+ NONE,
+ PENDING,
+ DELIVERY_FAILED
+ }
+
+ StreamInteractor stream_interactor;
+ public ContentItem content_item;
+ public Message.Marked marked { get; set; }
+
+ Label label = new Label("") { use_markup=true, xalign=0, selectable=true, wrap=true, wrap_mode=Pango.WrapMode.WORD_CHAR, vexpand=true, visible=true };
+ MessageItemEditMode? edit_mode = null;
+ ChatTextViewController? controller = null;
+ AdditionalInfo additional_info = AdditionalInfo.NONE;
+
+ ulong realize_id = -1;
+ ulong style_updated_id = -1;
+ ulong marked_notify_handler_id = -1;
+
+ construct {
+ this.append(label);
+ label.activate_link.connect(on_label_activate_link);
+ this.size_request_mode = SizeRequestMode.HEIGHT_FOR_WIDTH;
+ }
+
+ public MessageItemWidget(StreamInteractor stream_interactor, ContentItem content_item) {
+ this.stream_interactor = stream_interactor;
+ this.content_item = content_item;
+
+ Message message = ((MessageItem) content_item).message;
+ if (message.direction == Message.DIRECTION_SENT && !(message.marked in Message.MARKED_RECEIVED)) {
+ var binding = message.bind_property("marked", this, "marked");
+ marked_notify_handler_id = this.notify["marked"].connect(() => {
+ // Currently "pending", but not anymore
+ if (additional_info == AdditionalInfo.PENDING &&
+ message.marked != Message.Marked.SENDING && message.marked != Message.Marked.UNSENT) {
+ update_label();
+ }
+
+ // Currently "error", but not anymore
+ if (additional_info == AdditionalInfo.DELIVERY_FAILED && message.marked != Message.Marked.ERROR) {
+ update_label();
+ }
+
+ // Currently not error, but should be
+ if (additional_info != AdditionalInfo.DELIVERY_FAILED && message.marked == Message.Marked.ERROR) {
+ update_label();
+ }
+
+ // Nothing bad can happen anymore
+ if (message.marked in Message.MARKED_RECEIVED) {
+ binding.unbind();
+ this.disconnect(marked_notify_handler_id);
+ }
+ });
+ }
+
+ update_label();
+ }
+
+ public void set_edit_mode() {
+
+ MessageItem message_item = content_item as MessageItem;
+ Message message = message_item.message;
+
+ if (edit_mode == null) {
+ edit_mode = new MessageItemEditMode();
+ controller = new ChatTextViewController(edit_mode.chat_text_view, stream_interactor);
+ Conversation conversation = message_item.conversation;
+ controller.initialize_for_conversation(conversation);
+
+ edit_mode.cancelled.connect(() => {
+ edit_cancelled();
+ unset_edit_mode();
+ });
+ edit_mode.send.connect(() => {
+ if (((MessageItem) content_item).message.body != edit_mode.chat_text_view.text_view.buffer.text) {
+ edit_sent(edit_mode.chat_text_view.text_view.buffer.text);
+ } else {
+ edit_cancelled();
+ }
+ unset_edit_mode();
+ });
+ }
+
+ edit_mode.chat_text_view.text_view.buffer.text = message.body;
+
+ this.remove(label);
+ this.append(edit_mode);
+
+ edit_mode.chat_text_view.text_view.grab_focus();
+ }
+
+ public void unset_edit_mode() {
+ this.remove(edit_mode);
+ this.append(label);
+ label.grab_focus();
+ label.selectable = false;
+ label.selectable = true;
+ }
+
+ public void update_label() {
+ label.label = generate_markup_text(content_item);
+ }
+
+ private string generate_markup_text(ContentItem item) {
+ MessageItem message_item = item as MessageItem;
+ Conversation conversation = message_item.conversation;
+ Message message = message_item.message;
+
+ bool theme_dependent = false;
+
+ string markup_text = message.body;
+ if (markup_text.length > 10000) {
+ markup_text = markup_text.substring(0, 10000) + " [" + _("Message too long") + "]";
+ }
+ if (message.body.has_prefix("/me ")) {
+ markup_text = markup_text.substring(4);
+ }
+
+ if (conversation.type_ == Conversation.Type.GROUPCHAT) {
+ markup_text = Util.parse_add_markup_theme(markup_text, conversation.nickname, true, true, true, Util.is_dark_theme(this), ref theme_dependent);
+ } else {
+ markup_text = Util.parse_add_markup_theme(markup_text, null, true, true, true, Util.is_dark_theme(this), ref theme_dependent);
+ }
+
+ if (message.body.has_prefix("/me ")) {
+ string display_name = Util.get_participant_display_name(stream_interactor, conversation, message.from);
+ markup_text = @"<i><b>$(Markup.escape_text(display_name))</b> " + markup_text + "</i>";
+ }
+
+ int only_emoji_count = Util.get_only_emoji_count(markup_text);
+ if (only_emoji_count != -1) {
+ string size_str = only_emoji_count < 5 ? "xx-large" : "large";
+ markup_text = @"<span size=\'$size_str\'>" + markup_text + "</span>";
+ }
+
+ string dim_color = Util.is_dark_theme(this) ? "#BDBDBD" : "#707070";
+
+ if (message.edit_to != null) {
+ markup_text += @" <span size='small' color='$dim_color'>(%s)</span>".printf(_("edited"));
+ theme_dependent = true;
+ }
+
+ // Append message status info
+ additional_info = AdditionalInfo.NONE;
+ if (message.direction == Message.DIRECTION_SENT && (message.marked == Message.Marked.SENDING || message.marked == Message.Marked.UNSENT)) {
+ // Append "pending..." iff message has not been sent yet
+ if (message.time.compare(new DateTime.now_utc().add_seconds(-10)) < 0) {
+ markup_text += @" <span size='small' color='$dim_color'>%s</span>".printf(_("pending…"));
+ theme_dependent = true;
+ additional_info = AdditionalInfo.PENDING;
+ } else {
+ int time_diff = (- (int) message.time.difference(new DateTime.now_utc()) / 1000);
+ Timeout.add(10000 - time_diff, () => {
+ update_label();
+ return false;
+ });
+ }
+ } else if (message.direction == Message.DIRECTION_SENT && message.marked == Message.Marked.ERROR) {
+ // Append "delivery failed" if there was a server error
+ string error_color = Util.rgba_to_hex(Util.get_label_pango_color(label, "@error_color"));
+ markup_text += " <span size='small' color='%s'>%s</span>".printf(error_color, _("delivery failed"));
+ theme_dependent = true;
+ additional_info = AdditionalInfo.DELIVERY_FAILED;
+ }
+
+ if (theme_dependent && realize_id == -1) {
+ realize_id = label.realize.connect(update_label);
+ // style_updated_id = label.style_updated.connect(update_label);
+ } else if (!theme_dependent && realize_id != -1) {
+ label.disconnect(realize_id);
+ label.disconnect(style_updated_id);
+ }
+ return markup_text;
+ }
+
+ public static bool on_label_activate_link(string uri) {
+ // Always handle xmpp URIs with Dino
+ if (!uri.has_prefix("xmpp:")) return false;
+ File file = File.new_for_uri(uri);
+ Dino.Application.get_default().open(new File[]{file}, "");
+ return true;
+ }
+ }
+
+ [GtkTemplate (ui = "/im/dino/Dino/message_item_widget_edit_mode.ui")]
+ public class MessageItemEditMode : Box {
+
+ public signal void cancelled();
+ public signal void send();
+
+ [GtkChild] public unowned MenuButton emoji_button;
+ [GtkChild] public unowned ChatTextView chat_text_view;
+ [GtkChild] public unowned Button cancel_button;
+ [GtkChild] public unowned Button send_button;
+ [GtkChild] public unowned Frame frame;
+
+ construct {
+ Util.force_css(frame, "* { border-radius: 3px; }");
+
+ EmojiChooser chooser = new EmojiChooser();
+ chooser.emoji_picked.connect((emoji) => {
+ chat_text_view.text_view.buffer.insert_at_cursor(emoji, emoji.data.length);
+ });
+ emoji_button.set_popover(chooser);
+
+ chat_text_view.text_view.buffer.changed.connect_after(on_text_view_changed);
+
+ cancel_button.clicked.connect(() => cancelled());
+ send_button.clicked.connect(() => send());
+ chat_text_view.cancel_input.connect(() => cancelled());
+ chat_text_view.send_text.connect(() => send());
+ }
+
+ private void on_text_view_changed() {
+ send_button.sensitive = chat_text_view.text_view.buffer.text != "";
+ }
+ }
+} \ No newline at end of file
diff --git a/main/src/ui/conversation_content_view/message_widget.vala b/main/src/ui/conversation_content_view/message_widget.vala
index e7bd1282..3ebce0ee 100644
--- a/main/src/ui/conversation_content_view/message_widget.vala
+++ b/main/src/ui/conversation_content_view/message_widget.vala
@@ -10,84 +10,16 @@ namespace Dino.Ui.ConversationSummary {
public class MessageMetaItem : ContentMetaItem {
- private StreamInteractor stream_interactor;
- private MessageItemWidget message_item_widget;
- private MessageItem message_item;
-
- public MessageMetaItem(ContentItem content_item, StreamInteractor stream_interactor) {
- base(content_item);
- message_item = content_item as MessageItem;
- this.stream_interactor = stream_interactor;
- }
-
- public override Object? get_widget(Plugins.WidgetType type) {
- message_item_widget = new MessageItemWidget(stream_interactor, content_item) { visible=true };
-
- message_item_widget.edit_cancelled.connect(() => { this.in_edit_mode = false; });
- message_item_widget.edit_sent.connect(on_edit_send);
-
- stream_interactor.get_module(MessageCorrection.IDENTITY).received_correction.connect(on_received_correction);
-
- this.notify["in-edit-mode"].connect(() => {
- if (in_edit_mode == false) return;
- bool allowed = stream_interactor.get_module(MessageCorrection.IDENTITY).is_own_correction_allowed(message_item.conversation, message_item.message);
- if (allowed) {
- message_item_widget.set_edit_mode();
- } else {
- this.in_edit_mode = false;
- }
- });
-
- return message_item_widget;
- }
-
- public override Gee.List<Plugins.MessageAction>? get_item_actions(Plugins.WidgetType type) {
- if (content_item as FileItem != null) return null;
-
- bool allowed = stream_interactor.get_module(MessageCorrection.IDENTITY).is_own_correction_allowed(message_item.conversation, message_item.message);
- Gee.List<Plugins.MessageAction> actions = new ArrayList<Plugins.MessageAction>();
- if (allowed && !in_edit_mode) {
- Plugins.MessageAction action1 = new Plugins.MessageAction();
- action1.icon_name = "document-edit-symbolic";
- action1.callback = (button, content_meta_item_activated, widget) => {
- this.in_edit_mode = true;
- };
- actions.add(action1);
- }
- return actions;
- }
-
- private void on_edit_send(string text) {
- stream_interactor.get_module(MessageCorrection.IDENTITY).send_correction(message_item.conversation, message_item.message, text);
- this.in_edit_mode = false;
- }
-
- private void on_received_correction(ContentItem content_item) {
- if (this.content_item.id == content_item.id) {
- this.content_item = content_item;
- message_item = content_item as MessageItem;
- message_item_widget.content_item = content_item;
- message_item_widget.update_label();
- }
- }
-}
-
-public class MessageItemWidget : SizeRequestBin {
-
- public signal void edit_cancelled();
- public signal void edit_sent(string text);
-
enum AdditionalInfo {
NONE,
PENDING,
DELIVERY_FAILED
}
- StreamInteractor stream_interactor;
- public ContentItem content_item;
+ private StreamInteractor stream_interactor;
+ private MessageItem message_item;
public Message.Marked marked { get; set; }
- Label label = new Label("") { use_markup=true, xalign=0, selectable=true, wrap=true, wrap_mode=Pango.WrapMode.WORD_CHAR, vexpand=true, visible=true };
MessageItemEditMode? edit_mode = null;
ChatTextViewController? controller = null;
AdditionalInfo additional_info = AdditionalInfo.NONE;
@@ -96,15 +28,14 @@ public class MessageItemWidget : SizeRequestBin {
ulong style_updated_id = -1;
ulong marked_notify_handler_id = -1;
- construct {
- this.add(label);
- label.activate_link.connect(on_label_activate_link);
- this.size_request_mode = SizeRequestMode.HEIGHT_FOR_WIDTH;
- }
+ public Label label = new Label("") { use_markup=true, xalign=0, selectable=true, wrap=true, wrap_mode=Pango.WrapMode.WORD_CHAR, hexpand=true, vexpand=true, can_focus=false };
- public MessageItemWidget(StreamInteractor stream_interactor, ContentItem content_item) {
+ public MessageMetaItem(ContentItem content_item, StreamInteractor stream_interactor) {
+ base(content_item);
+ message_item = content_item as MessageItem;
this.stream_interactor = stream_interactor;
- this.content_item = content_item;
+
+ label.activate_link.connect(on_label_activate_link);
Message message = ((MessageItem) content_item).message;
if (message.direction == Message.DIRECTION_SENT && !(message.marked in Message.MARKED_RECEIVED)) {
@@ -137,51 +68,6 @@ public class MessageItemWidget : SizeRequestBin {
update_label();
}
- public void set_edit_mode() {
-
- MessageItem message_item = content_item as MessageItem;
- Message message = message_item.message;
-
- if (edit_mode == null) {
- edit_mode = new MessageItemEditMode();
- controller = new ChatTextViewController(edit_mode.chat_text_view, stream_interactor);
- Conversation conversation = message_item.conversation;
- controller.initialize_for_conversation(conversation);
-
- edit_mode.cancelled.connect(() => {
- edit_cancelled();
- unset_edit_mode();
- });
- edit_mode.send.connect(() => {
- if (((MessageItem) content_item).message.body != edit_mode.chat_text_view.text_view.buffer.text) {
- edit_sent(edit_mode.chat_text_view.text_view.buffer.text);
- } else {
- edit_cancelled();
- }
- unset_edit_mode();
- });
- }
-
- edit_mode.chat_text_view.text_view.buffer.text = message.body;
-
- this.remove(label);
- this.add(edit_mode);
-
- edit_mode.chat_text_view.text_view.grab_focus();
- }
-
- public void unset_edit_mode() {
- this.remove(edit_mode);
- this.add(label);
- label.grab_focus();
- label.selectable = false;
- label.selectable = true;
- }
-
- public void update_label() {
- label.label = generate_markup_text(content_item);
- }
-
private string generate_markup_text(ContentItem item) {
MessageItem message_item = item as MessageItem;
Conversation conversation = message_item.conversation;
@@ -198,9 +84,9 @@ public class MessageItemWidget : SizeRequestBin {
}
if (conversation.type_ == Conversation.Type.GROUPCHAT) {
- markup_text = Util.parse_add_markup_theme(markup_text, conversation.nickname, true, true, true, Util.is_dark_theme(this), ref theme_dependent);
+ markup_text = Util.parse_add_markup_theme(markup_text, conversation.nickname, true, true, true, Util.is_dark_theme(this.label), ref theme_dependent);
} else {
- markup_text = Util.parse_add_markup_theme(markup_text, null, true, true, true, Util.is_dark_theme(this), ref theme_dependent);
+ markup_text = Util.parse_add_markup_theme(markup_text, null, true, true, true, Util.is_dark_theme(this.label), ref theme_dependent);
}
if (message.body.has_prefix("/me ")) {
@@ -214,7 +100,7 @@ public class MessageItemWidget : SizeRequestBin {
markup_text = @"<span size=\'$size_str\'>" + markup_text + "</span>";
}
- string dim_color = Util.is_dark_theme(this) ? "#BDBDBD" : "#707070";
+ string dim_color = Util.is_dark_theme(this.label) ? "#BDBDBD" : "#707070";
if (message.edit_to != null) {
markup_text += @" <span size='small' color='$dim_color'>(%s)</span>".printf(_("edited"));
@@ -246,7 +132,7 @@ public class MessageItemWidget : SizeRequestBin {
if (theme_dependent && realize_id == -1) {
realize_id = label.realize.connect(update_label);
- style_updated_id = label.style_updated.connect(update_label);
+// style_updated_id = label.style_updated.connect(update_label);
} else if (!theme_dependent && realize_id != -1) {
label.disconnect(realize_id);
label.disconnect(style_updated_id);
@@ -254,6 +140,83 @@ public class MessageItemWidget : SizeRequestBin {
return markup_text;
}
+ public void update_label() {
+ label.label = generate_markup_text(content_item);
+ }
+
+ public override Object? get_widget(Plugins.ConversationItemWidgetInterface outer, Plugins.WidgetType type) {
+
+ stream_interactor.get_module(MessageCorrection.IDENTITY).received_correction.connect(on_received_correction);
+
+ this.notify["in-edit-mode"].connect(() => {
+ if (in_edit_mode == false) return;
+ bool allowed = stream_interactor.get_module(MessageCorrection.IDENTITY).is_own_correction_allowed(message_item.conversation, message_item.message);
+ if (allowed) {
+ MessageItem message_item = content_item as MessageItem;
+ Message message = message_item.message;
+
+ edit_mode = new MessageItemEditMode();
+ controller = new ChatTextViewController(edit_mode.chat_text_view, stream_interactor);
+ Conversation conversation = message_item.conversation;
+ controller.initialize_for_conversation(conversation);
+
+ edit_mode.cancelled.connect(() => {
+ in_edit_mode = false;
+ outer.set_widget(label, Plugins.WidgetType.GTK4);
+ label.grab_focus();
+ });
+ edit_mode.send.connect(() => {
+ if (((MessageItem) content_item).message.body != edit_mode.chat_text_view.text_view.buffer.text) {
+ on_edit_send(edit_mode.chat_text_view.text_view.buffer.text);
+ } else {
+// edit_cancelled();
+ }
+ in_edit_mode = false;
+ outer.set_widget(label, Plugins.WidgetType.GTK4);
+ label.grab_focus();
+ });
+
+ edit_mode.chat_text_view.text_view.buffer.text = message.body;
+
+ outer.set_widget(edit_mode, Plugins.WidgetType.GTK4);
+ edit_mode.chat_text_view.text_view.grab_focus();
+ } else {
+ this.in_edit_mode = false;
+ }
+ });
+
+ return label;
+ }
+
+ public override Gee.List<Plugins.MessageAction>? get_item_actions(Plugins.WidgetType type) {
+ if (content_item as FileItem != null) return null;
+
+ bool allowed = stream_interactor.get_module(MessageCorrection.IDENTITY).is_own_correction_allowed(message_item.conversation, message_item.message);
+ Gee.List<Plugins.MessageAction> actions = new ArrayList<Plugins.MessageAction>();
+ if (allowed && !in_edit_mode) {
+ Plugins.MessageAction action1 = new Plugins.MessageAction();
+ action1.icon_name = "document-edit-symbolic";
+ action1.callback = (button, content_meta_item_activated, widget) => {
+ this.in_edit_mode = true;
+ };
+ actions.add(action1);
+ }
+ return actions;
+ }
+
+ private void on_edit_send(string text) {
+ stream_interactor.get_module(MessageCorrection.IDENTITY).send_correction(message_item.conversation, message_item.message, text);
+ this.in_edit_mode = false;
+ }
+
+ private void on_received_correction(ContentItem content_item) {
+ if (this.content_item.id == content_item.id) {
+ this.content_item = content_item;
+ message_item = content_item as MessageItem;
+ update_label();
+ }
+ }
+
public static bool on_label_activate_link(string uri) {
// Always handle xmpp URIs with Dino
if (!uri.has_prefix("xmpp:")) return false;
@@ -276,7 +239,7 @@ public class MessageItemEditMode : Box {
[GtkChild] public unowned Frame frame;
construct {
- Util.force_css(frame, "* { border-radius: 3px; }");
+ Util.force_css(frame, "* { border-radius: 3px; padding: 5px 7px; }");
EmojiChooser chooser = new EmojiChooser();
chooser.emoji_picked.connect((emoji) => {
diff --git a/main/src/ui/conversation_content_view/subscription_notification.vala b/main/src/ui/conversation_content_view/subscription_notification.vala
index d493ff78..1f0f39d8 100644
--- a/main/src/ui/conversation_content_view/subscription_notification.vala
+++ b/main/src/ui/conversation_content_view/subscription_notification.vala
@@ -34,8 +34,8 @@ public class SubscriptionNotitication : Object {
private void show_notification() {
Box box = new Box(Orientation.HORIZONTAL, 5) { visible=true };
- Button accept_button = new Button() { label=_("Accept"), visible=true };
- Button deny_button = new Button() { label=_("Deny"), visible=true };
+ Button accept_button = new Button.with_label(_("Accept")) { visible=true };
+ Button deny_button = new Button.with_label(_("Deny")) { visible=true };
GLib.Application app = GLib.Application.get_default();
accept_button.clicked.connect(() => {
app.activate_action("accept-subscription", conversation.id);
@@ -45,9 +45,9 @@ public class SubscriptionNotitication : Object {
app.activate_action("deny-subscription", conversation.id);
conversation_view.remove_notification(box);
});
- box.add(new Label(_("This contact would like to add you to their contact list")) { margin_end=10, visible=true });
- box.add(accept_button);
- box.add(deny_button);
+ box.append(new Label(_("This contact would like to add you to their contact list")) { margin_end=10, visible=true });
+ box.append(accept_button);
+ box.append(deny_button);
conversation_view.add_notification(box);
}
}
diff --git a/main/src/ui/conversation_list/conversation_list_item_factory.vala b/main/src/ui/conversation_list/conversation_list_item_factory.vala
new file mode 100644
index 00000000..dc26d2f1
--- /dev/null
+++ b/main/src/ui/conversation_list/conversation_list_item_factory.vala
@@ -0,0 +1,245 @@
+using Gtk;
+using Dino.Entities;
+using Dino;
+using Gee;
+using Pango;
+using Xmpp;
+
+namespace Dino.Ui.ConversationList {
+
+ public static ListItemFactory get_item_factory() {
+ SignalListItemFactory item_factory = new SignalListItemFactory();
+ item_factory.setup.connect((list_item) => { on_setup(list_item); });
+ item_factory.bind.connect((list_item) => { on_bind(list_item); });
+ return item_factory;
+ }
+
+ public static void on_setup(ListItem listitem) {
+ listitem.child = new ConversationListRow();
+ }
+
+ public static void on_bind(ListItem listitem) {
+ ConversationViewModel list_model = (ConversationViewModel) listitem.get_item();
+ ConversationListRow view = (ConversationListRow) listitem.get_child();
+ StreamInteractor stream_interactor = list_model.stream_interactor;
+
+ list_model.bind_property("name", view.name_label, "label");
+ list_model.notify["latest-content-item"].connect((obj, _) => {
+ update_content_item(view, list_model.conversation, stream_interactor, ((ConversationViewModel) obj).latest_content_item);
+ });
+ list_model.notify["unread-count"].connect((obj, _) => {
+ update_read(view, list_model.conversation, stream_interactor, (int) obj);
+ });
+
+ view.x_button.clicked.connect(() => list_model.closed() );
+
+ ConversationViewModel view_model = (ConversationViewModel) listitem.get_item();
+ view.name_label.label = view_model.name;
+ if (view_model.latest_content_item != null) {
+ update_content_item(view, view_model.conversation, stream_interactor, view_model.latest_content_item);
+ }
+ update_read(view, view_model.conversation, stream_interactor, view_model.unread_count);
+ }
+
+ private static void update_content_item(ConversationListRow view, Conversation conversation, StreamInteractor stream_interactor, ContentItem last_content_item) {
+ view.time_label.label = get_relative_time(last_content_item.time.to_local());
+ view.image.set_conversation(stream_interactor, conversation);
+
+ Label nick_label = view.nick_label;
+ Label message_label = view.message_label;
+
+ switch (last_content_item.type_) {
+ case MessageItem.TYPE:
+ MessageItem message_item = last_content_item as MessageItem;
+ Message last_message = message_item.message;
+
+ string body = last_message.body;
+ bool me_command = body.has_prefix("/me ");
+
+ /* If we have a /me command, we always show the display
+ * name, and we don't set me_is_me on
+ * get_participant_display_name, since that will return
+ * "Me" (internationalized), whereas /me commands expect to
+ * be in the third person. We also omit the colon in this
+ * case, and strip off the /me prefix itself. */
+
+ if (conversation.type_ == Conversation.Type.GROUPCHAT || me_command) {
+ nick_label.label = Util.get_participant_display_name(stream_interactor, conversation, last_message.from, !me_command);
+ } else if (last_message.direction == Message.DIRECTION_SENT) {
+ nick_label.label = _("Me");
+ } else {
+ nick_label.label = "";
+ }
+
+ if (me_command) {
+ /* Don't slice off the space after /me */
+ body = body.slice("/me".length, body.length);
+ } else if (nick_label.label.length > 0) {
+ /* TODO: Is this valid for RTL languages? */
+ nick_label.label += ": ";
+ }
+
+ message_label.attributes.filter((attr) => attr.equal(attr_style_new(Pango.Style.ITALIC)));
+ message_label.label = Util.summarize_whitespaces_to_space(body);
+
+ break;
+ case FileItem.TYPE:
+ FileItem file_item = last_content_item as FileItem;
+ FileTransfer transfer = file_item.file_transfer;
+
+ if (conversation.type_ == Conversation.Type.GROUPCHAT) {
+ // TODO properly display nick for oneself
+ nick_label.label = Util.get_participant_display_name(stream_interactor, conversation, file_item.file_transfer.from, true) + ": ";
+ } else {
+ nick_label.label = transfer.direction == Message.DIRECTION_SENT ? _("Me") + ": " : "";
+ }
+
+ bool file_is_image = transfer.mime_type != null && transfer.mime_type.has_prefix("image");
+ message_label.attributes.insert(attr_style_new(Pango.Style.ITALIC));
+ if (transfer.direction == Message.DIRECTION_SENT) {
+ message_label.label = (file_is_image ? _("Image sent") : _("File sent") );
+ } else {
+ message_label.label = (file_is_image ? _("Image received") : _("File received") );
+ }
+ break;
+ case CallItem.TYPE:
+ CallItem call_item = (CallItem) last_content_item;
+ Call call = call_item.call;
+
+ nick_label.label = call.direction == Call.DIRECTION_OUTGOING ? _("Me") + ": " : "";
+ message_label.attributes.insert(attr_style_new(Pango.Style.ITALIC));
+ message_label.label = call.direction == Call.DIRECTION_OUTGOING ? _("Outgoing call") : _("Incoming call");
+ break;
+ }
+ nick_label.visible = true;
+ message_label.visible = true;
+ }
+
+ private void update_read(ConversationListRow view, Conversation conversation, StreamInteractor stream_interactor, int num_unread) {
+ Label unread_count_label = view.unread_count_label;
+ Label name_label = view.name_label;
+ Label time_label = view.time_label;
+ Label nick_label = view.nick_label;
+ Label message_label = view.message_label;
+ if (num_unread == 0) {
+ unread_count_label.visible = false;
+
+ name_label.attributes.filter((attr) => attr.equal(attr_weight_new(Weight.BOLD)));
+ time_label.attributes.filter((attr) => attr.equal(attr_weight_new(Weight.BOLD)));
+ nick_label.attributes.filter((attr) => attr.equal(attr_weight_new(Weight.BOLD)));
+ message_label.attributes.filter((attr) => attr.equal(attr_weight_new(Weight.BOLD)));
+ } else {
+ unread_count_label.label = num_unread.to_string();
+ unread_count_label.visible = true;
+
+ if (conversation.get_notification_setting(stream_interactor) == Conversation.NotifySetting.ON) {
+ unread_count_label.get_style_context().add_class("unread-count-notify");
+ unread_count_label.get_style_context().remove_class("unread-count");
+ } else {
+ unread_count_label.get_style_context().add_class("unread-count");
+ unread_count_label.get_style_context().remove_class("unread-count-notify");
+ }
+
+ name_label.attributes.insert(attr_weight_new(Weight.BOLD));
+ time_label.attributes.insert(attr_weight_new(Weight.BOLD));
+ nick_label.attributes.insert(attr_weight_new(Weight.BOLD));
+ message_label.attributes.insert(attr_weight_new(Weight.BOLD));
+ }
+
+ name_label.label = name_label.label; // TODO initializes redrawing, which would otherwise not happen. nicer?
+ time_label.label = time_label.label;
+ nick_label.label = nick_label.label;
+ message_label.label = message_label.label;
+ }
+
+ private Widget generate_tooltip(StreamInteractor stream_interactor, Conversation conversation) {
+ Grid grid = new Grid() { row_spacing=5, column_homogeneous=false, column_spacing=5, margin_start=7, margin_end=7, margin_top=7, margin_bottom=7 };
+
+ Label label = new Label(conversation.counterpart.to_string()) { valign=Align.START, xalign=0, visible=true };
+ label.attributes = new AttrList();
+ label.attributes.insert(attr_weight_new(Weight.BOLD));
+
+ grid.attach(label, 0, 0, 2, 1);
+
+ Gee.List<Jid>? full_jids = stream_interactor.get_module(PresenceManager.IDENTITY).get_full_jids(conversation.counterpart, conversation.account);
+ if (full_jids == null) return grid;
+
+ for (int i = 0; i < full_jids.size; i++) {
+ Jid full_jid = full_jids[i];
+ string? show = stream_interactor.get_module(PresenceManager.IDENTITY).get_last_show(full_jid, conversation.account);
+ if (show == null) continue;
+
+ int i_cache = i;
+ stream_interactor.get_module(EntityInfo.IDENTITY).get_identity.begin(conversation.account, full_jid, (_, res) => {
+ Xep.ServiceDiscovery.Identity? identity = stream_interactor.get_module(EntityInfo.IDENTITY).get_identity.end(res);
+
+ Image image = new Image() { hexpand=false, valign=Align.CENTER, visible=true };
+ if (identity != null && (identity.type_ == Xep.ServiceDiscovery.Identity.TYPE_PHONE || identity.type_ == Xep.ServiceDiscovery.Identity.TYPE_TABLET)) {
+ image.set_from_icon_name("dino-device-phone-symbolic");
+ } else {
+ image.set_from_icon_name("dino-device-desktop-symbolic");
+ }
+
+ if (show == Presence.Stanza.SHOW_AWAY) {
+ Util.force_color(image, "#FF9800");
+ } else if (show == Presence.Stanza.SHOW_XA || show == Presence.Stanza.SHOW_DND) {
+ Util.force_color(image, "#FF5722");
+ } else {
+ Util.force_color(image, "#4CAF50");
+ }
+
+ string? status = null;
+ if (show == Presence.Stanza.SHOW_AWAY) {
+ status = "away";
+ } else if (show == Presence.Stanza.SHOW_XA) {
+ status = "not available";
+ } else if (show == Presence.Stanza.SHOW_DND) {
+ status = "do not disturb";
+ }
+
+ var sb = new StringBuilder();
+ if (identity != null && identity.name != null) {
+ sb.append(identity.name);
+ } else if (full_jid.resourcepart != null) {
+ sb.append(full_jid.resourcepart);
+ } else {
+ return;
+ }
+ if (status != null) {
+ sb.append(" <i>(").append(status).append(")</i>");
+ }
+
+ Label resource = new Label(sb.str) { use_markup=true, hexpand=true, xalign=0, visible=true };
+
+ grid.attach(image, 0, i_cache + 1, 1, 1);
+ grid.attach(resource, 1, i_cache + 1, 1, 1);
+ });
+ }
+ return grid;
+ }
+
+ private static string get_relative_time(DateTime datetime) {
+ DateTime now = new DateTime.now_local();
+ TimeSpan timespan = now.difference(datetime);
+ if (timespan > 365 * TimeSpan.DAY) {
+ return datetime.get_year().to_string();
+ } else if (timespan > 7 * TimeSpan.DAY) {
+ // Day and month
+ // xgettext:no-c-format
+ return datetime.format(_("%b %d"));
+ } else if (timespan > 2 * TimeSpan.DAY) {
+ return datetime.format("%a");
+ } else if (datetime.get_day_of_month() != now.get_day_of_month()) {
+ return _("Yesterday");
+ } else if (timespan > 9 * TimeSpan.MINUTE) {
+ return datetime.format(Util.is_24h_format() ?
+ /* xgettext:no-c-format */ /* Time in 24h format (w/o seconds) */ _("%H∶%M") :
+ /* xgettext:no-c-format */ /* Time in 12h format (w/o seconds) */ _("%l∶%M %p"));
+ } else if (timespan > 1 * TimeSpan.MINUTE) {
+ ulong mins = (ulong) (timespan.abs() / TimeSpan.MINUTE);
+ return n("%i min ago", "%i mins ago", mins).printf(mins);
+ } else {
+ return _("Just now");
+ }
+ }
+} \ No newline at end of file
diff --git a/main/src/ui/conversation_list/conversation_list_model.vala b/main/src/ui/conversation_list/conversation_list_model.vala
new file mode 100644
index 00000000..9412e64a
--- /dev/null
+++ b/main/src/ui/conversation_list/conversation_list_model.vala
@@ -0,0 +1,141 @@
+using Gtk;
+using Dino.Entities;
+using Dino;
+using Gee;
+using Xmpp;
+
+public class Dino.Ui.ConversationViewModel : Object {
+ public signal void closed();
+
+ public StreamInteractor stream_interactor { get; set; }
+ public Conversation conversation { get; set; }
+ public string name { get; set; }
+ public ContentItem? latest_content_item { get; set; }
+ public int unread_count { get; set; }
+}
+
+public class Dino.Ui.ConversationListModel : Object, ListModel {
+
+ public signal void closed_conversation(Conversation conversation);
+
+ private HashMap<Conversation, ConversationViewModel> conversation_view_model_hm = new HashMap<Conversation, ConversationViewModel>(Conversation.hash_func, Conversation.equals_func);
+ private ArrayList<ConversationViewModel> view_models = new ArrayList<ConversationViewModel>();
+ private StreamInteractor stream_interactor;
+
+ public ConversationListModel(StreamInteractor stream_interactor) {
+ this.stream_interactor = stream_interactor;
+
+ stream_interactor.get_module(ConversationManager.IDENTITY).conversation_activated.connect(add_conversation);
+ stream_interactor.get_module(ConversationManager.IDENTITY).conversation_deactivated.connect(remove_conversation);
+ stream_interactor.get_module(ContentItemStore.IDENTITY).new_item.connect(on_content_item_received);
+
+ foreach (Conversation conversation in stream_interactor.get_module(ConversationManager.IDENTITY).get_active_conversations()) {
+ var view_model = create_view_model(conversation);
+ view_models.add(view_model);
+ conversation_view_model_hm[conversation] = view_model;
+ }
+ view_models.sort(sort);
+ items_changed(0, 0, get_n_items());
+
+ stream_interactor.get_module(RosterManager.IDENTITY).updated_roster_item.connect((account, jid, roster_item) => {
+ ConversationViewModel? view_model = get_view_model(account, jid, Conversation.Type.CHAT);
+ if (view_model == null) return;
+ view_model.name = Util.get_conversation_display_name(stream_interactor, view_model.conversation);
+ });
+ stream_interactor.get_module(MucManager.IDENTITY).room_info_updated.connect((account, jid) => {
+ ConversationViewModel? view_model = get_view_model(account, jid, Conversation.Type.GROUPCHAT);
+ if (view_model == null) return;
+ view_model.name = Util.get_conversation_display_name(stream_interactor, view_model.conversation);
+ // bubble color might have changed
+ view_model.unread_count = stream_interactor.get_module(ChatInteraction.IDENTITY).get_num_unread(view_model.conversation);
+ });
+ stream_interactor.get_module(MucManager.IDENTITY).private_room_occupant_updated.connect((account, room, occupant) => {
+ ConversationViewModel? view_model = get_view_model(account, room.bare_jid, Conversation.Type.GROUPCHAT);
+ if (view_model == null) return;
+ view_model.name = Util.get_conversation_display_name(stream_interactor, view_model.conversation);
+ });
+ }
+
+ public GLib.Object? get_item (uint position) {
+ if (position >= view_models.size) return null;
+ return view_models[(int)position];
+ }
+
+ public GLib.Type get_item_type () {
+ return GLib.Type.OBJECT;
+ }
+
+ public uint get_n_items () {
+ return view_models.size;
+ }
+
+ private ConversationViewModel create_view_model(Conversation conversation) {
+ var view_model = new ConversationViewModel();
+ view_model.stream_interactor = stream_interactor;
+ view_model.conversation = conversation;
+ view_model.name = Util.get_conversation_display_name(stream_interactor, conversation);
+ view_model.latest_content_item = stream_interactor.get_module(ContentItemStore.IDENTITY).get_latest(conversation);
+ view_model.unread_count = stream_interactor.get_module(ChatInteraction.IDENTITY).get_num_unread(conversation);
+ view_model.closed.connect(() => closed_conversation(conversation));
+
+ return view_model;
+ }
+
+ private void add_conversation(Conversation conversation) {
+ var view_model = create_view_model(conversation);
+
+ view_models.add(view_model);
+ conversation_view_model_hm[conversation] = view_model;
+ view_models.sort(sort);
+
+ int idx = view_models.index_of(view_model);
+ items_changed(idx, 0, 1);
+ }
+
+ private async void remove_conversation(Conversation conversation) {
+ ConversationViewModel? view_model = conversation_view_model_hm[conversation];
+ if (view_model == null) return;
+
+ int idx = view_models.index_of(view_model);
+ view_models.remove(view_model);
+ conversation_view_model_hm.unset(conversation);
+ items_changed(idx, 1, 0);
+ }
+
+ private void on_content_item_received(ContentItem item, Conversation conversation) {
+ ConversationViewModel? view_model = conversation_view_model_hm[conversation];
+ if (view_model == null) return;
+
+ view_model.latest_content_item = stream_interactor.get_module(ContentItemStore.IDENTITY).get_latest(conversation);
+ view_model.unread_count = stream_interactor.get_module(ChatInteraction.IDENTITY).get_num_unread(conversation);
+
+ view_models.sort(sort);
+ items_changed(0, view_models.size, view_models.size); // TODO better
+ }
+
+ private ConversationViewModel? get_view_model(Account account, Jid jid, Conversation.Type? conversation_ty) {
+ foreach (ConversationViewModel view_model in view_models) {
+ Conversation conversation = view_model.conversation;
+ if (conversation.account.equals(account) && conversation.counterpart.equals(jid)) {
+ if (conversation_ty != null && conversation.type_ != conversation_ty) continue;
+ return view_model;
+ }
+ }
+ return null;
+ }
+
+ private int sort(ConversationViewModel vm1, ConversationViewModel vm2) {
+ Conversation c1 = vm1.conversation;
+ Conversation c2 = vm2.conversation;
+
+ if (c1 == null || c2 == null) return 0;
+ if (c1.last_active == null) return -1;
+ if (c2.last_active == null) return 1;
+
+ int comp = c2.last_active.compare(c1.last_active);
+ if (comp != 0) return comp;
+
+ return Util.get_conversation_display_name(stream_interactor, c1)
+ .collate(Util.get_conversation_display_name(stream_interactor, c2));
+ }
+} \ No newline at end of file
diff --git a/main/src/ui/conversation_list/conversation_list_row.vala b/main/src/ui/conversation_list/conversation_list_row.vala
new file mode 100644
index 00000000..ab4e8cee
--- /dev/null
+++ b/main/src/ui/conversation_list/conversation_list_row.vala
@@ -0,0 +1,41 @@
+using Gee;
+using Gdk;
+using Gtk;
+using Pango;
+
+using Dino;
+using Dino.Entities;
+using Xmpp;
+
+[GtkTemplate (ui = "/im/dino/Dino/conversation_row.ui")]
+public class Dino.Ui.ConversationListRow : ListBoxRow {
+
+ [GtkChild] public unowned AvatarImage image;
+ [GtkChild] public unowned Label name_label;
+ [GtkChild] public unowned Label time_label;
+ [GtkChild] public unowned Label nick_label;
+ [GtkChild] public unowned Label message_label;
+ [GtkChild] public unowned Label unread_count_label;
+ [GtkChild] public unowned Button x_button;
+ [GtkChild] public unowned Revealer time_revealer;
+ [GtkChild] public unowned Revealer xbutton_revealer;
+ [GtkChild] public unowned Revealer unread_count_revealer;
+ [GtkChild] public unowned Revealer main_revealer;
+
+ construct {
+ name_label.attributes = new AttrList();
+ }
+
+ public override void state_flags_changed(StateFlags flags) {
+ StateFlags curr_flags = get_state_flags();
+ if ((curr_flags & StateFlags.PRELIGHT) != 0) {
+ time_revealer.set_reveal_child(false);
+ unread_count_revealer.set_reveal_child(false);
+ xbutton_revealer.set_reveal_child(true);
+ } else {
+ time_revealer.set_reveal_child(true);
+ unread_count_revealer.set_reveal_child(true);
+ xbutton_revealer.set_reveal_child(false);
+ }
+ }
+} \ No newline at end of file
diff --git a/main/src/ui/conversation_list_titlebar.vala b/main/src/ui/conversation_list_titlebar.vala
index d97bec6e..692ac680 100644
--- a/main/src/ui/conversation_list_titlebar.vala
+++ b/main/src/ui/conversation_list_titlebar.vala
@@ -15,18 +15,12 @@ public class ConversationListTitlebar : Gtk.Box {
}
}
-[GtkTemplate (ui = "/im/dino/Dino/conversation_list_titlebar_csd.ui")]
-public class ConversationListTitlebarCsd : Gtk.HeaderBar {
-
- [GtkChild] private unowned MenuButton add_button;
- [GtkChild] private unowned MenuButton menu_button;
-
- public ConversationListTitlebarCsd() {
- custom_title = new Label("Dino") { visible = true, hexpand = true, xalign = 0 };
- custom_title.get_style_context().add_class("title");
-
- create_add_menu(add_button, menu_button);
- }
+public static HeaderBar get_conversation_list_titlebar_csd() {
+ Builder builder = new Builder.from_resource("/im/dino/Dino/conversation_list_titlebar_csd.ui");
+ MenuButton add_button = (MenuButton) builder.get_object("add_button");
+ MenuButton menu_button = (MenuButton) builder.get_object("menu_button");
+ create_add_menu(add_button, menu_button);
+ return (HeaderBar) builder.get_object("header_bar");
}
private static void create_add_menu(MenuButton add_button, MenuButton menu_button) {
diff --git a/main/src/ui/conversation_selector/conversation_selector.vala b/main/src/ui/conversation_selector/conversation_selector.vala
index 4c7e6b8d..2e262bf1 100644
--- a/main/src/ui/conversation_selector/conversation_selector.vala
+++ b/main/src/ui/conversation_selector/conversation_selector.vala
@@ -6,16 +6,20 @@ using Dino.Entities;
namespace Dino.Ui {
-public class ConversationSelector : ListBox {
+public class ConversationSelector : Widget {
public signal void conversation_selected(Conversation conversation);
+ ListBox list_box = new ListBox() { hexpand=true };
+
private StreamInteractor stream_interactor;
private uint? drag_timeout;
private HashMap<Conversation, ConversationSelectorRow> rows = new HashMap<Conversation, ConversationSelectorRow>(Conversation.hash_func, Conversation.equals_func);
public ConversationSelector init(StreamInteractor stream_interactor) {
this.stream_interactor = stream_interactor;
+ list_box.insert_after(this, null);
+ this.layout_manager = new BinLayout();
stream_interactor.get_module(ConversationManager.IDENTITY).conversation_activated.connect(add_conversation);
stream_interactor.get_module(ConversationManager.IDENTITY).conversation_deactivated.connect(remove_conversation);
@@ -33,19 +37,21 @@ public class ConversationSelector : ListBox {
construct {
get_style_context().add_class("sidebar");
- set_header_func(header);
- set_sort_func(sort);
+ list_box.set_header_func(header);
+ list_box.set_sort_func(sort);
realize.connect(() => {
- ListBoxRow? first_row = get_row_at_index(0);
+ ListBoxRow? first_row = list_box.get_row_at_index(0);
if (first_row != null) {
- select_row(first_row);
+ list_box.select_row(first_row);
row_activated(first_row);
}
});
+
+ list_box.row_activated.connect(row_activated);
}
- public override void row_activated(ListBoxRow r) {
+ public void row_activated(ListBoxRow r) {
ConversationSelectorRow? row = r as ConversationSelectorRow;
if (row != null) {
conversation_selected(row.conversation);
@@ -56,12 +62,12 @@ public class ConversationSelector : ListBox {
if (!rows.has_key(conversation)) {
add_conversation(conversation);
}
- this.select_row(rows[conversation]);
+ list_box.select_row(rows[conversation]);
}
private void on_content_item_received(ContentItem item, Conversation conversation) {
if (rows.has_key(conversation)) {
- invalidate_sort();
+ list_box.invalidate_sort();
}
}
@@ -70,17 +76,17 @@ public class ConversationSelector : ListBox {
if (!rows.has_key(conversation)) {
row = new ConversationSelectorRow(stream_interactor, conversation);
rows[conversation] = row;
- add(row);
+ list_box.append(row);
row.main_revealer.set_reveal_child(true);
- drag_dest_set(row, DestDefaults.MOTION, null, Gdk.DragAction.COPY);
- drag_dest_set_track_motion(row, true);
- row.drag_motion.connect(this.on_drag_motion);
- row.drag_leave.connect(this.on_drag_leave);
+// drag_dest_set(row, DestDefaults.MOTION, null, Gdk.DragAction.COPY);
+// drag_dest_set_track_motion(row, true);
+// row.drag_motion.connect(this.on_drag_motion);
+// row.drag_leave.connect(this.on_drag_leave);
}
- invalidate_sort();
+ list_box.invalidate_sort();
}
- public bool on_drag_motion(Widget widget, Gdk.DragContext context,
+ /*public bool on_drag_motion(Widget widget, Gdk.DragContext context,
int x, int y, uint time) {
if (this.drag_timeout != null)
return false;
@@ -100,17 +106,17 @@ public class ConversationSelector : ListBox {
Source.remove(this.drag_timeout);
this.drag_timeout = null;
}
- }
+ }*/
private void select_fallback_conversation(Conversation conversation) {
- if (get_selected_row() == rows[conversation]) {
+ if (list_box.get_selected_row() == rows[conversation]) {
int index = rows[conversation].get_index();
- ListBoxRow? next_select_row = get_row_at_index(index + 1);
+ ListBoxRow? next_select_row = list_box.get_row_at_index(index + 1);
if (next_select_row == null) {
- next_select_row = get_row_at_index(index - 1);
+ next_select_row = list_box.get_row_at_index(index - 1);
}
if (next_select_row != null) {
- select_row(next_select_row);
+ list_box.select_row(next_select_row);
row_activated(next_select_row);
}
}
@@ -120,17 +126,17 @@ public class ConversationSelector : ListBox {
select_fallback_conversation(conversation);
if (rows.has_key(conversation)) {
yield rows[conversation].colapse();
- remove(rows[conversation]);
+ list_box.remove(rows[conversation]);
rows.unset(conversation);
}
}
public void loop_conversations(bool backwards) {
- int index = get_selected_row().get_index();
+ int index = list_box.get_selected_row().get_index();
int new_index = ((index + (backwards ? -1 : 1)) + rows.size) % rows.size;
- ListBoxRow? next_select_row = get_row_at_index(new_index);
+ ListBoxRow? next_select_row = list_box.get_row_at_index(new_index);
if (next_select_row != null) {
- select_row(next_select_row);
+ list_box.select_row(next_select_row);
row_activated(next_select_row);
}
}
diff --git a/main/src/ui/conversation_selector/conversation_selector_row.vala b/main/src/ui/conversation_selector/conversation_selector_row.vala
index 067f257c..f813ddfc 100644
--- a/main/src/ui/conversation_selector/conversation_selector_row.vala
+++ b/main/src/ui/conversation_selector/conversation_selector_row.vala
@@ -264,7 +264,7 @@ public class ConversationSelectorRow : ListBoxRow {
private static Regex dino_resource_regex = /^dino\.[a-f0-9]{8}$/;
private Widget generate_tooltip() {
- Grid grid = new Grid() { row_spacing=5, column_homogeneous=false, column_spacing=2, margin_start=5, margin_end=5, margin_top=2, margin_bottom=2, visible=true };
+ Grid grid = new Grid() { row_spacing=5, column_homogeneous=false, column_spacing=5, margin_start=7, margin_end=7, margin_top=7, margin_bottom=7 };
Label label = new Label(conversation.counterpart.to_string()) { valign=Align.START, xalign=0, visible=true };
label.attributes = new AttrList();
@@ -286,9 +286,9 @@ public class ConversationSelectorRow : ListBoxRow {
Image image = new Image() { hexpand=false, valign=Align.CENTER, visible=true };
if (identity != null && (identity.type_ == Xep.ServiceDiscovery.Identity.TYPE_PHONE || identity.type_ == Xep.ServiceDiscovery.Identity.TYPE_TABLET)) {
- image.set_from_icon_name("dino-device-phone-symbolic", IconSize.SMALL_TOOLBAR);
+ image.set_from_icon_name("dino-device-phone-symbolic");
} else {
- image.set_from_icon_name("dino-device-desktop-symbolic", IconSize.SMALL_TOOLBAR);
+ image.set_from_icon_name("dino-device-desktop-symbolic");
}
if (show == Presence.Stanza.SHOW_AWAY) {
diff --git a/main/src/ui/conversation_titlebar/call_entry.vala b/main/src/ui/conversation_titlebar/call_entry.vala
index 1b8b2a05..72376126 100644
--- a/main/src/ui/conversation_titlebar/call_entry.vala
+++ b/main/src/ui/conversation_titlebar/call_entry.vala
@@ -8,65 +8,42 @@ namespace Dino.Ui {
public class CallTitlebarEntry : Plugins.ConversationTitlebarEntry, Object {
public string id { get { return "call"; } }
-
- public CallButton call_button;
-
- private StreamInteractor stream_interactor;
-
- public CallTitlebarEntry(StreamInteractor stream_interactor) {
- this.stream_interactor = stream_interactor;
-
- call_button = new CallButton(stream_interactor) { tooltip_text=_("Start call") };
- call_button.set_image(new Gtk.Image.from_icon_name("dino-phone-symbolic", Gtk.IconSize.MENU) { visible=true });
- }
-
public double order { get { return 4; } }
- public Plugins.ConversationTitlebarWidget? get_widget(Plugins.WidgetType type) {
- if (type == Plugins.WidgetType.GTK) {
- return call_button;
- }
- return null;
- }
- }
- public class CallButton : Plugins.ConversationTitlebarWidget, Gtk.MenuButton {
+ private MenuButton button = new MenuButton() { tooltip_text=_("Start call") };
private StreamInteractor stream_interactor;
private Conversation conversation;
- private ModelButton audio_button = new ModelButton() { text=_("Audio call"), visible=true };
- private ModelButton video_button = new ModelButton() { text=_("Video call"), visible=true };
-
- public CallButton(StreamInteractor stream_interactor) {
+ public CallTitlebarEntry(StreamInteractor stream_interactor) {
this.stream_interactor = stream_interactor;
- use_popover = true;
- image = new Gtk.Image.from_icon_name("dino-phone-symbolic", Gtk.IconSize.MENU) { visible=true };
+ button.set_icon_name("dino-phone-symbolic");
- Gtk.PopoverMenu popover_menu = new Gtk.PopoverMenu();
- Box box = new Box(Orientation.VERTICAL, 0) { margin=10, visible=true };
- audio_button.clicked.connect(() => {
+ Menu menu_model = new Menu();
+ menu_model.append(_("Audio call"), "call.audio");
+ menu_model.append(_("Video call"), "call.video");
+ Gtk.PopoverMenu popover_menu = new Gtk.PopoverMenu.from_model(menu_model);
+ button.popover = popover_menu;
+
+ SimpleActionGroup action_group = new SimpleActionGroup();
+ SimpleAction audio_call_action = new SimpleAction("audio", null);
+ audio_call_action.activate.connect((parameter) => {
stream_interactor.get_module(Calls.IDENTITY).initiate_call.begin(conversation, false, (_, res) => {
CallState call_state = stream_interactor.get_module(Calls.IDENTITY).initiate_call.end(res);
open_call_window(call_state);
});
});
- box.add(audio_button);
-
- video_button.clicked.connect(() => {
+ action_group.insert(audio_call_action);
+ SimpleAction video_call_action = new SimpleAction("video", null);
+ video_call_action.activate.connect((parameter) => {
stream_interactor.get_module(Calls.IDENTITY).initiate_call.begin(conversation, true, (_, res) => {
CallState call_state = stream_interactor.get_module(Calls.IDENTITY).initiate_call.end(res);
open_call_window(call_state);
});
});
- box.add(video_button);
- popover_menu.add(box);
-
- popover = popover_menu;
-
- clicked.connect(() => {
- popover_menu.visible = true;
- });
+ action_group.insert(video_call_action);
+ button.insert_action_group("call", action_group);
stream_interactor.get_module(Calls.IDENTITY).call_incoming.connect((call, state,conversation) => {
update_button_state();
@@ -96,6 +73,7 @@ namespace Dino.Ui {
}
public new void set_conversation(Conversation conversation) {
+ print(@"set_conversation $(conversation.counterpart)\n");
this.conversation = conversation;
update_visibility.begin();
@@ -103,12 +81,12 @@ namespace Dino.Ui {
}
private void update_button_state() {
- this.sensitive = !stream_interactor.get_module(Calls.IDENTITY).is_call_in_progress();
+ button.sensitive = !stream_interactor.get_module(Calls.IDENTITY).is_call_in_progress();
}
private async void update_visibility() {
if (conversation == null) {
- visible = false;
+ button.visible = false;
return;
}
@@ -116,10 +94,15 @@ namespace Dino.Ui {
bool can_do_calls = yield stream_interactor.get_module(Calls.IDENTITY).can_conversation_do_calls(conversation);
if (conv_bak != conversation) return;
- visible = video_button.visible = can_do_calls;
+ button.visible = can_do_calls;
}
public new void unset_conversation() { }
+
+ public Object? get_widget(Plugins.WidgetType type) {
+ if (type != Plugins.WidgetType.GTK4) return null;
+ return button;
+ }
}
}
diff --git a/main/src/ui/conversation_titlebar/conversation_titlebar.vala b/main/src/ui/conversation_titlebar/conversation_titlebar.vala
index 60d8286b..0d13e48b 100644
--- a/main/src/ui/conversation_titlebar/conversation_titlebar.vala
+++ b/main/src/ui/conversation_titlebar/conversation_titlebar.vala
@@ -6,14 +6,17 @@ using Dino.Entities;
namespace Dino.Ui {
-public interface ConversationTitlebar : Widget {
+public interface ConversationTitlebar : Object {
public abstract string? subtitle { get; set; }
public abstract string? title { get; set; }
- public abstract void insert_entry(Plugins.ConversationTitlebarEntry entry);
+ public abstract void insert_button(Widget button);
+ public abstract Widget get_widget();
}
-public class ConversationTitlebarNoCsd : ConversationTitlebar, Gtk.Box {
+public class ConversationTitlebarNoCsd : ConversationTitlebar, Object {
+
+ public Box main = new Box(Orientation.HORIZONTAL, 0);
public string? title {
get { return title_label.label; }
@@ -33,49 +36,61 @@ public class ConversationTitlebarNoCsd : ConversationTitlebar, Gtk.Box {
private Label subtitle_label = new Label("") { use_markup=true, ellipsize=EllipsizeMode.END, visible=false };
construct {
- Box content_box = new Box(Orientation.HORIZONTAL, 0) { margin=5, margin_start=15, margin_end=10, hexpand=true, visible=true };
- this.add(content_box);
+ Box content_box = new Box(Orientation.HORIZONTAL, 0) { margin_start=15, margin_end=10, hexpand=true, visible=true };
+ main.append(content_box);
Box titles_box = new Box(Orientation.VERTICAL, 0) { valign=Align.CENTER, hexpand=true, visible=true };
- content_box.add(titles_box);
+ content_box.append(titles_box);
- titles_box.add(title_label);
+ titles_box.append(title_label);
subtitle_label.attributes = new AttrList();
subtitle_label.get_style_context().add_class("dim-label");
- titles_box.add(subtitle_label);
+ titles_box.append(subtitle_label);
- content_box.add(widgets_box);
+ content_box.append(widgets_box);
}
public ConversationTitlebarNoCsd() {
- this.get_style_context().add_class("dino-header-right");
+ main.get_style_context().add_class("dino-header-right");
}
- public void insert_entry(Plugins.ConversationTitlebarEntry entry) {
- Plugins.ConversationTitlebarWidget widget = entry.get_widget(Plugins.WidgetType.GTK);
- if (widget != null) {
- Button gtk_widget = (Gtk.Button) widget;
- gtk_widget.relief = ReliefStyle.NONE;
- widgets_box.pack_end(gtk_widget);
- }
+ public void insert_button(Widget button) {
+ widgets_box.prepend(button);
+ }
+
+ public Widget get_widget() {
+ return main;
}
}
-public class ConversationTitlebarCsd : ConversationTitlebar, Gtk.HeaderBar {
+public class ConversationTitlebarCsd : ConversationTitlebar, Object {
+
+ public new string? title { get { return title_label.label; } set { title_label.label = value; } }
+ public new string? subtitle { get { return subtitle_label.label; } set { subtitle_label.label = value; subtitle_label.visible = (value != null); } }
- public new string? title { get { return this.get_title(); } set { base.set_title(value); } }
- public new string? subtitle { get { return this.get_subtitle(); } set { base.set_subtitle(value); } }
+ public HeaderBar header_bar = new HeaderBar();
+ private Label title_label = new Label("") { ellipsize=EllipsizeMode.END };
+ private Label subtitle_label = new Label("") { ellipsize=EllipsizeMode.END, visible=false };
public ConversationTitlebarCsd() {
- this.get_style_context().add_class("dino-right");
- show_close_button = true;
- hexpand = true;
+ Box titles_box = new Box(Orientation.VERTICAL, 0) { valign=Align.CENTER };
+ title_label.attributes = new AttrList();
+ title_label.attributes.insert(Pango.attr_weight_new(Weight.BOLD));
+ titles_box.append(title_label);
+ subtitle_label.attributes = new AttrList();
+ subtitle_label.attributes.insert(Pango.attr_scale_new(Pango.Scale.SMALL));
+ subtitle_label.get_style_context().add_class("dim-label");
+ titles_box.append(subtitle_label);
+
+ header_bar.set_title_widget(titles_box);
+ }
+
+ public void insert_button(Widget button) {
+ header_bar.pack_end(button);
}
- public void insert_entry(Plugins.ConversationTitlebarEntry entry) {
- Plugins.ConversationTitlebarWidget widget = entry.get_widget(Plugins.WidgetType.GTK);
- Button gtk_widget = (Gtk.Button)widget;
- this.pack_end(gtk_widget);
+ public Widget get_widget() {
+ return header_bar;
}
}
diff --git a/main/src/ui/conversation_titlebar/menu_entry.vala b/main/src/ui/conversation_titlebar/menu_entry.vala
index 9b3b6ee2..28a06c24 100644
--- a/main/src/ui/conversation_titlebar/menu_entry.vala
+++ b/main/src/ui/conversation_titlebar/menu_entry.vala
@@ -6,58 +6,42 @@ namespace Dino.Ui {
class MenuEntry : Plugins.ConversationTitlebarEntry, Object {
public string id { get { return "menu"; } }
-
- StreamInteractor stream_interactor;
- MenuWidget widget;
-
- public MenuEntry(StreamInteractor stream_interactor) {
- this.stream_interactor = stream_interactor;
- }
-
public double order { get { return 0; } }
- public Plugins.ConversationTitlebarWidget? get_widget(Plugins.WidgetType type) {
- if (type == Plugins.WidgetType.GTK) {
- if (widget == null) {
- widget = new MenuWidget(stream_interactor) { visible=true, sensitive=false };
- }
- return widget;
- }
- return null;
- }
-}
-class MenuWidget : Button, Plugins.ConversationTitlebarWidget {
-
- private StreamInteractor stream_interactor;
+ StreamInteractor stream_interactor;
private Conversation? conversation;
- public MenuWidget(StreamInteractor stream_interactor) {
+ Button button = new Button() { icon_name="open-menu-symbolic" };
+
+ public MenuEntry(StreamInteractor stream_interactor) {
this.stream_interactor = stream_interactor;
- image = new Image.from_icon_name("open-menu-symbolic", IconSize.MENU);
- clicked.connect(on_clicked);
+ button.clicked.connect(on_clicked);
}
public new void set_conversation(Conversation conversation) {
- this.sensitive = true;
+ button.sensitive = true;
this.conversation = conversation;
if (conversation.type_ == Conversation.Type.GROUPCHAT) {
- tooltip_text = "Channel details";
+ button.tooltip_text = "Channel details";
} else {
- tooltip_text = "Conversation details";
+ button.tooltip_text = "Conversation details";
}
}
public new void unset_conversation() {
- this.sensitive = false;
+ button.sensitive = false;
}
private void on_clicked() {
ContactDetails.Dialog contact_details_dialog = new ContactDetails.Dialog(stream_interactor, conversation);
- contact_details_dialog.set_transient_for((Window) get_toplevel());
+ contact_details_dialog.set_transient_for((Window) button.get_root());
contact_details_dialog.present();
}
+ public Object? get_widget(Plugins.WidgetType type) {
+ if (type != Plugins.WidgetType.GTK4) return null;
+ return button;
+ }
}
-
}
diff --git a/main/src/ui/conversation_titlebar/occupants_entry.vala b/main/src/ui/conversation_titlebar/occupants_entry.vala
index a316be20..5c0da99b 100644
--- a/main/src/ui/conversation_titlebar/occupants_entry.vala
+++ b/main/src/ui/conversation_titlebar/occupants_entry.vala
@@ -6,53 +6,39 @@ namespace Dino.Ui {
class OccupantsEntry : Plugins.ConversationTitlebarEntry, Object {
public string id { get { return "occupants"; } }
+ public double order { get { return 3; } }
StreamInteractor stream_interactor;
- OccupantsWidget widget;
-
- public OccupantsEntry(StreamInteractor stream_interactor) {
- this.stream_interactor = stream_interactor;
- }
-
- public double order { get { return 3; } }
- public Plugins.ConversationTitlebarWidget? get_widget(Plugins.WidgetType type) {
- if (type == Plugins.WidgetType.GTK) {
- if (widget == null) {
- widget = new OccupantsWidget(stream_interactor);
- }
- return widget;
- }
- return null;
- }
-}
+ private Conversation? conversation;
-class OccupantsWidget : MenuButton, Plugins.ConversationTitlebarWidget {
+ private MenuButton button = new MenuButton() { icon_name="system-users-symbolic", tooltip_text=_("Members") };
- private Conversation? conversation;
- private StreamInteractor stream_interactor;
private OccupantMenu.View menu = null;
- public OccupantsWidget(StreamInteractor stream_interactor) {
- image = new Image.from_icon_name("system-users-symbolic", IconSize.MENU);
- tooltip_text = _("Members");
-
+ public OccupantsEntry(StreamInteractor stream_interactor) {
this.stream_interactor = stream_interactor;
- set_use_popover(true);
}
public new void set_conversation(Conversation conversation) {
this.conversation = conversation;
- visible = conversation.type_ == Conversation.Type.GROUPCHAT;
if (conversation.type_ == Conversation.Type.GROUPCHAT) {
+ button.visible = true;
OccupantMenu.View new_menu = new OccupantMenu.View(stream_interactor, conversation);
- set_popover(new_menu);
+ button.set_popover(new_menu);
menu = new_menu;
+ } else {
+ button.visible = false;
}
}
public new void unset_conversation() {
- visible = false;
+ button.visible = false;
+ }
+
+ public Object? get_widget(Plugins.WidgetType type) {
+ if (type != Plugins.WidgetType.GTK4) return null;
+ return button;
}
}
diff --git a/main/src/ui/conversation_titlebar/search_entry.vala b/main/src/ui/conversation_titlebar/search_entry.vala
index 109c54d7..a51d7d43 100644
--- a/main/src/ui/conversation_titlebar/search_entry.vala
+++ b/main/src/ui/conversation_titlebar/search_entry.vala
@@ -7,25 +7,21 @@ namespace Dino.Ui {
public class SearchMenuEntry : Plugins.ConversationTitlebarEntry, Object {
public string id { get { return "search"; } }
+ public double order { get { return 1; } }
- public GlobalSearchButton search_button = new GlobalSearchButton() { tooltip_text=_("Search messages"), visible = true };
+ public ToggleButton button = new ToggleButton() { tooltip_text=_("Search messages") };
public SearchMenuEntry() {
- search_button.set_image(new Gtk.Image.from_icon_name("system-search-symbolic", Gtk.IconSize.MENU) { visible = true });
+ button.set_icon_name("system-search-symbolic");
}
- public double order { get { return 1; } }
- public Plugins.ConversationTitlebarWidget? get_widget(Plugins.WidgetType type) {
- if (type == Plugins.WidgetType.GTK) {
- return search_button;
- }
- return null;
- }
-}
-
-public class GlobalSearchButton : Plugins.ConversationTitlebarWidget, Gtk.ToggleButton {
public new void set_conversation(Conversation conversation) { }
public new void unset_conversation() { }
+
+ public Object? get_widget(Plugins.WidgetType type) {
+ if (type != Plugins.WidgetType.GTK4) return null;
+ return button;
+ }
}
}
diff --git a/main/src/ui/conversation_view.vala b/main/src/ui/conversation_view.vala
index af7e32c1..a51b2381 100644
--- a/main/src/ui/conversation_view.vala
+++ b/main/src/ui/conversation_view.vala
@@ -7,23 +7,37 @@ using Dino.Entities;
namespace Dino.Ui {
[GtkTemplate (ui = "/im/dino/Dino/conversation_view.ui")]
-public class ConversationView : Gtk.Overlay {
+public class ConversationView : Widget {
+// [GtkChild] public unowned ScrolledWindow conversation_scrolled;
+ [GtkChild] public unowned Overlay overlay;
[GtkChild] public unowned Revealer goto_end_revealer;
[GtkChild] public unowned Button goto_end_button;
[GtkChild] public unowned ChatInput.View chat_input;
[GtkChild] public unowned ConversationSummary.ConversationView conversation_frame;
[GtkChild] public unowned Revealer white_revealer;
+ public ListView list_view = new ListView(null, null);
+
+ public bool at_current_content = true;
+
construct {
+ this.layout_manager = new BinLayout();
white_revealer.notify["child-revealed"].connect_after(on_child_revealed_changed);
+
+// conversation_scrolled.set_child(list_view);
+// list_view.set_factory(get_item_factory());
+
+// conversation_scrolled.vadjustment.notify["upper"].connect_after(on_upper_notify);
+// conversation_scrolled.vadjustment.notify["value"].connect(on_value_notify);
+
}
public void add_overlay_dialog(Widget widget) {
Revealer revealer = new Revealer() { transition_type=RevealerTransitionType.CROSSFADE , transition_duration= 100, visible=true };
- revealer.add(widget);
+ revealer.set_child(widget);
- this.add_overlay(revealer);
+ overlay.add_overlay(revealer);
revealer.reveal_child = true;
white_revealer.visible = true;
@@ -44,7 +58,22 @@ public class ConversationView : Gtk.Overlay {
public override void dispose() {
// To prevent a warning when closing Dino
// "Can't set a target list on a widget until you've called gtk_drag_dest_set() to make the widget into a drag destination"
- Gtk.drag_dest_unset(this);
+// Gtk.drag_dest_unset(this);
+ }
+
+ private void on_upper_notify() {
+ print("on_upper_notify\n");
+ if (at_current_content) {
+ print("on_upper_notify2\n");
+ // scroll down
+// conversation_scrolled.vadjustment.value = conversation_scrolled.vadjustment.upper - conversation_scrolled.vadjustment.page_size;
+// conversation_scrolled.scroll_child(ScrollType.END, false);
+ }
+ }
+
+ private void on_value_notify() {
+ print("on_value_notify\n");
+// at_current_content = false;
}
}
diff --git a/main/src/ui/conversation_view_controller.vala b/main/src/ui/conversation_view_controller.vala
index 32b69835..759984c8 100644
--- a/main/src/ui/conversation_view_controller.vala
+++ b/main/src/ui/conversation_view_controller.vala
@@ -11,9 +11,9 @@ enum Target {
STRING
}
-const TargetEntry[] target_list = {
- { "text/uri-list", 0, Target.URI_LIST }
-};
+//const TargetEntry[] target_list = {
+// { "text/uri-list", 0, Target.URI_LIST }
+//};
public class ConversationViewController : Object {
@@ -25,6 +25,7 @@ public class ConversationViewController : Object {
private Widget? overlay_dialog;
private ConversationTitlebar titlebar;
public SearchMenuEntry search_menu_entry = new SearchMenuEntry();
+ public ListView list_view = new ListView(null, null);
private ChatInputController chat_input_controller;
private StreamInteractor stream_interactor;
@@ -37,21 +38,21 @@ public class ConversationViewController : Object {
this.app = GLib.Application.get_default() as Application;
this.chat_input_controller = new ChatInputController(view.chat_input, stream_interactor);
- chat_input_controller.activate_last_message_correction.connect(view.conversation_frame.activate_last_message_correction);
+// chat_input_controller.activate_last_message_correction.connect(view.conversation_frame.activate_last_message_correction);
chat_input_controller.file_picker_selected.connect(open_file_picker);
chat_input_controller.clipboard_pasted.connect(on_clipboard_paste);
view.conversation_frame.init(stream_interactor);
// drag 'n drop file upload
- view.drag_data_received.connect(this.on_drag_data_received);
+// view.drag_data_received.connect(this.on_drag_data_received);
// forward key presses
- view.chat_input.key_press_event.connect(forward_key_press_to_chat_input);
- view.conversation_frame.key_press_event.connect(forward_key_press_to_chat_input);
- titlebar.key_press_event.connect(forward_key_press_to_chat_input);
+// view.chat_input.key_press_event.connect(forward_key_press_to_chat_input);
+// view.conversation_frame.key_press_event.connect(forward_key_press_to_chat_input);
+// titlebar.key_press_event.connect(forward_key_press_to_chat_input);
- // goto-end floating button
+// goto-end floating button
var vadjustment = view.conversation_frame.scrolled.vadjustment;
vadjustment.notify["value"].connect(() => {
bool button_active = vadjustment.value < vadjustment.upper - vadjustment.page_size;
@@ -94,20 +95,24 @@ public class ConversationViewController : Object {
app.plugin_registry.register_contact_titlebar_entry(new OccupantsEntry(stream_interactor));
app.plugin_registry.register_contact_titlebar_entry(new CallTitlebarEntry(stream_interactor));
foreach(var entry in app.plugin_registry.conversation_titlebar_entries) {
- titlebar.insert_entry(entry);
+ Widget? button = entry.get_widget(Plugins.WidgetType.GTK4) as Widget;
+ if (button == null) {
+ continue;
+ }
+ titlebar.insert_button(button);
}
- AccelGroup accel_group = new AccelGroup();
- accel_group.connect(Gdk.Key.U, ModifierType.CONTROL_MASK, AccelFlags.VISIBLE, () => {
- if (conversation == null) return false;
- stream_interactor.get_module(FileManager.IDENTITY).is_upload_available.begin(conversation, (_, res) => {
- if (stream_interactor.get_module(FileManager.IDENTITY).is_upload_available.end(res)) {
- open_file_picker();
- }
- });
- return false;
- });
- ((Gtk.Window)view.get_toplevel()).add_accel_group(accel_group);
+// AccelGroup accel_group = new AccelGroup();
+// accel_group.connect(Gdk.Key.U, ModifierType.CONTROL_MASK, AccelFlags.VISIBLE, () => {
+// if (conversation == null) return false;
+// stream_interactor.get_module(FileManager.IDENTITY).is_upload_available.begin(conversation, (_, res) => {
+// if (stream_interactor.get_module(FileManager.IDENTITY).is_upload_available.end(res)) {
+// open_file_picker();
+// }
+// });
+// return false;
+// });
+// ((Gtk.Window)view.get_toplevel()).add_accel_group(accel_group);
}
public void select_conversation(Conversation? conversation, bool default_initialize_conversation) {
@@ -120,6 +125,13 @@ public class ConversationViewController : Object {
this.conversation = conversation;
+ // Set list model onto list view
+// Dino.Application app = GLib.Application.get_default() as Dino.Application;
+// var map_list_model = get_conversation_content_model(new ContentItemMetaModel(app.db, conversation, stream_interactor), stream_interactor);
+// NoSelection selection_model = new NoSelection(map_list_model);
+// view.list_view.set_model(selection_model);
+// view.at_current_content = true;
+
conversation.notify["encryption"].connect(update_file_upload_status);
chat_input_controller.set_conversation(conversation);
@@ -127,11 +139,8 @@ public class ConversationViewController : Object {
update_conversation_display_name();
update_conversation_topic();
- foreach(var e in this.app.plugin_registry.conversation_titlebar_entries) {
- Plugins.ConversationTitlebarWidget view = e.get_widget(Plugins.WidgetType.GTK);
- if (view != null) {
- view.set_conversation(conversation);
- }
+ foreach(Plugins.ConversationTitlebarEntry e in this.app.plugin_registry.conversation_titlebar_entries) {
+ e.set_conversation(conversation);
}
if (default_initialize_conversation) {
@@ -151,9 +160,9 @@ public class ConversationViewController : Object {
bool upload_available = stream_interactor.get_module(FileManager.IDENTITY).is_upload_available.end(res);
chat_input_controller.set_file_upload_active(upload_available);
if (upload_available && overlay_dialog == null) {
- Gtk.drag_dest_set(view, DestDefaults.ALL, target_list, Gdk.DragAction.COPY);
+// Gtk.drag_dest_set(view, DestDefaults.ALL, target_list, Gdk.DragAction.COPY);
} else {
- Gtk.drag_dest_unset(view);
+// Gtk.drag_dest_unset(view);
}
});
}
@@ -178,48 +187,49 @@ public class ConversationViewController : Object {
}
private void on_clipboard_paste() {
- Clipboard clipboard = Clipboard.get(Gdk.SELECTION_CLIPBOARD);
- if (clipboard.wait_is_image_available()) {
- clipboard.request_image((_, pixbuf) => {
- File file = File.new_for_path(Path.build_filename(FileManager.get_storage_dir(), Xmpp.random_uuid() + ".png"));
- try {
- FileOutputStream fos = file.create(FileCreateFlags.REPLACE_DESTINATION);
- pixbuf.save_to_stream_async.begin(fos, "png", null, () => {
- open_send_file_overlay(file);
- });
- } catch (Error e) {
- warning("Could not create file to store pasted image in %s, %s", file.get_path(), e.message);
- }
- });
- }
+ Clipboard clipboard = view.get_clipboard();
+// if (clipboard.wait_is_image_available()) {
+// clipboard.request_image((_, pixbuf) => {
+// File file = File.new_for_path(Path.build_filename(FileManager.get_storage_dir(), Xmpp.random_uuid() + ".png"));
+// try {
+// FileOutputStream fos = file.create(FileCreateFlags.REPLACE_DESTINATION);
+// pixbuf.save_to_stream_async.begin(fos, "png", null, () => {
+// open_send_file_overlay(file);
+// });
+// } catch (Error e) {
+// warning("Could not create file to store pasted image in %s, %s", file.get_path(), e.message);
+// }
+// });
+// }
}
- private void on_drag_data_received(Widget widget, Gdk.DragContext context, int x, int y, SelectionData selection_data, uint target_type, uint time) {
- if ((selection_data != null) && (selection_data.get_length() >= 0)) {
- switch (target_type) {
- case Target.URI_LIST:
- string[] uris = selection_data.get_uris();
- // For now we only process the first dragged file
- if (uris.length >= 1) {
- try {
- string file_path = Filename.from_uri(uris[0]);
- open_send_file_overlay(File.new_for_path(file_path));
- } catch (ConvertError e) {
- warning("Could not handle dragged file %s, %s", uris[0], e.message);
- }
- }
- break;
- default:
- break;
- }
- }
- }
+// private void on_drag_data_received(Widget widget, Gdk.DragContext context, int x, int y, SelectionData selection_data, uint target_type, uint time) {
+// if ((selection_data != null) && (selection_data.get_length() >= 0)) {
+// switch (target_type) {
+// case Target.URI_LIST:
+// string[] uris = selection_data.get_uris();
+// // For now we only process the first dragged file
+// if (uris.length >= 1) {
+// try {
+// string file_path = Filename.from_uri(uris[0]);
+// open_send_file_overlay(File.new_for_path(file_path));
+// } catch (ConvertError e) {
+// warning("Could not handle dragged file %s, %s", uris[0], e.message);
+// }
+// }
+// break;
+// default:
+// break;
+// }
+// }
+// }
private void open_file_picker() {
- PreviewFileChooserNative chooser = new PreviewFileChooserNative(_("Select file"), view.get_toplevel() as Gtk.Window, FileChooserAction.OPEN, _("Select"), _("Cancel"));
- if (chooser.run() == Gtk.ResponseType.ACCEPT) {
- open_send_file_overlay(File.new_for_path(chooser.get_filename()));
- }
+ FileChooserNative chooser = new FileChooserNative(_("Select file"), view.get_root() as Gtk.Window, FileChooserAction.OPEN, _("Select"), _("Cancel"));
+ chooser.response.connect(() => {
+ open_send_file_overlay(File.new_for_path(chooser.get_file().get_path()));
+ });
+ chooser.show();
}
private void open_send_file_overlay(File file) {
@@ -252,8 +262,8 @@ public class ConversationViewController : Object {
update_file_upload_status();
});
- view.add_overlay_dialog(overlay);
- overlay_dialog = overlay;
+ view.add_overlay_dialog(overlay.get_widget());
+ overlay_dialog = overlay.get_widget();
update_file_upload_status();
}
@@ -262,24 +272,24 @@ public class ConversationViewController : Object {
stream_interactor.get_module(FileManager.IDENTITY).send_file.begin(file, conversation);
}
- private bool forward_key_press_to_chat_input(EventKey event) {
- if (((Gtk.Window)view.get_toplevel()).get_focus() is TextView) {
- return false;
- }
-
- // Don't forward / change focus on Control / Alt
- if (event.keyval == Gdk.Key.Control_L || event.keyval == Gdk.Key.Control_R ||
- event.keyval == Gdk.Key.Alt_L || event.keyval == Gdk.Key.Alt_R) {
- return false;
- }
- // Don't forward / change focus on Control + ...
- if ((event.state & ModifierType.CONTROL_MASK) > 0) {
- return false;
- }
- if (view.chat_input.chat_text_view.text_view.key_press_event(event)) {
- return true;
- }
- return false;
- }
+// private bool forward_key_press_to_chat_input(EventKey event) {
+// if (((Gtk.Window)view.get_toplevel()).get_focus() is TextView) {
+// return false;
+// }
+//
+// // Don't forward / change focus on Control / Alt
+// if (event.keyval == Gdk.Key.Control_L || event.keyval == Gdk.Key.Control_R ||
+// event.keyval == Gdk.Key.Alt_L || event.keyval == Gdk.Key.Alt_R) {
+// return false;
+// }
+// // Don't forward / change focus on Control + ...
+// if ((event.state & ModifierType.CONTROL_MASK) > 0) {
+// return false;
+// }
+// if (view.chat_input.chat_text_view.text_view.key_press_event(event)) {
+// return true;
+// }
+// return false;
+// }
}
}
diff --git a/main/src/ui/file_send_overlay.vala b/main/src/ui/file_send_overlay.vala
index 369d291a..ceb78818 100644
--- a/main/src/ui/file_send_overlay.vala
+++ b/main/src/ui/file_send_overlay.vala
@@ -6,33 +6,41 @@ using Dino.Entities;
namespace Dino.Ui {
-[GtkTemplate (ui = "/im/dino/Dino/file_send_overlay.ui")]
-public class FileSendOverlay : Gtk.EventBox {
+public class FileSendOverlay {
public signal void close();
public signal void send_file();
- [GtkChild] public unowned Button close_button;
- [GtkChild] public unowned Button send_button;
- [GtkChild] public unowned SizingBin file_widget_insert;
- [GtkChild] public unowned Label info_label;
+ public Box main_box;
+ public Button close_button;
+ public Button send_button;
+ public SizingBin file_widget_insert;
+ public Label info_label;
private bool can_send = true;
public FileSendOverlay(File file, FileInfo file_info) {
+ Builder builder = new Builder.from_resource("/im/dino/Dino/file_send_overlay.ui");
+ main_box = (Box) builder.get_object("main_box");
+ close_button = (Button) builder.get_object("close_button");
+ send_button = (Button) builder.get_object("send_button");
+ file_widget_insert = (SizingBin) builder.get_object("file_widget_insert");
+ info_label = (Label) builder.get_object("info_label");
+
close_button.clicked.connect(() => {
- this.close();
- this.destroy();
+ main_box.unparent();
+ main_box.destroy();
});
send_button.clicked.connect(() => {
send_file();
this.close();
- this.destroy();
+ main_box.unparent();
+ main_box.destroy();
});
load_file_widget.begin(file, file_info);
- this.realize.connect(() => {
+ main_box.realize.connect(() => {
if (can_send) {
send_button.grab_focus();
} else {
@@ -40,12 +48,12 @@ public class FileSendOverlay : Gtk.EventBox {
}
});
- this.key_release_event.connect((event) => {
- if (event.keyval == Gdk.Key.Escape) {
- this.destroy();
- }
- return false;
- });
+// this.key_release_event.connect((event) => {
+// if (event.keyval == Gdk.Key.Escape) {
+// this.destroy();
+// }
+// return false;
+// });
}
private async void load_file_widget(File file, FileInfo file_info) {
@@ -78,7 +86,7 @@ public class FileSendOverlay : Gtk.EventBox {
widget = default_widget;
}
- file_widget_insert.add(widget);
+ widget.set_parent(file_widget_insert);
}
public void set_file_too_large() {
@@ -87,6 +95,10 @@ public class FileSendOverlay : Gtk.EventBox {
send_button.sensitive = false;
can_send = false;
}
+
+ public Widget get_widget() {
+ return main_box;
+ }
}
}
diff --git a/main/src/ui/global_search.vala b/main/src/ui/global_search.vala
index 30f8f65a..bee409fe 100644
--- a/main/src/ui/global_search.vala
+++ b/main/src/ui/global_search.vala
@@ -6,25 +6,38 @@ using Dino.Entities;
namespace Dino.Ui {
-[GtkTemplate (ui = "/im/dino/Dino/global_search.ui")]
-public class GlobalSearch : Overlay {
+public class GlobalSearch {
public signal void selected_item(MessageItem item);
private StreamInteractor stream_interactor;
private string search = "";
private int loaded_results = -1;
private Mutex reloading_mutex = Mutex();
- [GtkChild] public unowned SearchEntry search_entry;
- [GtkChild] public unowned Label entry_number_label;
- [GtkChild] public unowned ScrolledWindow results_scrolled;
- [GtkChild] public unowned Box results_box;
- [GtkChild] public unowned Stack results_empty_stack;
- [GtkChild] public unowned Frame auto_complete_overlay;
- [GtkChild] public unowned ListBox auto_complete_list;
+ public Overlay overlay;
+ public SearchEntry search_entry;
+ public Label entry_number_label;
+ public ScrolledWindow results_scrolled;
+ public Box results_box;
+ public Stack results_empty_stack;
+ public Frame auto_complete_overlay;
+ public ListBox auto_complete_list;
- public GlobalSearch init(StreamInteractor stream_interactor) {
+ private ArrayList<Widget> auto_complete_children = new ArrayList<Widget>();
+ private ArrayList<Widget> results_box_children = new ArrayList<Widget>();
+
+ public GlobalSearch(StreamInteractor stream_interactor) {
this.stream_interactor = stream_interactor;
+ Builder builder = new Builder.from_resource("/im/dino/Dino/global_search.ui");
+ overlay = (Overlay) builder.get_object("overlay");
+ search_entry = (SearchEntry) builder.get_object("search_entry");
+ entry_number_label = (Label) builder.get_object("entry_number_label");
+ results_scrolled = (ScrolledWindow) builder.get_object("results_scrolled");
+ results_box = (Box) builder.get_object("results_box");
+ results_empty_stack = (Stack) builder.get_object("results_empty_stack");
+ auto_complete_overlay = (Frame) builder.get_object("auto_complete_overlay");
+ auto_complete_list = (ListBox) builder.get_object("auto_complete_list");
+
search_entry.search_changed.connect(() => {
set_search(search_entry.text);
});
@@ -34,9 +47,10 @@ public class GlobalSearch : Overlay {
results_scrolled.vadjustment.notify["value"].connect(on_scrolled_window_vadjustment_value);
results_scrolled.vadjustment.notify["upper"].connect_after(on_scrolled_window_vadjustment_upper);
- event.connect(on_event);
-
- return this;
+ var overlay_key_events = new EventControllerKey();
+ overlay_key_events.key_pressed.connect(on_key_pressed);
+ overlay_key_events.key_released.connect(on_key_released);
+ overlay.add_controller(overlay_key_events);
}
private void on_scrolled_window_vadjustment_value() {
@@ -57,38 +71,49 @@ public class GlobalSearch : Overlay {
reloading_mutex.unlock();
}
- private bool on_event(Gdk.Event event) {
- if (auto_complete_overlay.visible) {
- if (event.type == Gdk.EventType.KEY_PRESS && event.key.keyval == Gdk.Key.Up) {
- var row = auto_complete_list.get_selected_row();
- var index = row == null ? -1 : row.get_index() - 1;
- if (index == -1) index = (int)auto_complete_list.get_children().length() - 1;
- auto_complete_list.select_row(auto_complete_list.get_row_at_index(index));
- return true;
- }
- if (event.type == Gdk.EventType.KEY_PRESS && event.key.keyval == Gdk.Key.Down) {
- var row = auto_complete_list.get_selected_row();
- var index = row == null ? 0 : row.get_index() + 1;
- if (index == auto_complete_list.get_children().length()) index = 0;
- auto_complete_list.select_row(auto_complete_list.get_row_at_index(index));
- return true;
- }
- if (event.type == Gdk.EventType.KEY_PRESS && event.key.keyval == Gdk.Key.Tab ||
- event.type == Gdk.EventType.KEY_RELEASE && event.key.keyval == Gdk.Key.Return) {
- auto_complete_list.get_selected_row().activate();
- return true;
- }
+ private bool on_key_pressed(uint keyval, uint keycode, Gdk.ModifierType state) {
+ if (!auto_complete_overlay.visible) return false;
+
+ if (keyval == Gdk.Key.Up) {
+ var row = auto_complete_list.get_selected_row();
+ var index = row == null ? -1 : row.get_index() - 1;
+ if (index == -1) index = (int)auto_complete_children.size - 1;
+ auto_complete_list.select_row(auto_complete_list.get_row_at_index(index));
+ return true;
+ }
+ if (keyval == Gdk.Key.Down) {
+ var row = auto_complete_list.get_selected_row();
+ var index = row == null ? 0 : row.get_index() + 1;
+ if (index == auto_complete_children.size) index = 0;
+ auto_complete_list.select_row(auto_complete_list.get_row_at_index(index));
+ return true;
+ }
+ if (keyval == Gdk.Key.Tab) {
+ auto_complete_list.get_selected_row().activate();
+ return true;
}
// TODO: Handle cursor movement in results
// TODO: Direct all keystrokes to text input
return false;
}
+ private void on_key_released(uint keyval, uint keycode, Gdk.ModifierType state) {
+ if (keyval == Gdk.Key.Return) {
+ auto_complete_list.get_selected_row().activate();
+ }
+ }
+
private void update_auto_complete() {
Gee.List<SearchSuggestion> suggestions = stream_interactor.get_module(SearchProcessor.IDENTITY).suggest_auto_complete(search_entry.text, search_entry.cursor_position);
auto_complete_overlay.visible = suggestions.size > 0;
if (suggestions.size > 0) {
- auto_complete_list.@foreach((widget) => auto_complete_list.remove(widget));
+ // Remove current suggestions
+ foreach (Widget widget in auto_complete_children) {
+ auto_complete_list.remove(widget);
+ }
+ auto_complete_children.clear();
+
+ // Populate new suggestions
foreach(SearchSuggestion suggestion in suggestions) {
Builder builder = new Builder.from_resource("/im/dino/Dino/search_autocomplete.ui");
AvatarImage avatar = (AvatarImage)builder.get_object("image");
@@ -107,24 +132,29 @@ public class GlobalSearch : Overlay {
label.label = display_name;
}
ListBoxRow row = new ListBoxRow() { visible = true, can_focus = false };
- row.add((Widget)builder.get_object("root"));
+ row.set_child((Widget)builder.get_object("root"));
row.activate.connect(() => {
handle_suggestion(suggestion);
});
- auto_complete_list.add(row);
+ auto_complete_list.append(row);
+ auto_complete_children.add(row);
}
auto_complete_list.select_row(auto_complete_list.get_row_at_index(0));
}
}
private void handle_suggestion(SearchSuggestion suggestion) {
- search_entry.move_cursor(MovementStep.LOGICAL_POSITIONS, suggestion.start_index - search_entry.cursor_position, false);
- search_entry.delete_from_cursor(DeleteType.CHARS, suggestion.end_index - suggestion.start_index);
- search_entry.insert_at_cursor(suggestion.completion + " ");
+ search_entry.delete_text(suggestion.start_index, suggestion.end_index);
+ int position = search_entry.cursor_position;
+ search_entry.insert_text(suggestion.completion + " ", suggestion.completion.length + 1, ref position);
+ search_entry.set_position(-1);
}
private void clear_search() {
- results_box.@foreach((widget) => { results_box.remove(widget); });
+ foreach (Widget widget in results_box_children) {
+ results_box.remove(widget);
+ }
+ results_box_children.clear();
loaded_results = 0;
}
@@ -157,30 +187,32 @@ public class GlobalSearch : Overlay {
Box context_box = new Box(Orientation.VERTICAL, 5) { visible=true };
if (before_message != null && before_message.size > 0) {
- context_box.add(get_context_message_widget(before_message.first()));
+ context_box.append(get_context_message_widget(before_message.first()));
}
Widget match_widget = get_match_message_widget(item);
- context_box.add(match_widget);
+ context_box.append(match_widget);
if (after_message != null && after_message.size > 0) {
- context_box.add(get_context_message_widget(after_message.first()));
+ context_box.append(get_context_message_widget(after_message.first()));
}
- Label date_label = new Label(ConversationSummary.ItemMetaDataHeader.get_relative_time(item.time.to_local())) { xalign=0, visible=true };
+ Label date_label = new Label(ConversationSummary.ConversationItemSkeleton.get_relative_time(item.time.to_local())) { xalign=0, visible=true };
date_label.get_style_context().add_class("dim-label");
string display_name = Util.get_conversation_display_name(stream_interactor, item.conversation);
string title = item.message.type_ == Message.Type.GROUPCHAT ? _("In %s").printf(display_name) : _("With %s").printf(display_name);
Box header_box = new Box(Orientation.HORIZONTAL, 10) { margin_start=7, visible=true };
- header_box.add(new Label(@"<b>$(Markup.escape_text(title))</b>") { ellipsize=EllipsizeMode.END, xalign=0, use_markup=true, visible=true });
- header_box.add(date_label);
+ header_box.append(new Label(@"<b>$(Markup.escape_text(title))</b>") { ellipsize=EllipsizeMode.END, xalign=0, use_markup=true, visible=true });
+ header_box.append(date_label);
Box result_box = new Box(Orientation.VERTICAL, 7) { visible=true };
- result_box.add(header_box);
- result_box.add(context_box);
+ result_box.append(header_box);
+ result_box.append(context_box);
+
+ results_box.append(result_box);
+ results_box_children.add(result_box);
- results_box.add(result_box);
}
}
@@ -237,11 +269,11 @@ public class GlobalSearch : Overlay {
label.label = markup_text;
grid.attach(label, 1, 1, 1, 1);
- Button button = new Button() { relief=ReliefStyle.NONE, visible=true };
+ Button button = new Button() { has_frame=false, visible=true };
button.clicked.connect(() => {
selected_item(item);
});
- button.add(grid);
+ button.child = grid;
return button;
}
@@ -278,6 +310,10 @@ public class GlobalSearch : Overlay {
}
return ret;
}
+
+ public Widget get_widget() {
+ return overlay;
+ }
}
}
diff --git a/main/src/ui/main_window.vala b/main/src/ui/main_window.vala
index 04c01b26..a7e78b5d 100644
--- a/main/src/ui/main_window.vala
+++ b/main/src/ui/main_window.vala
@@ -13,21 +13,19 @@ public class MainWindow : Gtk.Window {
public new string? title { get; set; }
public string? subtitle { get; set; }
- public WelcomePlaceholder welcome_placeholder = new WelcomePlaceholder() { visible=true };
- public NoAccountsPlaceholder accounts_placeholder = new NoAccountsPlaceholder() { visible=true };
+ public WelcomePlaceholder welcome_placeholder = new WelcomePlaceholder();
+ public NoAccountsPlaceholder accounts_placeholder = new NoAccountsPlaceholder();
public ConversationView conversation_view;
public ConversationSelector conversation_selector;
public ConversationTitlebar conversation_titlebar;
- public ConversationTitlebarCsd conversation_titlebar_csd;
- public ConversationListTitlebarCsd conversation_list_titlebar_csd;
- public HeaderBar placeholder_headerbar = new HeaderBar() { title="Dino", show_close_button=true, visible=true };
- public Box box = new Box(Orientation.VERTICAL, 0) { orientation=Orientation.VERTICAL, visible=true };
- public Paned headerbar_paned = new Paned(Orientation.HORIZONTAL) { visible=true };
+ public Widget conversation_list_titlebar;
+ public HeaderBar placeholder_headerbar = new HeaderBar() { show_title_buttons=true };
+ public Box box = new Box(Orientation.VERTICAL, 0) { orientation=Orientation.VERTICAL };
+ public Paned headerbar_paned = new Paned(Orientation.HORIZONTAL) { resize_start_child=false, shrink_start_child=false, shrink_end_child=false };
public Paned paned;
public Revealer search_revealer;
- public SearchEntry search_entry;
- public GlobalSearch search_box;
- private Stack stack = new Stack() { visible=true };
+ public GlobalSearch global_search;
+ private Stack stack = new Stack();
private Stack left_stack;
private Stack right_stack;
@@ -35,18 +33,29 @@ public class MainWindow : Gtk.Window {
private Database db;
private Config config;
+ class construct {
+ var shortcut = new Shortcut(new KeyvalTrigger(Key.F, ModifierType.CONTROL_MASK), new CallbackAction((widget, args) => {
+ ((MainWindow) widget).search_revealer.reveal_child = true;
+ return false;
+ }));
+ add_shortcut(shortcut);
+ }
+
public MainWindow(Application application, StreamInteractor stream_interactor, Database db, Config config) {
Object(application : application);
this.stream_interactor = stream_interactor;
this.db = db;
this.config = config;
- restore_window_size();
+ this.title = "Dino";
this.get_style_context().add_class("dino-main");
- setup_headerbar();
+
Gtk.Settings.get_default().notify["gtk-decoration-layout"].connect(set_window_buttons);
- this.realize.connect(set_window_buttons);
+ ((Widget)this).realize.connect(set_window_buttons);
+ ((Widget)this).realize.connect(restore_window_size);
+
+ setup_headerbar();
setup_unified();
setup_stack();
@@ -56,35 +65,32 @@ public class MainWindow : Gtk.Window {
private void setup_unified() {
Builder builder = new Builder.from_resource("/im/dino/Dino/unified_main_content.ui");
paned = (Paned) builder.get_object("paned");
- box.add(paned);
+ box.append(paned);
left_stack = (Stack) builder.get_object("left_stack");
right_stack = (Stack) builder.get_object("right_stack");
conversation_view = (ConversationView) builder.get_object("conversation_view");
- conversation_selector = ((ConversationSelector) builder.get_object("conversation_list")).init(stream_interactor);
- search_box = ((GlobalSearch) builder.get_object("search_box")).init(stream_interactor);
search_revealer = (Revealer) builder.get_object("search_revealer");
- search_entry = (SearchEntry) builder.get_object("search_entry");
+ conversation_selector = ((ConversationSelector) builder.get_object("conversation_list")).init(stream_interactor);
+
+ Frame search_frame = (Frame) builder.get_object("search_frame");
+ global_search = new GlobalSearch(stream_interactor);
+ search_frame.set_child(global_search.get_widget());
+
Image conversation_list_placeholder_image = (Image) builder.get_object("conversation_list_placeholder_image");
conversation_list_placeholder_image.set_from_pixbuf(new Pixbuf.from_resource("/im/dino/Dino/icons/dino-conversation-list-placeholder-arrow.svg"));
}
private void setup_headerbar() {
if (Util.use_csd()) {
- conversation_list_titlebar_csd = new ConversationListTitlebarCsd() { visible=true };
- headerbar_paned.pack1(conversation_list_titlebar_csd, false, false);
-
- conversation_titlebar_csd = new ConversationTitlebarCsd() { visible=true };
- conversation_titlebar = conversation_titlebar_csd;
- headerbar_paned.pack2(conversation_titlebar_csd, true, false);
+ conversation_list_titlebar = get_conversation_list_titlebar_csd();
+ conversation_titlebar = new ConversationTitlebarCsd();
} else {
- ConversationListTitlebar conversation_list_titlebar = new ConversationListTitlebar() { visible=true };
- headerbar_paned.pack1(conversation_list_titlebar, false, false);
-
- conversation_titlebar = new ConversationTitlebarNoCsd() { visible=true };
- headerbar_paned.pack2(conversation_titlebar, true, false);
-
- box.add(headerbar_paned);
+ conversation_list_titlebar = new ConversationListTitlebar();
+ conversation_titlebar = new ConversationTitlebarNoCsd();
+ box.append(headerbar_paned);
}
+ headerbar_paned.set_start_child(conversation_list_titlebar);
+ headerbar_paned.set_end_child(conversation_titlebar.get_widget());
}
private void set_window_buttons() {
@@ -93,15 +99,17 @@ public class MainWindow : Gtk.Window {
if (gtk_settings == null) return;
string[] buttons = gtk_settings.gtk_decoration_layout.split(":");
- this.conversation_list_titlebar_csd.decoration_layout = buttons[0] + ":";
- this.conversation_titlebar_csd.decoration_layout = ((buttons.length == 2) ? ":" + buttons[1] : "");
+ HeaderBar conversation_headerbar = this.conversation_titlebar.get_widget() as HeaderBar;
+ conversation_headerbar.decoration_layout = ((buttons.length == 2) ? ":" + buttons[1] : "");
+ HeaderBar conversation_list_headerbar = this.conversation_list_titlebar as HeaderBar;
+ conversation_list_headerbar.decoration_layout = buttons[0] + ":";
}
private void setup_stack() {
stack.add_named(box, "main");
stack.add_named(welcome_placeholder, "welcome_placeholder");
stack.add_named(accounts_placeholder, "accounts_placeholder");
- add(stack);
+ set_child(stack);
}
public enum StackState {
@@ -146,10 +154,8 @@ public class MainWindow : Gtk.Window {
public void restore_window_size() {
Gdk.Display? display = Gdk.Display.get_default();
if (display != null) {
- Gdk.Monitor? monitor = display.get_primary_monitor();
- if (monitor == null) {
- monitor = display.get_monitor_at_point(1, 1);
- }
+ Gdk.Surface? surface = get_surface();
+ Gdk.Monitor? monitor = display.get_monitor_at_surface(surface);
if (monitor != null &&
config.window_width <= monitor.geometry.width &&
@@ -157,37 +163,30 @@ public class MainWindow : Gtk.Window {
set_default_size(config.window_width, config.window_height);
}
}
- this.window_position = Gtk.WindowPosition.CENTER;
if (config.window_maximize) {
maximize();
}
- this.delete_event.connect(() => {
+ ((Widget)this).unrealize.connect(() => {
save_window_size();
- config.window_maximize = this.is_maximized;
- return false;
+ config.window_maximize = this.maximized;
});
}
public void save_window_size() {
- if (this.is_maximized) return;
+ if (this.maximized) return;
Gdk.Display? display = get_display();
- Gdk.Window? window = get_window();
- if (display != null && window != null) {
- Gdk.Monitor monitor = display.get_monitor_at_window(window);
-
- int width = 0;
- int height = 0;
- get_size(out width, out height);
-
+ Gdk.Surface? surface = get_surface();
+ if (display != null && surface != null) {
+ Gdk.Monitor monitor = display.get_monitor_at_surface(surface);
// Only store if the values have changed and are reasonable-looking.
- if (config.window_width != width && width > 0 && width <= monitor.geometry.width) {
- config.window_width = width;
+ if (config.window_width != default_width && default_width > 0 && default_width <= monitor.geometry.width) {
+ config.window_width = default_width;
}
- if (config.window_height != height && height > 0 && height <= monitor.geometry.height) {
- config.window_height = height;
+ if (config.window_height != default_height && default_height > 0 && default_height <= monitor.geometry.height) {
+ config.window_height = default_height;
}
}
}
diff --git a/main/src/ui/main_window_controller.vala b/main/src/ui/main_window_controller.vala
index dceb4094..7049aa40 100644
--- a/main/src/ui/main_window_controller.vala
+++ b/main/src/ui/main_window_controller.vala
@@ -30,18 +30,18 @@ public class MainWindowController : Object {
this.conversation_view_controller = new ConversationViewController(window.conversation_view, window.conversation_titlebar, stream_interactor);
- conversation_view_controller.search_menu_entry.search_button.bind_property("active", window.search_revealer, "reveal_child");
+ conversation_view_controller.search_menu_entry.button.bind_property("active", window.search_revealer, "reveal_child", BindingFlags.SYNC_CREATE | BindingFlags.BIDIRECTIONAL);
window.search_revealer.notify["child-revealed"].connect(() => {
if (window.search_revealer.child_revealed) {
- if (window.conversation_view.conversation_frame.conversation != null && window.search_box.search_entry.text == "") {
+ if (window.conversation_view.conversation_frame.conversation != null && window.global_search.search_entry.text == "") {
reset_search_entry();
}
- window.search_box.search_entry.grab_focus_without_selecting();
- window.search_box.search_entry.set_position((int)window.search_box.search_entry.text_length);
+ window.global_search.search_entry.grab_focus();
+ window.global_search.search_entry.set_position((int)window.global_search.search_entry.text.length);
}
});
- window.search_box.selected_item.connect((item) => {
+ window.global_search.selected_item.connect((item) => {
select_conversation(item.conversation, false, false);
window.conversation_view.conversation_frame.initialize_around_message(item.conversation, item);
close_search();
@@ -55,30 +55,53 @@ public class MainWindowController : Object {
window.accounts_placeholder.primary_button.clicked.connect(() => { app.activate_action("accounts", null); });
window.conversation_selector.conversation_selected.connect((conversation) => select_conversation(conversation));
- window.event.connect((event) => {
- if (event.type == EventType.BUTTON_PRESS) {
- int dest_x, dest_y;
- bool ret = window.search_box.translate_coordinates(window, 0, 0, out dest_x, out dest_y);
- int geometry_x, geometry_y, geometry_width, geometry_height;
- window.get_window().get_geometry(out geometry_x, out geometry_y, out geometry_width, out geometry_height);
- if (ret && event.button.x_root - geometry_x < dest_x || event.button.y_root - geometry_y < dest_y) {
- close_search();
- }
- } else if (event.type == EventType.KEY_RELEASE) {
- if (event.key.keyval == Gdk.Key.Escape) {
- close_search();
- }
+// ConversationListModel list_model = new ConversationListModel(stream_interactor);
+// list_model.closed_conversation.connect((conversation) => {
+// print(@"closed $(conversation.counterpart.bare_jid)\n");
+// stream_interactor.get_module(ConversationManager.IDENTITY).close_conversation(conversation);
+// });
+// SingleSelection selection_model = new SingleSelection(list_model) { autoselect=false };
+// selection_model.notify["selected-item"].connect(() => {
+// ConversationViewModel view_model = (ConversationViewModel) selection_model.selected_item;
+// if (view_model.conversation.equals(conversation)) return;
+// print(@"selected conversation $(view_model.conversation.counterpart)\n");
+// select_conversation(view_model.conversation);
+// });
+// window.conversation_list_view.set_model(selection_model);
+// print(list_model.get_n_items().to_string() + " " + selection_model.get_n_items().to_string() + "<<");
+// print(selection_model.get_selected().to_string() + "<<");
+// window.conversation_list_view.realize.connect(() => {
+// selection_model.set_selected(0);
+// });
+
+ Widget window_widget = ((Widget) window);
+
+ GestureClick gesture_click_controller = new GestureClick();
+ window_widget.add_controller(gesture_click_controller);
+ gesture_click_controller.pressed.connect((n_press, click_x, click_y) => {
+ double search_x, search_y;
+ bool ret = window.search_revealer.translate_coordinates(window, 0, 0, out search_x, out search_y);
+ if (ret && click_x < search_x) {
+ close_search();
+ }
+ });
+
+ EventControllerKey key_event_controller = new EventControllerKey();
+ window_widget.add_controller(key_event_controller);
+ // TODO GTK4: Why doesn't this work with key_pressed signal
+ key_event_controller.key_released.connect((keyval) => {
+ if (keyval == Gdk.Key.Escape) {
+ close_search();
}
- return false;
});
- window.focus_in_event.connect(() => {
+
+ EventControllerFocus focus_event_controller = new EventControllerFocus();
+ window_widget.add_controller(focus_event_controller);
+ focus_event_controller.enter.connect(() => {
stream_interactor.get_module(ChatInteraction.IDENTITY).on_window_focus_in(conversation);
- window.urgency_hint = false;
- return false;
});
- window.focus_out_event.connect(() => {
+ focus_event_controller.leave.connect(() => {
stream_interactor.get_module(ChatInteraction.IDENTITY).on_window_focus_out(conversation);
- return false;
});
window.conversation_selected.connect(conversation => select_conversation(conversation));
@@ -89,12 +112,12 @@ public class MainWindowController : Object {
stream_interactor.get_module(ConversationManager.IDENTITY).conversation_deactivated.connect(() => update_stack_state());
update_stack_state();
- AccelGroup accel_group = new AccelGroup();
- accel_group.connect(Gdk.Key.F, ModifierType.CONTROL_MASK, AccelFlags.VISIBLE, () => {
- window.search_revealer.reveal_child = true;
- return false;
- });
- window.add_accel_group(accel_group);
+// AccelGroup accel_group = new AccelGroup();
+// accel_group.connect(Gdk.Key.F, ModifierType.CONTROL_MASK, AccelFlags.VISIBLE, () => {
+// window.search_revealer.reveal_child = true;
+// return false;
+// });
+// window.add_accel_group(accel_group);
}
public void select_conversation(Conversation? conversation, bool do_reset_search = true, bool default_initialize_conversation = true) {
@@ -122,11 +145,8 @@ public class MainWindowController : Object {
conversation_view_controller.unset_conversation();
- foreach(var e in this.app.plugin_registry.conversation_titlebar_entries) {
- Plugins.ConversationTitlebarWidget widget = e.get_widget(Plugins.WidgetType.GTK);
- if (widget != null) {
- widget.unset_conversation();
- }
+ foreach(Plugins.ConversationTitlebarEntry e in this.app.plugin_registry.conversation_titlebar_entries) {
+ e.unset_conversation();
}
}
@@ -150,18 +170,17 @@ public class MainWindowController : Object {
switch (conversation.type_) {
case Conversation.Type.CHAT:
case Conversation.Type.GROUPCHAT_PM:
- window.search_box.search_entry.text = @"with:$(conversation.counterpart) ";
+ window.global_search.search_entry.text = @"with:$(conversation.counterpart) ";
break;
case Conversation.Type.GROUPCHAT:
- window.search_box.search_entry.text = @"in:$(conversation.counterpart) ";
+ window.global_search.search_entry.text = @"in:$(conversation.counterpart) ";
break;
}
}
}
private void close_search() {
- conversation_view_controller.search_menu_entry.search_button.active = false;
- window.search_revealer.reveal_child = false;
+ conversation_view_controller.search_menu_entry.button.active = false;
}
}
diff --git a/main/src/ui/manage_accounts/add_account_dialog.vala b/main/src/ui/manage_accounts/add_account_dialog.vala
index 9cf60e3f..d3dbf390 100644
--- a/main/src/ui/manage_accounts/add_account_dialog.vala
+++ b/main/src/ui/manage_accounts/add_account_dialog.vala
@@ -110,9 +110,9 @@ public class AddAccountDialog : Gtk.Dialog {
foreach (string server in server_list) {
ListBoxRow list_box_row = new ListBoxRow() { visible=true };
- list_box_row.add(new Label(server) { xalign=0, margin=3, margin_start=7, margin_end=7, visible=true });
+ list_box_row.set_child(new Label(server) { xalign=0, margin_start=7, margin_end=7, visible=true });
list_box_jids[list_box_row] = server;
- server_list_box.add(list_box_row);
+ server_list_box.append(list_box_row);
}
// Register Form
@@ -134,7 +134,7 @@ public class AddAccountDialog : Gtk.Dialog {
create_account_box.visible = false;
register_box.visible = false;
success_box.visible = false;
- set_default(sign_in_jid_continue_button);
+// set_default(sign_in_jid_continue_button);
sign_in_jid_error_label.label = "";
jid_entry.sensitive = true;
@@ -174,7 +174,7 @@ public class AddAccountDialog : Gtk.Dialog {
create_account_box.visible = false;
register_box.visible = false;
success_box.visible = false;
- set_default(sign_in_password_continue_button);
+// set_default(sign_in_password_continue_button);
sign_in_password_error_label.label = "";
sign_in_password_title.label = _("Sign in to %s").printf(login_jid.to_string());
@@ -184,7 +184,7 @@ public class AddAccountDialog : Gtk.Dialog {
private void show_select_server() {
server_entry.text = "";
server_entry.grab_focus();
- set_default(select_server_continue);
+// set_default(select_server_continue);
server_list_box.row_activated.disconnect(on_server_list_row_activated);
server_list_box.unselect_all();
@@ -209,7 +209,7 @@ public class AddAccountDialog : Gtk.Dialog {
create_account_box.visible = false;
success_box.visible = false;
- set_default(register_form_continue);
+// set_default(register_form_continue);
animate_window_resize(register_box);
}
@@ -223,7 +223,7 @@ public class AddAccountDialog : Gtk.Dialog {
register_box.visible = false;
success_description.label = _("You can now use the account %s.").printf("<b>" + Markup.escape_text(account.bare_jid.to_string()) + "</b>");
- set_default(success_continue_button);
+// set_default(success_continue_button);
}
private void on_jid_entry_changed() {
@@ -329,36 +329,41 @@ public class AddAccountDialog : Gtk.Dialog {
}
private void set_register_form(Jid server, Xep.InBandRegistration.Form form) {
- form_box.foreach((widget) => { form_box.remove(widget); });
+ Widget widget = form_box.get_first_child();
+ while (widget != null) {
+ form_box.remove(widget);
+ widget = form_box.get_first_child();
+ }
+// form_box.foreach((widget) => { form_box.remove(widget); });
register_title.label = _("Register on %s").printf(server.to_string());
if (form.oob != null) {
- form_box.add(new Label(_("The server requires to sign up through a website")){ visible=true } );
- form_box.add(new Label(@"<a href=\"$(form.oob)\">$(form.oob)</a>") { use_markup=true, visible=true });
+ form_box.append(new Label(_("The server requires to sign up through a website")){ visible=true } );
+ form_box.append(new Label(@"<a href=\"$(form.oob)\">$(form.oob)</a>") { use_markup=true, visible=true });
register_form_continue_label.label = _("Open website");
register_form_continue.visible = true;
register_form_continue.grab_focus();
} else if (form.fields.size > 0) {
if (form.instructions != null && form.instructions != "") {
string markup_instructions = Util.parse_add_markup(form.instructions, null, true, false);
- form_box.add(new Label(markup_instructions) { use_markup=true, halign=Align.CENTER, xalign=0, margin_top=7,
+ form_box.append(new Label(markup_instructions) { use_markup=true, halign=Align.CENTER, xalign=0, margin_top=7,
wrap=true, wrap_mode=Pango.WrapMode.WORD_CHAR, visible=true });
}
foreach (Xep.DataForms.DataForm.Field field in form.fields) {
Widget? field_widget = Util.get_data_form_field_widget(field);
if (field.label != null && field.label != "" && field_widget != null) {
- form_box.add(new Label(field.label) { xalign=0, margin_top=7, visible=true });
- form_box.add(field_widget);
+ form_box.append(new Label(field.label) { xalign=0, margin_top=7, visible=true });
+ form_box.append(field_widget);
} else if (field.type_ == Xep.DataForms.DataForm.Type.FIXED && field.get_value_string() != "") {
string markup_fixed_field = Util.parse_add_markup(field.get_value_string(), null, true, false);
- form_box.add(new Label(markup_fixed_field) { use_markup=true, xalign=0, margin_top=7,
+ form_box.append(new Label(markup_fixed_field) { use_markup=true, xalign=0, margin_top=7,
wrap=true, wrap_mode=Pango.WrapMode.WORD_CHAR, visible=true });
}
}
register_form_continue.visible = true;
register_form_continue_label.label = _("Register");
} else {
- form_box.add(new Label(_("Check %s for information on how to sign up").printf(@"<a href=\"http://$(server)\">$(server)</a>")) { use_markup=true, visible=true });
+ form_box.append(new Label(_("Check %s for information on how to sign up").printf(@"<a href=\"http://$(server)\">$(server)</a>")) { use_markup=true, visible=true });
register_form_continue.visible = false;
}
}
@@ -418,21 +423,21 @@ public class AddAccountDialog : Gtk.Dialog {
}
private void animate_window_resize(Widget widget) { // TODO code duplication
- int def_height, curr_width, curr_height;
- get_size(out curr_width, out curr_height);
- widget.get_preferred_height(null, out def_height);
- def_height += 5;
- int difference = def_height - curr_height;
- Timer timer = new Timer();
- Timeout.add((int) (stack.transition_duration / 30),
- () => {
- ulong microsec;
- timer.elapsed(out microsec);
- ulong millisec = microsec / 1000;
- double partial = double.min(1, (double) millisec / stack.transition_duration);
- resize(curr_width, (int) (curr_height + difference * partial));
- return millisec < stack.transition_duration;
- });
+// int def_height, curr_width, curr_height;
+// get_size(out curr_width, out curr_height);
+// widget.get_preferred_height(null, out def_height);
+// def_height += 5;
+// int difference = def_height - curr_height;
+// Timer timer = new Timer();
+// Timeout.add((int) (stack.transition_duration / 30),
+// () => {
+// ulong microsec;
+// timer.elapsed(out microsec);
+// ulong millisec = microsec / 1000;
+// double partial = double.min(1, (double) millisec / stack.transition_duration);
+// resize(curr_width, (int) (curr_height + difference * partial));
+// return millisec < stack.transition_duration;
+// });
}
}
diff --git a/main/src/ui/manage_accounts/dialog.vala b/main/src/ui/manage_accounts/dialog.vala
index 5d596bc2..88dc7485 100644
--- a/main/src/ui/manage_accounts/dialog.vala
+++ b/main/src/ui/manage_accounts/dialog.vala
@@ -17,8 +17,8 @@ public class Dialog : Gtk.Dialog {
[GtkChild] public unowned Stack main_stack;
[GtkChild] public unowned ListBox account_list;
[GtkChild] public unowned Button no_accounts_add;
- [GtkChild] public unowned ToolButton add_account_button;
- [GtkChild] public unowned ToolButton remove_account_button;
+ [GtkChild] public unowned Button add_account_button;
+ [GtkChild] public unowned Button remove_account_button;
[GtkChild] public unowned AvatarImage image;
[GtkChild] public unowned Button image_button;
[GtkChild] public unowned Label jid_label;
@@ -28,8 +28,6 @@ public class Dialog : Gtk.Dialog {
[GtkChild] public unowned Util.EntryLabelHybrid alias_hybrid;
[GtkChild] public unowned Grid settings_list;
- private ArrayList<Plugins.AccountSettingsWidget> plugin_widgets = new ArrayList<Plugins.AccountSettingsWidget>();
-
private Database db;
private StreamInteractor stream_interactor;
private Account? selected_account;
@@ -44,8 +42,8 @@ public class Dialog : Gtk.Dialog {
if (selected_account != null) remove_account(account_row);
});
image_button.clicked.connect(show_select_avatar);
- alias_hybrid.entry.key_release_event.connect(() => { selected_account.alias = alias_hybrid.text; return false; });
- password_hybrid.entry.key_release_event.connect(() => { selected_account.password = password_hybrid.text; return false; });
+// alias_hybrid.entry.key_release_event.connect(() => { selected_account.alias = alias_hybrid.text; return false; });
+// password_hybrid.entry.key_release_event.connect(() => { selected_account.password = password_hybrid.text; return false; });
Util.LabelHybridGroup label_hybrid_group = new Util.LabelHybridGroup();
label_hybrid_group.add(alias_hybrid);
@@ -54,25 +52,18 @@ public class Dialog : Gtk.Dialog {
main_stack.set_visible_child_name("no_accounts");
int row_index = 4;
- int16 default_top_padding = new Gtk.Button().get_style_context().get_padding(Gtk.StateFlags.NORMAL).top + 1;
+ int16 default_top_padding = new Gtk.Button().get_style_context().get_padding().top + 1;
Application app = GLib.Application.get_default() as Application;
- foreach (var e in app.plugin_registry.account_settings_entries) {
- Plugins.AccountSettingsWidget widget = e.get_widget(Plugins.WidgetType.GTK);
- plugin_widgets.add(widget);
+ foreach (Plugins.AccountSettingsEntry e in app.plugin_registry.account_settings_entries) {
+ Widget? widget = e.get_widget(Plugins.WidgetType.GTK4) as Widget;
+ if (widget == null) continue;
Label label = new Label(e.name) { xalign=1, yalign=0, visible=true };
label.get_style_context().add_class("dim-label");
label.margin_top = e.label_top_padding == -1 ? default_top_padding : e.label_top_padding;
-
settings_list.attach(label, 0, row_index);
- if (widget is Widget) {
- Widget gtkw = (Widget) widget;
- plugin_widgets.add(widget);
- gtkw.visible = true;
- settings_list.attach(gtkw, 1, row_index, 2);
- } else {
- // TODO
- }
+
+ settings_list.attach(widget, 1, row_index, 2);
row_index++;
}
}
@@ -102,7 +93,7 @@ public class Dialog : Gtk.Dialog {
public AccountRow add_account(Account account) {
AccountRow account_item = new AccountRow (stream_interactor, account);
- account_list.add(account_item);
+ account_list.append(account_item);
main_stack.set_visible_child_name("accounts_exist");
return account_item;
}
@@ -127,7 +118,7 @@ public class Dialog : Gtk.Dialog {
Button ok_button = msg.get_widget_for_response(ResponseType.OK) as Button;
ok_button.label = _("Remove");
ok_button.get_style_context().add_class("destructive-action");
- if (msg.run() == Gtk.ResponseType.OK) {
+ if (/*msg.run() == Gtk.ResponseType.OK*/ true) {
account_list.remove(account_item);
if (account_item.account.enabled) account_disabled(account_item.account);
account_item.account.remove();
@@ -149,7 +140,7 @@ public class Dialog : Gtk.Dialog {
}
private void show_select_avatar() {
- PreviewFileChooserNative chooser = new PreviewFileChooserNative(_("Select avatar"), this, FileChooserAction.OPEN, _("Select"), _("Cancel"));
+ FileChooserNative chooser = new FileChooserNative(_("Select avatar"), this, FileChooserAction.OPEN, _("Select"), _("Cancel"));
FileFilter filter = new FileFilter();
foreach (PixbufFormat pixbuf_format in Pixbuf.get_formats()) {
foreach (string mime_type in pixbuf_format.get_mime_types()) {
@@ -164,10 +155,12 @@ public class Dialog : Gtk.Dialog {
filter.add_pattern("*");
chooser.add_filter(filter);
- if (chooser.run() == Gtk.ResponseType.ACCEPT) {
- string uri = chooser.get_filename();
+ chooser.response.connect(() => {
+ string uri = chooser.get_file().get_path();
stream_interactor.get_module(AvatarManager.IDENTITY).publish(selected_account, uri);
- }
+ });
+
+ chooser.show();
}
private bool change_account_state(bool state) {
@@ -201,8 +194,9 @@ public class Dialog : Gtk.Dialog {
active_switch.state_set.connect(change_account_state);
- foreach(Plugins.AccountSettingsWidget widget in plugin_widgets) {
- widget.set_account(account);
+ Application app = GLib.Application.get_default() as Application;
+ foreach (Plugins.AccountSettingsEntry e in app.plugin_registry.account_settings_entries) {
+ e.set_account(account);
}
}
diff --git a/main/src/ui/occupant_menu/list.vala b/main/src/ui/occupant_menu/list.vala
index fcb039d7..20a98cf6 100644
--- a/main/src/ui/occupant_menu/list.vala
+++ b/main/src/ui/occupant_menu/list.vala
@@ -4,7 +4,7 @@ using Gtk;
using Dino.Entities;
using Xmpp;
-namespace Dino.Ui.OccupantMenu{
+namespace Dino.Ui.OccupantMenu {
[GtkTemplate (ui = "/im/dino/Dino/occupant_list.ui")]
public class List : Box {
@@ -17,7 +17,8 @@ public class List : Box {
private Conversation conversation;
private string[]? filter_values;
- private HashMap<Jid, ListRow> rows = new HashMap<Jid, ListRow>(Jid.hash_func, Jid.equals_func);
+ private HashMap<Jid, Widget> rows = new HashMap<Jid, Widget>(Jid.hash_func, Jid.equals_func);
+ public HashMap<Widget, ListRow> row_wrappers = new HashMap<Widget, ListRow>();
public List(StreamInteractor stream_interactor, Conversation conversation) {
this.stream_interactor = stream_interactor;
@@ -53,8 +54,12 @@ public class List : Box {
}
public void add_occupant(Jid jid) {
- rows[jid] = new ListRow(stream_interactor, conversation, jid);
- list_box.add(rows[jid]);
+ var row_wrapper = new ListRow(stream_interactor, conversation, jid);
+ var widget = row_wrapper.get_widget();
+
+ row_wrappers[widget] = row_wrapper;
+ rows[jid] = widget;
+ list_box.append(widget);
}
public void remove_occupant(Jid jid) {
@@ -81,13 +86,13 @@ public class List : Box {
}
private void header(ListBoxRow row, ListBoxRow? before_row) {
- ListRow c1 = row as ListRow;
- Xmpp.Xep.Muc.Affiliation? a1 = stream_interactor.get_module(MucManager.IDENTITY).get_affiliation(conversation.counterpart, c1.jid, c1.conversation.account);
+ ListRow row_wrapper1 = row_wrappers[row.get_child()];
+ Xmpp.Xep.Muc.Affiliation? a1 = stream_interactor.get_module(MucManager.IDENTITY).get_affiliation(conversation.counterpart, row_wrapper1.jid, row_wrapper1.conversation.account);
if (a1 == null) return;
if (before_row != null) {
- ListRow c2 = (ListRow) before_row;
- Xmpp.Xep.Muc.Affiliation? a2 = stream_interactor.get_module(MucManager.IDENTITY).get_affiliation(conversation.counterpart, c2.jid, c2.conversation.account);
+ ListRow row_wrapper2 = row_wrappers[before_row.get_child()];
+ Xmpp.Xep.Muc.Affiliation? a2 = stream_interactor.get_module(MucManager.IDENTITY).get_affiliation(conversation.counterpart, row_wrapper2.jid, row_wrapper2.conversation.account);
if (a1 != a2) {
row.set_header(generate_header_widget(a1, false));
} else if (row.get_header() != null){
@@ -112,7 +117,7 @@ public class List : Box {
}
int count = 0;
- foreach (ListRow row in rows.values) {
+ foreach (ListRow row in row_wrappers.values) {
Xmpp.Xep.Muc.Affiliation aff = stream_interactor.get_module(MucManager.IDENTITY).get_affiliation(conversation.counterpart, row.jid, conversation.account);
if (aff == affiliation) count++;
}
@@ -120,36 +125,34 @@ public class List : Box {
Label title_label = new Label("") { margin_start=10, xalign=0, visible=true };
title_label.set_markup(@"<b>$(Markup.escape_text(aff_str))</b>");
- Label count_label = new Label(@"$count") { xalign=0, margin_end=7, expand=true, visible=true };
+ Label count_label = new Label(@"$count") { xalign=0, margin_end=7, hexpand=true, visible=true };
count_label.get_style_context().add_class("dim-label");
Grid grid = new Grid() { margin_top=top?5:15, column_spacing=5, hexpand=true, visible=true };
grid.attach(title_label, 0, 0, 1, 1);
grid.attach(count_label, 1, 0, 1, 1);
- grid.attach(new Separator(Orientation.HORIZONTAL) { expand=true, visible=true }, 0, 1, 2, 1);
+ grid.attach(new Separator(Orientation.HORIZONTAL) { hexpand=true, vexpand=true, visible=true }, 0, 1, 2, 1);
return grid;
}
private bool filter(ListBoxRow r) {
- if (r.get_type().is_a(typeof(ListRow))) {
- ListRow row = r as ListRow;
- foreach (string filter in filter_values) {
- return row.name_label.label.down().contains(filter.down());
- }
+ ListRow row_wrapper = row_wrappers[r.get_child()];
+ foreach (string filter in filter_values) {
+ return row_wrapper.name_label.label.down().contains(filter.down());
}
return true;
}
private int sort(ListBoxRow row1, ListBoxRow row2) {
- if (row1.get_type().is_a(typeof(ListRow)) && row2.get_type().is_a(typeof(ListRow))) {
- ListRow c1 = row1 as ListRow;
- ListRow c2 = row2 as ListRow;
- int affiliation1 = get_affiliation_ranking(stream_interactor.get_module(MucManager.IDENTITY).get_affiliation(conversation.counterpart, c1.jid, c1.conversation.account) ?? Xmpp.Xep.Muc.Affiliation.NONE);
- int affiliation2 = get_affiliation_ranking(stream_interactor.get_module(MucManager.IDENTITY).get_affiliation(conversation.counterpart, c2.jid, c2.conversation.account) ?? Xmpp.Xep.Muc.Affiliation.NONE);
- if (affiliation1 < affiliation2) return -1;
- else if (affiliation1 > affiliation2) return 1;
- else return c1.name_label.label.collate(c2.name_label.label);
- }
+ ListRow row_wrapper1 = row_wrappers[row1.get_child()];
+ ListRow row_wrapper2 = row_wrappers[row2.get_child()];
+
+ int affiliation1 = get_affiliation_ranking(stream_interactor.get_module(MucManager.IDENTITY).get_affiliation(conversation.counterpart, row_wrapper1.jid, row_wrapper1.conversation.account) ?? Xmpp.Xep.Muc.Affiliation.NONE);
+ int affiliation2 = get_affiliation_ranking(stream_interactor.get_module(MucManager.IDENTITY).get_affiliation(conversation.counterpart, row_wrapper2.jid, row_wrapper2.conversation.account) ?? Xmpp.Xep.Muc.Affiliation.NONE);
+
+ if (affiliation1 < affiliation2) return -1;
+ else if (affiliation1 > affiliation2) return 1;
+ else return row_wrapper1.name_label.label.collate(row_wrapper2.name_label.label);
return 0;
}
diff --git a/main/src/ui/occupant_menu/list_row.vala b/main/src/ui/occupant_menu/list_row.vala
index e0fb3f14..6b43fe7f 100644
--- a/main/src/ui/occupant_menu/list_row.vala
+++ b/main/src/ui/occupant_menu/list_row.vala
@@ -5,15 +5,22 @@ using Xmpp;
namespace Dino.Ui.OccupantMenu {
-[GtkTemplate (ui = "/im/dino/Dino/occupant_list_item.ui")]
-public class ListRow : ListBoxRow {
+public class ListRow : Object {
- [GtkChild] private unowned AvatarImage image;
- [GtkChild] public unowned Label name_label;
+ private Grid main_grid;
+ private AvatarImage image;
+ public Label name_label;
public Conversation? conversation;
public Jid? jid;
+ construct {
+ Builder builder = new Builder.from_resource("/im/dino/Dino/occupant_list_item.ui");
+ main_grid = (Grid) builder.get_object("main_grid");
+ image = (AvatarImage) builder.get_object("image");
+ name_label = (Label) builder.get_object("name_label");
+ }
+
public ListRow(StreamInteractor stream_interactor, Conversation conversation, Jid jid) {
this.conversation = conversation;
this.jid = jid;
@@ -26,6 +33,10 @@ public class ListRow : ListBoxRow {
name_label.label = text;
image.set_text(c);
}
+
+ public Widget get_widget() {
+ return main_grid;
+ }
}
}
diff --git a/main/src/ui/occupant_menu/view.vala b/main/src/ui/occupant_menu/view.vala
index 428d2c99..35aa95f4 100644
--- a/main/src/ui/occupant_menu/view.vala
+++ b/main/src/ui/occupant_menu/view.vala
@@ -22,15 +22,14 @@ public class View : Popover {
this.stream_interactor = stream_interactor;
this.conversation = conversation;
-
this.show.connect(initialize_list);
- invite_list.add(new ListRow.label("+", _("Invite")) {visible=true});
- list_box.add(invite_list);
+ invite_list.append(new ListRow.label("+", _("Invite")).get_widget());
+ list_box.append(invite_list);
invite_list.row_activated.connect(on_invite_clicked);
stack.add_named(list_box, "list");
- add(stack);
+ set_child(stack);
stack.visible_child_name = "list";
hide.connect(reset);
@@ -46,12 +45,11 @@ public class View : Popover {
private void initialize_list() {
if (list == null) {
list = new List(stream_interactor, conversation) { visible=true };
- list_box.add(list);
- list_box.reorder_child(list, 0);
+ list_box.prepend(list);
list.list_box.row_activated.connect((row) => {
- ListRow list_row = row as ListRow;
- show_menu(list_row.jid, list_row.name_label.label);
+ ListRow row_wrapper = list.row_wrappers[row.get_child()];
+ show_menu(row_wrapper.jid, row_wrapper.name_label.label);
});
}
}
@@ -71,37 +69,37 @@ public class View : Popover {
if (real_jid != null) name += "\n<span font=\'8\'>%s</span>".printf(Markup.escape_text(real_jid.bare_jid.to_string()));
Box header_box = new Box(Orientation.HORIZONTAL, 5) { visible=true };
- header_box.add(new Image.from_icon_name("pan-start-symbolic", IconSize.SMALL_TOOLBAR) { visible=true });
- header_box.add(new Label(name) { xalign=0, use_markup=true, hexpand=true, visible=true });
- Button header_button = new Button() { relief=ReliefStyle.NONE, visible=true };
- header_button.add(header_box);
+ header_box.append(new Image.from_icon_name("pan-start-symbolic") { visible=true });
+ header_box.append(new Label(name) { xalign=0, use_markup=true, hexpand=true, visible=true });
+ Button header_button = new Button() { has_frame=false, visible=true };
+ header_button.child = header_box;
- Box outer_box = new Box(Orientation.VERTICAL, 5) { margin=10, visible=true };
- outer_box.add(header_button);
+ Box outer_box = new Box(Orientation.VERTICAL, 5) { visible=true };
+ outer_box.append(header_button);
header_button.clicked.connect(show_list);
- ModelButton private_button = new ModelButton() { active=true, text=_("Start private conversation"), visible=true };
- outer_box.add(private_button);
+ Button private_button = new Button.with_label(_("Start private conversation")) { visible=true };
+ outer_box.append(private_button);
private_button.clicked.connect(private_conversation_button_clicked);
Jid? own_jid = stream_interactor.get_module(MucManager.IDENTITY).get_own_jid(conversation.counterpart, conversation.account);
Xmpp.Xep.Muc.Role? role = stream_interactor.get_module(MucManager.IDENTITY).get_role(own_jid, conversation.account);
if (role == Xmpp.Xep.Muc.Role.MODERATOR && stream_interactor.get_module(MucManager.IDENTITY).kick_possible(conversation.account, jid)) {
- ModelButton kick_button = new ModelButton() { active=true, text=_("Kick"), visible=true };
- outer_box.add(kick_button);
+ Button kick_button = new Button.with_label(_("Kick")) { visible=true };
+ outer_box.append(kick_button);
kick_button.clicked.connect(kick_button_clicked);
}
if (stream_interactor.get_module(MucManager.IDENTITY).is_moderated_room(conversation.account, conversation.counterpart) && role == Xmpp.Xep.Muc.Role.MODERATOR){
if (stream_interactor.get_module(MucManager.IDENTITY).get_role(selected_jid, conversation.account) == Xmpp.Xep.Muc.Role.VISITOR) {
- ModelButton voice_button = new ModelButton() { active=true, text=_("Grant write permission"), visible=true };
- outer_box.add(voice_button);
+ Button voice_button = new Button.with_label(_("Grant write permission")) { visible=true };
+ outer_box.append(voice_button);
voice_button.clicked.connect(() =>
voice_button_clicked("participant"));
}
else if (stream_interactor.get_module(MucManager.IDENTITY).get_role(selected_jid, conversation.account) == Xmpp.Xep.Muc.Role.PARTICIPANT){
- ModelButton voice_button = new ModelButton() { active=true, text=_("Revoke write permission"), visible=true };
- outer_box.add(voice_button);
+ Button voice_button = new Button.with_label(_("Revoke write permission")) { visible=true };
+ outer_box.append(voice_button);
voice_button.clicked.connect(() =>
voice_button_clicked("visitor"));
}
@@ -141,7 +139,7 @@ public class View : Popover {
Gee.List<Account> acc_list = new ArrayList<Account>(Account.equals_func);
acc_list.add(conversation.account);
SelectContactDialog add_chat_dialog = new SelectContactDialog(stream_interactor, acc_list);
- add_chat_dialog.set_transient_for((Window) get_toplevel());
+ add_chat_dialog.set_transient_for((Window) get_root());
add_chat_dialog.title = _("Invite to Conference");
add_chat_dialog.ok_button.label = _("Invite");
add_chat_dialog.selected.connect((account, jid) => {
diff --git a/main/src/ui/util/data_forms.vala b/main/src/ui/util/data_forms.vala
index b36012de..53439149 100644
--- a/main/src/ui/util/data_forms.vala
+++ b/main/src/ui/util/data_forms.vala
@@ -36,18 +36,20 @@ public static Widget? get_data_form_field_widget(DataForms.DataForm.Field field)
case DataForms.DataForm.Type.TEXT_PRIVATE:
DataForms.DataForm.TextPrivateField text_private_field = field as DataForms.DataForm.TextPrivateField;
Entry entry = new Entry() { text=text_private_field.value ?? "", valign=Align.CENTER, visible=true, visibility=false };
- entry.key_release_event.connect(() => {
+ var entry_key_events = new EventControllerKey();
+ entry_key_events.key_released.connect(() => {
text_private_field.value = entry.text;
- return false;
});
+ entry.add_controller(entry_key_events);
return entry;
case DataForms.DataForm.Type.TEXT_SINGLE:
DataForms.DataForm.TextSingleField text_single_field = field as DataForms.DataForm.TextSingleField;
Entry entry = new Entry() { text=text_single_field.value ?? "", valign=Align.CENTER, visible=true };
- entry.key_release_event.connect(() => {
+ var entry_key_events = new EventControllerKey();
+ entry_key_events.key_released.connect(() => {
text_single_field.value = entry.text;
- return false;
});
+ entry.add_controller(entry_key_events);
return entry;
default:
return null;
diff --git a/main/src/ui/util/helper.vala b/main/src/ui/util/helper.vala
index 427c2d3a..98abb48e 100644
--- a/main/src/ui/util/helper.vala
+++ b/main/src/ui/util/helper.vala
@@ -134,29 +134,30 @@ public static string get_occupant_display_name(StreamInteractor stream_interacto
return Dino.get_occupant_display_name(stream_interactor, conversation, jid, me_is_me ? _("Me") : null);
}
-public static void image_set_from_scaled_pixbuf(Image image, Gdk.Pixbuf pixbuf, int scale = 0, int width = 0, int height = 0) {
- if (scale == 0) scale = image.scale_factor;
- Cairo.Surface surface = Gdk.cairo_surface_create_from_pixbuf(pixbuf, scale, image.get_window());
- if (height == 0 && width != 0) {
- height = (int) ((double) width / pixbuf.width * pixbuf.height);
- } else if (height != 0 && width == 0) {
- width = (int) ((double) height / pixbuf.height * pixbuf.width);
- }
- if (width != 0) {
- Cairo.Surface surface_new = new Cairo.Surface.similar_image(surface, Cairo.Format.ARGB32, width, height);
- Cairo.Context context = new Cairo.Context(surface_new);
- context.scale((double) width * scale / pixbuf.width, (double) height * scale / pixbuf.height);
- context.set_source_surface(surface, 0, 0);
- context.get_source().set_filter(Cairo.Filter.BEST);
- context.paint();
- surface = surface_new;
- }
- image.set_from_surface(surface);
-}
+// TODO this has no usages?
+//public static void image_set_from_scaled_pixbuf(Image image, Gdk.Pixbuf pixbuf, int scale = 0, int width = 0, int height = 0) {
+// if (scale == 0) scale = image.scale_factor;
+// Cairo.Surface surface = Gdk.cairo_surface_create_from_pixbuf(pixbuf, scale, image.get_window());
+// if (height == 0 && width != 0) {
+// height = (int) ((double) width / pixbuf.width * pixbuf.height);
+// } else if (height != 0 && width == 0) {
+// width = (int) ((double) height / pixbuf.height * pixbuf.width);
+// }
+// if (width != 0) {
+// Cairo.Surface surface_new = new Cairo.Surface.similar_image(surface, Cairo.Format.ARGB32, width, height);
+// Cairo.Context context = new Cairo.Context(surface_new);
+// context.scale((double) width * scale / pixbuf.width, (double) height * scale / pixbuf.height);
+// context.set_source_surface(surface, 0, 0);
+// context.get_source().set_filter(Cairo.Filter.BEST);
+// context.paint();
+// surface = surface_new;
+// }
+// image.set_from_surface(surface);
+//}
public static Gdk.RGBA get_label_pango_color(Label label, string css_color) {
Gtk.CssProvider provider = force_color(label, css_color);
- Gdk.RGBA color_rgba = label.get_style_context().get_color(StateFlags.NORMAL);
+ Gdk.RGBA color_rgba = label.get_style_context().get_color();
label.get_style_context().remove_provider(provider);
return color_rgba;
}
@@ -176,7 +177,7 @@ private const string force_color_css = "%s { color: %s; }";
public static Gtk.CssProvider force_css(Gtk.Widget widget, string css) {
var p = new Gtk.CssProvider();
try {
- p.load_from_data(css);
+ p.load_from_data(css.data);
widget.get_style_context().add_provider(p, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION);
} catch (GLib.Error err) {
// handle err
@@ -197,7 +198,7 @@ public static void force_error_color(Gtk.Widget widget, string selector = "*") {
}
public static bool is_dark_theme(Gtk.Widget widget) {
- Gdk.RGBA bg = widget.get_style_context().get_color(StateFlags.NORMAL);
+ Gdk.RGBA bg = widget.get_style_context().get_color();
return (bg.red > 0.5 && bg.green > 0.5 && bg.blue > 0.5);
}
diff --git a/main/src/ui/util/label_hybrid.vala b/main/src/ui/util/label_hybrid.vala
index b992f169..d880ba6f 100644
--- a/main/src/ui/util/label_hybrid.vala
+++ b/main/src/ui/util/label_hybrid.vala
@@ -3,15 +3,18 @@ using Gtk;
namespace Dino.Ui.Util {
-public class LabelHybrid : Stack {
+public class LabelHybrid : Widget {
+ public Stack stack = new Stack();
public Label label = new Label("") { visible=true, max_width_chars=1, ellipsize=Pango.EllipsizeMode.END };
- protected Button button = new Button() { relief=ReliefStyle.NONE, visible=true };
+ protected Button button = new Button() { has_frame=false, visible=true };
internal virtual void init(Widget widget) {
- button.add(label);
- add_named(button, "label");
- add_named(widget, "widget");
+ this.layout_manager = new BinLayout();
+ stack.insert_after(this, null);
+ button.child = label;
+ stack.add_named(button, "label");
+ stack.add_named(widget, "widget");
button.clicked.connect(() => {
show_widget();
@@ -19,12 +22,12 @@ public class LabelHybrid : Stack {
}
public void show_widget() {
- visible_child_name = "widget";
- get_child_by_name("widget").grab_focus();
+ stack.visible_child_name = "widget";
+ stack.get_child_by_name("widget").grab_focus();
}
public void show_label() {
- visible_child_name = "label";
+ stack.visible_child_name = "label";
}
}
@@ -73,18 +76,25 @@ public class EntryLabelHybrid : LabelHybrid {
base.init(entry);
update_label();
- entry.key_release_event.connect((event) => {
- if (event.keyval == Gdk.Key.Return) {
- show_label();
- } else {
- set_label_label(entry.text);
- }
- return false;
- });
- entry.focus_out_event.connect(() => {
+ var key_events = new EventControllerKey();
+ key_events.key_released.connect(on_key_released);
+ entry.add_controller(key_events);
+
+ var focus_events = new EventControllerFocus();
+ focus_events.leave.connect(on_focus_leave);
+ entry.add_controller(focus_events);
+ }
+
+ private void on_key_released(uint keyval) {
+ if (keyval == Gdk.Key.Return) {
show_label();
- return false;
- });
+ } else {
+ set_label_label(entry.text);
+ }
+ }
+
+ private void on_focus_leave() {
+ show_label();
}
private void set_label_label(string value) {
@@ -143,14 +153,18 @@ public class ComboBoxTextLabelHybrid : LabelHybrid {
update_label();
show_label();
});
- combobox.focus_out_event.connect(() => {
- update_label();
- show_label();
- return false;
- });
button.clicked.connect(() => {
combobox.popup();
});
+
+ var focus_events = new EventControllerFocus();
+ focus_events.leave.connect(on_focus_leave);
+ combobox.add_controller(focus_events);
+ }
+
+ private void on_focus_leave() {
+ update_label();
+ show_label();
}
private void update_label() {
@@ -166,10 +180,10 @@ public class LabelHybridGroup {
hybrids.add(hybrid);
hybrid.notify["visible-child-name"].connect(() => {
- if (hybrid.visible_child_name == "label") return;
+ if (hybrid.stack.visible_child_name == "label") return;
foreach (LabelHybrid h in hybrids) {
if (h != hybrid) {
- h.set_visible_child_name("label");
+ h.stack.set_visible_child_name("label");
}
}
});
diff --git a/main/src/ui/util/scaling_image.vala b/main/src/ui/util/scaling_image.vala
index 477432c5..3dd3221f 100644
--- a/main/src/ui/util/scaling_image.vala
+++ b/main/src/ui/util/scaling_image.vala
@@ -2,7 +2,7 @@ using Gdk;
using Gtk;
namespace Dino.Ui {
-class ScalingImage : Misc {
+class ScalingImage : Widget {
public int min_width { get; set; default = -1; }
public int target_width { get; set; default = -1; }
public int max_width { get; set; default = -1; }
@@ -65,23 +65,28 @@ class ScalingImage : Misc {
if (exact_height < min_height) exact_height = min_height;
}
- public override void size_allocate(Allocation allocation) {
- if (max_width != -1) allocation.width = int.min(allocation.width, max_width);
- if (max_height != -1) allocation.height = int.min(allocation.height, max_height);
- allocation.height = int.max(allocation.height, min_height);
- allocation.width = int.max(allocation.width, min_width);
- double exact_width = allocation.width, exact_height = allocation.height;
+ public override void size_allocate(int width, int height, int baseline) {
+ if (max_width != -1) width = int.min(width, max_width);
+ if (max_height != -1) height = int.min(height, max_height);
+ height = int.max(height, min_height);
+ width = int.max(width, min_width);
+ double exact_width = width, exact_height = height;
calculate_size(ref exact_width, ref exact_height);
- base.size_allocate(allocation);
- if (last_allocation_height != allocation.height || last_allocation_width != allocation.width || last_scale_factor != scale_factor) {
- last_allocation_height = allocation.height;
- last_allocation_width = allocation.width;
+ base.size_allocate(width, height, baseline);
+ if (last_allocation_height != height || last_allocation_width != width || last_scale_factor != scale_factor) {
+ last_allocation_height = height;
+ last_allocation_width = width;
last_scale_factor = scale_factor;
cached_surface = null;
}
}
- public override bool draw(Cairo.Context ctx_in) {
+ public override void snapshot(Gtk.Snapshot snapshot) {
+ Cairo.Context context = snapshot.append_cairo(Graphene.Rect.alloc().init(0, 0, get_allocated_width(), get_allocated_height()));
+ draw(context);
+ }
+
+ public bool draw(Cairo.Context ctx_in) {
if (image == null) return false;
Cairo.Context ctx = ctx_in;
int width = this.get_allocated_width(), height = this.get_allocated_height(), base_factor = 1;
@@ -148,33 +153,43 @@ class ScalingImage : Misc {
return buffer;
}
- public override void get_preferred_width(out int minimum_width, out int natural_width) {
- minimum_width = int.max(0, min_width);
- double exact_width = -1, exact_height = -1;
- calculate_size(ref exact_width, ref exact_height);
- natural_width = (int) Math.ceil(exact_width);
- }
-
- public override void get_preferred_height(out int minimum_height, out int natural_height) {
- minimum_height = int.max(0, min_height);
- double exact_width = -1, exact_height = -1;
- calculate_size(ref exact_width, ref exact_height);
- natural_height = (int) Math.ceil(exact_height);
- }
-
- public override void get_preferred_height_for_width(int width, out int minimum_height, out int natural_height) {
- double exact_width = width, exact_height = -1;
- calculate_size(ref exact_width, ref exact_height);
- natural_height = (int) Math.ceil(exact_height);
- minimum_height = natural_height;
+ public override void measure(Orientation orientation, int for_size, out int minimum, out int natural, out int minimum_baseline, out int natural_baseline) {
+ double natural_width = -1, natural_height = -1;
+ calculate_size(ref natural_width, ref natural_height);
+ if (orientation == Orientation.HORIZONTAL) {
+ natural = (int) Math.ceil(natural_width);
+ } else {
+ natural = (int) Math.ceil(natural_height);
+ }
+ if (for_size == -1) {
+ minimum = 0;
+ } else {
+ if (orientation == Orientation.HORIZONTAL) {
+ double exact_width = -1, exact_height = for_size;
+ calculate_size(ref exact_width, ref exact_height);
+ minimum = int.max((int)Math.floor(exact_width), min_width);
+ } else {
+ double exact_width = for_size, exact_height = -1;
+ calculate_size(ref exact_width, ref exact_height);
+ minimum = int.max((int)Math.floor(exact_height), min_height);
+ }
+ }
+ minimum_baseline = natural_baseline = -1;
}
- public override void get_preferred_width_for_height(int height, out int minimum_width, out int natural_width) {
- double exact_width = -1, exact_height = height;
- calculate_size(ref exact_width, ref exact_height);
- natural_width = (int) Math.ceil(exact_width);
- minimum_width = natural_width;
- }
+// public override void get_preferred_height_for_width(int width, out int minimum_height, out int natural_height) {
+// double exact_width = width, exact_height = -1;
+// calculate_size(ref exact_width, ref exact_height);
+// natural_height = (int) Math.ceil(exact_height);
+// minimum_height = natural_height;
+// }
+//
+// public override void get_preferred_width_for_height(int height, out int minimum_width, out int natural_width) {
+// double exact_width = -1, exact_height = height;
+// calculate_size(ref exact_width, ref exact_height);
+// natural_width = (int) Math.ceil(exact_width);
+// minimum_width = natural_width;
+// }
public override SizeRequestMode get_request_mode() {
return SizeRequestMode.HEIGHT_FOR_WIDTH;
diff --git a/main/src/ui/util/size_request_box.vala b/main/src/ui/util/size_request_box.vala
index a2828262..7d7b6185 100644
--- a/main/src/ui/util/size_request_box.vala
+++ b/main/src/ui/util/size_request_box.vala
@@ -9,9 +9,13 @@ public class SizeRequestBox : Box {
}
}
-public class SizeRequestBin : Bin {
+public class SizeRequestBin : Widget {
public SizeRequestMode size_request_mode { get; set; default = SizeRequestMode.CONSTANT_SIZE; }
+ construct {
+ this.layout_manager = new BinLayout();
+ }
+
public override Gtk.SizeRequestMode get_request_mode() {
return size_request_mode;
}
diff --git a/main/src/ui/util/sizing_bin.vala b/main/src/ui/util/sizing_bin.vala
index 9c5ff4c7..939022a1 100644
--- a/main/src/ui/util/sizing_bin.vala
+++ b/main/src/ui/util/sizing_bin.vala
@@ -1,7 +1,7 @@
using Gtk;
namespace Dino.Ui {
-public class SizingBin : Bin {
+public class SizingBin : Widget {
public int min_width { get; set; default = -1; }
public int target_width { get; set; default = -1; }
public int max_width { get; set; default = -1; }
@@ -9,27 +9,29 @@ public class SizingBin : Bin {
public int target_height { get; set; default = -1; }
public int max_height { get; set; default = -1; }
- public override void size_allocate(Allocation allocation) {
- if (max_height != -1) allocation.height = int.min(allocation.height, max_height);
- if (max_width != -1) allocation.width = int.min(allocation.width, max_width);
- base.size_allocate(allocation);
+ construct {
+ layout_manager = new BinLayout();
}
- public override void get_preferred_width(out int minimum_width, out int natural_width) {
- base.get_preferred_width(out minimum_width, out natural_width);
- if (min_width != -1) minimum_width = int.max(minimum_width, min_width);
- if (max_width != -1) natural_width = int.min(natural_width, max_width);
- if (target_width != -1) natural_width = int.max(natural_width, target_width);
- natural_width = int.max(natural_width, minimum_width);
+ public override void size_allocate(int width, int height, int baseline) {
+ if (max_height != -1) height = int.min(height, max_height);
+ if (max_width != -1) width = int.min(width, max_width);
+ base.size_allocate(width, height, baseline);
}
- public override void get_preferred_height_for_width(int width, out int minimum_height, out int natural_height) {
- base.get_preferred_height_for_width(width, out minimum_height, out natural_height);
- if (min_height != -1) minimum_height = int.max(minimum_height, min_height);
- if (max_height != -1) natural_height = int.min(natural_height, max_height);
- if (target_height != -1) natural_height = int.max(natural_height, target_height);
- natural_height = int.max(natural_height, minimum_height);
+ public override void measure(Orientation orientation, int for_size, out int minimum, out int natural, out int minimum_baseline, out int natural_baseline) {
+ base.measure(orientation, for_size, out minimum, out natural, out minimum_baseline, out natural_baseline);
+ if (orientation == Orientation.HORIZONTAL) {
+ if (min_width != -1) minimum = int.max(minimum, min_width);
+ if (max_width != -1) natural = int.min(natural, max_width);
+ if (target_width != -1) natural = int.max(natural, target_width);
+ natural = int.max(natural, minimum);
+ } else {
+ if (min_height != -1) minimum = int.max(minimum, min_height);
+ if (max_height != -1) natural = int.min(natural, max_height);
+ if (target_height != -1) natural = int.max(natural, target_height);
+ natural = int.max(natural, minimum);
+ }
}
-
}
}