diff --git a/src/misc_gui.cpp b/src/misc_gui.cpp index bcd152e822..076e742316 100644 --- a/src/misc_gui.cpp +++ b/src/misc_gui.cpp @@ -1072,11 +1072,10 @@ bool HandleCaret(Textbuf *tb) bool QueryString::HasEditBoxFocus(const Window *w, int wid) const { - return (w->window_class == WC_OSK && - _focused_window == w->parent && - w->parent->focused_widget != NULL && - w->parent->focused_widget->type == WWT_EDITBOX) || - w->IsWidgetGloballyFocused(wid); + if (w->IsWidgetGloballyFocused(wid)) return true; + if (w->window_class != WC_OSK || _focused_window != w->parent) return false; + return (w->parent->focused_widget != NULL && w->parent->focused_widget->type == WWT_EDITBOX) || + (w->parent->nested_focus != NULL && w->parent->nested_focus->type == WWT_EDITBOX); } HandleEditBoxResult QueryString::HandleEditBoxKey(Window *w, int wid, uint16 key, uint16 keycode, Window::EventState &state) diff --git a/src/osk_gui.cpp b/src/osk_gui.cpp index 1b2a825d4e..43b434edb3 100644 --- a/src/osk_gui.cpp +++ b/src/osk_gui.cpp @@ -149,8 +149,14 @@ struct OskWindow : public Window { switch (widget) { case OSK_WIDGET_TEXT: { /* Find the edit box of the parent window and give focus to that */ - const Widget *wi = this->parent->GetWidgetOfType(WWT_EDITBOX); - if (wi != NULL) this->parent->focused_widget = wi; + if (this->parent->widget != NULL) { + const Widget *wi = this->parent->GetWidgetOfType(WWT_EDITBOX); + if (wi != NULL) this->parent->focused_widget = wi; + } + if (this->parent->nested_root != NULL) { + const NWidgetCore *nwid = dynamic_cast(this->parent->nested_root->GetWidgetOfType(WWT_EDITBOX)); + if (nwid != NULL) this->parent->nested_focus = nwid; + } /* Give focus to parent window */ SetFocusedWindow(this->parent); diff --git a/src/window.cpp b/src/window.cpp index ec829dc5b1..68634e21ae 100644 --- a/src/window.cpp +++ b/src/window.cpp @@ -97,9 +97,12 @@ 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); + if (_focused_window != NULL) { + if (_focused_window->focused_widget != NULL) { + uint focused_widget_id = _focused_window->focused_widget - _focused_window->widget; + _focused_window->InvalidateWidget(focused_widget_id); + } + if (_focused_window->nested_focus != NULL) _focused_window->nested_focus->Invalidate(_focused_window); } /* Remember which window was previously focused */ @@ -118,11 +121,15 @@ void SetFocusedWindow(Window *w) */ bool EditBoxInGlobalFocus() { - const Widget *wi = (_focused_window != NULL) ? _focused_window->focused_widget : NULL; + if (_focused_window == NULL) return false; /* 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); + if (_focused_window->window_class == WC_CONSOLE) return true; + + if (_focused_window->nested_array != NULL) { + return _focused_window->nested_focus != NULL && _focused_window->nested_focus->type == WWT_EDITBOX; + } + return _focused_window->focused_widget != NULL && _focused_window->focused_widget->type == WWT_EDITBOX; } /** @@ -264,8 +271,19 @@ static void DispatchLeftClickEvent(Window *w, int x, int y, bool double_click) { int widget_index = 0; if (w->desc_flags & WDF_DEF_WIDGET) { - widget_index = GetWidgetFromPos(w, x, y); - WidgetType widget_type = (widget_index >= 0) ? w->widget[widget_index].type : WWT_EMPTY; + const Widget *wi = NULL; + const NWidgetCore *nw = NULL; + WidgetType widget_type; + if (w->widget != NULL) { + widget_index = GetWidgetFromPos(w, x, y); + wi = &w->widget[widget_index]; + widget_type = (widget_index >= 0) ? wi->type : WWT_EMPTY; + } else { + assert(w->nested_root != NULL); + nw = w->nested_root->GetWidgetFromPos(x, y); + widget_index = (nw != NULL) ? nw->index : -1; + widget_type = (widget_index >= 0) ? nw->type : WWT_EMPTY; + } bool focused_widget_changed = false; /* If clicked on a window that previously did dot have focus */ @@ -288,15 +306,14 @@ static void DispatchLeftClickEvent(Window *w, int x, int y, bool double_click) /* don't allow any interaction if the button has been disabled */ if (w->IsWidgetDisabled(widget_index)) return; - const Widget *wi = &w->widget[widget_index]; - /* Clicked on a widget that is not disabled. * So unless the clicked widget is the caption bar, change focus to this widget */ if (widget_type != WWT_CAPTION) { /* Close the OSK window if a edit box loses focus */ - if (w->focused_widget != NULL && 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 + if ((w->widget != NULL && w->focused_widget != NULL && w->focused_widget->type == WWT_EDITBOX && // An edit box was previously selected + w->focused_widget != wi && w->window_class != WC_OSK) || // and focus is going to change and it is not the OSK window + (w->nested_root != NULL && w->nested_focus != NULL && w->nested_focus->type == WWT_EDITBOX && + w->nested_focus != nw && w->window_class != WC_OSK)) { DeleteWindowById(WC_OSK, 0); } @@ -314,7 +331,11 @@ static void DispatchLeftClickEvent(Window *w, int x, int y, bool double_click) break; } } else if (widget_type == WWT_SCROLLBAR || widget_type == WWT_SCROLL2BAR || widget_type == WWT_HSCROLLBAR) { - ScrollbarClickHandler(w, wi, x, y); + if (wi != NULL) { + ScrollbarClickHandler(w, wi, x, y); + } else { + ScrollbarClickHandler(w, nw, x, y); + } } else if (widget_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(w); @@ -342,7 +363,8 @@ static void DispatchLeftClickEvent(Window *w, int x, int y, bool double_click) if ((w->desc_flags & WDF_RESIZABLE) && widget_type == WWT_RESIZEBOX) { /* When the resize widget is on the left size of the window * we assume that that button is used to resize to the left. */ - StartWindowSizing(w, wi->left < (w->width / 2)); + int left_pos = (wi != NULL) ? wi->left : nw->pos_x; + StartWindowSizing(w, left_pos < (w->width / 2)); w->InvalidateWidget(widget_index); return; } @@ -372,14 +394,24 @@ static void DispatchLeftClickEvent(Window *w, int x, int y, bool double_click) static void DispatchRightClickEvent(Window *w, int x, int y) { int widget = 0; + StringID tooltip = 0; /* default tooltips handler? */ if (w->desc_flags & WDF_STD_TOOLTIPS) { - widget = GetWidgetFromPos(w, x, y); - if (widget < 0) return; // exit if clicked outside of widgets + if (w->nested_root != NULL) { + NWidgetCore *wid = w->nested_root->GetWidgetFromPos(x, y); + if (wid == NULL || wid->index < 0) return; + widget = wid->index; + tooltip = wid->tool_tip; + } + if (w->widget != NULL) { + widget = GetWidgetFromPos(w, x, y); + if (widget < 0) return; // exit if clicked outside of widgets + tooltip = w->widget[widget].tooltips; + } - if (w->widget[widget].tooltips != 0) { - GuiShowTooltips(w->widget[widget].tooltips); + if (tooltip != 0) { + GuiShowTooltips(tooltip); return; } } @@ -817,6 +849,7 @@ void Window::Initialize(int x, int y, int min_width, int min_height, this->height = min_height; AssignWidgetToWindow(this, widget); this->focused_widget = NULL; + this->nested_focus = NULL; this->resize.width = min_width; this->resize.height = min_height; this->resize.step_width = 1; @@ -826,7 +859,9 @@ void Window::Initialize(int x, int y, int min_width, int min_height, /* 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->GetWidgetOfType(WWT_EDITBOX) != NULL)) SetFocusedWindow(this); + bool has_editbox = (this->widget != NULL && this->GetWidgetOfType(WWT_EDITBOX) != NULL) || + (this->nested_root != NULL && this->nested_root->GetWidgetOfType(WWT_EDITBOX) != NULL); + if (this->window_class != WC_OSK && (!EditBoxInGlobalFocus() || has_editbox)) SetFocusedWindow(this); /* Hacky way of specifying always-on-top windows. These windows are * always above other windows because they are moved below them. @@ -1345,44 +1380,56 @@ static bool HandleMouseOver() */ void ResizeWindow(Window *w, int delta_x, int delta_y) { - bool resize_height = false; - bool resize_width = false; - if (delta_x == 0 && delta_y == 0) return; w->SetDirty(); - for (Widget *wi = w->widget; wi->type != WWT_LAST; wi++) { - /* Isolate the resizing flags */ - byte rsizeflag = GB(wi->display_flags, 0, 4); - if (rsizeflag == RESIZE_NONE) continue; + if (w->nested_root != NULL) { + uint new_xinc = max(0u, (w->nested_root->resize_x == 0) ? 0 : (w->nested_root->current_x - w->nested_root->smallest_x) + delta_x); + uint new_yinc = max(0u, (w->nested_root->resize_y == 0) ? 0 : (w->nested_root->current_y - w->nested_root->smallest_y) + delta_y); + assert(w->nested_root->resize_x == 0 || new_xinc % w->nested_root->resize_x == 0); + assert(w->nested_root->resize_y == 0 || new_yinc % w->nested_root->resize_y == 0); - /* Resize the widget based on its resize-flag */ - if (rsizeflag & RESIZE_LEFT) { - wi->left += delta_x; - resize_width = true; - } + w->nested_root->AssignSizePosition(ST_RESIZE, 0, 0, w->nested_root->smallest_x + new_xinc, w->nested_root->smallest_y + new_yinc, false, false, false); + w->width = w->nested_root->current_x; + w->height = w->nested_root->current_y; + } else { + assert(w->widget != NULL); - if (rsizeflag & RESIZE_RIGHT) { - wi->right += delta_x; - resize_width = true; - } + bool resize_height = false; + bool resize_width = false; + for (Widget *wi = w->widget; wi->type != WWT_LAST; wi++) { + /* Isolate the resizing flags */ + byte rsizeflag = GB(wi->display_flags, 0, 4); - if (rsizeflag & RESIZE_TOP) { - wi->top += delta_y; - resize_height = true; - } + if (rsizeflag == RESIZE_NONE) continue; - if (rsizeflag & RESIZE_BOTTOM) { - wi->bottom += delta_y; - resize_height = true; - } - } + /* Resize the widget based on its resize-flag */ + if (rsizeflag & RESIZE_LEFT) { + wi->left += delta_x; + resize_width = true; + } - /* We resized at least 1 widget, so let's resize the window totally */ - if (resize_width) w->width += delta_x; - if (resize_height) w->height += delta_y; + if (rsizeflag & RESIZE_RIGHT) { + wi->right += delta_x; + resize_width = true; + } + + if (rsizeflag & RESIZE_TOP) { + wi->top += delta_y; + resize_height = true; + } + if (rsizeflag & RESIZE_BOTTOM) { + wi->bottom += delta_y; + resize_height = true; + } + } + + /* We resized at least 1 widget, so let's resize the window totally */ + if (resize_width) w->width += delta_x; + if (resize_height) w->height += delta_y; + } w->SetDirty(); } @@ -1486,16 +1533,31 @@ static bool HandleWindowDragging() } } - /* Search for the title bar */ - const Widget *caption = w->GetWidgetOfType(WWT_CAPTION); - assert(caption != NULL); + /* Search for the title bar rectangle. */ + Rect caption_rect; + if (w->widget != NULL) { + const Widget *caption = w->GetWidgetOfType(WWT_CAPTION); + assert(caption != NULL); + caption_rect.left = caption->left; + caption_rect.right = caption->right; + caption_rect.top = caption->top; + caption_rect.bottom = caption->bottom; + } else { + assert(w->nested_root != NULL); + const NWidgetBase *caption = w->nested_root->GetWidgetOfType(WWT_CAPTION); + assert(caption != NULL); + caption_rect.left = caption->pos_x; + caption_rect.right = caption->pos_x + caption->current_x; + caption_rect.top = caption->pos_y; + caption_rect.bottom = caption->pos_y + caption->current_y; + } /* The minimum number of pixels of the title bar must be visible * in both the X or Y direction */ static const int MIN_VISIBLE_TITLE_BAR = 13; /* Make sure the window doesn't leave the screen */ - nx = Clamp(nx, MIN_VISIBLE_TITLE_BAR - caption->right, _screen.width - MIN_VISIBLE_TITLE_BAR - caption->left); + nx = Clamp(nx, MIN_VISIBLE_TITLE_BAR - caption_rect.right, _screen.width - MIN_VISIBLE_TITLE_BAR - caption_rect.left); ny = Clamp(ny, 0, _screen.height - MIN_VISIBLE_TITLE_BAR); /* Make sure the title bar isn't hidden by behind the main tool bar */ @@ -1503,19 +1565,19 @@ static bool HandleWindowDragging() if (v != NULL) { int v_bottom = v->top + v->height; int v_right = v->left + v->width; - if (ny + caption->top >= v->top && ny + caption->top < v_bottom) { - if ((v->left < MIN_VISIBLE_TITLE_BAR && nx + caption->left < v->left) || - (v_right > _screen.width - MIN_VISIBLE_TITLE_BAR && nx + caption->right > v_right)) { + if (ny + caption_rect.top >= v->top && ny + caption_rect.top < v_bottom) { + if ((v->left < MIN_VISIBLE_TITLE_BAR && nx + caption_rect.left < v->left) || + (v_right > _screen.width - MIN_VISIBLE_TITLE_BAR && nx + caption_rect.right > v_right)) { ny = v_bottom; } else { - if (nx + caption->left > v->left - MIN_VISIBLE_TITLE_BAR && - nx + caption->right < v_right + MIN_VISIBLE_TITLE_BAR) { + if (nx + caption_rect.left > v->left - MIN_VISIBLE_TITLE_BAR && + nx + caption_rect.right < v_right + MIN_VISIBLE_TITLE_BAR) { if (w->top >= v_bottom) { ny = v_bottom; } else if (w->left < nx) { - nx = v->left - MIN_VISIBLE_TITLE_BAR - caption->left; + nx = v->left - MIN_VISIBLE_TITLE_BAR - caption_rect.left; } else { - nx = v_right + MIN_VISIBLE_TITLE_BAR - caption->right; + nx = v_right + MIN_VISIBLE_TITLE_BAR - caption_rect.right; } } } @@ -1549,11 +1611,15 @@ static bool HandleWindowDragging() x = _cursor.pos.x - _drag_delta.x; } + if (w->nested_root != NULL) { + /* Nested widgets also allow resize.step_width and/or resize.step_height to become 0 which means no resize is possible. */ + if (w->resize.step_width == 0) x = 0; + if (w->resize.step_height == 0) y = 0; + } /* X and Y has to go by step.. calculate it. * The cast to int is necessary else x/y are implicitly casted to * unsigned int, which won't work. */ - if (w->resize.step_width > 1) x -= x % (int)w->resize.step_width; - + if (w->resize.step_width > 1) x -= x % (int)w->resize.step_width; if (w->resize.step_height > 1) y -= y % (int)w->resize.step_height; /* Check if we don't go below the minimum set size */ diff --git a/src/window_gui.h b/src/window_gui.h index 36ebaec619..62757f9c46 100644 --- a/src/window_gui.h +++ b/src/window_gui.h @@ -169,18 +169,19 @@ public: Owner owner; ///< The owner of the content shown in this window. Company colour is acquired from this variable. - ViewportData *viewport; ///< Pointer to viewport data, if present. - 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 \c NULL if no widget has focus. - NWidgetBase *nested_root; ///< Root of the nested tree. - NWidgetCore **nested_array; ///< Array of pointers into the tree. - uint nested_array_size; ///< Size of the nested array. + ViewportData *viewport; ///< Pointer to viewport data, if present. + 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 \c NULL if no widget has focus. + const NWidgetCore *nested_focus; ///< Currently focused nested widget, or \c NULL if no nested widget has focus. + NWidgetBase *nested_root; ///< Root of the nested tree. + NWidgetCore **nested_array; ///< Array of pointers into the tree. + uint nested_array_size; ///< Size of the nested array. - Window *parent; ///< Parent window - Window *z_front; ///< The window in front of us in z-order - Window *z_back; ///< The window behind us in z-order + Window *parent; ///< Parent window. + Window *z_front; ///< The window in front of us in z-order. + Window *z_back; ///< The window behind us in z-order. /** * Sets the enabled/disabled status of a widget. @@ -302,7 +303,8 @@ public: */ inline bool IsWidgetFocused(byte widget_index) const { - return this->focused_widget == &this->widget[widget_index]; + return (this->widget != NULL && this->focused_widget == &this->widget[widget_index]) || + (this->nested_focus != NULL && this->nested_focus->index == widget_index); } /** @@ -693,6 +695,7 @@ void SetFocusedWindow(Window *w); bool EditBoxInGlobalFocus(); void ScrollbarClickHandler(Window *w, const Widget *wi, int x, int y); +void ScrollbarClickHandler(Window *w, const NWidgetCore *nw, int x, int y); void ResizeButtons(Window *w, byte left, byte right);