changeset 13075:0a2c74e86245 draft

(svn r17575) -Codechange: Adding a new combined button+dropdown widget.
author alberth <alberth@openttd.org>
date Sat, 19 Sep 2009 13:08:37 +0000
parents c6408d5b9f99
children 955e6a7abd7b
files src/widget.cpp src/widget_type.h src/widgets/dropdown.cpp
diffstat 3 files changed, 86 insertions(+), 16 deletions(-) [+]
line wrap: on
line diff
--- a/src/widget.cpp
+++ b/src/widget.cpp
@@ -562,22 +562,44 @@
 	if (str != STR_NULL) DrawString(r.left + WD_CAPTIONTEXT_LEFT, r.right - WD_CAPTIONTEXT_RIGHT, r.top + WD_CAPTIONTEXT_TOP, str, TC_FROMSTRING, SA_CENTER);
 }
 
-static inline void DrawDropdown(const Rect &r, Colours colour, bool clicked, StringID str)
+/**
+ * Draw a button with a dropdown (#WWT_DROPDOWN and #NWID_BUTTON_DRPDOWN).
+ * @param r                Rectangle containing the widget.
+ * @param colour           Background colour of the widget.
+ * @param clicked_button   The button-part is lowered.
+ * @param clicked_dropdown The drop-down part is lowered.
+ * @param str              Text of the button.
+ *
+ * @note Magic constants are also used in #NWidgetLeaf::ButtonHit.
+ */
+static inline void DrawButtonDropdown(const Rect &r, Colours colour, bool clicked_button, bool clicked_dropdown, StringID str)
 {
 	if (_dynlang.text_dir == TD_LTR) {
-		DrawFrameRect(r.left, r.top, r.right - 12, r.bottom, colour, FR_NONE);
-		DrawFrameRect(r.right - 11, r.top, r.right, r.bottom, colour, clicked ? FR_LOWERED : FR_NONE);
-		DrawString(r.right - (clicked ? 10 : 11), r.right, r.top + (clicked ? 2 : 1), STR_BLACK_ARROW_DOWN, TC_FROMSTRING, SA_CENTER);
-		if (str != STR_NULL) DrawString(r.left + WD_DROPDOWNTEXT_LEFT, r.right - WD_DROPDOWNTEXT_RIGHT, r.top + WD_DROPDOWNTEXT_TOP, str, TC_BLACK);
+		DrawFrameRect(r.left, r.top, r.right - 12, r.bottom, colour, clicked_button ? FR_LOWERED : FR_NONE);
+		DrawFrameRect(r.right - 11, r.top, r.right, r.bottom, colour, clicked_dropdown ? FR_LOWERED : FR_NONE);
+		DrawString(r.right - (clicked_dropdown ? 10 : 11), r.right, r.top + (clicked_dropdown ? 2 : 1), STR_BLACK_ARROW_DOWN, TC_FROMSTRING, SA_CENTER);
+		if (str != STR_NULL) DrawString(r.left + WD_DROPDOWNTEXT_LEFT + clicked_button, r.right - WD_DROPDOWNTEXT_RIGHT + clicked_button, r.top + WD_DROPDOWNTEXT_TOP + clicked_button, str, TC_BLACK);
 	} else {
-		DrawFrameRect(r.left + 12, r.top, r.right, r.bottom, colour, FR_NONE);
-		DrawFrameRect(r.left, r.top, r.left + 11, r.bottom, colour, clicked ? FR_LOWERED : FR_NONE);
-		DrawString(r.left + clicked, r.left + 11, r.top + (clicked ? 2 : 1), STR_BLACK_ARROW_DOWN, TC_FROMSTRING, SA_CENTER);
-		if (str != STR_NULL) DrawString(r.left + WD_DROPDOWNTEXT_RIGHT, r.right - WD_DROPDOWNTEXT_LEFT, r.top + WD_DROPDOWNTEXT_TOP, str, TC_BLACK);
+		DrawFrameRect(r.left + 12, r.top, r.right, r.bottom, colour, clicked_button ? FR_LOWERED : FR_NONE);
+		DrawFrameRect(r.left, r.top, r.left + 11, r.bottom, colour, clicked_dropdown ? FR_LOWERED : FR_NONE);
+		DrawString(r.left + clicked_dropdown, r.left + 11, r.top + (clicked_dropdown ? 2 : 1), STR_BLACK_ARROW_DOWN, TC_FROMSTRING, SA_CENTER);
+		if (str != STR_NULL) DrawString(r.left + WD_DROPDOWNTEXT_RIGHT + clicked_button, r.right - WD_DROPDOWNTEXT_LEFT + clicked_button, r.top + WD_DROPDOWNTEXT_TOP + clicked_button, str, TC_BLACK);
 	}
 }
 
 /**
+ * Draw a dropdown #WWT_DROPDOWN widget.
+ * @param r       Rectangle containing the widget.
+ * @param colour  Background colour of the widget.
+ * @param clicked The widget is lowered.
+ * @param str     Text of the button.
+ */
+static inline void DrawDropdown(const Rect &r, Colours colour, bool clicked, StringID str)
+{
+	DrawButtonDropdown(r, colour, false, clicked, str);
+}
+
+/**
  * Paint all widgets of a window.
  */
 void Window::DrawWidgets() const
@@ -2003,6 +2025,7 @@
 		case WWT_TEXT:
 		case WWT_MATRIX:
 		case WWT_EDITBOX:
+		case NWID_BUTTON_DRPDOWN:
 			this->SetFill(false, false);
 			break;
 
@@ -2177,7 +2200,8 @@
 			size = maxdim(size, d2);
 			break;
 		}
-		case WWT_DROPDOWN: {
+		case WWT_DROPDOWN:
+		case NWID_BUTTON_DRPDOWN: {
 			static const Dimension extra = {WD_DROPDOWNTEXT_LEFT + WD_DROPDOWNTEXT_RIGHT, WD_DROPDOWNTEXT_TOP + WD_DROPDOWNTEXT_BOTTOM};
 			padding = &extra;
 			if (this->index >= 0) w->SetStringParameters(this->index);
@@ -2300,6 +2324,11 @@
 			DrawDropdown(r, this->colour, clicked, this->widget_data);
 			break;
 
+		case NWID_BUTTON_DRPDOWN:
+			if (this->index >= 0) w->SetStringParameters(this->index);
+			DrawButtonDropdown(r, this->colour, clicked, (this->disp_flags & ND_DROPDOWN_ACTIVE) != 0, this->widget_data);
+			break;
+
 		default:
 			NOT_REACHED();
 	}
@@ -2323,6 +2352,24 @@
 }
 
 /**
+ * For a #NWID_BUTTON_DRPDOWN, test whether \a pt refers to the button or to the drop-down.
+ * @param pt Point in the widget.
+ * @return The point refers to the button.
+ *
+ * @param The magic constants are also used at #DrawButtonDropdown.
+ */
+bool NWidgetLeaf::ButtonHit(const Point &pt)
+{
+	if (_dynlang.text_dir == TD_LTR) {
+		int button_width = this->pos_x + this->current_x - 12;
+		return pt.x < button_width;
+	} else {
+		int button_left = this->pos_x + 12;
+		return pt.x >= button_left;
+	}
+}
+
+/**
  * Intialize nested widget tree and convert to widget array.
  * @param nwid Nested widget tree.
  * @param rtl  Direction of the language.
@@ -2545,7 +2592,7 @@
 
 			default:
 				if (*dest != NULL) return num_used;
-				assert((parts->type & WWT_MASK) < WWT_LAST);
+				assert((parts->type & WWT_MASK) < WWT_LAST || parts->type == NWID_BUTTON_DRPDOWN);
 				*dest = new NWidgetLeaf(parts->type, parts->u.widget.colour, parts->u.widget.index, 0x0, STR_NULL);
 				*biggest_index = max(*biggest_index, (int)parts->u.widget.index);
 				break;
--- a/src/widget_type.h
+++ b/src/widget_type.h
@@ -121,6 +121,7 @@
 	NWID_SELECTION,      ///< Stacked widgets, only one visible at a time (eg in a panel with tabs).
 	NWID_LAYERED,        ///< Widgets layered on top of each other, all visible at the same time.
 	NWID_VIEWPORT,       ///< Nested widget containing a viewport.
+	NWID_BUTTON_DRPDOWN, ///< Button with a drop-down.
 
 	/* Nested widget part types. */
 	WPT_RESIZE,       ///< Widget part for specifying resizing.
