changeset 11081:0a26df8849a2 draft

(svn r15424) -Codechange: make it possible to have multiple windows with edit box open simultaniously (Zuu).
author rubidium <rubidium@openttd.org>
date Mon, 09 Feb 2009 01:22:29 +0000
parents 02d314ea4f63
children 9ec1f7eac0a0
files src/console_gui.cpp src/genworld_gui.cpp src/misc_gui.cpp src/network/network_chat_gui.cpp src/network/network_content_gui.cpp src/network/network_gui.cpp src/osk_gui.cpp src/querystring_gui.h src/signs_gui.cpp src/statusbar_gui.cpp src/toolbar_gui.cpp src/window.cpp src/window_gui.h
diffstat 13 files changed, 232 insertions(+), 55 deletions(-) [+]
line wrap: on
line diff
--- a/src/console_gui.cpp
+++ b/src/console_gui.cpp
@@ -150,7 +150,6 @@
 	IConsoleWindow(const WindowDesc *desc) : Window(desc)
 	{
 		_iconsole_mode = ICONSOLE_OPENED;
-		_no_scroll++; // override cursor arrows; the gamefield will not scroll
 
 		this->height = _screen.height / 3;
 		this->width  = _screen.width;
@@ -159,7 +158,6 @@
 	~IConsoleWindow()
 	{
 		_iconsole_mode = ICONSOLE_CLOSED;
-		_no_scroll--;
 	}
 
 	virtual void OnPaint()
@@ -180,7 +178,7 @@
 
 		DoDrawString(_iconsole_cmdline.buf, 10 + delta, this->height - ICON_LINE_HEIGHT, CC_COMMAND);
 
-		if (_iconsole_cmdline.caret) {
+		if (_focused_window == this && _iconsole_cmdline.caret) {
 			DoDrawString("_", 10 + delta + _iconsole_cmdline.caretxoffs, this->height - ICON_LINE_HEIGHT, TC_WHITE);
 		}
 	}
@@ -201,6 +199,8 @@
 
 	virtual EventState OnKeyPress(uint16 key, uint16 keycode)
 	{
+		if (_focused_window != this) return ES_NOT_HANDLED;
+
 		const int scroll_height = (this->height / ICON_LINE_HEIGHT) - 1;
 		switch (keycode) {
 			case WKC_UP:
--- a/src/genworld_gui.cpp
+++ b/src/genworld_gui.cpp
@@ -302,6 +302,7 @@
 		/* snprintf() always outputs trailing '\0', so whole buffer can be used */
 		snprintf(this->edit_str_buf, this->edit_str_size, "%u", _settings_newgame.game_creation.generation_seed);
 		InitializeTextBuffer(&this->text, this->edit_str_buf, this->edit_str_size, 120);
+		this->SetFocusedWidget(GLAND_RANDOM_EDITBOX);
 		this->caption = STR_NULL;
 		this->afilter = CS_NUMERAL;
 
--- a/src/misc_gui.cpp
+++ b/src/misc_gui.cpp
@@ -951,8 +951,19 @@
 	return false;
 }
 
+bool QueryString::HasEditBoxFocus(const Window *w, int wid) const
+{
+	return ((w->window_class == WC_OSK &&
+			_focused_window == w->parent &&
+			w->parent->focused_widget &&
+			w->parent->focused_widget->type == WWT_EDITBOX) ||
+			w->IsWidgetGloballyFocused(wid));
+}
+
 HandleEditBoxResult QueryString::HandleEditBoxKey(Window *w, int wid, uint16 key, uint16 keycode, Window::EventState &state)
 {
+	if (!QueryString::HasEditBoxFocus(w, wid)) return HEBR_NOT_FOCUSED;
+
 	state = Window::ES_HANDLED;
 
 	switch (keycode) {
@@ -990,7 +1001,7 @@
 
 void QueryString::HandleEditBox(Window *w, int wid)
 {
-	if (HandleCaret(&this->text)) {
+	if (HasEditBoxFocus(w, wid) && HandleCaret(&this->text)) {
 		w->InvalidateWidget(wid);
 		/* When we're not the OSK, notify 'our' OSK to redraw the widget,
 		 * so the caret changes appropriately. */
@@ -1034,7 +1045,7 @@
 	if (tb->caretxoffs + delta < 0) delta = -tb->caretxoffs;
 
 	DoDrawString(tb->buf, delta, 0, TC_YELLOW);
-	if (tb->caret) DoDrawString("_", tb->caretxoffs + delta, 0, TC_WHITE);
+	if (HasEditBoxFocus(w, wid) && tb->caret) DoDrawString("_", tb->caretxoffs + delta, 0, TC_WHITE);
 
 	_cur_dpi = old_dpi;
 }
@@ -1072,6 +1083,7 @@
 	QueryStringWindow(uint16 size, const WindowDesc *desc, Window *parent) : QueryStringBaseWindow(size, desc)
 	{
 		this->parent = parent;
+		this->SetFocusedWidget(QUERY_STR_WIDGET_TEXT);
 
 		this->FindWindowPlacementAndResize(desc);
 	}
@@ -1130,6 +1142,7 @@
 			case HEBR_CONFIRM: this->OnOk();
 			/* FALL THROUGH */
 			case HEBR_CANCEL: delete this; break; // close window, abandon changes
+			case HEBR_NOT_FOCUSED: break;
 		}
 		return state;
 	}
@@ -1180,7 +1193,6 @@
 void ShowQueryString(StringID str, StringID caption, uint maxsize, uint maxwidth, Window *parent, CharSetFilter afilter, QueryStringFlags flags)
 {
 	DeleteWindowById(WC_QUERY_STRING, 0);
-	DeleteWindowById(WC_SAVELOAD, 0);
 
 	QueryStringWindow *w = new QueryStringWindow(maxsize, &_query_string_desc, parent);
 
@@ -1500,6 +1512,11 @@
 				strecpy(o_dir.name, _personal_dir, lastof(o_dir.name));
 		}
 
+		/* Focus the edit box by default in the save windows */
+		if (_saveload_mode == SLD_SAVE_GAME || _saveload_mode == SLD_SAVE_SCENARIO) {
+			this->SetFocusedWidget(SLWW_SAVE_OSK_TITLE);
+		}
+
 		this->vscroll.cap = 10;
 		this->resize.step_width = 2;
 		this->resize.step_height = 10;
@@ -1708,7 +1725,6 @@
 
 void ShowSaveLoadDialog(SaveLoadDialogMode mode)
 {
-	DeleteWindowById(WC_QUERY_STRING, 0);
 	DeleteWindowById(WC_SAVELOAD, 0);
 
 	const WindowDesc *sld;
--- a/src/network/network_chat_gui.cpp
+++ b/src/network/network_chat_gui.cpp
@@ -278,6 +278,7 @@
 		this->dest    = dest;
 		this->afilter = CS_ALPHANUMERAL;
 		InitializeTextBuffer(&this->text, this->edit_str_buf, this->edit_str_size, 0);
+		this->SetFocusedWidget(NWCW_TEXTBOX);
 
 		InvalidateWindowData(WC_NEWS_WINDOW, 0, this->height);
 
@@ -480,6 +481,7 @@
 					SendChat(this->text.buf, this->dtype, this->dest);
 				/* FALLTHROUGH */
 				case HEBR_CANCEL: delete this; break;
+				case HEBR_NOT_FOCUSED: break;
 			}
 		}
 		return state;
--- a/src/network/network_content_gui.cpp
+++ b/src/network/network_content_gui.cpp
@@ -317,6 +317,7 @@
 		ttd_strlcpy(this->edit_str_buf, "", this->edit_str_size);
 		this->afilter = CS_ALPHANUMERAL;
 		InitializeTextBuffer(&this->text, this->edit_str_buf, this->edit_str_size, EDITBOX_MAX_LENGTH);
+		this->SetFocusedWidget(NCLWW_FILTER);
 
 		this->vscroll.cap = 14;
 		this->resize.step_height = 14;
--- a/src/network/network_gui.cpp
+++ b/src/network/network_gui.cpp
@@ -308,6 +308,7 @@
 		ttd_strlcpy(this->edit_str_buf, _settings_client.network.client_name, this->edit_str_size);
 		this->afilter = CS_ALPHANUMERAL;
 		InitializeTextBuffer(&this->text, this->edit_str_buf, this->edit_str_size, 120);
+		this->SetFocusedWidget(NGWW_CLIENT);
 
 		UpdateNetworkGameWindow(true);
 
@@ -879,6 +880,7 @@
 
 		this->afilter = CS_ALPHANUMERAL;
 		InitializeTextBuffer(&this->text, this->edit_str_buf, this->edit_str_size, 160);
+		this->SetFocusedWidget(NSSW_GAMENAME);
 
 		this->field = NSSW_GAMENAME;
 
@@ -1882,6 +1884,7 @@
 		this->parent = parent;
 		this->afilter = CS_ALPHANUMERAL;
 		InitializeTextBuffer(&this->text, this->edit_str_buf, this->edit_str_size, 0);
+		this->SetFocusedWidget(NCPWW_PASSWORD);
 
 		this->FindWindowPlacementAndResize(desc);
 	}
--- a/src/osk_gui.cpp
+++ b/src/osk_gui.cpp
@@ -131,6 +131,21 @@
 		}
 
 		switch (widget) {
+			case OSK_WIDGET_TEXT:
+				/* Find the edit box of the parent window and give focus to that */
+				for (uint i = 0; i < this->parent->widget_count; i++) {
+					Widget &wi = this->parent->widget[i];
+					if (wi.type == WWT_EDITBOX) {
+						this->parent->focused_widget = &wi;
+						break;
+					}
+				}
+
+				/* Give focus to parent window */
+				SetFocusedWindow(this->parent);
+
+				break;
+
 			case OSK_WIDGET_BACKSPACE:
 				if (DeleteTextBufferChar(&this->qs->text, WKC_BACKSPACE)) this->InvalidateParent();
 				break;
--- a/src/querystring_gui.h
+++ b/src/querystring_gui.h
@@ -16,6 +16,7 @@
 	HEBR_EDITING = 0, // Other key pressed.
 	HEBR_CONFIRM,     // Return or enter key pressed.
 	HEBR_CANCEL,      // Escape key pressed.
+	HEBR_NOT_FOCUSED, // Edit box widget not focused.
 };
 
 /**
@@ -43,6 +44,7 @@
 		free((void*)this->orig);
 	}
 
+	bool HasEditBoxFocus(const Window *w, int wid) const;
 	void DrawEditBox(Window *w, int wid);
 	void HandleEditBox(Window *w, int wid);
 	HandleEditBoxResult HandleEditBoxKey(Window *w, int wid, uint16 key, uint16 keycode, Window::EventState &state);
--- a/src/signs_gui.cpp
+++ b/src/signs_gui.cpp
@@ -196,6 +196,7 @@
 		this->LowerWidget(QUERY_EDIT_SIGN_WIDGET_TEXT);
 
 		UpdateSignEditWindow(si);
+		this->SetFocusedWidget(QUERY_EDIT_SIGN_WIDGET_TEXT);
 		this->FindWindowPlacementAndResize(desc);
 	}
 
@@ -216,6 +217,7 @@
 		InitializeTextBuffer(&this->text, this->edit_str_buf, this->edit_str_size, MAX_LENGTH_SIGN_NAME_PIXELS);
 
 		this->InvalidateWidget(QUERY_EDIT_SIGN_WIDGET_TEXT);
+		this->SetFocusedWidget(QUERY_EDIT_SIGN_WIDGET_TEXT);
 	}
 
 	/**
@@ -347,9 +349,8 @@
 
 void ShowRenameSignWindow(const Sign *si)
 {
-	/* Delete all other edit windows and the save window */
+	/* Delete all other edit windows */
 	DeleteWindowById(WC_QUERY_STRING, 0);
-	DeleteWindowById(WC_SAVELOAD, 0);
 
 	new SignWindow(&_query_sign_edit_desc, si);
 }
--- a/src/statusbar_gui.cpp
+++ b/src/statusbar_gui.cpp
@@ -185,7 +185,7 @@
 static WindowDesc _main_status_desc = {
 	WDP_CENTER, 0, 320, 12, 640, 12,
 	WC_STATUS_BAR, WC_NONE,
-	WDF_STD_TOOLTIPS | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS,
+	WDF_STD_TOOLTIPS | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_NO_FOCUS,
 	_main_status_widgets,
 };
 
--- a/src/toolbar_gui.cpp
+++ b/src/toolbar_gui.cpp
@@ -1184,7 +1184,7 @@
 static const WindowDesc _toolb_normal_desc = {
 	0, 0, 0, TBP_BUTTONHEIGHT, 640, TBP_BUTTONHEIGHT,
 	WC_MAIN_TOOLBAR, WC_NONE,
-	WDF_STD_TOOLTIPS | WDF_DEF_WIDGET,
+	WDF_STD_TOOLTIPS | WDF_DEF_WIDGET | WDF_NO_FOCUS,
 	_toolb_normal_widgets,
 };
 
@@ -1437,7 +1437,7 @@
 static const WindowDesc _toolb_scen_desc = {
 	0, 0, 130, TBP_BUTTONHEIGHT, 640, TBP_BUTTONHEIGHT,
 	WC_MAIN_TOOLBAR, WC_NONE,
-	WDF_STD_TOOLTIPS | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS,
+	WDF_STD_TOOLTIPS | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_NO_FOCUS,
 	_toolb_scen_widgets,
 };
 
--- a/src/window.cpp
+++ b/src/window.cpp
@@ -34,7 +34,13 @@
 /** List of windows opened at the screen sorted from the back. */
 Window *_z_back_window  = NULL;
 
-byte _no_scroll;
+/*
+ * Window that currently have focus. - The main purpose is to generate
+ * FocusLost events, not to give next window in z-order focus when a
+ * window is closed.
+ */
+Window *_focused_window;
+
 Point _cursorpos_drag_start;
 
 int _scrollbar_start_pos;
@@ -46,6 +52,51 @@
 
 byte _special_mouse_mode;
 
+/**
+ * Set the window that has the focus
+ * @param w The window to set the focus on
+ */
+void SetFocusedWindow(Window *w)
+{
+	if (_focused_window == w) return;
+
+	/* Invalidate focused widget */
+	if (_focused_window != NULL && _focused_window->focused_widget != NULL) {
+		uint focused_widget_id = _focused_window->focused_widget - _focused_window->widget;
+		_focused_window->InvalidateWidget(focused_widget_id);
+	}
+
+	/* Remember which window was previously focused */
+	Window *old_focused = _focused_window;
+	_focused_window = w;
+
+	/* So we can inform it that it lost focus */
+	if (old_focused != NULL) old_focused->OnFocusLost();
+	if (_focused_window != NULL) _focused_window->OnFocus();
+}
+
+/**
+ * Gets the globally focused widget. Which is the focused widget of the focused window.
+ * @return A pointer to the globally focused Widget, or NULL if there is no globally focused widget.
+ */
+const Widget *GetGloballyFocusedWidget()
+{
+	return _focused_window != NULL ? _focused_window->focused_widget : NULL;
+}
+
+/**
+ * Check if an edit box is in global focus. That is if focused window
+ * has a edit box as focused widget, or if a console is focused.
+ * @return returns true if an edit box is in global focus or if the focused window is a console, else false
+ */
+bool EditBoxInGlobalFocus()
+{
+	const Widget *wi = GetGloballyFocusedWidget();
+
+	/* The console does not have an edit box so a special case is needed. */
+	return (wi != NULL && wi->type == WWT_EDITBOX) ||
+			(_focused_window != NULL && _focused_window->window_class == WC_CONSOLE);
+}
 
 /**
  * Sets the enabled/disabled status of a list of widgets.
@@ -147,6 +198,18 @@
 	this->InvalidateWidget(widget);
 }
 
+/**
+ * Checks if the window has at least one widget of given type
+ * @param widget_type the widget type to look for
+ */
+bool Window::HasWidgetOfType(WidgetType widget_type) const
+{
+	for (uint i = 0; i < this->widget_count; i++) {
+		if (this->widget[i].type == widget_type) return true;
+	}
+	return false;
+}
+
 static void StartWindowDrag(Window *w);
 static void StartWindowSizing(Window *w);
 
