[WIP] gui: software drawn keyboard (code only)

Change-Id: I9f05c85f8d43ab012228a78b79220a27876a299d
diff --git a/gui/keyboard.cpp b/gui/keyboard.cpp
index 914c944..8ab2280 100644
--- a/gui/keyboard.cpp
+++ b/gui/keyboard.cpp
@@ -53,13 +53,35 @@
 	mRendered = false;
 	currentLayout = 1;
 	CapsLockOn = false;
-	KeyboardHeight = KeyboardWidth = 0;
 
 	if (!node)  return;
 
 	mHighlightColor = LoadAttrColor(FindNode(node, "highlight"), "color", &hasHighlight);
 	mCapsHighlightColor = LoadAttrColor(FindNode(node, "capshighlight"), "color", &hasCapsHighlight);
 
+	child = FindNode(node, "keymargin");
+	mKeyMarginX = LoadAttrIntScaleX(child, "x", 0);
+	mKeyMarginY = LoadAttrIntScaleY(child, "y", 0);
+
+	child = FindNode(node, "background");
+	mBackgroundColor = LoadAttrColor(child, "color", COLOR(32,32,32,255));
+
+	child = FindNode(node, "key-alphanumeric");
+	mFont = PageManager::GetResources()->FindFont(LoadAttrString(child, "font", "keylabel"));
+	mFontColor = LoadAttrColor(child, "textcolor", COLOR(255,255,255,255));
+	mKeyColorAlphanumeric = LoadAttrColor(child, "color", COLOR(0,0,0,0));
+
+	child = FindNode(node, "key-other");
+	mSmallFont = PageManager::GetResources()->FindFont(LoadAttrString(child, "font", "keylabel-small"));
+	mFontColorSmall = LoadAttrColor(child, "textcolor", COLOR(192,192,192,255));
+	mKeyColorOther = LoadAttrColor(child, "color", COLOR(0,0,0,0));
+
+	child = FindNode(node, "longpress");
+	mLongpressFont = PageManager::GetResources()->FindFont(LoadAttrString(child, "font", "keylabel-longpress"));
+	mLongpressFontColor = LoadAttrColor(child, "textcolor", COLOR(128,128,128,255));
+
+	LoadKeyLabels(node, 0); // load global key labels
+
 	// compatibility ugliness: resources should be specified in the layouts themselves instead
 	// Load the images for the different layouts
 	child = FindNode(node, "layout");
@@ -80,8 +102,8 @@
 	// Check the first image to get height and width
 	if (layouts[0].keyboardImg && layouts[0].keyboardImg->GetResource())
 	{
-		KeyboardWidth = layouts[0].keyboardImg->GetWidth();
-		KeyboardHeight = layouts[0].keyboardImg->GetHeight();
+		mRenderW = layouts[0].keyboardImg->GetWidth();
+		mRenderH = layouts[0].keyboardImg->GetHeight();
 	}
 
 	// Load all of the layout maps
@@ -95,6 +117,8 @@
 			return;
 		}
 
+		LoadKeyLabels(keylayout, layoutindex); // load per-layout key labels
+
 		Layout& lay = layouts[layoutindex - 1];
 
 		child = keylayout->first_node("keysize");
@@ -170,8 +194,8 @@
 
 	int x, y;
 	// Load the placement
-	LoadPlacement(FindNode(node, "placement"), &x, &y);
-	SetRenderPos(x, y, KeyboardWidth, KeyboardHeight);
+	LoadPlacement(FindNode(node, "placement"), &x, &y, &mRenderW, &mRenderH);
+	SetRenderPos(x, y, mRenderW, mRenderH);
 	return;
 }
 
