state drawables; use state drawables for buttons

This commit is contained in:
Vadim Lopatin 2014-04-11 14:22:20 +04:00
parent 9e562548ba
commit 5accf91de5
10 changed files with 112 additions and 18 deletions

View File

@ -13,7 +13,7 @@
<verbose>0</verbose> <verbose>0</verbose>
<vtls>0</vtls> <vtls>0</vtls>
<symdebug>1</symdebug> <symdebug>1</symdebug>
<optimize>1</optimize> <optimize>0</optimize>
<cpu>0</cpu> <cpu>0</cpu>
<isX86_64>0</isX86_64> <isX86_64>0</isX86_64>
<isLinux>0</isLinux> <isLinux>0</isLinux>
@ -32,7 +32,7 @@
<noboundscheck>0</noboundscheck> <noboundscheck>0</noboundscheck>
<useSwitchError>0</useSwitchError> <useSwitchError>0</useSwitchError>
<useUnitTests>0</useUnitTests> <useUnitTests>0</useUnitTests>
<useInline>1</useInline> <useInline>0</useInline>
<release>0</release> <release>0</release>
<preservePaths>0</preservePaths> <preservePaths>0</preservePaths>
<warnings>0</warnings> <warnings>0</warnings>

View File

@ -13,7 +13,7 @@
<verbose>0</verbose> <verbose>0</verbose>
<vtls>0</vtls> <vtls>0</vtls>
<symdebug>1</symdebug> <symdebug>1</symdebug>
<optimize>1</optimize> <optimize>0</optimize>
<cpu>0</cpu> <cpu>0</cpu>
<isX86_64>0</isX86_64> <isX86_64>0</isX86_64>
<isLinux>0</isLinux> <isLinux>0</isLinux>
@ -29,7 +29,7 @@
<useIn>0</useIn> <useIn>0</useIn>
<useOut>0</useOut> <useOut>0</useOut>
<useArrayBounds>0</useArrayBounds> <useArrayBounds>0</useArrayBounds>
<noboundscheck>1</noboundscheck> <noboundscheck>0</noboundscheck>
<useSwitchError>0</useSwitchError> <useSwitchError>0</useSwitchError>
<useUnitTests>0</useUnitTests> <useUnitTests>0</useUnitTests>
<useInline>0</useInline> <useInline>0</useInline>

View File

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android"
android:constantSize="true"
android:dither="false"
android:variablePadding="false" >
<item
android:drawable="btn_default_small_normal_disable_focused"
android:state_enabled="false" />
android:state_focused="true" />
<item
android:drawable="btn_default_small_normal_disable"
android:state_focused="true" />
<item
android:drawable="btn_default_small_pressed"
android:state_pressed="true" />
<item
android:drawable="btn_default_small_selected"
android:state_selected="true" />
<item
android:drawable="btn_default_small_normal_hover"
android:state_hovered="true" />
<item
android:drawable="btn_default_small_normal" />
</selector>

View File

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android"
android:constantSize="true"
android:dither="false"
android:variablePadding="false" >
<item
android:drawable="btn_default_small_normal_disable_focused"
android:state_enabled="false" />
android:state_focused="true" />
<item
android:drawable="btn_default_small_normal_disable"
android:state_focused="true" />
<item
android:drawable="btn_default_small_pressed"
android:state_pressed="true" />
<item
android:drawable="btn_default_small_selected"
android:state_selected="true" />
<item
android:drawable="btn_default_small_normal_hover"
android:state_hovered="true" />
<item
android:drawable="@null" />
</selector>

View File

