[gnome-flashback/wip/segeiger/gnome-3-18-inputmethods: 1/3] input-sources: implement candidate popup
- From: Sebastian Geiger <segeiger src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-flashback/wip/segeiger/gnome-3-18-inputmethods: 1/3] input-sources: implement candidate popup
- Date: Sun, 17 Jan 2016 12:13:33 +0000 (UTC)
commit 433f5536c58817fe5e0a01c861f9cc1c5c5aad06
Author: Kjartan Maraas <kmaraas gnome org>
Date: Tue Jan 12 08:24:36 2016 +0100
input-sources: implement candidate popup
gnome-flashback/libinput-sources/Makefile.am | 2 +
.../libinput-sources/gf-candidate-area.c | 239 +++++++++++++++++++
.../libinput-sources/gf-candidate-area.h | 46 ++++
.../libinput-sources/gf-candidate-popup.c | 248 ++++++++++++++++++--
.../libinput-sources/gf-candidate-popup.h | 4 +-
5 files changed, 522 insertions(+), 17 deletions(-)
---
diff --git a/gnome-flashback/libinput-sources/Makefile.am b/gnome-flashback/libinput-sources/Makefile.am
index e7ce173..80fb88d 100644
--- a/gnome-flashback/libinput-sources/Makefile.am
+++ b/gnome-flashback/libinput-sources/Makefile.am
@@ -15,6 +15,8 @@ libinput_sources_la_CFLAGS = \
$(NULL)
libinput_sources_la_SOURCES = \
+ gf-candidate-area.c \
+ gf-candidate-area.h \
gf-candidate-popup.c \
gf-candidate-popup.h \
gf-ibus-manager.c \
diff --git a/gnome-flashback/libinput-sources/gf-candidate-area.c
b/gnome-flashback/libinput-sources/gf-candidate-area.c
new file mode 100644
index 0000000..e485898
--- /dev/null
+++ b/gnome-flashback/libinput-sources/gf-candidate-area.c
@@ -0,0 +1,239 @@
+/*
+ * Copyright (C) 2016 Sebastian Geiger
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include "gf-candidate-area.h"
+
+#define MAX_CANDIDATES_PER_PAGE 16
+
+const gchar* DEFAULT_INDEX_LABELS[] =
+ {
+ "1", "2", "3", "4", "5", "6", "7", "8",
+ "9", "0", "a", "b", "c", "d", "e", "f"
+ };
+
+struct _GfCandidateArea
+{
+ GtkBox parent;
+
+ GtkWidget *button_box;
+ GtkWidget *prev_button;
+ GtkWidget *next_button;
+
+ GSList *candidate_boxes;
+ GSList *index_labels;
+ GSList *candidate_labels;
+
+ IBusOrientation orientation;
+ guint cursor_position;
+};
+
+enum
+{
+ SIGNAL_CANDIDATE_CLICKED,
+ SIGNAL_PREV_PAGE,
+ SIGNAL_NEXT_PAGE,
+
+ SIGNAL_LAST
+};
+
+static guint signals[SIGNAL_LAST] = { 0 };
+
+G_DEFINE_TYPE (GfCandidateArea, gf_candidate_area, GTK_TYPE_BOX)
+
+static gboolean
+button_clicked_cb (GtkWidget *widget,
+ GdkEvent *event,
+ GfCandidateArea *area)
+{
+ int index;
+
+ index = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (widget), "index"));
+
+ g_signal_emit (area, signals[SIGNAL_CANDIDATE_CLICKED], 0, index, event);
+
+ return GDK_EVENT_PROPAGATE;
+}
+
+static gboolean
+prev_button_clicked_cb (GtkButton *button,
+ GfCandidateArea *area)
+{
+ g_signal_emit (area, signals[SIGNAL_PREV_PAGE], 0);
+
+ return GDK_EVENT_PROPAGATE;
+}
+
+static gboolean
+next_button_clicked_cb (GtkButton *button,
+ GfCandidateArea *area)
+{
+ g_signal_emit (area, signals[SIGNAL_NEXT_PAGE], 0);
+
+ return GDK_EVENT_PROPAGATE;
+}
+
+void
+gf_candidate_area_set_candidates (GfCandidateArea *area,
+ GSList *indexes,
+ GSList *candidates,
+ guint cursor_position,
+ gboolean cursor_visible)
+{
+ guint i;
+
+ for (i = 0; i < MAX_CANDIDATES_PER_PAGE; i++)
+ {
+ gboolean visible;
+ GtkWidget *candidate_box;
+ GtkWidget *index_label;
+ GtkWidget *candidate_label;
+ const gchar *index_text;
+
+ visible = i < g_slist_length (candidates);
+ candidate_box = g_slist_nth_data (area->candidate_boxes, i);
+
+ gtk_widget_set_visible (candidate_box, visible);
+
+ if (!visible)
+ continue;
+
+ index_label = g_slist_nth_data (area->index_labels, i);
+
+ if (indexes && g_slist_nth_data (indexes, i))
+ index_text = g_slist_nth_data (indexes, i);
+ else
+ index_text = DEFAULT_INDEX_LABELS [i];
+
+ gtk_label_set_text (GTK_LABEL(index_label), index_text);
+
+ candidate_label = g_slist_nth_data (area->candidate_labels, i);
+
+ gtk_label_set_text (GTK_LABEL (candidate_label),
+ g_slist_nth_data (candidates, i));
+ }
+
+ area->cursor_position = cursor_position;
+}
+
+void
+gf_candidate_area_update_buttons (GfCandidateArea *area,
+ gboolean wraps_around,
+ gint page,
+ gint n_pages)
+{
+ gtk_widget_set_visible (area->button_box, n_pages > 1);
+
+ if (n_pages < 2)
+ return;
+
+ gtk_widget_set_sensitive (area->prev_button, wraps_around || page > 0);
+ gtk_widget_set_sensitive (area->next_button,
+ wraps_around || page < n_pages - 1);
+}
+
+static void
+gf_candidate_area_class_init (GfCandidateAreaClass *area_class)
+{
+ signals[SIGNAL_CANDIDATE_CLICKED] =
+ g_signal_new ("candidate-clicked", G_OBJECT_CLASS_TYPE (area_class),
+ G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE,
+ 2, G_TYPE_UINT, GDK_TYPE_EVENT);
+
+ signals[SIGNAL_PREV_PAGE] =
+ g_signal_new ("previous-page", G_OBJECT_CLASS_TYPE (area_class),
+ G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0);
+
+ signals[SIGNAL_NEXT_PAGE] =
+ g_signal_new ("next-page", G_OBJECT_CLASS_TYPE (area_class),
+ G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0);
+}
+
+static void
+gf_candidate_area_init (GfCandidateArea *area)
+{
+ GtkWidget *prev_image, *next_image;
+ int i;
+
+ for (i = 0; i < MAX_CANDIDATES_PER_PAGE; i++)
+ {
+ GtkWidget *event_box,
+ *box,
+ *index_label,
+ *candidate_label;
+
+ event_box = gtk_event_box_new();
+ box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
+
+ gtk_container_add (GTK_CONTAINER (area), event_box);
+ gtk_container_add (GTK_CONTAINER (event_box), box);
+ g_object_set_data (G_OBJECT (box), "index,", GINT_TO_POINTER(i));
+
+ index_label = gtk_label_new (NULL);
+ candidate_label = gtk_label_new (NULL);
+
+ gtk_container_add (GTK_CONTAINER (box), index_label);
+ gtk_container_add (GTK_CONTAINER (box), candidate_label);
+
+ gtk_widget_show_all (box);
+
+ area->candidate_boxes = g_slist_append (area->candidate_boxes, box);
+ area->index_labels = g_slist_append (area->index_labels,
+ index_label);
+ area->candidate_labels = g_slist_append (area->candidate_labels,
+ candidate_label);
+
+ g_signal_connect (event_box, "button-release-event",
+ G_CALLBACK (button_clicked_cb), area);
+ }
+
+ area->button_box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
+
+ gtk_container_add (GTK_CONTAINER (area), area->button_box);
+
+ prev_image = gtk_image_new_from_icon_name ("go-previous-symbolic",
+ GTK_ICON_SIZE_BUTTON);
+ area->prev_button = gtk_button_new_with_label ("Prev");
+ gtk_button_set_image (GTK_BUTTON (area->prev_button), prev_image);
+
+ next_image = gtk_image_new_from_icon_name ("go-next-symbolic",
+ GTK_ICON_SIZE_BUTTON);
+ area->next_button = gtk_button_new_with_label ("Next");
+ gtk_button_set_image (GTK_BUTTON (area->next_button), next_image);
+
+ gtk_container_add (GTK_CONTAINER (area->button_box), area->prev_button);
+ gtk_container_add (GTK_CONTAINER (area->button_box), area->next_button);
+
+ gtk_widget_show_all (GTK_WIDGET (area));
+
+ g_signal_connect (area->prev_button, "clicked",
+ G_CALLBACK (prev_button_clicked_cb), area);
+ g_signal_connect (area->next_button, "clicked",
+ G_CALLBACK (next_button_clicked_cb), area);
+
+ area->orientation = IBUS_ORIENTATION_HORIZONTAL;
+ area->cursor_position = 0;
+}
+
+GtkWidget*
+gf_candidate_area_new (void)
+{
+ return g_object_new (GF_TYPE_CANDIDATE_AREA,
+ "orientation", GTK_ORIENTATION_VERTICAL,
+ NULL);
+}
diff --git a/gnome-flashback/libinput-sources/gf-candidate-area.h
b/gnome-flashback/libinput-sources/gf-candidate-area.h
new file mode 100644
index 0000000..9137dce
--- /dev/null
+++ b/gnome-flashback/libinput-sources/gf-candidate-area.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2016 Sebastian Geiger
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GF_CANDIDATE_AREA_H
+#define GF_CANDIDATE_AREA_H
+
+#include <glib-object.h>
+#include <gtk/gtk.h>
+#include <ibus-1.0/ibus.h>
+
+G_BEGIN_DECLS
+
+#define GF_TYPE_CANDIDATE_AREA gf_candidate_area_get_type ()
+G_DECLARE_FINAL_TYPE (GfCandidateArea, gf_candidate_area,
+ GF, CANDIDATE_AREA, GtkBox)
+
+GtkWidget* gf_candidate_area_new (void);
+
+void gf_candidate_area_set_candidates (GfCandidateArea *area,
+ GSList *indexes,
+ GSList *candidates,
+ guint cursor_position,
+ gboolean cursor_visible);
+
+void gf_candidate_area_update_buttons (GfCandidateArea *area,
+ gboolean wraps_around,
+ gint page,
+ gint n_pages);
+
+G_END_DECLS
+
+#endif
diff --git a/gnome-flashback/libinput-sources/gf-candidate-popup.c
b/gnome-flashback/libinput-sources/gf-candidate-popup.c
index 32d6a63..ee01b07 100644
--- a/gnome-flashback/libinput-sources/gf-candidate-popup.c
+++ b/gnome-flashback/libinput-sources/gf-candidate-popup.c
@@ -17,16 +17,53 @@
#include "config.h"
+/* _XOPEN_SOURCE macro needed for floor and ceil functions from math library */
+#define _XOPEN_SOURCE 600
+
+#include <math.h>
+
+#include "gf-candidate-area.h"
#include "gf-candidate-popup.h"
struct _GfCandidatePopup
{
- GObject parent;
+ GfPopupWindow parent;
IBusPanelService *service;
+
+ GtkWidget *window;
+
+ GtkWidget *box_layout;
+
+ GtkWidget *pre_edit_text;
+ GtkWidget *aux_text;
+ GtkWidget *candidate_area;
+
+ gint cursor_x_pos;
+ gint cursor_y_pos;
+ gint cursor_height;
+ gint cursor_width;
};
-G_DEFINE_TYPE (GfCandidatePopup, gf_candidate_popup, G_TYPE_OBJECT)
+G_DEFINE_TYPE (GfCandidatePopup, gf_candidate_popup, GF_TYPE_POPUP_WINDOW)
+
+static void
+set_text_attributes (GfCandidatePopup *popup, IBusAttrList *attributes)
+{
+ IBusAttribute *attribute;
+ guint i, start, end;
+
+ for (i = 0; (attribute = ibus_attr_list_get (attributes, i)) != NULL; i++)
+ {
+ if (ibus_attribute_get_type () == IBUS_ATTR_TYPE_BACKGROUND)
+ {
+ start = ibus_attribute_get_start_index (attribute);
+ end = ibus_attribute_get_end_index (attribute);
+ gtk_label_select_region (GTK_LABEL (popup->pre_edit_text),
+ start, end);
+ }
+ }
+}
static void
set_cursor_location_cb (IBusPanelService *service,
@@ -36,6 +73,58 @@ set_cursor_location_cb (IBusPanelService *service,
gint h,
gpointer user_data)
{
+ GfCandidatePopup *popup;
+
+ popup = GF_CANDIDATE_POPUP (user_data);
+
+ popup->cursor_x_pos = x;
+ popup->cursor_y_pos = y;
+ popup->cursor_height = h;
+ popup->cursor_width = w;
+}
+
+static void
+update_window_location (GfCandidatePopup *popup)
+{
+ gint popup_height, popup_width;
+ GdkScreen *screen;
+ GdkWindow *window;
+ GdkRectangle rectangle;
+ gint monitor;
+ gint x, y;
+ gint box_min_height, box_natural_height;
+ gint box_min_width, box_natural_width;
+
+ gtk_window_get_size (GTK_WINDOW (popup->window), &popup_width, &popup_height);
+
+ window = gtk_widget_get_window (popup->window);
+
+ if (window == NULL)
+ return;
+
+ screen = gdk_window_get_screen (window);
+ monitor = gdk_screen_get_monitor_at_window (screen, window);
+ gdk_screen_get_monitor_geometry (screen, monitor, &rectangle);
+
+ x = popup->cursor_x_pos;
+ y = popup->cursor_y_pos;
+
+ if (x + popup_width > rectangle.width)
+ x = x - popup_width + popup->cursor_width;
+
+ if (y + popup_height > rectangle.height)
+ y = y - popup_height;
+ else
+ y = y + popup->cursor_height;
+
+ gtk_window_move (GTK_WINDOW (popup->window), x, y);
+
+ gtk_widget_get_preferred_height (popup->window,
+ &box_min_height, &box_natural_height);
+ gtk_widget_get_preferred_width (popup->window,
+ &box_min_width, &box_natural_width);
+ gtk_window_resize (GTK_WINDOW (popup->window),
+ box_natural_width, box_natural_height);
}
static void
@@ -43,67 +132,170 @@ update_preedit_text_cb (IBusPanelService *service,
IBusText *text,
guint cursor_pos,
gboolean visible,
- gpointer user_data)
+ GfCandidatePopup *popup)
{
+ IBusAttrList *attributes;
+
+ gtk_widget_set_visible (popup->pre_edit_text, visible);
+
+ gtk_label_set_text (GTK_LABEL (popup->pre_edit_text),
+ ibus_text_get_text (text));
+
+ attributes = ibus_text_get_attributes (text);
+
+ if (attributes)
+ set_text_attributes (popup, attributes);
}
static void
show_preedit_text_cb (IBusPanelService *service,
- gpointer user_data)
+ GfCandidatePopup *popup)
{
+ gtk_widget_show (popup->pre_edit_text);
}
static void
hide_preedit_text_cb (IBusPanelService *service,
- gpointer user_data)
+ GfCandidatePopup *popup)
{
+ gtk_widget_hide (popup->pre_edit_text);
}
static void
update_auxiliary_text_cb (IBusPanelService *service,
IBusText *text,
gboolean visible,
- gpointer user_data)
+ GfCandidatePopup *popup)
{
+ gtk_widget_set_visible (popup->aux_text, visible);
+
+ gtk_label_set_text (GTK_LABEL (popup->aux_text), ibus_text_get_text (text));
}
static void
show_auxiliary_text_cb (IBusPanelService *service,
- gpointer user_data)
+ GfCandidatePopup *popup)
{
+ gtk_widget_show (popup->aux_text);
}
static void
hide_auxiliary_text_cb (IBusPanelService *service,
- gpointer user_data)
+ GfCandidatePopup *popup)
{
+ gtk_widget_hide (popup->aux_text);
}
static void
update_lookup_table_cb (IBusPanelService *service,
IBusLookupTable *lookup_table,
gboolean visible,
- gpointer user_data)
+ GfCandidatePopup *popup)
{
+ guint n_candidates,
+ cursor_position,
+ page_size, n_pages, page,
+ start_index, end_index,
+ i;
+ GSList *indexes, *candidates;
+ IBusText *index_label;
+ gint orientation;
+
+ indexes = NULL;
+ candidates = NULL;
+
+ gtk_widget_set_visible (popup->window, visible);
+ update_window_location (popup);
+ n_candidates = ibus_lookup_table_get_number_of_candidates (lookup_table);
+ cursor_position = ibus_lookup_table_get_cursor_pos (lookup_table);
+ page_size = ibus_lookup_table_get_page_size (lookup_table);
+ n_pages = (guint) ceil (n_candidates / ((gdouble) page_size));
+ page = ((cursor_position == 0) ? 0 :
+ (guint) floor (cursor_position / ((gdouble) page_size)));
+ start_index = page * page_size;
+ end_index = MIN ((page + 1) * page_size, n_candidates);
+
+ for (i = 0; (index_label = ibus_lookup_table_get_label (lookup_table, i)) != NULL; i++)
+ {
+ gchar *text;
+
+ text = g_strdup (ibus_text_get_text (index_label));
+ indexes = g_slist_append (indexes, text);
+ }
+
+ for (i = start_index; i < end_index; i++)
+ {
+ IBusText *ibus_text;
+ const gchar* text;
+
+ ibus_text = ibus_lookup_table_get_candidate(lookup_table, i);
+ text = ibus_text_get_text(ibus_text);
+ candidates = g_slist_append (candidates, (gpointer) text);
+ }
+
+ gf_candidate_area_set_candidates (GF_CANDIDATE_AREA (popup->candidate_area),
+ indexes, candidates, cursor_position,
+ visible);
+
+ orientation = ibus_lookup_table_get_orientation (lookup_table);
+
+ gf_candidate_area_set_orientation (GF_CANDIDATE_AREA (popup->candidate_area),
+ (IBusOrientation) orientation);
+
+ gf_candidate_area_update_buttons (GF_CANDIDATE_AREA (popup->candidate_area),
+ ibus_lookup_table_is_round (lookup_table),
+ page, n_pages);
+
+ g_slist_free (indexes);
+ g_slist_free (candidates);
}
static void
show_lookup_table_cb (IBusPanelService *service,
- gpointer user_data)
+ GfCandidatePopup *popup)
{
+ gtk_widget_show (popup->window);
+ update_window_location (popup);
}
static void
hide_lookup_table_cb (IBusPanelService *service,
- gpointer user_data)
+ GfCandidatePopup *popup)
+{
+ gtk_widget_hide (popup->window);
+ update_window_location (popup);
+}
+
+static void
+area_page_prev_cb (GfCandidateArea *area,
+ GfCandidatePopup *popup)
+{
+ ibus_panel_service_page_up (popup->service);
+}
+
+static void
+area_page_next_cb (GfCandidateArea *area,
+ GfCandidatePopup *popup)
{
+ ibus_panel_service_page_down (popup->service);
}
static void
-focus_out_cb (IBusPanelService *service,
- const gchar *input_context_path,
- gpointer user_data)
+area_candidate_clicked_cb (GfCandidateArea *area,
+ guint index,
+ GdkEvent *event,
+ GfCandidatePopup *popup)
{
+ guint button;
+ GdkModifierType state;
+
+ gdk_event_get_button (event, &button);
+ gdk_event_get_state (event, &state);
+
+ ibus_panel_service_candidate_clicked (popup->service,
+ index,
+ button,
+ state);
}
static void
@@ -131,6 +323,32 @@ gf_candidate_popup_class_init (GfCandidatePopupClass *popup_class)
static void
gf_candidate_popup_init (GfCandidatePopup *popup)
{
+ popup->candidate_area = gf_candidate_area_new();
+
+ popup->window = gtk_window_new (GTK_WINDOW_POPUP);
+ gtk_window_set_default_size (GTK_WINDOW (popup->window), 1, 1);
+
+ popup->box_layout = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
+ gtk_container_add (GTK_CONTAINER (popup->window), popup->box_layout);
+
+ popup->pre_edit_text = gtk_label_new (NULL);
+ popup->aux_text = gtk_label_new (NULL);
+
+ gtk_container_add (GTK_CONTAINER (popup->box_layout), popup->pre_edit_text);
+ gtk_container_add (GTK_CONTAINER (popup->box_layout), popup->aux_text);
+
+ gtk_container_add (GTK_CONTAINER (popup->box_layout),
+ popup->candidate_area);
+
+ gtk_widget_show (popup->box_layout);
+ gtk_widget_show (popup->candidate_area);
+
+ g_signal_connect (popup->candidate_area, "previous-page",
+ G_CALLBACK (area_page_prev_cb), popup);
+ g_signal_connect (popup->candidate_area, "next-page",
+ G_CALLBACK (area_page_next_cb), popup);
+ g_signal_connect (popup->candidate_area, "candidate-clicked",
+ G_CALLBACK (area_candidate_clicked_cb), popup);
}
GfCandidatePopup *
@@ -170,6 +388,4 @@ gf_candidate_popup_set_panel_service (GfCandidatePopup *popup,
G_CALLBACK (show_lookup_table_cb), popup);
g_signal_connect (service, "hide-lookup-table",
G_CALLBACK (hide_lookup_table_cb), popup);
- g_signal_connect (service, "focus-out",
- G_CALLBACK (focus_out_cb), popup);
}
diff --git a/gnome-flashback/libinput-sources/gf-candidate-popup.h
b/gnome-flashback/libinput-sources/gf-candidate-popup.h
index 8a79979..d35aeee 100644
--- a/gnome-flashback/libinput-sources/gf-candidate-popup.h
+++ b/gnome-flashback/libinput-sources/gf-candidate-popup.h
@@ -21,11 +21,13 @@
#include <glib-object.h>
#include <ibus-1.0/ibus.h>
+#include <libcommon/gf-popup-window.h>
+
G_BEGIN_DECLS
#define GF_TYPE_CANDIDATE_POPUP gf_candidate_popup_get_type ()
G_DECLARE_FINAL_TYPE (GfCandidatePopup, gf_candidate_popup,
- GF, CANDIDATE_POPUP, GObject)
+ GF, CANDIDATE_POPUP, GfPopupWindow)
GfCandidatePopup *gf_candidate_popup_new (void);
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]