changeset 20706:79930f9362c3 draft

-Change: [Win32] Position the IME composition window at the caret position.
author Michael Lutz <michi@icosahedron.de>
date Sun, 07 Apr 2013 17:10:38 +0200
parents 3b9077f142b8
children 59932ff02821
files src/console_gui.cpp src/misc_gui.cpp src/querystring_gui.h src/video/win32_v.cpp src/window.cpp src/window_func.h src/window_gui.h
diffstat 7 files changed, 88 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- a/src/console_gui.cpp
+++ b/src/console_gui.cpp
@@ -317,6 +317,14 @@
 		}
 	}
 
+	virtual Point GetCaretPosition() const
+	{
+		int delta = min(this->width - this->line_offset - _iconsole_cmdline.pixels - ICON_RIGHT_BORDERWIDTH, 0);
+		Point pt = {this->line_offset + delta + _iconsole_cmdline.caretxoffs, this->height - this->line_height};
+
+		return pt;
+	}
+
 	virtual void OnMouseWheel(int wheel)
 	{
 		this->Scroll(-wheel);
--- a/src/misc_gui.cpp
+++ b/src/misc_gui.cpp
@@ -785,6 +785,34 @@
 	_cur_dpi = old_dpi;
 }
 
+/**
+ * Get the current caret position.
+ * @param w Window the edit box is in.
+ * @param wid Widget index.
+ * @return Top-left location of the caret, relative to the window.
+ */
+Point QueryString::GetCaretPosition(const Window *w, int wid) const
+{
+	const NWidgetLeaf *wi = w->GetWidget<NWidgetLeaf>(wid);
+
+	assert((wi->type & WWT_MASK) == WWT_EDITBOX);
+
+	bool rtl = _current_text_dir == TD_RTL;
+	Dimension sprite_size = GetSpriteSize(rtl ? SPR_IMG_DELETE_RIGHT : SPR_IMG_DELETE_LEFT);
+	int clearbtn_width = sprite_size.width + WD_IMGBTN_LEFT + WD_IMGBTN_RIGHT;
+
+	int left   = wi->pos_x + (rtl ? clearbtn_width : 0);
+	int right  = wi->pos_x + (rtl ? wi->current_x : wi->current_x - clearbtn_width) - 1;
+
+	/* Clamp caret position to be inside out current width. */
+	const Textbuf *tb = &this->text;
+	int delta = min(0, (right - left) - tb->pixels - 10);
+	if (tb->caretxoffs + delta < 0) delta = -tb->caretxoffs;
+
+	Point pt = {left + WD_FRAMERECT_LEFT + tb->caretxoffs + delta, wi->pos_y + WD_FRAMERECT_TOP};
+	return pt;
+}
+
 void QueryString::ClickEditBox(Window *w, Point pt, int wid, int click_count, bool focus_changed)
 {
 	const NWidgetLeaf *wi = w->GetWidget<NWidgetLeaf>(wid);
--- a/src/querystring_gui.h
+++ b/src/querystring_gui.h
@@ -53,6 +53,8 @@
 	void DrawEditBox(const Window *w, int wid) const;
 	void ClickEditBox(Window *w, Point pt, int wid, int click_count, bool focus_changed);
 	void HandleEditBox(Window *w, int wid);
+
+	Point GetCaretPosition(const Window *w, int wid) const;
 };
 
 void ShowOnScreenKeyboard(Window *parent, int button);
--- a/src/video/win32_v.cpp
+++ b/src/video/win32_v.cpp
@@ -21,6 +21,7 @@
 #include "../texteff.hpp"
 #include "../thread/thread.h"
 #include "../progress.h"
+#include "../window_gui.h"
 #include "../window_func.h"
 #include "win32_v.h"
 #include <windows.h>
@@ -494,6 +495,28 @@
 }
 
 #if !defined(WINCE) || _WIN32_WCE >= 0x400
