Index: src/window.cpp =================================================================== --- src/window.cpp (revision 22851) +++ src/window.cpp (working copy) @@ -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,134 @@ } /** - * 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 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; + assert(w->window_class != WC_INVALID); - /* Bring the window just below the vital windows */ - for (; v != NULL && v != w && IsVitalWindow(v); v = v->z_back) { } + switch (w->window_class) { + case WC_ENDSCREEN: + return 10; - if (v == NULL || w == v) return; // window is already in the right position + case WC_HIGHSCORE: + return 9; - /* w cannot be at the top already! */ - assert(w != _z_front_window); + case WC_TOOLTIPS: + return 8; + case WC_DROPDOWN_MENU: + return 7; + + case WC_MAIN_TOOLBAR: + case WC_STATUS_BAR: + return 6; + + case WC_CONSOLE: + return 5; + + case WC_ERRMSG: + return 4; + + case WC_SAVELOAD: + return 3; + + case WC_SEND_NETWORK_MSG: + case WC_NEWS_WINDOW: + return 2; + + default: + return 1; + + case WC_MAIN_WINDOW: + return 0; + } +} + +/** + * 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) { + _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; } - w->z_front->z_back = w->z_back; - w->z_front = v->z_front; - w->z_back = v; + w->z_front = w->z_back = NULL; +} - if (v->z_front == NULL) { - _z_front_window = w; - } else { - v->z_front->z_back = w; - } - v->z_front = w; +/** + * 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 +1045,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); } /** @@ -1335,7 +1390,7 @@ } /** Empty constructor, initialization has been moved to #InitNested() called from the constructor of the derived class. */ -Window::Window() : scrolling_scrollbar(-1) +Window::Window() : scrolling_scrollbar(-1), z_front(NULL), z_back(NULL) { } @@ -1954,13 +2009,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) @@ -2391,20 +2446,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); }