changeset 18492:827e51e4c9b8 draft

(svn r23336) -Fix [FS#4709]: bring some more order in the ordering of the windows, e.g. don't let a save or load dialog get hidden by a new message (monoid)
author rubidium <rubidium@openttd.org>
date Sat, 26 Nov 2011 16:09:25 +0000
parents b29b61d08720
children 404967fca61c
files src/window.cpp
diffstat 1 files changed, 134 insertions(+), 84 deletions(-) [+]
line wrap: on
line diff
--- a/src/window.cpp
+++ b/src/window.cpp
@@ -837,7 +837,7 @@
 static void BringWindowToFront(Window *w);
 
 /**
- * Find a window and make it the top-window on the screen.
+ * Find a window and make it the relative top-window on the screen.
  * The window gets unshaded if it was shaded, and a white border is drawn at its edges for a brief period of time to visualize its "activation".
  * @param cls WindowClass of the window to activate
  * @param number WindowNumber of the window to activate
@@ -873,42 +873,142 @@
 }
 
 /**
- * On clicking on a window, make it the frontmost window of all. However
- * there are certain windows that always need to be on-top; these include
- * - Toolbar, Statusbar (always on)
- * - New window, Chatbar (only if open)
- * The window is marked dirty for a repaint if the window is actually moved
- * @param w window that is put into the foreground
- * @return pointer to the window, the same as the input pointer
+ * Get the z-priority for a given window. This is used in comparison with other z-priority values;
+ * a window with a given z-priority will appear above other windows with a lower value, and below
+ * those with a higher one (the ordering within z-priorities is arbitrary).
+ * @param w The window to get the z-priority for
+ * @pre w->window_class != WC_INVALID
+ * @return The window's z-priority
  */
-static void BringWindowToFront(Window *w)
+static uint GetWindowZPriority(const Window *w)
 {
-	Window *v = _z_front_window;
-
-	/* Bring the window just below the vital windows */
-	for (; v != NULL && v != w && IsVitalWindow(v); v = v->z_back) { }
-
-	if (v == NULL || w == v) return; // window is already in the right position
-
-	/* w cannot be at the top already! */
-	assert(w != _z_front_window);
+	assert(w->window_class != WC_INVALID);
+
+	uint z_priority = 0;
+
+	switch (w->window_class) {
+		case WC_ENDSCREEN:
+			++z_priority;
+
+		case WC_HIGHSCORE:
+			++z_priority;
+
+		case WC_TOOLTIPS:
+			++z_priority;
+
+		case WC_DROPDOWN_MENU:
+			++z_priority;
+
+		case WC_MAIN_TOOLBAR:
+		case WC_STATUS_BAR:
+			++z_priority;
+
+		case WC_ERRMSG:
+		case WC_CONFIRM_POPUP_QUERY:
+			++z_priority;
+
+		case WC_SAVELOAD:
+			++z_priority;
+
+		case WC_MODAL_PROGRESS:
+			++z_priority;
+
+		case WC_CONSOLE:
+			++z_priority;
+
+		case WC_SEND_NETWORK_MSG:
+		case WC_NEWS_WINDOW:
+			++z_priority;
+
+		default:
+			++z_priority;
+
+		case WC_MAIN_WINDOW:
+			return z_priority;
+	}
+}
+
+/**
+ * Adds a window to the z-ordering, according to its z-priority.
+ * @param w Window to add
+ */
+static void AddWindowToZOrdering(Window *w)
+{
+	assert(w->z_front == NULL && w->z_back == NULL);
+
+	if (_z_front_window == NULL) {
+		/* It's the only window. */
+		_z_front_window = _z_back_window = w;
+		w->z_front = w->z_back = NULL;
+	} else {
+		/* Search down the z-ordering for its location. */
+		Window *v = _z_front_window;
+		uint last_z_priority = UINT_MAX;
+		while (v != NULL && (v->window_class == WC_INVALID || GetWindowZPriority(v) > GetWindowZPriority(w))) {
+			if (v->window_class != WC_INVALID) {
+				/* Sanity check z-ordering, while we're at it. */
+				assert(last_z_priority >= GetWindowZPriority(v));
+				last_z_priority = GetWindowZPriority(v);
+			}
+
+			v = v->z_back;
+		}
+
+		if (v == NULL) {
+			/* It's the new back window. */
+			w->z_front = _z_back_window;
+			w->z_back = NULL;
+			_z_back_window->z_back = w;
+			_z_back_window = w;
+		} else if (v == _z_front_window) {
+			/* It's the new front window. */
+			w->z_front = NULL;
+			w->z_back = _z_front_window;
+			_z_front_window->z_front = w;
+			_z_front_window = w;
+		} else {
+			/* It's somewhere else in the z-ordering. */
+			w->z_front = v->z_front;
+			w->z_back = v;
+			v->z_front->z_back = w;
+			v->z_front = w;
+		}
+	}
+}
+
+
+/**
+ * Removes a window from the z-ordering.
+ * @param w Window to remove
+ */
+static void RemoveWindowFromZOrdering(Window *w)
+{
+	if (w->z_front == NULL) {
+		assert(_z_front_window == w);
+		_z_front_window = w->z_back;
+	} else {
+		w->z_front->z_back = w->z_back;
+	}
 
 	if (w->z_back == NULL) {
+		assert(_z_back_window == w);
 		_z_back_window = w->z_front;
 	} else {
 		w->z_back->z_front = w->z_front;
 	}
-	w->z_front->z_back = w->z_back;
-
-	w->z_front = v->z_front;
-	w->z_back = v;
-
-	if (v->z_front == NULL) {
-		_z_front_window = w;
-	} else {
-		v->z_front->z_back = w;
-	}
-	v->z_front = w;
+
+	w->z_front = w->z_back = NULL;
+}
+
+/**
+ * On clicking on a window, make it the frontmost window of all windows with an equal
+ * or lower z-priority. The window is marked dirty for a repaint
+ * @param w window that is put into the relative foreground
+ */
+static void BringWindowToFront(Window *w)
+{
+	RemoveWindowFromZOrdering(w);
+	AddWindowToZOrdering(w);
 
 	w->SetDirty();
 }
@@ -953,45 +1053,8 @@
 	 * window has a text box, then take focus anyway. */
 	if (this->window_class != WC_OSK && (!EditBoxInGlobalFocus() || this->nested_root->GetWidgetOfType(WWT_EDITBOX) != NULL)) 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.
-	 * Also, as the chat-window is excluded from this, it will always be
-	 * the last window, thus always on top.
-	 * XXX - Yes, ugly, probably needs something like w->always_on_top flag
-	 * to implement correctly, but even then you need some kind of distinction
-	 * between on-top of chat/news and status windows, because these conflict */
-	Window *w = _z_front_window;
-	if (w != NULL && this->window_class != WC_SEND_NETWORK_MSG && this->window_class != WC_HIGHSCORE && this->window_class != WC_ENDSCREEN) {
-		if (FindWindowById(WC_MAIN_TOOLBAR, 0)     != NULL) w = w->z_back;
-		if (FindWindowById(WC_STATUS_BAR, 0)       != NULL) w = w->z_back;
-		if (FindWindowById(WC_NEWS_WINDOW, 0)      != NULL) w = w->z_back;
-		if (FindWindowByClass(WC_SEND_NETWORK_MSG) != NULL) w = w->z_back;
-
-		if (w == NULL) {
-			_z_back_window->z_front = this;
-			this->z_back = _z_back_window;
-			_z_back_window = this;
-		} else {
-			if (w->z_front == NULL) {
-				_z_front_window = this;
-			} else {
-				this->z_front = w->z_front;
-				w->z_front->z_back = this;
-			}
-
-			this->z_back = w;
-			w->z_front = this;
-		}
-	} else {
-		this->z_back = _z_front_window;
-		if (_z_front_window != NULL) {
-			_z_front_window->z_front = this;
-		} else {
-			_z_back_window = this;
-		}
-		_z_front_window = this;
-	}
+	/* Insert the window into the correct location in the z-ordering. */
+	AddWindowToZOrdering(this);
 }
 
 /**
@@ -1955,13 +2018,13 @@
 }
 
 /**
- * Check if a window can be made top-most window, and if so do
+ * Check if a window can be made relative top-most window, and if so do
  * it. If a window does not obscure any other windows, it will not
  * be brought to the foreground. Also if the only obscuring windows
  * are so-called system-windows, the window will not be moved.
  * The function will return false when a child window of this window is a
  * modal-popup; function returns a false and child window gets a white border
- * @param w Window to bring on-top
+ * @param w Window to bring relatively on-top
  * @return false if the window has an active modal child, true otherwise
  */
 static bool MaybeBringWindowToFront(Window *w)
@@ -2392,20 +2455,7 @@
 
 		if (w->window_class != WC_INVALID) continue;
 
-		/* Find the window in the z-array, and effectively remove it
-		 * by moving all windows after it one to the left. This must be
-		 * done before removing the child so we cannot cause recursion
-		 * between the deletion of the parent and the child. */
-		if (w->z_front == NULL) {
-			_z_front_window = w->z_back;
-		} else {
-			w->z_front->z_back = w->z_back;
-		}
-		if (w->z_back == NULL) {
-			_z_back_window  = w->z_front;
-		} else {
-			w->z_back->z_front = w->z_front;
-		}
+		RemoveWindowFromZOrdering(w);
 		free(w);
 	}