GTK: add some support for input methods.

We do not currently support pre-edit texts, or
retrieving/modifying text surrounding the caret.
This does, at least, finally allow the use of
dead keys during text input, but probably is
insufficient for useful input of CJKV &c.
This commit is contained in:
John-Mark Bell 2014-02-17 00:21:49 +00:00
parent c47a497bca
commit b928095652
3 changed files with 109 additions and 19 deletions

View File

@ -35,12 +35,14 @@
#include "gtk/plotters.h"
#include "gtk/treeview.h"
#include "utils/log.h"
#include "utils/utf8.h"
#include "utils/utils.h"
struct nsgtk_treeview {
GtkWindow *window;
GtkScrolledWindow *scrolled;
GtkDrawingArea *drawing_area;
GtkIMContext *input_method;
bool mouse_pressed;
int mouse_pressed_x;
int mouse_pressed_y;
@ -52,6 +54,7 @@ struct nsgtk_treeview {
void nsgtk_treeview_destroy(struct nsgtk_treeview *tv)
{
tree_delete(tv->tree);
g_object_unref(tv->input_method);
gtk_widget_destroy(GTK_WIDGET(tv->window));
free(tv);
}
@ -206,12 +209,14 @@ void nsgtk_tree_window_hide(GtkWidget *widget, gpointer g)
{
}
gboolean nsgtk_tree_window_button_press_event(GtkWidget *widget,
static gboolean
nsgtk_tree_window_button_press_event(GtkWidget *widget,
GdkEventButton *event, gpointer g)
{
struct nsgtk_treeview *tw = g;
struct tree *tree = tw->tree;
gtk_im_context_reset(tw->input_method);
gtk_widget_grab_focus(GTK_WIDGET(tw->drawing_area));
tw->mouse_pressed = true;
@ -243,7 +248,8 @@ gboolean nsgtk_tree_window_button_press_event(GtkWidget *widget,
return TRUE;
}
gboolean nsgtk_tree_window_button_release_event(GtkWidget *widget,
static gboolean
nsgtk_tree_window_button_release_event(GtkWidget *widget,
GdkEventButton *event, gpointer g)
{
bool shift = event->state & GDK_SHIFT_MASK;
@ -307,7 +313,8 @@ gboolean nsgtk_tree_window_button_release_event(GtkWidget *widget,
return TRUE;
}
gboolean nsgtk_tree_window_motion_notify_event(GtkWidget *widget,
static gboolean
nsgtk_tree_window_motion_notify_event(GtkWidget *widget,
GdkEventMotion *event, gpointer g)
{
bool shift = event->state & GDK_SHIFT_MASK;
@ -369,7 +376,8 @@ gboolean nsgtk_tree_window_motion_notify_event(GtkWidget *widget,
}
gboolean nsgtk_tree_window_keypress_event(GtkWidget *widget, GdkEventKey *event,
static gboolean
nsgtk_tree_window_keypress_event(GtkWidget *widget, GdkEventKey *event,
gpointer g)
{
struct nsgtk_treeview *tw = (struct nsgtk_treeview *) g;
@ -381,6 +389,9 @@ gboolean nsgtk_tree_window_keypress_event(GtkWidget *widget, GdkEventKey *event,
GtkAdjustment *scroll = NULL;
gdouble hpage, vpage;
if (gtk_im_context_filter_keypress(tw->input_method, event))
return TRUE;
nskey = gtk_gui_gdkkey_to_nskey(event);
if (tree_keypress(tree, nskey) == true)
@ -473,6 +484,32 @@ gboolean nsgtk_tree_window_keypress_event(GtkWidget *widget, GdkEventKey *event,
return TRUE;
}
static gboolean
nsgtk_tree_window_keyrelease_event(GtkWidget *widget, GdkEventKey *event,
gpointer g)
{
struct nsgtk_treeview *tw = (struct nsgtk_treeview *) g;
return gtk_im_context_filter_keypress(tw->input_method, event);
}
static void
nsgtk_tree_window_input_method_commit(GtkIMContext *ctx,
const gchar *str, gpointer data)
{
struct nsgtk_treeview *tw = (struct nsgtk_treeview *) data;
size_t len = strlen(str), offset = 0;
while (offset < len) {
uint32_t nskey = utf8_to_ucs4(str + offset, len - offset);
tree_keypress(tw->tree, nskey);
offset = utf8_next(str, len, offset);
}
}
static const struct treeview_table nsgtk_tree_callbacks = {
.redraw_request = nsgtk_tree_redraw_request,
.resized = nsgtk_tree_resized,
@ -498,6 +535,7 @@ struct nsgtk_treeview *nsgtk_treeview_create(unsigned int flags,
tv->window = window;
tv->scrolled = scrolled;
tv->drawing_area = drawing_area;
tv->input_method = gtk_im_multicontext_new();
tv->tree = tree_create(flags, &nsgtk_tree_callbacks, tv);
tv->mouse_state = 0;
tv->mouse_pressed = false;
@ -510,17 +548,31 @@ struct nsgtk_treeview *nsgtk_treeview_create(unsigned int flags,
#define CONNECT(obj, sig, callback, ptr) \
g_signal_connect(G_OBJECT(obj), (sig), G_CALLBACK(callback), (ptr))
CONNECT(drawing_area, "button_press_event",
CONNECT(drawing_area, "button-press-event",
nsgtk_tree_window_button_press_event,
tv);
CONNECT(drawing_area, "button_release_event",
CONNECT(drawing_area, "button-release-event",
nsgtk_tree_window_button_release_event,
tv);
CONNECT(drawing_area, "motion_notify_event",
CONNECT(drawing_area, "motion-notify-event",
nsgtk_tree_window_motion_notify_event,
tv);
CONNECT(drawing_area, "key_press_event",
CONNECT(drawing_area, "key-press-event",
nsgtk_tree_window_keypress_event,
tv);
CONNECT(drawing_area, "key-release-event",
nsgtk_tree_window_keyrelease_event,
tv);
/* input method */
gtk_im_context_set_client_window(tv->input_method,
nsgtk_widget_get_window(GTK_WIDGET(tv->window)));
gtk_im_context_set_use_preedit(tv->input_method, FALSE);
/* input method signals */
CONNECT(tv->input_method, "commit",
nsgtk_tree_window_input_method_commit,
tv);
return tv;
}

View File

@ -33,16 +33,6 @@ void nsgtk_treeview_destroy(struct nsgtk_treeview *tv);
struct tree *nsgtk_treeview_get_tree(struct nsgtk_treeview *tv);
gboolean nsgtk_tree_window_expose_event(GtkWidget *, GdkEventExpose *,
gpointer g);
void nsgtk_tree_window_hide(GtkWidget *widget, gpointer g);
gboolean nsgtk_tree_window_button_press_event(GtkWidget *widget,
GdkEventButton *event, gpointer g);
gboolean nsgtk_tree_window_button_release_event(GtkWidget *widget,
GdkEventButton *event, gpointer g);
gboolean nsgtk_tree_window_motion_notify_event(GtkWidget *widget,
GdkEventMotion *event, gpointer g);
gboolean nsgtk_tree_window_keypress_event(GtkWidget *widget, GdkEventKey *event,
gpointer g);
#endif /*__NSGTK_TREEVIEW_H__*/

View File

@ -27,6 +27,7 @@
#include <gdk-pixbuf/gdk-pixdata.h>
#include "utils/log.h"
#include "utils/utf8.h"
#include "utils/utils.h"
#include "utils/nsoption.h"
#include "content/hlcache.h"
@ -105,6 +106,9 @@ struct gui_window {
/** The icon this window should have */
GdkPixbuf *icon;
/** The input method to use with this window */
GtkIMContext *input_method;
/** list for cleanup */
struct gui_window *next, *prev;
};
@ -315,6 +319,7 @@ static gboolean nsgtk_window_button_press_event(GtkWidget *widget,
{
struct gui_window *g = data;
gtk_im_context_reset(g->input_method);
gtk_widget_grab_focus(GTK_WIDGET(g->layout));
gtk_widget_hide(GTK_WIDGET(nsgtk_scaffolding_history_window(
g->scaffold)->window));
@ -490,7 +495,12 @@ static gboolean nsgtk_window_keypress_event(GtkWidget *widget,
GdkEventKey *event, gpointer data)
{
struct gui_window *g = data;
uint32_t nskey = gtk_gui_gdkkey_to_nskey(event);
uint32_t nskey;
if (gtk_im_context_filter_keypress(g->input_method, event))
return TRUE;
nskey = gtk_gui_gdkkey_to_nskey(event);
if (browser_window_key_press(g->bw, nskey))
return TRUE;
@ -598,6 +608,31 @@ static gboolean nsgtk_window_keypress_event(GtkWidget *widget,
return TRUE;
}
static gboolean nsgtk_window_keyrelease_event(GtkWidget *widget,
GdkEventKey *event, gpointer data)
{
struct gui_window *g = data;
return gtk_im_context_filter_keypress(g->input_method, event);
}
static void nsgtk_window_input_method_commit(GtkIMContext *ctx,
const gchar *str, gpointer data)
{
struct gui_window *g = data;
size_t len = strlen(str), offset = 0;
while (offset < len) {
uint32_t nskey = utf8_to_ucs4(str + offset, len - offset);
browser_window_key_press(g->bw, nskey);
offset = utf8_next(str, len, offset);
}
}
static gboolean nsgtk_window_size_allocate_event(GtkWidget *widget,
GtkAllocation *allocation, gpointer data)
{
@ -658,6 +693,8 @@ static void window_destroy(GtkWidget *widget, gpointer data)
struct gui_window *gw = data;
browser_window_destroy(gw->bw);
g_object_unref(gw->input_method);
}
/* Core interface documented in desktop/gui.h to create a gui_window */
@ -720,6 +757,7 @@ gui_window_create(struct browser_window *bw,
g->layout = GTK_LAYOUT(gtk_builder_get_object(xml, "layout"));
g->status_bar = GTK_LABEL(gtk_builder_get_object(xml, "status_bar"));
g->paned = GTK_PANED(gtk_builder_get_object(xml, "hpaned1"));
g->input_method = gtk_im_multicontext_new();
/* add new gui window to global list (push_top) */
@ -763,6 +801,8 @@ gui_window_create(struct browser_window *bw,
nsgtk_window_button_release_event, g);
CONNECT(g->layout, "key-press-event",
nsgtk_window_keypress_event, g);
CONNECT(g->layout, "key-release-event",
nsgtk_window_keyrelease_event, g);
CONNECT(g->layout, "size-allocate",
nsgtk_window_size_allocate_event, g);
CONNECT(g->layout, "scroll-event",
@ -779,6 +819,14 @@ gui_window_create(struct browser_window *bw,
CONNECT(g->container, "destroy",
window_destroy, g);
/* input method */
gtk_im_context_set_client_window(g->input_method,
nsgtk_layout_get_bin_window(g->layout));
gtk_im_context_set_use_preedit(g->input_method, FALSE);
/* input method signals */
CONNECT(g->input_method, "commit",
nsgtk_window_input_method_commit, g);
/* add the tab container to the scaffold notebook */
switch (temp_open_background) {
case -1: