changeset 20715:db681959a377 draft

-Change: [OSX] Position the candidate window at the caret position.
author Michael Lutz <michi@icosahedron.de>
date Fri, 26 Jul 2013 01:44:23 +0200
parents fdf6e212a787
children 42bd8405a50c
files src/console_gui.cpp src/misc_gui.cpp src/querystring_gui.h src/video/cocoa/cocoa_v.mm src/window.cpp src/window_gui.h
diffstat 6 files changed, 94 insertions(+), 2 deletions(-) [+]
line wrap: on
line diff
--- a/src/console_gui.cpp
+++ b/src/console_gui.cpp
@@ -346,6 +346,17 @@
 		return pt;
 	}
 
+	virtual Rect GetTextBoundingRect(const char *from, const char *to) const
+	{
+		int delta = min(this->width - this->line_offset - _iconsole_cmdline.pixels - ICON_RIGHT_BORDERWIDTH, 0);
+
+		Point p1 = GetCharPosInString(_iconsole_cmdline.buf, from, FS_NORMAL);
+		Point p2 = from != to ? GetCharPosInString(_iconsole_cmdline.buf, from, FS_NORMAL) : p1;
+
+		Rect r = {this->line_offset + delta + p1.x, this->height - this->line_height, this->line_offset + delta + p2.x, this->height};
+		return r;
+	}
+
 	virtual void OnMouseWheel(int wheel)
 	{
 		this->Scroll(-wheel);
--- a/src/misc_gui.cpp
+++ b/src/misc_gui.cpp
@@ -816,6 +816,44 @@
 	return pt;
 }
 
+/**
+ * Get the bounding rectangle for a range of the query string.
+ * @param w Window the edit box is in.
+ * @param wid Widget index.
+ * @param from Start of the string range.
+ * @param to End of the string range.
+ * @return Rectangle encompassing the string range, relative to the window.
+ */
+Rect QueryString::GetBoundingRect(const Window *w, int wid, const char *from, const char *to) 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;
+
+	int top    = wi->pos_y + WD_FRAMERECT_TOP;
+	int bottom = wi->pos_y + wi->current_y - 1 - WD_FRAMERECT_BOTTOM;
+
+	/* Clamp caret position to be inside our current width. */
+	const Textbuf *tb = &this->text;
+	int delta = min(0, (right - left) - tb->pixels - 10);
+	if (tb->caretxoffs + delta < 0) delta = -tb->caretxoffs;
+
+	/* Get location of first and last character. */
+	Point p1 = GetCharPosInString(tb->buf, from, FS_NORMAL);
+	Point p2 = from != to ? GetCharPosInString(tb->buf, to, FS_NORMAL) : p1;
+
+	Rect r = { Clamp(left + p1.x + delta + WD_FRAMERECT_LEFT, left, right), top, Clamp(left + p2.x + delta + WD_FRAMERECT_LEFT, left, right - WD_FRAMERECT_RIGHT), bottom };
+
+	return r;
+}
+
 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
@@ -55,6 +55,7 @@
 	void HandleEditBox(Window *w, int wid);
 
 	Point GetCaretPosition(const Window *w, int wid) const;
+	Rect GetBoundingRect(const Window *w, int wid, const char *from, const char *to) const;
 
 	/**
 	 * Get the current text.
--- a/src/video/cocoa/cocoa_v.mm
+++ b/src/video/cocoa/cocoa_v.mm
@@ -896,7 +896,14 @@
 /** Get a string corresponding to the given range. */
 - (NSAttributedString *)attributedSubstringFromRange:(NSRange)theRange
 {
-	return nil;
+	if (!EditBoxInGlobalFocus()) return nil;
+
+	NSString *s = [ NSString stringWithUTF8String:_focused_window->GetFocusedText() ];
+	NSRange valid_range = NSIntersectionRange(NSMakeRange(0, [ s length ]), theRange);
+
+	if (valid_range.length == 0) return nil;
+
+	return [ [ [ NSAttributedString alloc ] initWithString:[ s substringWithRange:valid_range ] ] autorelease ];
 }
 
 /** Get the character that is rendered at the given point. */
@@ -908,7 +915,25 @@
 /** Get the bounding rect for the given range. */
 - (NSRect)firstRectForCharacterRange:(NSRange)aRange
 {
-	return NSMakeRect(0, 0, 0, 0);
+	if (!EditBoxInGlobalFocus()) return NSMakeRect(0, 0, 0, 0);
+
+	/* Convert range to UTF-8 string pointers. */
+	const char *start = Utf8AdvanceByUtf16Units(_focused_window->GetFocusedText(), aRange.location);
+	const char *end = aRange.length != 0 ? Utf8AdvanceByUtf16Units(_focused_window->GetFocusedText(), aRange.location + aRange.length) : start;
+
+	/* Get the bounding rect for the text range.*/
+	Rect r = _focused_window->GetTextBoundingRect(start, end);
+	NSRect view_rect = NSMakeRect(_focused_window->left + r.left, [ self frame ].size.height - _focused_window->top - r.bottom, r.right - r.left, r.bottom - r.top);
+
+#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7
+	if ([ [ self window ] respondsToSelector:@selector(convertRectToScreen:) ]) {
+		return [ [ self window ] convertRectToScreen:[ self convertRect:view_rect toView:nil ] ];
+	}
+#endif
+
+	NSRect window_rect = [ self convertRect:view_rect toView:nil ];
+	NSPoint origin = [ [ self window ] convertBaseToScreen:window_rect.origin ];
+	return NSMakeRect(origin.x, origin.y, window_rect.size.width, window_rect.size.height);
 }
 
 /** Get all string attributes that we can process for marked text. */
--- a/src/window.cpp
+++ b/src/window.cpp
@@ -377,6 +377,22 @@
 	return pt;
 }
 
+/**
+ * Get the bounding rectangle for a text range if an edit box has the focus.
+ * @param from Start of the string range.
+ * @param to End of the string range.
+ * @return Rectangle encompassing the string range, relative to the window.
+ */
+/* virtual */ Rect Window::GetTextBoundingRect(const char *from, const char *to) const
+{
+	if (this->nested_focus != NULL && this->nested_focus->type == WWT_EDITBOX) {
+		return this->GetQueryString(this->nested_focus->index)->GetBoundingRect(this, this->nested_focus->index, from, to);
+	}
+
+	Rect r = {0, 0, 0, 0};
+	return r;
+}
+
 
 /**
  * Set the window that has the focus
--- a/src/window_gui.h
+++ b/src/window_gui.h
@@ -349,6 +349,7 @@
 	virtual const char *GetCaret() const;
 	virtual const char *GetMarkedText(size_t *length) const;
 	virtual Point GetCaretPosition() const;
+	virtual Rect GetTextBoundingRect(const char *from, const char *to) const;
 
 	void InitNested(WindowNumber number = 0);
 	void CreateNestedTree(bool fill_nested = true);