+/** Set position of the composition window to the caret position. */
+static void SetCompositionPos(HWND hwnd)
+{
+	HIMC hIMC = ImmGetContext(hwnd);
+	if (hIMC != NULL) {
+		COMPOSITIONFORM cf;
+		cf.dwStyle = CFS_POINT;
+
+		if (EditBoxInGlobalFocus()) {
+			/* Get caret position. */
+			Point pt = _focused_window->GetCaretPosition();
+			cf.ptCurrentPos.x = _focused_window->left + pt.x;
+			cf.ptCurrentPos.y = _focused_window->top  + pt.y;
+		} else {
+			cf.ptCurrentPos.x = 0;
+			cf.ptCurrentPos.y = 0;
+		}
+		ImmSetCompositionWindow(hIMC, &cf);
+	}
+	ImmReleaseContext(hwnd, hIMC);
+}
+
 /** Handle WM_IME_COMPOSITION messages. */
 static LRESULT HandleIMEComposition(HWND hwnd, WPARAM wParam, LPARAM lParam)
 {
@@ -509,6 +532,7 @@
 
 			/* Transmit text to windowing system. */
 			if (len > 0) HandleTextInput(FS2OTTD(str));
+			SetCompositionPos(hwnd);
 
 			/* Don't pass the result string on to the default window proc. */
 			lParam &= ~(GCS_RESULTSTR | GCS_RESULTCLAUSE | GCS_RESULTREADCLAUSE | GCS_RESULTREADSTR);
@@ -529,6 +553,7 @@
 
 #else
 
+static void SetCompositionPos(HWND hwnd) {}
 static void CancelIMEComposition(HWND hwnd) {}
 
 #endif /* !defined(WINCE) || _WIN32_WCE >= 0x400 */
@@ -542,6 +567,7 @@
 	switch (msg) {
 		case WM_CREATE:
 			SetTimer(hwnd, TID_POLLMOUSE, MOUSE_POLL_DELAY, (TIMERPROC)TrackMouseTimerProc);
+			SetCompositionPos(hwnd);
 			break;
 
 		case WM_ENTERSIZEMOVE:
@@ -668,6 +694,10 @@
 		}
 
 #if !defined(WINCE) || _WIN32_WCE >= 0x400
+		case WM_IME_STARTCOMPOSITION:
+			SetCompositionPos(hwnd);
+			break;
+
 		case WM_IME_COMPOSITION:
 			return HandleIMEComposition(hwnd, wParam, lParam);
 
@@ -855,6 +885,7 @@
 
 		case WM_SETFOCUS:
 			_wnd.has_focus = true;
+			SetCompositionPos(hwnd);
 			break;
 
 		case WM_KILLFOCUS:
@@ -1214,4 +1245,5 @@
 void VideoDriver_Win32::EditBoxLostFocus()
 {
 	CancelIMEComposition(_wnd.main_wnd);
+	SetCompositionPos(_wnd.main_wnd);
 }
--- a/src/window.cpp
+++ b/src/window.cpp
@@ -323,6 +323,20 @@
 	return query != this->querystrings.End() ? query->second : NULL;
 }
 
+/**
+ * Get the current caret position if an edit box has the focus.
+ * @return Top-left location of the caret, relative to the window.
+ */
+/* virtual */ Point Window::GetCaretPosition() const
+{
+	if (this->nested_focus != NULL && this->nested_focus->type == WWT_EDITBOX) {
+		return this->GetQueryString(this->nested_focus->index)->GetCaretPosition(this, this->nested_focus->index);
+	}
+
+	Point pt = {0, 0};
+	return pt;
+}
+
 
 /**
  * Set the window that has the focus
--- a/src/window_func.h
+++ b/src/window_func.h
@@ -14,6 +14,7 @@
 
 #include "window_type.h"
 #include "company_type.h"
+#include "core/geometry_type.hpp"
 
 Window *FindWindowById(WindowClass cls, WindowNumber number);
 Window *FindWindowByClass(WindowClass cls);
@@ -53,5 +54,6 @@
 void DeleteWindowByClass(WindowClass cls);
 
 bool EditBoxInGlobalFocus();
+Point GetCaretPosition();
 
 #endif /* WINDOW_FUNC_H */
--- a/src/window_gui.h
+++ b/src/window_gui.h
@@ -345,6 +345,8 @@
 	const QueryString *GetQueryString(uint widnum) const;
 	QueryString *GetQueryString(uint widnum);
 
+	virtual Point GetCaretPosition() const;
+
 	void InitNested(WindowNumber number = 0);
 	void CreateNestedTree(bool fill_nested = true);
 	void FinishInitNested(WindowNumber window_number = 0);