@@ -159,9 +222,26 @@
  */
 static void DispatchLeftClickEvent(Window *w, int x, int y, bool double_click)
 {
+	bool focused_widget_changed = false;
 	int widget = 0;
 	if (w->desc_flags & WDF_DEF_WIDGET) {
 		widget = GetWidgetFromPos(w, x, y);
+
+		/* If clicked on a window that previously did dot have focus */
+		if (_focused_window != w &&
+				(w->desc_flags & WDF_NO_FOCUS) == 0 &&           // Don't lose focus to toolbars
+				!(w->desc_flags & WDF_STD_BTN && widget == 0)) { // Don't change focused window if 'X' (close button) was clicked
+			focused_widget_changed = true;
+			if (_focused_window != NULL) {
+				_focused_window->OnFocusLost();
+
+				/* The window that lost focus may have had opened a OSK, window so close it, unless the user has clicked on the OSK window. */
+				if (w->window_class != WC_OSK) DeleteWindowById(WC_OSK, 0);
+			}
+			SetFocusedWindow(w);
+			w->OnFocus();
+		}
+
 		if (widget < 0) return; // exit if clicked outside of widgets
 
 		/* don't allow any interaction if the button has been disabled */
@@ -169,6 +249,24 @@
 
 		const Widget *wi = &w->widget[widget];
 
+		/* Clicked on a widget that is not disabled.
+		 * So unless the clicked widget is the caption bar, change focus to this widget */
+		if (wi->type != WWT_CAPTION) {
+			/* Close the OSK window if a edit box loses focus */
+			if (w->focused_widget && w->focused_widget->type == WWT_EDITBOX && // An edit box was previously selected
+					w->focused_widget != wi &&                                 // and focus is going to change
+					w->window_class != WC_OSK) {                               // and it is not the OSK window
+				DeleteWindowById(WC_OSK, 0);
+			}
+
+			if (w->focused_widget != wi) {
+				/* Repaint the widget that loss focus. A focused edit box may else leave the caret left on the screen */
+				if (w->focused_widget) w->InvalidateWidget(w->focused_widget - w->widget);
+				focused_widget_changed = true;
+				w->focused_widget = wi;
+			}
+		}
+
 		if (wi->type & WWB_MASK) {
 			/* special widget handling for buttons*/
 			switch (wi->type) {
@@ -181,7 +279,7 @@
 			}
 		} else if (wi->type == WWT_SCROLLBAR || wi->type == WWT_SCROLL2BAR || wi->type == WWT_HSCROLLBAR) {
 			ScrollbarClickHandler(w, wi, x, y);
-		} else if (wi->type == WWT_EDITBOX) {
+		} else if (wi->type == WWT_EDITBOX && !focused_widget_changed) { // Only open the OSK window if clicking on an already focused edit box
 			/* Open the OSK window if clicked on an edit box */
 			QueryStringBaseWindow *qs = dynamic_cast<QueryStringBaseWindow*>(w);
 			if (qs != NULL) {
@@ -431,18 +529,16 @@
 	/* Prevent Mouseover() from resetting mouse-over coordinates on a non-existing window */
 	if (_mouseover_last_w == this) _mouseover_last_w = NULL;
 
+	/* Make sure we don't try to access this window as the focused window when it don't exist anymore. */
+	if (_focused_window == this) _focused_window = NULL;
+
 	this->DeleteChildWindows();
 
 	if (this->viewport != NULL) DeleteWindowViewport(this);
 
 	this->SetDirty();
 
-	if (this->widget != NULL) {
-		for (const Widget *wi = this->widget; wi->type != WWT_LAST; wi++) {
-			if (wi->type == WWT_EDITBOX) _no_scroll--;
-		}
-		free(this->widget);
-	}
+	free(this->widget);
 
 	this->window_class = WC_INVALID;
 }
@@ -646,10 +742,6 @@
 		w->widget = MallocT<Widget>(index);
 		memcpy(w->widget, widget, sizeof(*w->widget) * index);
 		w->widget_count = index - 1;
-
-		for (const Widget *wi = w->widget; wi->type != WWT_LAST; wi++) {
-			if (wi->type == WWT_EDITBOX) _no_scroll++;
-		}
 	} else {
 		w->widget = NULL;
 		w->widget_count = 0;
@@ -682,12 +774,18 @@
 	this->width = min_width;
 	this->height = min_height;
 	AssignWidgetToWindow(this, widget);
+	this->focused_widget = 0;
 	this->resize.width = min_width;
 	this->resize.height = min_height;
 	this->resize.step_width = 1;
 	this->resize.step_height = 1;
 	this->window_number = window_number;
 
+	/* Give focus to the opened window unless it is the OSK window or a text box
+	 * of focused window has focus (so we don't interrupt typing). But if the new
+	 * window has a text box, then take focus anyway. */
+	if (this->window_class != WC_OSK && (!EditBoxInGlobalFocus() || this->HasWidgetOfType(WWT_EDITBOX))) SetFocusedWindow(this);
+
 	/* Hacky way of specifying always-on-top windows. These windows are
 		* always above other windows because they are moved below them.
 		* status-bar is above news-window because it has been created earlier.
@@ -1072,8 +1170,8 @@
 
 	_z_back_window = NULL;
 	_z_front_window = NULL;
+	_focused_window = NULL;
 	_mouseover_last_w = NULL;
-	_no_scroll = 0;
 	_scrolling_viewport = 0;
 }
 
@@ -1620,11 +1718,6 @@
  */
 void HandleKeypress(uint32 raw_key)
 {
-	/* Stores if a window with a textfield for typing is open
-	 * If this is the case, keypress events are only passed to windows with text fields and
-	 * to thein this main toolbar. */
-	bool query_open = false;
-
 	/*
 	* During the generation of the world, there might be
 	* another thread that is currently building for example
@@ -1654,29 +1747,16 @@
 	 */
 	if (key == 0 && keycode == 0) return;
 
-	/* check if we have a query string window open before allowing hotkeys */
-	if (FindWindowById(WC_QUERY_STRING,            0) != NULL ||
-			FindWindowById(WC_SEND_NETWORK_MSG,        0) != NULL ||
-			FindWindowById(WC_GENERATE_LANDSCAPE,      0) != NULL ||
-			FindWindowById(WC_CONSOLE,                 0) != NULL ||
-			FindWindowById(WC_SAVELOAD,                0) != NULL ||
-			FindWindowById(WC_COMPANY_PASSWORD_WINDOW, 0) != NULL) {
-		query_open = true;
+	/* Check if the focused window has a focused editbox */
+	if (EditBoxInGlobalFocus()) {
+		/* All input will in this case go to the focused window */
+		_focused_window->OnKeyPress(key, keycode);
+		return;
 	}
 
 	/* Call the event, start with the uppermost window. */
 	Window *w;
 	FOR_ALL_WINDOWS_FROM_FRONT(w) {
-		/* if a query window is open, only call the event for certain window types */
-		if (query_open &&
-				w->window_class != WC_QUERY_STRING &&
-				w->window_class != WC_SEND_NETWORK_MSG &&
-				w->window_class != WC_GENERATE_LANDSCAPE &&
-				w->window_class != WC_CONSOLE &&
-				w->window_class != WC_SAVELOAD &&
-				w->window_class != WC_COMPANY_PASSWORD_WINDOW) {
-			continue;
-		}
 		if (w->OnKeyPress(key, keycode) == Window::ES_HANDLED) return;
 	}
 
@@ -1791,7 +1871,11 @@
 
 static void HandleKeyScrolling()
 {
-	if (_dirkeys && !_no_scroll) {
+	/*
+	 * Check that any of the dirkeys is pressed and that the focused window
+	 * dont has an edit-box as focused widget.
+	 */
+	if (_dirkeys && !EditBoxInGlobalFocus()) {
 		int factor = _shift_pressed ? 50 : 10;
 		ScrollMainViewport(scrollamt[_dirkeys][0] * factor, scrollamt[_dirkeys][1] * factor);
 	}
--- a/src/window_gui.h
+++ b/src/window_gui.h
@@ -59,6 +59,8 @@
 	WDF_STICKY_BUTTON   =   1 << 5, ///< Set window to sticky mode; they are not closed unless closed with 'X' (widget 2)
 	WDF_RESIZABLE       =   1 << 6, ///< Window can be resized
 	WDF_MODAL           =   1 << 7, ///< The window is a modal child of some other window, meaning the parent is 'inactive'
+
+	WDF_NO_FOCUS        =   1 << 8, ///< This window won't get focus/make any other window lose focus when click
 };
 
 /**
@@ -154,6 +156,7 @@
 	Widget *widget;        ///< Widgets of the window
 	uint widget_count;     ///< Number of widgets of the window
 	uint32 desc_flags;     ///< Window/widgets default flags setting, @see WindowDefaultFlag
+	const Widget *focused_widget; ///< Currently focused widget or NULL, if no widget has focus
 
 	Window *parent;        ///< Parent window
 	Window *z_front;       ///< The window in front of us in z-order
@@ -169,6 +172,9 @@
 	void HideWidget(byte widget_index);
 	void ShowWidget(byte widget_index);
 	bool IsWidgetHidden(byte widget_index) const;
+	void SetFocusedWidget(byte widget_index);
+	bool IsWidgetGloballyFocused(byte widget_index) const;
+	bool IsWidgetFocused(byte widget_index) const;
 	void SetWidgetLoweredState(byte widget_index, bool lowered_stat);
 	void ToggleWidgetLoweredState(byte widget_index);
 	void LowerWidget(byte widget_index);
@@ -176,6 +182,7 @@
 	bool IsWidgetLowered(byte widget_index) const;
 	void AlignWidgetRight(byte widget_index_a, byte widget_index_b);
 	int  GetWidgetWidth(byte widget_index) const;
+	bool HasWidgetOfType(WidgetType widget_type) const;
 
 	void RaiseButtons();
 	void CDECL SetWidgetsDisabledState(bool disab_stat, int widgets, ...);
@@ -198,6 +205,15 @@
 	 */
 	virtual void OnPaint() {}
 
+	/**
+	 * Called when window gains focus
+	 */
+	virtual void OnFocus() {}
+
+	/**
+	 * Called when window looses focus
+	 */
+	virtual void OnFocusLost() {}
 
 	/**
 	 * A key has been pressed.
@@ -423,6 +439,7 @@
 /* window.cpp */
 extern Window *_z_front_window;
 extern Window *_z_back_window;
+extern Window *_focused_window;
 
 /** Iterate over all windows */
 #define FOR_ALL_WINDOWS_FROM_BACK_FROM(w, start)  for (w = start; w != NULL; w = w->z_front) if (w->window_class != WC_INVALID)
@@ -430,12 +447,6 @@
 #define FOR_ALL_WINDOWS_FROM_BACK(w)  FOR_ALL_WINDOWS_FROM_BACK_FROM(w, _z_back_window)
 #define FOR_ALL_WINDOWS_FROM_FRONT(w) FOR_ALL_WINDOWS_FROM_FRONT_FROM(w, _z_front_window)
 
-/**
- * Disable scrolling of the main viewport when an input-window is active.
- * This contains the count of windows with a textbox in them.
- */
-extern byte _no_scroll;
-
 extern Point _cursorpos_drag_start;
 
 extern int _scrollbar_start_pos;
@@ -455,6 +466,10 @@
 
 Window *GetCallbackWnd();
 
+void SetFocusedWindow(Window *w);
+const Widget *GetGloballyFocusedWidget();
+bool EditBoxInGlobalFocus();
+
 void ScrollbarClickHandler(Window *w, const Widget *wi, int x, int y);
 
 void ResizeButtons(Window *w, byte left, byte right);
@@ -550,6 +565,43 @@
 	return HasBit(this->widget[widget_index].display_flags, WIDG_HIDDEN);
 }
 
+ /**
+ * Set focus within this window to given widget. The function however don't
+ * change which window that has focus.
+ * @param widget_index : index of the widget in the window to set focus to
+ */
+inline void Window::SetFocusedWidget(byte widget_index)
+{
+	if (widget_index < this->widget_count) {
+		/* Repaint the widget that loss focus. A focused edit box may else leave the caret left on the screen */
+		if (this->focused_widget && this->focused_widget - this->widget != widget_index) {
+			this->InvalidateWidget(this->focused_widget - this->widget);
+		}
+		this->focused_widget = &this->widget[widget_index];
+	}
+}
+
+/**
+ * Check if given widget has user input focus. This means that both the window
+ * has focus and that the given widget has focus within the window.
+ * @param widget_index : index of the widget in the window to check
+ * @return true if given widget is the focused window in this window and this window has focus
+ */
+inline bool Window::IsWidgetGloballyFocused(byte widget_index) const
+{
+	return _focused_window == this && IsWidgetFocused(widget_index);
+}
+
+/**
+ * Check if given widget is focused within this window
+ * @param widget_index : index of the widget in the window to check
+ * @return true if given widget is the focused window in this window
+ */
+inline bool Window::IsWidgetFocused(byte widget_index) const
+{
+	return this->focused_widget == &this->widget[widget_index];
+}
+
 /**
  * Sets the lowered/raised status of a widget.
  * @param widget_index index of this widget in the window