@@ -278,17 +279,22 @@
 
 /** Nested widget flags that affect display and interaction withe 'real' widgets. */
 enum NWidgetDisplay {
+	/* Generic. */
 	NDB_LOWERED         = 0, ///< Widget is lowered (pressed down) bit.
 	NDB_DISABLED        = 1, ///< Widget is disabled (greyed out) bit.
+	/* Viewport widget. */
 	NDB_NO_TRANSPARENCY = 2, ///< Viewport is never transparent.
 	NDB_SHADE_GREY      = 3, ///< Shade viewport to grey-scale.
 	NDB_SHADE_DIMMED    = 4, ///< Display dimmed colours in the viewport.
+	/* Button dropdown widget. */
+	NDB_DROPDOWN_ACTIVE = 5, ///< Dropdown menu of the button dropdown widget is active. @see #NWID_BUTTON_DRPDOWN
 
 	ND_LOWERED  = 1 << NDB_LOWERED,                ///< Bit value of the lowered flag.
 	ND_DISABLED = 1 << NDB_DISABLED,               ///< Bit value of the disabled flag.
 	ND_NO_TRANSPARENCY = 1 << NDB_NO_TRANSPARENCY, ///< Bit value of the 'no transparency' flag.
 	ND_SHADE_GREY      = 1 << NDB_SHADE_GREY,      ///< Bit value of the 'shade to grey' flag.
 	ND_SHADE_DIMMED    = 1 << NDB_SHADE_DIMMED,    ///< Bit value of the 'dimmed colours' flag.
+	ND_DROPDOWN_ACTIVE = 1 << NDB_DROPDOWN_ACTIVE, ///< Bit value of the 'dropdown active' flag.
 };
 DECLARE_ENUM_AS_BIT_SET(NWidgetDisplay);
 
@@ -526,6 +532,8 @@
 	/* virtual */ void Draw(const Window *w);
 	/* virtual */ Scrollbar *FindScrollbar(Window *w, bool allow_next = true);
 
+	bool ButtonHit(const Point &pt);
+
 	static void InvalidateDimensionCache();
 private:
 	static Dimension stickybox_dimension; ///< Cached size of a stickybox widget.
--- a/src/widgets/dropdown.cpp
+++ b/src/widgets/dropdown.cpp
@@ -103,7 +103,16 @@
 	{
 		Window *w2 = FindWindowById(this->parent_wnd_class, this->parent_wnd_num);
 		if (w2 != NULL) {
-			w2->RaiseWidget(this->parent_button);
+			if (w2->nested_array != NULL) {
+				NWidgetCore *nwi2 = w2->GetWidget<NWidgetCore>(this->parent_button);
+				if (nwi2->type == NWID_BUTTON_DRPDOWN) {
+					nwi2->disp_flags &= ~ND_DROPDOWN_ACTIVE;
+				} else {
+					w2->RaiseWidget(this->parent_button);
+				}
+			} else {
+				w2->RaiseWidget(this->parent_button);
+			}
 			w2->SetWidgetDirty(this->parent_button);
 		}
 
@@ -251,20 +260,23 @@
 
 	DeleteWindowById(WC_DROPDOWN_MENU, 0);
 
-	w->LowerWidget(button);
-	w->SetWidgetDirty(button);
-
 	/* Our parent's button widget is used to determine where to place the drop
 	 * down list window. */
 	Rect wi_rect;
 	Colours wi_colour;
 	if (w->nested_array != NULL) {
-		const NWidgetCore *nwi = w->GetWidget<NWidgetCore>(button);
+		NWidgetCore *nwi = w->GetWidget<NWidgetCore>(button);
 		wi_rect.left   = nwi->pos_x;
 		wi_rect.right  = nwi->pos_x + nwi->current_x - 1;
 		wi_rect.top    = nwi->pos_y;
 		wi_rect.bottom = nwi->pos_y + nwi->current_y - 1;
 		wi_colour = nwi->colour;
+
+		if (nwi->type == NWID_BUTTON_DRPDOWN) {
+			nwi->disp_flags |= ND_DROPDOWN_ACTIVE;
+		} else {
+			w->LowerWidget(button);
+		}
 	} else {
 		const Widget *wi = &w->widget[button];
 		wi_rect.left   = wi->left;
@@ -272,7 +284,10 @@
 		wi_rect.top    = wi->top;
 		wi_rect.bottom = wi->bottom;
 		wi_colour = wi->colour;
+
+		w->LowerWidget(button);
 	}
+	w->SetWidgetDirty(button);
 
 	/* The preferred position is just below the dropdown calling widget */
 	int top = w->top + wi_rect.bottom + 1;