gui: support scrollable lists without headers

If the <header> element doesn't exist, the header is not drawn.

- minuitwrp: add function for clipping all drawing to a rectangle
...and another one to cancel it.

Change-Id: If0f688a5debee6ebd9c457999fe571705a42c5cc
diff --git a/gui/objects.hpp b/gui/objects.hpp
index 537de20..b6937a2 100644
--- a/gui/objects.hpp
+++ b/gui/objects.hpp
@@ -552,7 +552,7 @@
 	// Handle scrolling changes for drags and kinetic scrolling
 	void HandleScrolling();
 
-	// Returns many rows the list is capable of displaying
+	// Returns many full rows the list is capable of displaying
 	int GetDisplayItemCount();
 
 	// Returns the size in pixels of a partial item or row size
@@ -569,7 +569,7 @@
 	COLOR mHeaderFontColor;
 	std::string mHeaderText; // Original header text without parsing any variables
 	std::string mLastHeaderValue; // Header text after parsing variables
-	int mHeaderIsStatic; // indicates if the header is static (no need to check for changes in NotifyVarChange)
+	bool mHeaderIsStatic; // indicates if the header is static (no need to check for changes in NotifyVarChange)
 	int mHeaderH; // actual header height including font, icon, padding, and separator heights
 	ImageResource* mHeaderIcon;
 	int mHeaderIconHeight, mHeaderIconWidth; // width and height of the header icon if present
@@ -579,9 +579,8 @@
 	// Per-item layout
 	FontResource* mFont;
 	COLOR mFontColor;
-	bool hasHighlightColor; // indicates if a hightlight color was set
-	bool hasFontHighlightColor; // indicates if the font hightlight color is set
-	COLOR mHighlightColor; // background row hightlight color
+	bool hasHighlightColor; // indicates if a highlight color was set
+	COLOR mHighlightColor; // background row highlight color
 	COLOR mFontHighlightColor;
 	int mFontHeight;
 	int actualItemHeight; // Actual height of each item in pixels including max icon size, font size, and padding
diff --git a/gui/scrolllist.cpp b/gui/scrolllist.cpp
index 822fa76..05141b1 100644
--- a/gui/scrolllist.cpp
+++ b/gui/scrolllist.cpp
@@ -36,11 +36,11 @@
 {
 	xml_attribute<>* attr;
 	xml_node<>* child;
-	int header_separator_color_specified = 0, header_separator_height_specified = 0, header_text_color_specified = 0, header_background_color_specified = 0;
 
 	firstDisplayedItem = mItemSpacing = mFontHeight = mSeparatorH = y_offset = scrollingSpeed = 0;
 	maxIconWidth = maxIconHeight =  mHeaderIconHeight = mHeaderIconWidth = 0;
-	mHeaderSeparatorH = mHeaderIsStatic = mHeaderH = actualItemHeight = 0;
+	mHeaderSeparatorH = mHeaderH = actualItemHeight = 0;
+	mHeaderIsStatic = false;
 	mBackground = mHeaderIcon = NULL;
 	mFont = NULL;
 	mBackgroundW = mBackgroundH = 0;
@@ -57,45 +57,14 @@
 	ConvertStrToColor("white", &mFastScrollLineColor);
 	ConvertStrToColor("white", &mFastScrollRectColor);
 	hasHighlightColor = false;
-	hasFontHighlightColor = false;
 	selectedItem = NO_ITEM;
 
 	// Load header text
-	child = node->first_node("header");
-	if (child)
-	{
-		mHeaderIcon = LoadAttrImage(child, "icon");
-
-		attr = child->first_attribute("background");
-		if (attr)
-		{
-			std::string color = attr->value();
-			ConvertStrToColor(color, &mHeaderBackgroundColor);
-			header_background_color_specified = -1;
-		}
-		attr = child->first_attribute("textcolor");
-		if (attr)
-		{
-			std::string color = attr->value();
-			ConvertStrToColor(color, &mHeaderFontColor);
-			header_text_color_specified = -1;
-		}
-		attr = child->first_attribute("separatorcolor");
-		if (attr)
-		{
-			std::string color = attr->value();
-			ConvertStrToColor(color, &mHeaderSeparatorColor);
-			header_separator_color_specified = -1;
-		}
-		attr = child->first_attribute("separatorheight");
-		if (attr) {
-			string parsevalue = gui_parse_text(attr->value());
-			mHeaderSeparatorH = scale_theme_y(atoi(parsevalue.c_str()));
-			header_separator_height_specified = -1;
-		}
-	}
 	child = node->first_node("text");
 	if (child)  mHeaderText = child->value();
+	// Simple way to check for static state
+	mLastHeaderValue = gui_parse_text(mHeaderText);
+	mHeaderIsStatic = (mLastHeaderValue == mHeaderText);
 
 	memset(&mHighlightColor, 0, sizeof(COLOR));
 	child = node->first_node("highlight");
@@ -108,25 +77,11 @@
 		}
 	}
 