@ -24,6 +24,17 @@ struct Rect {
top += dy; top += dy;
bottom += dy; bottom += dy;
} }
/// for all fields, sets this.field to rc.field if rc.field > this.field
void setMax(Rect rc) {
if (left < rc.left)
left = rc.left;
if (right < rc.right)
right = rc.right;
if (top < rc.top)
top = rc.top;
if (bottom < rc.bottom)
bottom = rc.bottom;
}
@property int width() { return right - left; } @property int width() { return right - left; }
@property int height() { return bottom - top; } @property int height() { return bottom - top; }
this(int x0, int y0, int x1, int y1) { this(int x0, int y0, int x1, int y1) {

View File

@ -235,14 +235,19 @@ class StateDrawable : Drawable {
return (stateMask & state) == stateValue; return (stateMask & state) == stateValue;
} }
} }
// list of states
protected StateItem[] _stateList; protected StateItem[] _stateList;
// max paddings for all states
protected Rect _paddings;
// max drawable size for all states
protected Point _size;
void addState(uint stateMask, uint stateValue, string resourceId) { void addState(uint stateMask, uint stateValue, string resourceId) {
StateItem item; StateItem item;
item.stateMask = stateMask; item.stateMask = stateMask;
item.stateValue = stateValue; item.stateValue = stateValue;
item.drawable = drawableCache.get(resourceId); item.drawable = drawableCache.get(resourceId);
_stateList ~= item; itemAdded(item);
} }
void addState(uint stateMask, uint stateValue, DrawableRef drawable) { void addState(uint stateMask, uint stateValue, DrawableRef drawable) {
@ -250,7 +255,18 @@ class StateDrawable : Drawable {
item.stateMask = stateMask; item.stateMask = stateMask;
item.stateValue = stateValue; item.stateValue = stateValue;
item.drawable = drawable; item.drawable = drawable;
itemAdded(item);
}
private void itemAdded(ref StateItem item) {
_stateList ~= item; _stateList ~= item;
if (!item.drawable.isNull) {
if (_size.x < item.drawable.width)
_size.x = item.drawable.width;
if (_size.y < item.drawable.height)
_size.y = item.drawable.height;
_paddings.setMax(item.drawable.padding);
}
} }
bool load(Element element) { bool load(Element element) {
@ -296,19 +312,20 @@ class StateDrawable : Drawable {
override void drawTo(DrawBuf buf, Rect rc, uint state = 0, int tilex0 = 0, int tiley0 = 0) { override void drawTo(DrawBuf buf, Rect rc, uint state = 0, int tilex0 = 0, int tiley0 = 0) {
foreach(ref item; _stateList) foreach(ref item; _stateList)
if (item.matchState(state)) { if (item.matchState(state)) {
if (!item.drawable.isNull)
item.drawable.drawTo(buf, rc, state, tilex0, tiley0); item.drawable.drawTo(buf, rc, state, tilex0, tiley0);
return; return;
} }
} }
@property override int width() { @property override int width() {
return (_stateList.length > 0) ? _stateList[0].drawable.width : 0; return _size.x;
} }
@property override int height() { @property override int height() {
return (_stateList.length > 0) ? _stateList[0].drawable.height : 0; return _size.y;
} }
@property override Rect padding() { @property override Rect padding() {
return (_stateList.length > 0) ? _stateList[0].drawable.padding : Rect(0,0,0,0); return _paddings;
} }
} }
@ -498,7 +515,10 @@ class DrawableCache {
string[] _resourcePaths; string[] _resourcePaths;
string[string] _idToFileMap; string[string] _idToFileMap;
DrawableCacheItem[string] _idToDrawableMap; DrawableCacheItem[string] _idToDrawableMap;
DrawableRef _nullDrawable;
ref DrawableRef get(string id) { ref DrawableRef get(string id) {
if (id.equal("@null"))
return _nullDrawable;
if (id in _idToDrawableMap) if (id in _idToDrawableMap)
return _idToDrawableMap[id].drawable; return _idToDrawableMap[id].drawable;
string resourceId = id; string resourceId = id;

View File

@ -123,7 +123,7 @@ class ImageWidget : Widget {
sz.x = img.width; sz.x = img.width;
sz.y = img.height; sz.y = img.height;
applyAlign(rc, sz); applyAlign(rc, sz);
img.drawTo(buf, rc); img.drawTo(buf, rc, state);
} }
} }
} }

View File

