From ff13dce0818d48dc05a6fb0dc5aac776ac4e2156 Mon Sep 17 00:00:00 2001 From: Vadim Lopatin Date: Fri, 18 Apr 2014 11:50:49 +0400 Subject: [PATCH] support of frame drawables; use frame drawable as list item background --- res/list_item_background.xml | 18 ++++++ src/dlangui/graphics/drawbuf.d | 44 +++++++++++++ src/dlangui/graphics/resources.d | 107 ++++++++++++++++++++++++++++++- src/dlangui/widgets/styles.d | 6 +- 4 files changed, 171 insertions(+), 4 deletions(-) create mode 100644 res/list_item_background.xml diff --git a/res/list_item_background.xml b/res/list_item_background.xml new file mode 100644 index 00000000..e65bfc32 --- /dev/null +++ b/res/list_item_background.xml @@ -0,0 +1,18 @@ + + + + + + + + diff --git a/src/dlangui/graphics/drawbuf.d b/src/dlangui/graphics/drawbuf.d index 713b3c01..379cb6b9 100644 --- a/src/dlangui/graphics/drawbuf.d +++ b/src/dlangui/graphics/drawbuf.d @@ -101,6 +101,11 @@ ubyte blendGray(ubyte dst, ubyte src, uint alpha) { return cast(ubyte)(((src * ialpha + dst * alpha) >> 8) & 0xFF); } +/// returns true if color is #FFxxxxxx (color alpha is 255) +bool isFullyTransparentColor(uint color) pure nothrow { + return (color >> 24) == 0xFF; +} + /** * 9-patch image scaling information (see Android documentation). * @@ -304,6 +309,45 @@ class DrawBuf : RefCountedObject { void drawImage(int x, int y, DrawBuf src) { drawFragment(x, y, src, Rect(0, 0, src.width, src.height)); } + /// draws rectangle frame of specified color and widths (per side), and optinally fills inner area + void drawFrame(Rect rc, uint frameColor, Rect frameSideWidths, uint innerAreaColor = 0xFFFFFFFF) { + // draw frame + if (!isFullyTransparentColor(frameColor)) { + Rect r; + // left side + r = rc; + r.right = r.left + frameSideWidths.left; + if (!r.empty) + fillRect(r, frameColor); + // right side + r = rc; + r.left = r.right - frameSideWidths.right; + if (!r.empty) + fillRect(r, frameColor); + // top side + r = rc; + r.left -= frameSideWidths.left; + r.right -= frameSideWidths.right; + Rect rc2 = r; + rc2.bottom = r.top + frameSideWidths.top; + if (!rc2.empty) + fillRect(rc2, frameColor); + // bottom side + rc2 = r; + rc2.top = r.bottom - frameSideWidths.bottom; + if (!rc2.empty) + fillRect(rc2, frameColor); + } + // draw internal area + if (!isFullyTransparentColor(innerAreaColor)) { + rc.left += frameSideWidths.left; + rc.top += frameSideWidths.top; + rc.right -= frameSideWidths.right; + rc.bottom -= frameSideWidths.bottom; + if (!rc.empty) + fillRect(rc, innerAreaColor); + } + } /// create drawbuf with copy of current buffer with changed colors (returns this if not supported) DrawBuf transformColors(ref ColorTransform transform) { diff --git a/src/dlangui/graphics/resources.d b/src/dlangui/graphics/resources.d index 6ce5605c..8ce9b299 100644 --- a/src/dlangui/graphics/resources.d +++ b/src/dlangui/graphics/resources.d @@ -70,6 +70,102 @@ class SolidFillDrawable : Drawable { @property override int height() { return 1; } } +/// solid borders (may be of different width) and, optionally, solid inner area +class FrameDrawable : Drawable { + protected uint _frameColor; // frame color + protected Rect _frameWidths; // left, top, right, bottom border widths, in pixels + protected uint _middleColor; // middle area color (may be transparent) + this(uint frameColor, Rect borderWidths, uint innerAreaColor = 0xFFFFFFFF) { + _frameColor = frameColor; + _frameWidths = borderWidths; + _middleColor = innerAreaColor; + } + this(uint frameColor, int borderWidth, uint innerAreaColor = 0xFFFFFFFF) { + _frameColor = frameColor; + _frameWidths = Rect(borderWidth, borderWidth, borderWidth, borderWidth); + _middleColor = innerAreaColor; + } + override void drawTo(DrawBuf buf, Rect rc, uint state = 0, int tilex0 = 0, int tiley0 = 0) { + buf.drawFrame(rc, _frameColor, _frameWidths, _middleColor); + } + @property override int width() { return 1 + _frameWidths.left + _frameWidths.right; } + @property override int height() { return 1 + _frameWidths.top + _frameWidths.bottom; } + @property override Rect padding() { return _frameWidths; } +} + +/// decode color string #AARRGGBB, e.g. #5599AA +static uint decodeHexColor(string s) { + uint value = 0; + foreach(c; s) { + int digit = -1; + if (c >='0' && c <= '9') + digit = c - '0'; + else if (c >='a' && c <= 'f') + digit = c - 'a' + 10; + else if (c >='A' && c <= 'F') + digit = c - 'A' + 10; + if (digit >= 0) + value = (value << 4) | digit; + } + return value; +} +/// decode size string, e.g. 1px or 2 or 3pt +static uint decodeDimension(string s) { + uint value = 0; + int units = 0; + foreach(c; s) { + int digit = -1; + if (c >='0' && c <= '9') + digit = c - '0'; + if (digit >= 0) + value = value * 10 + digit; + else if (c == 't') // just test by containing 't' - for NNNpt + units = 1; // "pt" + } + // TODO: convert points to pixels + return value; +} + +/// decode solid color / gradient / frame drawable from string like #AARRGGBB, e.g. #5599AA +/// --- +/// SolidFillDrawable: #AARRGGBB - e.g. #8090A0 or #80ffffff +/// FrameDrawable: #frameColor,frameWidth[,#middleColor] +/// or #frameColor,leftBorderWidth,topBorderWidth,rightBorderWidth,bottomBorderWidth[,#middleColor] +/// e.g. #000000,2,#C0FFFFFF - black frame of width 2 with 75% transparent white middle +/// e.g. #0000FF,2,3,4,5,#FFFFFF - blue frame with left,top,right,bottom borders of width 2,3,4,5 and white inner area +static Drawable createColorDrawable(string s) { + Log.d("creating color drawable ", s); + uint[6] values; + int valueCount = 0; + int start = 0; + for (int i = 0; i <= s.length; i++) { + if (i == s.length || s[i] == ',') { + if (i > start) { + string item = s[start .. i]; + if (item.startsWith("#")) + values[valueCount++] = decodeHexColor(item); + else + values[valueCount++] = decodeDimension(item); + if (valueCount >= 6) + break; + } + start = i + 1; + } + } + if (valueCount == 1) // only color #AARRGGBB + return new SolidFillDrawable(values[0]); + else if (valueCount == 2) // frame color and frame width, with transparent inner area - #AARRGGBB,NN + return new FrameDrawable(values[0], values[1]); + else if (valueCount == 3) // frame color, frame width, inner area color - #AARRGGBB,NN,#AARRGGBB + return new FrameDrawable(values[0], values[1], values[2]); + else if (valueCount == 5) // frame color, frame widths for left,top,right,bottom and transparent inner area - #AARRGGBB,NNleft,NNtop,NNright,NNbottom + return new FrameDrawable(values[0], Rect(values[1], values[2], values[3], values[4])); + else if (valueCount == 6) // frame color, frame widths for left,top,right,bottom, inner area color - #AARRGGBB,NNleft,NNtop,NNright,NNbottom,#AARRGGBB + return new FrameDrawable(values[0], Rect(values[1], values[2], values[3], values[4]), values[5]); + Log.e("Invalid drawable string format: ", s); + return new EmptyDrawable(); // invalid format - just return empty drawable +} + class ImageDrawable : Drawable { protected DrawBufRef _image; protected bool _tiled; @@ -423,7 +519,7 @@ alias DrawableRef = Ref!Drawable; -/// decoded image cache +/// decoded raster images cache (png, jpeg) -- access by filenames class ImageCache { static class ImageCacheItem { @@ -587,6 +683,7 @@ class DrawableCache { if (!_used) compact(); } + /// returns drawable (loads from file if necessary) @property ref DrawableRef drawable() { _used = true; @@ -603,6 +700,9 @@ class DrawableCache { } else { _drawable = d; } + } else if (_filename.startsWith("#")) { + // color reference #AARRGGBB, e.g. #5599AA, or FrameDrawable description string #frameColor,frameSize,#innerColor + _drawable = createColorDrawable(_filename); } else { // PNG/JPEG drawables support DrawBufRef image = imageCache.get(_filename); @@ -637,6 +737,9 @@ class DrawableCache { Log.d("loaded .xml drawable from ", _filename); _drawable = d; } + } else if (_filename.startsWith("#")) { + // color reference #AARRGGBB, e.g. #5599AA, or FrameDrawable description string #frameColor,frameSize,#innerColor + _drawable = createColorDrawable(_filename); } else { // PNG/JPEG drawables support DrawBufRef image = imageCache.get(_filename, transform); @@ -739,6 +842,8 @@ class DrawableCache { return null; } string findResource(string id) { + if (id.startsWith("#")) + return id; // it's not a file name, just a color #AARRGGBB if (id in _idToFileMap) return _idToFileMap[id]; foreach(string path; _resourcePaths) { diff --git a/src/dlangui/widgets/styles.d b/src/dlangui/widgets/styles.d index 9e09c070..97194b8d 100644 --- a/src/dlangui/widgets/styles.d +++ b/src/dlangui/widgets/styles.d @@ -709,9 +709,9 @@ Theme createDefaultTheme() { Style poopupMenu = res.createSubstyle("POPUP_MENU").backgroundImageId("popup_menu_background_normal"); - Style listItem = res.createSubstyle("LIST_ITEM"); - listItem.createState(State.Selected, State.Selected).backgroundColor(0xC04040FF).textColor(0x000000); - listItem.createState(State.Enabled, 0).textColor(0x80000000); // half transparent text for disabled item + Style listItem = res.createSubstyle("LIST_ITEM").backgroundImageId("list_item_background"); + //listItem.createState(State.Selected, State.Selected).backgroundColor(0xC04040FF).textColor(0x000000); + //listItem.createState(State.Enabled, 0).textColor(0x80000000); // half transparent text for disabled item return res; }