-	// Simple way to check for static state
-	mLastHeaderValue = gui_parse_text(mHeaderText);
-	if (mLastHeaderValue != mHeaderText)
-		mHeaderIsStatic = 0;
-	else
-		mHeaderIsStatic = -1;
-
 	child = node->first_node("background");
 	if (child)
 	{
 		mBackground = LoadAttrImage(child, "resource");
-		attr = child->first_attribute("color");
-		if (attr)
-		{
-			std::string color = attr->value();
-			ConvertStrToColor(color, &mBackgroundColor);
-			if (!header_background_color_specified)
-				ConvertStrToColor(color, &mHeaderBackgroundColor);
-		}
+		mBackgroundColor = LoadAttrColor(child, "color");
 	}
 
 	// Load the placement
@@ -138,109 +93,61 @@
 	if (child)
 	{
 		mFont = LoadAttrFont(child, "resource");
-
-		attr = child->first_attribute("color");
-		if (attr)
-		{
-			std::string color = attr->value();
-			ConvertStrToColor(color, &mFontColor);
-			if (!header_text_color_specified)
-				ConvertStrToColor(color, &mHeaderFontColor);
-		}
-
-		attr = child->first_attribute("spacing");
-		if (attr) {
-			string parsevalue = gui_parse_text(attr->value());
-			mItemSpacing = scale_theme_y(atoi(parsevalue.c_str()));
-		}
-
-		attr = child->first_attribute("highlightcolor");
-		memset(&mFontHighlightColor, 0, sizeof(COLOR));
-		if (attr)
-		{
-			std::string color = attr->value();
-			ConvertStrToColor(color, &mFontHighlightColor);
-			hasFontHighlightColor = true;
-		}
+		mFontColor = LoadAttrColor(child, "color");
+		mFontHighlightColor = LoadAttrColor(child, "highlightcolor", mFontColor);
+		mItemSpacing = LoadAttrIntScaleY(child, "spacing");
 	}
 
 	// Load the separator if it exists
 	child = node->first_node("separator");
 	if (child)
 	{
-		attr = child->first_attribute("color");
-		if (attr)
-		{
-			std::string color = attr->value();
-			ConvertStrToColor(color, &mSeparatorColor);
-			if (!header_separator_color_specified)
-				ConvertStrToColor(color, &mHeaderSeparatorColor);
-		}
-
-		attr = child->first_attribute("height");
-		if (attr) {
-			string parsevalue = gui_parse_text(attr->value());
-			mSeparatorH = scale_theme_y(atoi(parsevalue.c_str()));
-			if (!header_separator_height_specified)
-				mHeaderSeparatorH = mSeparatorH;
-		}
+		mSeparatorColor = LoadAttrColor(child, "color");
+		mSeparatorH = LoadAttrIntScaleY(child, "height");
 	}
 