@ -482,7 +482,7 @@ class Style {
if (state == 0) if (state == 0)
return this; return this;
//Log.d("forState ", state, " styleId=", _id, " substates=", _substates.length); //Log.d("forState ", state, " styleId=", _id, " substates=", _substates.length);
if (parentStyle !is null && _substates.length == 0) //id is null && if (parentStyle !is null && _substates.length == 0 && parentStyle._substates.length > 0) //id is null &&
return parentStyle.forState(state); return parentStyle.forState(state);
foreach(item; _substates) { foreach(item; _substates) {
if ((item._stateMask & state) == item._stateValue) if ((item._stateMask & state) == item._stateValue)
@ -584,6 +584,11 @@ class Theme : Style {
return this; return this;
} }
/// find substyle based on widget state (e.g. focused, pressed, ...)
override const(Style) forState(uint state) const {
return this;
}
void dumpStats() { void dumpStats() {
Log.d("Theme ", _id, ": children:", _children.length, ", substates:", _substates.length, ", mapsize:", _byId.length); Log.d("Theme ", _id, ": children:", _children.length, ", substates:", _substates.length, ", mapsize:", _byId.length);
} }
@ -610,13 +615,14 @@ Theme createDefaultTheme() {
Log.d("Creating default theme"); Log.d("Creating default theme");
Theme res = new Theme("default"); Theme res = new Theme("default");
res.fontSize(14); res.fontSize(14);
Style button = res.createSubstyle("BUTTON").backgroundImageId("btn_default_small_normal").alignment(Align.Center); Style button = res.createSubstyle("BUTTON").backgroundImageId("btn_default_small").alignment(Align.Center);
Style buttonTransparent = res.createSubstyle("BUTTON_TRANSPARENT").backgroundImageId("btn_default_small_transparent").alignment(Align.Center);
Style text = res.createSubstyle("TEXT").margins(Rect(2,2,2,2)).padding(Rect(1,1,1,1)); Style text = res.createSubstyle("TEXT").margins(Rect(2,2,2,2)).padding(Rect(1,1,1,1));
button.createState(State.Enabled | State.Focused, State.Focused).backgroundImageId("btn_default_small_normal_disable_focused"); //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.Enabled, 0).backgroundImageId("btn_default_small_normal_disable");
button.createState(State.Pressed, State.Pressed).backgroundImageId("btn_default_small_pressed"); //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.Focused, State.Focused).backgroundImageId("btn_default_small_selected");
button.createState(State.Hovered, State.Hovered).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_UP, "scrollbar_btn_up");
res.setCustomDrawable(ATTR_SCROLLBAR_BUTTON_DOWN, "scrollbar_btn_down"); res.setCustomDrawable(ATTR_SCROLLBAR_BUTTON_DOWN, "scrollbar_btn_down");
res.setCustomDrawable(ATTR_SCROLLBAR_BUTTON_LEFT, "scrollbar_btn_left"); res.setCustomDrawable(ATTR_SCROLLBAR_BUTTON_LEFT, "scrollbar_btn_left");

View File

@ -49,6 +49,7 @@ class TabItemWidget : HorizontalLayout {
_label.styleId = "TAB_UP_BUTTON_TEXT"; _label.styleId = "TAB_UP_BUTTON_TEXT";
_label.state = State.Parent; _label.state = State.Parent;
_closeButton = new ImageButton("CLOSE"); _closeButton = new ImageButton("CLOSE");
_closeButton.styleId = "BUTTON_TRANSPARENT";
_closeButton.drawableId = "close"; _closeButton.drawableId = "close";
_closeButton.onClickListener = &onClick; _closeButton.onClickListener = &onClick;
if (_enableCloseButton) { if (_enableCloseButton) {
@ -152,6 +153,7 @@ class TabControl : WidgetGroup {
super(ID); super(ID);
_items = new TabItemList(); _items = new TabItemList();
_moreButton = new ImageButton("MORE", "tab_more"); _moreButton = new ImageButton("MORE", "tab_more");
_moreButton.styleId = "BUTTON_TRANSPARENT";
_moreButton.onClickListener = &onClick; _moreButton.onClickListener = &onClick;
_enableCloseButton = true; _enableCloseButton = true;
styleId = "TAB_UP"; styleId = "TAB_UP";
@ -231,6 +233,7 @@ class TabControl : WidgetGroup {
} }
/// Measure widget according to desired width and height constraints. (Step 1 of two phase layout). /// Measure widget according to desired width and height constraints. (Step 1 of two phase layout).
override void measure(int parentWidth, int parentHeight) { override void measure(int parentWidth, int parentHeight) {
//Log.d("tabControl.measure enter");
Rect m = margins; Rect m = margins;
Rect p = padding; Rect p = padding;
// calc size constraints for children // calc size constraints for children
@ -257,9 +260,11 @@ class TabControl : WidgetGroup {
sz.x += tab.measuredWidth; sz.x += tab.measuredWidth;
} }
measuredContent(parentWidth, parentHeight, sz.x, sz.y); measuredContent(parentWidth, parentHeight, sz.x, sz.y);
//Log.d("tabControl.measure exit");
} }
/// Set widget rectangle to specified value and layout widget contents. (Step 2 of two phase layout). /// Set widget rectangle to specified value and layout widget contents. (Step 2 of two phase layout).
override void layout(Rect rc) { override void layout(Rect rc) {
//Log.d("tabControl.layout enter");
_needLayout = false; _needLayout = false;
if (visibility == Visibility.Gone) { if (visibility == Visibility.Gone) {
return; return;
@ -297,9 +302,11 @@ class TabControl : WidgetGroup {
widget.layout(rc); widget.layout(rc);
rc.left += w; rc.left += w;
} }
//Log.d("tabControl.layout exit");
} }
/// Draw widget at its position to buffer /// Draw widget at its position to buffer
override void onDraw(DrawBuf buf) { override void onDraw(DrawBuf buf) {
//Log.d("tabControl.onDraw enter");
if (visibility != Visibility.Visible) if (visibility != Visibility.Visible)
return; return;
super.onDraw(buf); super.onDraw(buf);
@ -313,6 +320,7 @@ class TabControl : WidgetGroup {
continue; continue;
item.onDraw(buf); item.onDraw(buf);
} }
//Log.d("tabControl.onDraw exit");
} }
protected string _selectedTabId; protected string _selectedTabId;

View File

@ -77,6 +77,7 @@ class Widget {
/// create widget, with optional id /// create widget, with optional id
this(string ID = null) { this(string ID = null) {
_id = ID; _id = ID;
_state = State.Enabled;
//Log.d("Created widget, count = ", ++_instanceCount); //Log.d("Created widget, count = ", ++_instanceCount);
} }
~this() { ~this() {
@ -413,7 +414,7 @@ class Widget {
applyMargins(rc); applyMargins(rc);
DrawableRef bg = stateStyle.backgroundDrawable; DrawableRef bg = stateStyle.backgroundDrawable;
if (!bg.isNull) { if (!bg.isNull) {
bg.drawTo(buf, rc); bg.drawTo(buf, rc, state);
} }
applyPadding(rc); applyPadding(rc);
_needDraw = false; _needDraw = false;