From 435b5a08c4f9a3b9a079da6651bac0b287f0218d Mon Sep 17 00:00:00 2001 From: rubidium Date: Sat, 26 Nov 2011 16:09:25 +0000 Subject: (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) --- src/window.cpp | 208 +++++++++++++++++++++++++++++++++++---------------------- 1 file changed, 129 insertions(+), 79 deletions(-) (limited to 'src') diff --git a/src/window.cpp b/src/window.cpp index bc61d0c73..b1b37ff65 100644 --- a/src/window.cpp +++ b/src/window.cpp @@ -837,7 +837,7 @@ void ChangeWindowOwner(Owner old_owner, Owner new_owner) 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 @@ static inline bool IsVitalWindow(const Window *w) } /** - * 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) +{ + 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) { - Window *v = _z_front_window; + 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; + } - /* Bring the window just below the vital windows */ - for (; v != NULL && v != w && IsVitalWindow(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; + } + } +} - 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); +/** + * 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; + 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 +1053,8 @@ void Window::InitializeData(const WindowDesc *desc, WindowNumber window_number) * 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 @@ static EventState HandleViewportScroll() } /** - * 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 @@ void InputLoop() 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); } -- cgit v1.2.3-70-g09d2