-	// Fast scroll colors
+	// Fast scroll
 	child = node->first_node("fastscroll");
 	if (child)
 	{
-		attr = child->first_attribute("linecolor");
-		if(attr)
-			ConvertStrToColor(attr->value(), &mFastScrollLineColor);
+		mFastScrollLineColor = LoadAttrColor(child, "linecolor");
+		mFastScrollRectColor = LoadAttrColor(child, "rectcolor");
 
-		attr = child->first_attribute("rectcolor");
-		if(attr)
-			ConvertStrToColor(attr->value(), &mFastScrollRectColor);
-
-		attr = child->first_attribute("w");
-		if (attr) {
-			string parsevalue = gui_parse_text(attr->value());
-			mFastScrollW = scale_theme_x(atoi(parsevalue.c_str()));
-		}
-
-		attr = child->first_attribute("linew");
-		if (attr) {
-			string parsevalue = gui_parse_text(attr->value());
-			mFastScrollLineW = scale_theme_x(atoi(parsevalue.c_str()));
-		}
-
-		attr = child->first_attribute("rectw");
-		if (attr) {
-			string parsevalue = gui_parse_text(attr->value());
-			mFastScrollRectW = scale_theme_x(atoi(parsevalue.c_str()));
-		}
-
-		attr = child->first_attribute("recth");
-		if (attr) {
-			string parsevalue = gui_parse_text(attr->value());
-			mFastScrollRectH = scale_theme_y(atoi(parsevalue.c_str()));
-		}
+		mFastScrollW = LoadAttrIntScaleX(child, "w");
+		mFastScrollLineW = LoadAttrIntScaleX(child, "linew");
+		mFastScrollRectW = LoadAttrIntScaleX(child, "rectw");
+		mFastScrollRectH = LoadAttrIntScaleY(child, "recth");
 	}
 
 	// Retrieve the line height
 	mFontHeight = mFont->GetHeight();
-	mHeaderH = mFontHeight;
-
-	if (mHeaderIcon && mHeaderIcon->GetResource())
-	{
-		mHeaderIconWidth = mHeaderIcon->GetWidth();
-		mHeaderIconHeight = mHeaderIcon->GetHeight();
-		if (mHeaderIconHeight > mHeaderH)
-			mHeaderH = mHeaderIconHeight;
-		if (mHeaderIconWidth > maxIconWidth)
-			maxIconWidth = mHeaderIconWidth;
-	}
-
-	mHeaderH += mItemSpacing + mHeaderSeparatorH;
 	actualItemHeight = mFontHeight + mItemSpacing + mSeparatorH;
-	if (mHeaderH < actualItemHeight)
-		mHeaderH = actualItemHeight;
+
+	// Load the header if it exists
+	child = node->first_node("header");
+	if (child)
+	{
+		mHeaderH = mFontHeight;
+		mHeaderIcon = LoadAttrImage(child, "icon");
+		mHeaderBackgroundColor = LoadAttrColor(child, "background", mBackgroundColor);
+		mHeaderFontColor = LoadAttrColor(child, "textcolor", mFontColor);
+		mHeaderSeparatorColor = LoadAttrColor(child, "separatorcolor", mSeparatorColor);
+		mHeaderSeparatorH = LoadAttrIntScaleY(child, "separatorheight", mSeparatorH);
+
+		if (mHeaderIcon && mHeaderIcon->GetResource())
+		{
+			mHeaderIconWidth = mHeaderIcon->GetWidth();
+			mHeaderIconHeight = mHeaderIcon->GetHeight();
+			if (mHeaderIconHeight > mHeaderH)
+				mHeaderH = mHeaderIconHeight;
+			if (mHeaderIconWidth > maxIconWidth)
+				maxIconWidth = mHeaderIconWidth;
+		}
+
+		mHeaderH += mItemSpacing + mHeaderSeparatorH;
+		if (mHeaderH < actualItemHeight)
+			mHeaderH = actualItemHeight;
+	}
 
 	if (actualItemHeight / 3 > 6)
 		touchDebounce = actualItemHeight / 3;
@@ -264,7 +171,7 @@
 		maxIconHeight = h;
 	if (maxIconHeight > mFontHeight) {
 		actualItemHeight = maxIconHeight + mItemSpacing + mSeparatorH;
-		if (actualItemHeight > mHeaderH)
+		if (mHeaderH > 0 && actualItemHeight > mHeaderH)
 			mHeaderH = actualItemHeight;
 	}
 }
@@ -300,9 +207,12 @@
 		return 0;
 
 	// First step, fill background
-	gr_color(mBackgroundColor.red, mBackgroundColor.green, mBackgroundColor.blue, 255);
+	gr_color(mBackgroundColor.red, mBackgroundColor.green, mBackgroundColor.blue, mBackgroundColor.alpha);
 	gr_fill(mRenderX, mRenderY + mHeaderH, mRenderW, mRenderH - mHeaderH);
 
+	// don't paint outside of the box
+	gr_clip(mRenderX, mRenderY, mRenderW, mRenderH);
+
 	// Next, render the background resource (if it exists)
 	if (mBackground && mBackground->GetResource())
 	{
@@ -311,7 +221,7 @@
 		gr_blit(mBackground->GetResource(), 0, 0, mBackgroundW, mBackgroundH, mBackgroundX, mBackgroundY);
 	}
 
