Input Method support (Win32, MacOS, Linux)

This commit is contained in:
Mirek Fidler 2022-02-26 18:31:33 +01:00
parent 7ec93f4a40
commit 21eebae3db
28 changed files with 523 additions and 52 deletions

View file

@ -130,8 +130,10 @@ gboolean Ctrl::GtkEvent(GtkWidget *widget, GdkEvent *event, gpointer user_data)
switch(event->type) {
case GDK_DELETE:
p->CancelPreedit();
break;
case GDK_FOCUS_CHANGE:
p->CancelPreedit();
if(p) {
if(((GdkEventFocus *)event)->in)
gtk_im_context_focus_in(p->top->im_context);
@ -144,16 +146,19 @@ gboolean Ctrl::GtkEvent(GtkWidget *widget, GdkEvent *event, gpointer user_data)
case GDK_LEAVE_NOTIFY:
break;
case GDK_BUTTON_PRESS:
p->CancelPreedit();
value = DoButtonEvent(event, true);
if(IsNull(value))
return false;
break;
case GDK_2BUTTON_PRESS:
p->CancelPreedit();
value = DoButtonEvent(event, true);
if(IsNull(value))
return false;
break;
case GDK_BUTTON_RELEASE:
p->CancelPreedit();
value = DoButtonEvent(event, false);
if(IsNull(value))
return false;
@ -288,7 +293,7 @@ void Ctrl::AddEvent(gpointer user_data, int type, const Value& value, GdkEvent *
e.count = 1;
e.event = NULL;
#if GTK_CHECK_VERSION(3, 22, 0)
GdkDevice *d = gdk_event_get_source_device(event);
GdkDevice *d = event ? gdk_event_get_source_device(event) : NULL;
if(d && findarg(gdk_device_get_source(d), GDK_SOURCE_PEN, GDK_SOURCE_TOUCHSCREEN) >= 0) {
e.pen = true;
e.pen_barrel = MouseState & GDK_BUTTON3_MASK;
@ -328,6 +333,48 @@ void Ctrl::IMCommit(GtkIMContext *context, gchar *str, gpointer user_data)
AddEvent(user_data, EVENT_TEXT, ToUtf32(str), NULL);
}
void Ctrl::IMLocation(Ctrl *w)
{
if(w && w->HasFocusDeep() && focusCtrl && !IsNull(focusCtrl->GetPreedit())) {
GdkRectangle r;
Rect e = w->GetPreeditScreenRect();
Rect q = w->GetScreenRect();
GdkRectangle gr;
gr.x = LSC(e.left - q.left);
gr.y = LSC(e.top - q.top);
gr.width = LSC(e.GetWidth());
gr.height = LSC(e.GetHeight());
gtk_im_context_set_cursor_location(w->top->im_context, &gr);
}
}
void Ctrl::IMPreedit(GtkIMContext *context, gpointer user_data)
{
GuiLock __;
Ctrl *w = GetTopCtrlFromId((uint32)(uintptr_t)user_data);
if(w && w->HasFocusDeep() && focusCtrl && !IsNull(focusCtrl->GetPreedit())) {
PangoAttrList *attrs;
gchar *str;
gint cursor_pos;
gtk_im_context_get_preedit_string(context, &str, &attrs, &cursor_pos);
WString text = ToUtf32(str);
g_free(str);
pango_attr_list_unref(attrs);
w->ShowPreedit(text, cursor_pos);
IMLocation(w);
}
else
CancelPreedit();
}
void Ctrl::IMPreeditEnd(GtkIMContext *context, gpointer user_data)
{
GuiLock __;
Ctrl *w = GetTopCtrlFromId((uint32)(uintptr_t)user_data);
if(w && w->HasFocusDeep() && focusCtrl && !IsNull(focusCtrl->GetPreedit()))
w->HidePreedit();
}
bool Ctrl::ProcessInvalids()
{
GuiLock __;
@ -367,7 +414,7 @@ bool Ctrl::IsWaitingEvent()
struct ProcStop {
TimeStop tm;
String ev;
~ProcStop() { LOG("* " << ev << " elapsed " << tm); }
};
@ -439,7 +486,7 @@ void Ctrl::Proc()
pen.pressure = CurrentEvent.pen_pressure;
pen.rotation = CurrentEvent.pen_rotation;
pen.tilt = CurrentEvent.pen_tilt;
is_pen_event = CurrentEvent.pen;
auto DoPen = [&](Point p) {
@ -452,7 +499,7 @@ void Ctrl::Proc()
}
else
for(Ctrl *t = q; t; t=q->ChildFromPoint(p)) q = t;
q->Pen(p, pen, GetMouseFlags());
SyncCaret();
Image m = CursorOverride();
@ -464,7 +511,7 @@ void Ctrl::Proc()
findarg(CurrentEvent.type, GDK_MOTION_NOTIFY, GDK_BUTTON_PRESS, GDK_BUTTON_RELEASE) >= 0)
{
pen.action = decode(CurrentEvent.type, GDK_BUTTON_PRESS, PEN_DOWN, GDK_BUTTON_RELEASE, PEN_UP, 0);
DoPen(GetMousePos() - GetScreenRect().TopLeft());
}
#endif
@ -490,7 +537,7 @@ void Ctrl::Proc()
ignoreclick = false;
ignoremouseup = false;
}
if(!ignoreclick) {
bool dbl = msecs(clicktime) < 250;
clicktime = dbl ? clicktime - 1000 : msecs();
@ -609,7 +656,7 @@ void Ctrl::Proc()
kv |= K_CTRL;
if(GetAlt() && kv != K_ALT_KEY)
kv |= K_ALT;
LLOG(GetKeyDesc(kv) << ", pressed: " << pressed << ", count: " << CurrentEvent.count);
DLOG(GetKeyDesc(kv) << ", pressed: " << pressed << ", count: " << CurrentEvent.count);
#ifdef GDK_WINDOWING_X11
if(pressed)
for(int i = 0; i < hotkey.GetCount(); i++) {
@ -619,6 +666,7 @@ void Ctrl::Proc()
}
}
#endif
DDUMPHEX(kv);
DispatchKey(!pressed * K_KEYUP + kv, CurrentEvent.count);
}
break;
@ -693,8 +741,10 @@ bool Ctrl::ProcessEvent0(bool *quit, bool fetch)
Ctrl *w = GetTopCtrlFromId(e.windowid);
FocusSync();
CaptureSync();
if(w)
if(w) {
IMLocation(w);
w->Proc();
}
r = true;
}
if(quit)
@ -752,6 +802,9 @@ void WakeUpGuiThread()
void Ctrl::EventLoop(Ctrl *ctrl)
{
GuiLock __;
cancel_preedit = DoCancelPreedit; // We really need this just once, but whatever..
ASSERT_(IsMainThread(), "EventLoop can only run in the main thread");
ASSERT(LoopLevel == 0 || ctrl);
LoopLevel++;
@ -793,4 +846,4 @@ void Ctrl::GuiSleep(int ms)
}
#endif
#endif