focus rectangle support added

This commit is contained in:
Vadim Lopatin 2015-01-14 11:32:56 +03:00
parent c532ca84e6
commit c815bdbffe
4 changed files with 119 additions and 6 deletions

View File

@ -4,6 +4,7 @@
backgroundImageId="btn_background"
align="Center"
margins="5,5,5,5"
focusRectColors="#000"
/>
<style id="BUTTON_TRANSPARENT"
backgroundImageId="btn_background_transparent"

View File

@ -411,6 +411,32 @@ class DrawBuf : RefCountedObject {
}
}
/// draw focus rectangle; vertical gradient supported - colors[0] is top color, colors[1] is bottom color
void drawFocusRect(Rect rc, const uint[] colors) {
// override for faster performance when using OpenGL
if (colors.length < 1)
return;
uint color1 = colors[0];
uint color2 = colors.length > 1 ? colors[1] : color1;
if (isFullyTransparentColor(color1) && isFullyTransparentColor(color2))
return;
// draw horizontal lines
for (int x = rc.left; x < rc.right; x++) {
if ((x ^ rc.top) & 1)
fillRect(Rect(x, rc.top, x + 1, rc.top + 1), color1);
if ((x ^ (rc.bottom - 1)) & 1)
fillRect(Rect(x, rc.bottom - 1, x + 1, rc.bottom), color2);
}
// draw vertical lines
for (int y = rc.top + 1; y < rc.bottom - 1; y++) {
uint color = color1 == color2 ? color1 : blendARGB(color2, color1, 255 / (rc.bottom - rc.top));
if ((y ^ rc.left) & 1)
fillRect(Rect(rc.left, y, rc.left + 1, y + 1), color);
if ((y ^ (rc.right - 1)) & 1)
fillRect(Rect(rc.right - 1, y, rc.right, y + 1), color);
}
}
/// create drawbuf with copy of current buffer with changed colors (returns this if not supported)
DrawBuf transformColors(ref ColorTransform transform) {
return this;

View File

@ -150,6 +150,8 @@ class Style {
protected int _layoutHeight = SIZE_UNSPECIFIED;
protected int _layoutWeight = WEIGHT_UNSPECIFIED;
protected uint[] _focusRectColors;
protected Style[] _substates;
protected Style[] _children;
@ -550,6 +552,19 @@ class Style {
return this;
}
/// returns colors to draw focus rectangle (one for solid, two for vertical gradient) or null if no focus rect should be drawn for style
@property const(uint[]) focusRectColors() const {
if (_focusRectColors)
return cast(const)_focusRectColors;
return parentStyle.focusRectColors;
}
/// sets colors to draw focus rectangle or null if no focus rect should be drawn for style
@property Style focusRectColors(uint[] colors) {
_focusRectColors = colors;
return this;
}
Style setPadding(int left, int top, int right, int bottom) {
_padding.left = left;
_padding.top = top;
@ -634,7 +649,7 @@ class Theme : Style {
this(string id) {
super(this, id);
_parentStyle = null;
_backgroundColor = 0xFFFFFFFF; // transparent
_backgroundColor = COLOR_TRANSPARENT; // transparent
_textColor = 0x000000; // black
_align = Align.TopLeft;
_fontSize = 14; // TODO: from settings or screen properties / DPI
@ -711,7 +726,14 @@ class Theme : Style {
override uint customColor(string id) {
if (id in _customColors)
return _customColors[id];
return 0xFFFFFFFF;
return COLOR_TRANSPARENT;
}
/// returns colors to draw focus rectangle or null if no focus rect should be drawn for style
@property override const(uint[]) focusRectColors() const {
if (_focusRectColors)
return _focusRectColors;
return null;
}
/// create new named style or get existing
@ -794,7 +816,7 @@ Theme createDefaultTheme() {
scrollbar.backgroundColor(0xC0808080);
Style scrollbarButton = button.createSubstyle("SCROLLBAR_BUTTON");
Style scrollbarSlider = res.createSubstyle("SLIDER");
Style scrollbarPage = res.createSubstyle("PAGE_SCROLL").backgroundColor(0xFFFFFFFF);
Style scrollbarPage = res.createSubstyle("PAGE_SCROLL").backgroundColor(COLOR_TRANSPARENT);
scrollbarPage.createState(State.Pressed, State.Pressed).backgroundColor(0xC0404080);
scrollbarPage.createState(State.Hovered, State.Hovered).backgroundColor(0xF0404080);
@ -896,6 +918,29 @@ uint decodeHexDigit(char ch) {
/// supported formats: #RGB #ARGB #RRGGBB #AARRGGBB
uint decodeColor(string s, uint defValue) {
s = strip(s);
switch (s) {
case "@null":
case "transparent":
return COLOR_TRANSPARENT;
case "black":
return 0x000000;
case "white":
return 0xFFFFFF;
case "red":
return 0xFF0000;
case "green":
return 0x00FF00;
case "blue":
return 0x0000FF;
case "gray":
return 0x808080;
case "lightgray":
case "silver":
return 0xC0C0C0;
default:
break;
}
if (s.length != 4 && s.length != 5 && s.length != 7 && s.length != 9)
return defValue;
if (s[0] != '#')
@ -912,6 +957,23 @@ uint decodeColor(string s, uint defValue) {
return value;
}
private import std.array : split;
/// Decode color list attribute, e.g.: "#84A, #99FFFF" -> [0x8844aa, 0x99ffff]
uint[] decodeFocusRectColors(string s) {
string[] colors = split(s, ",");
if (colors.length < 1)
return null;
uint[] res = new uint[colors.length];
for (int i = 0; i < colors.length; i++) {
uint cl = decodeColor(colors[i], COLOR_UNSPECIFIED);
if (cl == COLOR_UNSPECIFIED)
return null;
res[i] = cl;
}
return res;
}
/// parses string like "Left|VCenter" to bit set of Align flags
ubyte decodeAlignment(string s) {
ubyte res = 0;
@ -1037,6 +1099,8 @@ bool loadStyleAttributes(Style style, Element elem, bool allowStates) {
style.alpha = decodeDimension(elem.tag.attr["alpha"]);
if ("textFlags" in elem.tag.attr)
style.textFlags = decodeTextFlags(elem.tag.attr["textFlags"]);
if ("focusRectColors" in elem.tag.attr)
style.focusRectColors = decodeFocusRectColors(elem.tag.attr["focusRectColors"]);
foreach(item; elem.elements) {
if (allowStates && item.tag.name.equal("state")) {
uint stateMask = 0;
@ -1056,7 +1120,7 @@ bool loadStyleAttributes(Style style, Element elem, bool allowStates) {
// <color id="buttons_panel_color" value="#303080"/>
string colorid = attrValue(item, "id");
string colorvalue = attrValue(item, "value");
uint color = decodeColor(colorvalue, 0xFFFFFFFF);
uint color = decodeColor(colorvalue, COLOR_TRANSPARENT);
if (colorid)
style.setCustomColor(colorid, color);
}

View File

@ -316,6 +316,7 @@ class Widget {
requestLayout();
return this;
}
immutable static int FOCUS_RECT_PADDING = 2;
/// get padding (between background bounds and content of widget)
@property Rect padding() const {
// get max padding from style padding and background drawable padding
@ -332,6 +333,10 @@ class Widget {
if (p.bottom < dp.bottom)
p.bottom = dp.bottom;
}
if (focusable && focusRectColors) {
// add two pixels to padding when focus rect is required - one pixel for focus rect, one for additional space
p.offset(FOCUS_RECT_PADDING, FOCUS_RECT_PADDING);
}
return p;
}
/// set padding for widget - override one from style
@ -360,6 +365,11 @@ class Widget {
return this;
}
/// returns colors to draw focus rectangle (one for solid, two for vertical gradient) or null if no focus rect should be drawn for style
@property const(uint[]) focusRectColors() const {
return style.focusRectColors;
}
/// background drawable
@property DrawableRef backgroundDrawable() const {
return stateStyle.backgroundDrawable;
@ -1108,6 +1118,14 @@ class Widget {
_needLayout = false;
}
/// draws focus rectangle, if enabled in styles
void drawFocusRect(DrawBuf buf, Rect rc) {
const uint[] colors = focusRectColors;
if (colors) {
buf.drawFocusRect(rc, colors);
}
}
/// Draw widget at its position to buffer
void onDraw(DrawBuf buf) {
if (visibility != Visibility.Visible)
@ -1120,6 +1138,10 @@ class Widget {
bg.drawTo(buf, rc, state);
}
applyPadding(rc);
if ((state & State.Focused) && focusable) {
rc.expand(FOCUS_RECT_PADDING, FOCUS_RECT_PADDING);
drawFocusRect(buf, rc);
}
_needDraw = false;
}