@@ -181,6 +205,7 @@
 
 int GUIKeyboard::ParseKey(const char* keyinfo, Key& key, int& Xindex, int keyWidth, bool longpress)
 {
+	key.layout = 0;
 	int keychar = 0;
 	if (strlen(keyinfo) == 1) {
 		// This is a single key, simple definition
@@ -222,6 +247,29 @@
 	return 0;
 }
 
+void GUIKeyboard::LoadKeyLabels(xml_node<>* parent, int layout)
+{
+	for (xml_node<>* child = parent->first_node(); child; child = child->next_sibling()) {
+		std::string name = child->name();
+		if (name == "keylabel") {
+			std::string keydef = LoadAttrString(child, "key", "");
+			Key tempkey;
+			int dummyX;
+			if (ParseKey(keydef.c_str(), tempkey, dummyX, 0, false) == 0) {
+				KeyLabel keylabel;
+				keylabel.key = tempkey.key;
+				keylabel.layout_from = layout;
+				keylabel.layout_to = tempkey.layout;
+				keylabel.text = LoadAttrString(child, "text", "");
+				keylabel.image = LoadAttrImage(child, "resource");
+				mKeyLabels.push_back(keylabel);
+			} else {
+				LOGERR("Ignoring invalid keylabel in layout %d: '%s'.\n", layout, keydef.c_str());
+			}
+		}
+	}
+}
+
 int GUIKeyboard::Render(void)
 {
 	if (!isConditionTrue())
@@ -233,9 +281,114 @@
 	Layout& lay = layouts[currentLayout - 1];
 
 	if (lay.keyboardImg && lay.keyboardImg->GetResource())
-		gr_blit(lay.keyboardImg->GetResource(), 0, 0, KeyboardWidth, KeyboardHeight, mRenderX, mRenderY);
+		gr_blit(lay.keyboardImg->GetResource(), 0, 0, mRenderW, mRenderH, mRenderX, mRenderY);
+	else {
+		gr_color(mBackgroundColor.red, mBackgroundColor.green, mBackgroundColor.blue, mBackgroundColor.alpha);
+		gr_fill(mRenderX, mRenderY, mRenderW, mRenderH);
+		// draw keys
+		int y1 = 0;
+		for (int row = 0; row < MAX_KEYBOARD_ROWS; ++row) {
+			int rowY = mRenderY + y1;
+			int rowH = lay.row_end_y[row] - y1;
+			y1 = lay.row_end_y[row];
+			int x1 = 0;
+			for (int col = 0; col < MAX_KEYBOARD_KEYS; ++col) {
+				Key& key = lay.keys[row][col];
+				int keyY = rowY;
+				int keyH = rowH;
+				int keyX = mRenderX + x1;
+				int keyW = key.end_x - x1;
+				x1 = key.end_x;
+
+				unsigned char keychar = key.key;
+				if (!keychar)
+					continue;
+
+				// draw a single key
+				// key background
+				COLOR& c = (keychar >= 32 && keychar < 127) ? mKeyColorAlphanumeric : mKeyColorOther;
+				gr_color(c.red, c.green, c.blue, c.alpha);
+				keyX += mKeyMarginX;
+				keyY += mKeyMarginY;
+				keyW -= mKeyMarginX * 2;
+				keyH -= mKeyMarginY * 2;
+				gr_fill(keyX, keyY, keyW, keyH);
+
+				// key label
+				FontResource* labelFont = mFont;
+				string labelText;
+				ImageResource* labelImage = NULL;
+				if (keychar > 32 && keychar < 127) {
+					labelText = (char) keychar;
+					gr_color(mFontColor.red, mFontColor.green, mFontColor.blue, mFontColor.alpha);
+				}
+				else {
+					/*
+					static const char* shiftLabels[] = {"abc", "ABC", "?123", "~\\{"};
+					switch (keychar) {
+						case KEYBOARD_LAYOUT:
+							labelText = shiftLabels[key.layout - 1];
+							break;
+						case KEYBOARD_BACKSPACE:
+							labelText = "Bksp";
+							break;
+						case KEYBOARD_ACTION:
+							labelText = "Enter";
+							break;
+					}
+					*/
+					// search for a special key label
+					for (std::vector<KeyLabel>::iterator it = mKeyLabels.begin(); it != mKeyLabels.end(); ++it) {
+						if (it->layout_from > 0 && it->layout_from != currentLayout)
+							continue; // this label is for another layout
+						if (it->key == key.key && it->layout_to == key.layout)
+						{
+							// found a label
+							labelText = it->text;
+							labelImage = it->image;
+							break;
+						}
+					}
+					labelFont = mSmallFont;
+					gr_color(mFontColorSmall.red, mFontColorSmall.green, mFontColorSmall.blue, mFontColorSmall.alpha);
+				}
+
+				if (labelImage)
+				{
+					int w = labelImage->GetWidth();
+					int h = labelImage->GetHeight();
+					int x = keyX + (keyW - w) / 2;
+					int y = keyY + (keyH - h) / 2;
+					gr_blit(labelImage->GetResource(), 0, 0, w, h, x, y);
+				}
+				else if (!labelText.empty())
+				{
+					void* fontResource = labelFont->GetResource();
+					int textW = gr_measureEx(labelText.c_str(), fontResource);
+					int textH = labelFont->GetHeight();
+					int textX = keyX + (keyW - textW) / 2;
+					int textY = keyY + (keyH - textH) / 2;
+					gr_textEx(textX, textY, labelText.c_str(), fontResource);
+				}
+
+				// longpress key label (only if font is defined)
+				keychar = key.longpresskey;
+				if (keychar > 32 && keychar < 127 && mLongpressFont->GetResource()) {
+					void* fontResource = mLongpressFont->GetResource();
+					gr_color(mLongpressFontColor.red, mLongpressFontColor.green, mLongpressFontColor.blue, mLongpressFontColor.alpha);
+					string text(1, keychar);
+					int textH = mLongpressFont->GetHeight();
+					int textW = gr_measureEx(text.c_str(), fontResource);
+					int textX = keyX + keyW - 5 - textW;	// TODO: configure these offsets
+					int textY = keyY + 0;
+					gr_textEx(textX, textY, text.c_str(), fontResource);
+				}
+			}
+		}
+	}
 
 	// Draw highlight for capslock
+	// TODO: integrate with key drawing
 	if (hasCapsHighlight && lay.is_caps && CapsLockOn) {
 		gr_color(mCapsHighlightColor.red, mCapsHighlightColor.green, mCapsHighlightColor.blue, mCapsHighlightColor.alpha);
 		for (int indexy=0; indexy<MAX_KEYBOARD_ROWS; indexy++) {
@@ -259,6 +412,7 @@
 		}
 	}
 
+	// TODO: integrate with key drawing
 	if (hasHighlight && highlightRenderCount != 0) {
 		int boxheight, boxwidth, x;
 		if (rowY == 0)
@@ -292,10 +446,7 @@
 
 int GUIKeyboard::SetRenderPos(int x, int y, int w, int h)
 {
-	mRenderX = x;
-	mRenderY = y;
-	mRenderW = KeyboardWidth;
-	mRenderH = KeyboardHeight;
+	RenderObject::SetRenderPos(x, y, w, h);
 	SetActionPos(mRenderX, mRenderY, mRenderW, mRenderH);
 	return 0;
 }
@@ -359,14 +510,14 @@
 		break;
 
 	case TOUCH_RELEASE:
-		if (x < startX - (KeyboardWidth * 0.5)) {
+		if (x < startX - (mRenderW * 0.5)) {
 			if (highlightRenderCount != 0) {
 				highlightRenderCount = 0;
 				mRendered = false;
 			}
 			PageManager::NotifyKeyboard(KEYBOARD_SWIPE_LEFT);
 			return 0;
-		} else if (x > startX + (KeyboardWidth * 0.5)) {
+		} else if (x > startX + (mRenderW * 0.5)) {
 			if (highlightRenderCount != 0) {
 				highlightRenderCount = 0;
 				mRendered = false;
diff --git a/gui/objects.hpp b/gui/objects.hpp
index 1991877..881b390 100644
--- a/gui/objects.hpp
+++ b/gui/objects.hpp
@@ -852,6 +852,7 @@
 		int layout;
 	};
 	int ParseKey(const char* keyinfo, Key& key, int& Xindex, int keyWidth, bool longpress);
+	void LoadKeyLabels(xml_node<>* parent, int layout);
 
 	enum {
 		MAX_KEYBOARD_LAYOUTS = 5,
@@ -868,6 +869,16 @@
 	};
 	Layout layouts[MAX_KEYBOARD_LAYOUTS];
 
+	struct KeyLabel
+	{
+		unsigned char key; // same as in struct Key
+		int layout_from; // 1-based; 0 for labels that apply to all layouts
+		int layout_to; // same as Key.layout
+		string text; // key label text
+		ImageResource* image; // image (overrides text if defined)
+	};
+	std::vector<KeyLabel> mKeyLabels;
+
 	// Find key at screen coordinates
 	Key* HitTestKey(int x, int y);
 
@@ -875,11 +886,20 @@
 	std::string mVariable;
 	int currentLayout;
 	bool CapsLockOn;
-	unsigned int KeyboardWidth, KeyboardHeight;
 	int rowY, colX, highlightRenderCount;
 	bool hasHighlight, hasCapsHighlight;
 	COLOR mHighlightColor;
 	COLOR mCapsHighlightColor;
+	COLOR mFontColor; // for centered key labels
+	COLOR mFontColorSmall; // for centered key labels
+	FontResource* mFont; // for main key labels
+	FontResource* mSmallFont; // for key labels like "?123"
+	FontResource* mLongpressFont; // for the small longpress label in the upper right corner
+	COLOR mLongpressFontColor;
+	COLOR mBackgroundColor; // keyboard background color
+	COLOR mKeyColorAlphanumeric; // key background color
+	COLOR mKeyColorOther; // key background color
+	int mKeyMarginX, mKeyMarginY; // space around key boxes - applied to left/right and top/bottom
 };
 
 // GUIInput - Used for keyboard input