diff --git a/examples/example1/res/tab_btn_up.xml b/examples/example1/res/tab_btn_up.xml
new file mode 100644
index 00000000..9e16cf29
--- /dev/null
+++ b/examples/example1/res/tab_btn_up.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
diff --git a/src/dlangui/graphics/drawbuf.d b/src/dlangui/graphics/drawbuf.d
index f8cd4874..abb8db38 100644
--- a/src/dlangui/graphics/drawbuf.d
+++ b/src/dlangui/graphics/drawbuf.d
@@ -687,14 +687,14 @@ class Drawable : RefCountedObject {
~this() {
//Log.d("Destroyed drawable, count=", --_instanceCount);
}
- abstract void drawTo(DrawBuf buf, Rect rc, int tilex0 = 0, int tiley0 = 0);
+ abstract void drawTo(DrawBuf buf, Rect rc, uint state = 0, int tilex0 = 0, int tiley0 = 0);
@property abstract int width();
@property abstract int height();
@property Rect padding() { return Rect(0,0,0,0); }
}
class EmptyDrawable : Drawable {
- override void drawTo(DrawBuf buf, Rect rc, int tilex0 = 0, int tiley0 = 0) {
+ override void drawTo(DrawBuf buf, Rect rc, uint state = 0, int tilex0 = 0, int tiley0 = 0) {
}
@property override int width() { return 0; }
@property override int height() { return 0; }
@@ -705,7 +705,7 @@ class SolidFillDrawable : Drawable {
this(uint color) {
_color = color;
}
- override void drawTo(DrawBuf buf, Rect rc, int tilex0 = 0, int tiley0 = 0) {
+ override void drawTo(DrawBuf buf, Rect rc, uint state = 0, int tilex0 = 0, int tiley0 = 0) {
if ((_color >> 24) != 0xFF) // not fully transparent
buf.fillRect(rc, _color);
}
@@ -755,7 +755,7 @@ class ImageDrawable : Drawable {
n3 = n4 = n3 + middledist;
}
}
- override void drawTo(DrawBuf buf, Rect rc, int tilex0 = 0, int tiley0 = 0) {
+ override void drawTo(DrawBuf buf, Rect rc, uint state = 0, int tilex0 = 0, int tiley0 = 0) {
if (_image.isNull)
return;
if (_image.hasNinePatch) {
@@ -833,4 +833,138 @@ class ImageDrawable : Drawable {
}
}
+import std.xml;
+import std.algorithm;
+import dlangui.widgets.styles;
+
+void extractStateFlag(ref string[string] attr, string attrName, State state, ref uint stateMask, ref uint stateValue) {
+ if (attrName in attr) {
+ string value = attr[attrName];
+ if (value.equal("true"))
+ stateValue |= state;
+ stateMask |= state;
+ }
+}
+
+/// converts XML attribute name to State (see http://developer.android.com/guide/topics/resources/drawable-resource.html#StateList)
+void extractStateFlags(ref string[string] attr, ref uint stateMask, ref uint stateValue) {
+ extractStateFlag(attr, "state_pressed", State.Pressed, stateMask, stateValue);
+ extractStateFlag(attr, "state_focused", State.Focused, stateMask, stateValue);
+ extractStateFlag(attr, "state_hovered", State.Hovered, stateMask, stateValue);
+ extractStateFlag(attr, "state_selected", State.Selected, stateMask, stateValue);
+ extractStateFlag(attr, "state_checkable", State.Checkable, stateMask, stateValue);
+ extractStateFlag(attr, "state_checked", State.Checked, stateMask, stateValue);
+ extractStateFlag(attr, "state_enabled", State.Enabled, stateMask, stateValue);
+ extractStateFlag(attr, "state_activated", State.Activated, stateMask, stateValue);
+ extractStateFlag(attr, "state_window_focused", State.WindowFocused, stateMask, stateValue);
+}
+
+/*
+sample:
+(prefix android: is optional)
+
+
+
+
+
+*/
+
+/// Drawable which is drawn depending on state (see http://developer.android.com/guide/topics/resources/drawable-resource.html#StateList)
+class StateDrawable : Drawable {
+
+ static struct StateItem {
+ uint stateMask;
+ uint stateValue;
+ DrawableRef drawable;
+ @property bool matchState(uint state) {
+ return (stateMask & state) == stateValue;
+ }
+ }
+ protected StateItem[] _stateList;
+
+ void addState(uint stateMask, uint stateValue, string resourceId) {
+ StateItem item;
+ item.stateMask = stateMask;
+ item.stateValue = stateValue;
+ item.drawable = dlangui.graphics.images.drawableCache.get(resourceId);
+ _stateList ~= item;
+ }
+
+ void addState(uint stateMask, uint stateValue, DrawableRef drawable) {
+ StateItem item;
+ item.stateMask = stateMask;
+ item.stateValue = stateValue;
+ item.drawable = drawable;
+ _stateList ~= item;
+ }
+
+ bool load(Element element) {
+ foreach(item; element.elements) {
+ if (item.tag.name.equal("item")) {
+ if ("drawable" in item.tag.attr) {
+ string drawableId = item.tag.attr["drawable"];
+ uint stateMask, stateValue;
+ extractStateFlags(item.tag.attr, stateMask, stateValue);
+ if (drawableId !is null) {
+ addState(stateMask, stateValue, drawableId);
+ }
+ }
+ }
+ }
+ return _stateList.length > 0;
+ }
+
+ /// load from XML file
+ bool load(string filename) {
+ import std.file;
+ import std.string;
+
+ try {
+ string s = cast(string)std.file.read(filename);
+
+ // Check for well-formedness
+ check(s);
+
+ // Make a DOM tree
+ auto doc = new Document(s);
+
+ return load(doc);
+ } catch (Exception e) {
+ Log.e("Cannot read drawable resource from file ", filename);
+ return false;
+ }
+ }
+
+ override void drawTo(DrawBuf buf, Rect rc, uint state = 0, int tilex0 = 0, int tiley0 = 0) {
+ foreach(ref item; _stateList)
+ if (item.matchState(state)) {
+ item.drawable.drawTo(buf, rc, state, tilex0, tiley0);
+ return;
+ }
+ }
+
+ @property override int width() {
+ return (_stateList.length > 0) ? _stateList[0].drawable.width : 0;
+ }
+ @property override int height() {
+ return (_stateList.length > 0) ? _stateList[0].drawable.height : 0;
+ }
+ @property override Rect padding() {
+ return (_stateList.length > 0) ? _stateList[0].drawable.padding : Rect(0,0,0,0);
+ }
+}
+
alias DrawableRef = Ref!Drawable;
diff --git a/src/dlangui/widgets/controls.d b/src/dlangui/widgets/controls.d
index 59a30b33..ef5c85d6 100644
--- a/src/dlangui/widgets/controls.d
+++ b/src/dlangui/widgets/controls.d
@@ -341,15 +341,15 @@ class ScrollBar : AbstractSlider, OnClickHandler {
return true;
}
if (event.action == MouseAction.Move && trackHover) {
- if (!(state & State.Hover)) {
+ if (!(state & State.Hovered)) {
Log.d("Hover ", id);
- setState(State.Hover);
+ setState(State.Hovered);
}
return true;
}
if ((event.action == MouseAction.Leave || event.action == MouseAction.Cancel) && trackHover) {
Log.d("Leave ", id);
- resetState(State.Hover);
+ resetState(State.Hovered);
return true;
}
if (event.action == MouseAction.Cancel) {
diff --git a/src/dlangui/widgets/styles.d b/src/dlangui/widgets/styles.d
index 8f86c9ad..6d8bf1ec 100644
--- a/src/dlangui/widgets/styles.d
+++ b/src/dlangui/widgets/styles.d
@@ -20,14 +20,20 @@ immutable int FILL_PARENT = int.max - 1;
immutable int WRAP_CONTENT = int.max - 2;
immutable int WEIGHT_UNSPECIFIED = -1;
+/// widget state flags - bits
enum State : uint {
+ /// state not specified / normal
Normal = 0,
Pressed = 1,
Focused = 2,
- Disabled = 4,
- Hover = 8, // mouse pointer is over control, buttons not pressed
+ Enabled = 4,
+ Hovered = 8, // mouse pointer is over control, buttons not pressed
Selected = 16,
- Parent = 128, // use parent's state
+ Checkable = 32,
+ Checked = 64,
+ Activated = 128,
+ WindowFocused = 256,
+ Parent = 0x10000, // use parent's state
}
enum Align : ubyte {
@@ -621,11 +627,11 @@ Theme createDefaultTheme() {
res.fontSize(14);
Style button = res.createSubstyle("BUTTON").backgroundImageId("btn_default_small_normal").alignment(Align.Center);
Style text = res.createSubstyle("TEXT").margins(Rect(2,2,2,2)).padding(Rect(1,1,1,1));
- button.createState(State.Disabled | State.Focused, State.Disabled | State.Focused).backgroundImageId("btn_default_small_normal_disable_focused");
- button.createState(State.Disabled, State.Disabled).backgroundImageId("btn_default_small_normal_disable");
+ button.createState(State.Enabled | State.Focused, State.Focused).backgroundImageId("btn_default_small_normal_disable_focused");
+ button.createState(State.Enabled, 0).backgroundImageId("btn_default_small_normal_disable");
button.createState(State.Pressed, State.Pressed).backgroundImageId("btn_default_small_pressed");
button.createState(State.Focused, State.Focused).backgroundImageId("btn_default_small_selected");
- button.createState(State.Hover, State.Hover).backgroundImageId("btn_default_small_normal_hover");
+ button.createState(State.Hovered, State.Hovered).backgroundImageId("btn_default_small_normal_hover");
res.setCustomDrawable(ATTR_SCROLLBAR_BUTTON_UP, "scrollbar_btn_up");
res.setCustomDrawable(ATTR_SCROLLBAR_BUTTON_DOWN, "scrollbar_btn_down");
res.setCustomDrawable(ATTR_SCROLLBAR_BUTTON_LEFT, "scrollbar_btn_left");
@@ -639,7 +645,7 @@ Theme createDefaultTheme() {
Style scrollbarSlider = res.createSubstyle("SLIDER");
Style scrollbarPage = res.createSubstyle("PAGE_SCROLL").backgroundColor(0xFFFFFFFF);
scrollbarPage.createState(State.Pressed, State.Pressed).backgroundColor(0xC0404080);
- scrollbarPage.createState(State.Hover, State.Hover).backgroundColor(0xF0404080);
+ scrollbarPage.createState(State.Hovered, State.Hovered).backgroundColor(0xF0404080);
Style tabUp = res.createSubstyle("TAB_UP");
tabUp.backgroundImageId("tab_up_background");
@@ -650,13 +656,13 @@ Theme createDefaultTheme() {
tabUpButtonText.createState(State.Selected, State.Selected).textColor(0x000000);
tabUpButtonText.createState(State.Selected|State.Focused, State.Selected|State.Focused).textColor(0x000000);
tabUpButtonText.createState(State.Focused, State.Focused).textColor(0x000000);
- tabUpButtonText.createState(State.Hover, State.Hover).textColor(0xFFE0E0);
+ tabUpButtonText.createState(State.Hovered, State.Hovered).textColor(0xFFE0E0);
Style tabUpButton = res.createSubstyle("TAB_UP_BUTTON");
tabUpButton.backgroundImageId("tab_btn_up_normal");
tabUpButton.createState(State.Selected, State.Selected).backgroundImageId("tab_btn_up_selected");
tabUpButton.createState(State.Selected|State.Focused, State.Selected|State.Focused).backgroundImageId("tab_btn_up_focused_selected");
tabUpButton.createState(State.Focused, State.Focused).backgroundImageId("tab_btn_up_focused");
- tabUpButton.createState(State.Hover, State.Hover).backgroundImageId("tab_btn_up_hover");
+ tabUpButton.createState(State.Hovered, State.Hovered).backgroundImageId("tab_btn_up_hover");
Style tabHost = res.createSubstyle("TAB_HOST");
tabHost.layoutWidth(FILL_PARENT).layoutHeight(FILL_PARENT);
tabHost.backgroundColor(0xF0F0F0);
diff --git a/src/dlangui/widgets/widget.d b/src/dlangui/widgets/widget.d
index c2cb4249..ad4f29eb 100644
--- a/src/dlangui/widgets/widget.d
+++ b/src/dlangui/widgets/widget.d
@@ -317,7 +317,7 @@ class Widget {
}
if (event.action == MouseAction.FocusOut || event.action == MouseAction.Cancel) {
resetState(State.Pressed);
- resetState(State.Hover);
+ resetState(State.Hovered);
return true;
}
if (event.action == MouseAction.FocusIn) {
@@ -325,15 +325,15 @@ class Widget {
return true;
}
if (event.action == MouseAction.Move && trackHover) {
- if (!(state & State.Hover)) {
+ if (!(state & State.Hovered)) {
Log.d("Hover ", id);
- setState(State.Hover);
+ setState(State.Hovered);
}
return true;
}
if (event.action == MouseAction.Leave && trackHover) {
Log.d("Leave ", id);
- resetState(State.Hover);
+ resetState(State.Hovered);
return true;
}
}