-	// This tells us how many lines we can actually render
+	// This tells us how many full lines we can actually render
 	size_t lines = GetDisplayItemCount();
 
 	size_t listSize = GetItemCount();
@@ -351,55 +261,46 @@
 
 		if (hasHighlightColor && itemindex == selectedItem) {
 			// Highlight the item background of the selected item
-			gr_color(mHighlightColor.red, mHighlightColor.green, mHighlightColor.blue, 255);
-			int HighlightHeight = actualItemHeight;
-			if (yPos + HighlightHeight > mRenderY + mRenderH) {
-				HighlightHeight = mRenderY + mRenderH - yPos;
-			}
-			gr_fill(mRenderX, yPos, mRenderW, HighlightHeight);
+			gr_color(mHighlightColor.red, mHighlightColor.green, mHighlightColor.blue, mHighlightColor.alpha);
+			gr_fill(mRenderX, yPos, mRenderW, actualItemHeight);
 		}
 
-		if (hasFontHighlightColor && itemindex == selectedItem) {
+		if (itemindex == selectedItem) {
 			// Use the highlight color for the font
-			gr_color(mFontHighlightColor.red, mFontHighlightColor.green, mFontHighlightColor.blue, 255);
+			gr_color(mFontHighlightColor.red, mFontHighlightColor.green, mFontHighlightColor.blue, mFontHighlightColor.alpha);
 		} else {
 			// Set the color for the font
-			gr_color(mFontColor.red, mFontColor.green, mFontColor.blue, 255);
+			gr_color(mFontColor.red, mFontColor.green, mFontColor.blue, mFontColor.alpha);
 		}
 
+		// render icon
 		if (icon && icon->GetResource()) {
 			int currentIconHeight = icon->GetHeight();
 			int currentIconWidth = icon->GetWidth();
-			int currentIconOffsetY = (int)((actualItemHeight - currentIconHeight) / 2);
+			int currentIconOffsetY = (actualItemHeight - currentIconHeight) / 2;
 			int currentIconOffsetX = (maxIconWidth - currentIconWidth) / 2;
-			int rect_y = 0, image_y = (yPos + currentIconOffsetY);
-			if (image_y + currentIconHeight > mRenderY + mRenderH)
-				rect_y = mRenderY + mRenderH - image_y;
-			else
-				rect_y = currentIconHeight;
-			gr_blit(icon->GetResource(), 0, 0, currentIconWidth, rect_y, mRenderX + currentIconOffsetX, image_y);
+			int image_y = (yPos + currentIconOffsetY);
+			gr_blit(icon->GetResource(), 0, 0, currentIconWidth, currentIconHeight, mRenderX + currentIconOffsetX, image_y);
 		}
 
-		gr_textExWH(mRenderX + maxIconWidth + 5, yPos + fontOffsetY, label.c_str(), fontResource, mRenderX + listW, mRenderY + mRenderH);
+		// render label text
+		gr_textEx(mRenderX + maxIconWidth + 5, yPos + fontOffsetY, label.c_str(), fontResource);
 
 		// Add the separator
-		if (yPos + actualItemHeight < mRenderH + mRenderY) {
-			gr_color(mSeparatorColor.red, mSeparatorColor.green, mSeparatorColor.blue, 255);
-			gr_fill(mRenderX, yPos + actualItemHeight - mSeparatorH, listW, mSeparatorH);
-		}
+		gr_color(mSeparatorColor.red, mSeparatorColor.green, mSeparatorColor.blue, mSeparatorColor.alpha);
+		gr_fill(mRenderX, yPos + actualItemHeight - mSeparatorH, listW, mSeparatorH);
 
 		// Move the yPos
 		yPos += actualItemHeight;
 	}
 
 	// Render the Header (last so that it overwrites the top most row for per pixel scrolling)
-	// First step, fill background
-	gr_color(mHeaderBackgroundColor.red, mHeaderBackgroundColor.green, mHeaderBackgroundColor.blue, 255);
-	gr_fill(mRenderX, mRenderY, mRenderW, mHeaderH);
-
-	// Now, we need the header (icon + text)
 	yPos = mRenderY;
-	{
+	if (mHeaderH > 0) {
+		// First step, fill background
+		gr_color(mHeaderBackgroundColor.red, mHeaderBackgroundColor.green, mHeaderBackgroundColor.blue, mHeaderBackgroundColor.alpha);
+		gr_fill(mRenderX, mRenderY, mRenderW, mHeaderH);
+
 		int mIconOffsetX = 0;
 
 		// render the icon if it exists
@@ -411,11 +312,11 @@
 		}
 
 		// render the text
-		gr_color(mHeaderFontColor.red, mHeaderFontColor.green, mHeaderFontColor.blue, 255);
-		gr_textExWH(mRenderX + mIconOffsetX + 5, yPos + (int)((mHeaderH - mFontHeight) / 2), mLastHeaderValue.c_str(), fontResource, mRenderX + mRenderW, mRenderY + mRenderH);
+		gr_color(mHeaderFontColor.red, mHeaderFontColor.green, mHeaderFontColor.blue, mHeaderFontColor.alpha);
+		gr_textEx(mRenderX + mIconOffsetX + 5, yPos + (int)((mHeaderH - mFontHeight) / 2), mLastHeaderValue.c_str(), fontResource);
 
 		// Add the separator
-		gr_color(mHeaderSeparatorColor.red, mHeaderSeparatorColor.green, mHeaderSeparatorColor.blue, 255);
+		gr_color(mHeaderSeparatorColor.red, mHeaderSeparatorColor.green, mHeaderSeparatorColor.blue, mHeaderSeparatorColor.alpha);
 		gr_fill(mRenderX, yPos + mHeaderH - mHeaderSeparatorH, mRenderW, mHeaderSeparatorH);
 	}
 
@@ -427,7 +328,7 @@
 		int fHeight = mRenderH - mHeaderH;
 
 		// line
-		gr_color(mFastScrollLineColor.red, mFastScrollLineColor.green, mFastScrollLineColor.blue, 255);
+		gr_color(mFastScrollLineColor.red, mFastScrollLineColor.green, mFastScrollLineColor.blue, mFastScrollLineColor.alpha);
 		gr_fill(startX + fWidth/2, mRenderY + mHeaderH, mFastScrollLineW, mRenderH - mHeaderH);
 
 		// rect
@@ -442,10 +343,12 @@
 		int mFastScrollRectX = startX + (fWidth - mFastScrollRectW)/2;
 		int mFastScrollRectY = mRenderY+mHeaderH + ((fHeight - mFastScrollRectH)*pct)/100;
 
-		gr_color(mFastScrollRectColor.red, mFastScrollRectColor.green, mFastScrollRectColor.blue, 255);
+		gr_color(mFastScrollRectColor.red, mFastScrollRectColor.green, mFastScrollRectColor.blue, mFastScrollRectColor.alpha);
 		gr_fill(mFastScrollRectX, mFastScrollRectY, mFastScrollRectW, mFastScrollRectH);
 	}
 	mUpdate = 0;
+	// reset clipping
+	gr_noclip();
 	return 0;
 }
 
diff --git a/minuitwrp/graphics.c b/minuitwrp/graphics.c
index 93123e4..7160c7e 100644
--- a/minuitwrp/graphics.c
+++ b/minuitwrp/graphics.c
@@ -558,6 +558,20 @@
     return x;
 }
 
+void gr_clip(int x, int y, int w, int h)
+{
+    GGLContext *gl = gr_context;
+    gl->scissor(gl, x, y, w, h);
+    gl->enable(gl, GGL_SCISSOR_TEST);
+}
+
+void gr_noclip()
+{
+    GGLContext *gl = gr_context;
+    gl->scissor(gl, 0, 0, gr_fb_width(), gr_fb_height());
+    gl->disable(gl, GGL_SCISSOR_TEST);
+}
+
 void gr_fill(int x, int y, int w, int h)
 {
     GGLContext *gl = gr_context;
diff --git a/minuitwrp/minui.h b/minuitwrp/minui.h
index fff7ddb..8ef0444 100644
--- a/minuitwrp/minui.h
+++ b/minuitwrp/minui.h
@@ -36,6 +36,8 @@
 int gr_fb_blank(int blank);
 
 void gr_color(unsigned char r, unsigned char g, unsigned char b, unsigned char a);
+void gr_clip(int x, int y, int w, int h);
+void gr_noclip();
 void gr_fill(int x, int y, int w, int h);
 
 int gr_textEx(int x, int y, const char *s, void* font);