From cd731525afbe139cb957be254f594e6e30952e2f Mon Sep 17 00:00:00 2001
From: Vadim Lopatin <buggins@fromru.com>
Date: Thu, 17 Apr 2014 22:48:13 +0400
Subject: [PATCH] fix buttons behavior

---
 examples/example1/src/main.d                  |   9 +--
 res/btn_default_small.xml                     |  58 ++++++++++--------
 res/mdpi/btn_default_small_normal.9.png       | Bin 0 -> 1508 bytes
 .../btn_default_small_normal_disable.9.png    | Bin 0 -> 1487 bytes
 ...default_small_normal_disable_focused.9.png | Bin 0 -> 1506 bytes
 res/mdpi/btn_default_small_pressed.9.png      | Bin 0 -> 1413 bytes
 res/mdpi/btn_default_small_selected.9.png     | Bin 0 -> 1391 bytes
 src/dlangui/widgets/controls.d                |  19 ++++++
 src/dlangui/widgets/lists.d                   |   2 +-
 src/dlangui/widgets/menu.d                    |   1 +
 src/dlangui/widgets/tabs.d                    |   2 +
 src/dlangui/widgets/widget.d                  |  51 ++++++++++++---
 12 files changed, 101 insertions(+), 41 deletions(-)
 create mode 100644 res/mdpi/btn_default_small_normal.9.png
 create mode 100644 res/mdpi/btn_default_small_normal_disable.9.png
 create mode 100644 res/mdpi/btn_default_small_normal_disable_focused.9.png
 create mode 100644 res/mdpi/btn_default_small_pressed.9.png
 create mode 100644 res/mdpi/btn_default_small_selected.9.png

diff --git a/examples/example1/src/main.d b/examples/example1/src/main.d
index 39512412..ba43def4 100644
--- a/examples/example1/src/main.d
+++ b/examples/example1/src/main.d
@@ -1,4 +1,4 @@
-module winmain;
+module main;
 
 import dlangui.all;
 import std.stdio;
@@ -24,13 +24,6 @@ extern (C) int UIAppMain(string[] args) {
     // select translation file - for english language
     i18n.load("en.ini"); //"ru.ini", "en.ini"
 
-
-    DrawBufRef img = imageCache.get("C:\\projects\\d\\dlangui\\res\\mdpi\\btn_radio_on_holo_light.png");
-    ColorDrawBuf buf = cast(ColorDrawBuf)img.get;
-    //GrayDrawBuf gbuf = cast(GrayDrawBuf)img.get;
-    uint * row = buf.scanLine(0);
-    Log.d("btn_radio_on_holo_light pixels: ", row[0], row[1], row[2]);
-
     // create window
     Window window = Platform.instance.createWindow("My Window", null);
 	
diff --git a/res/btn_default_small.xml b/res/btn_default_small.xml
index 358969f7..5485ea03 100644
--- a/res/btn_default_small.xml
+++ b/res/btn_default_small.xml
@@ -1,25 +1,33 @@
-<?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>
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2008 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_window_focused="false" android:state_enabled="true"
+        android:drawable="@drawable/btn_default_small_normal" />
+    <item android:state_window_focused="false" android:state_enabled="false"
+        android:drawable="@drawable/btn_default_small_normal_disable" />
+    <item android:state_pressed="true" 
+        android:drawable="@drawable/btn_default_small_pressed" />
+    <item android:state_focused="true" android:state_enabled="true"
+        android:drawable="@drawable/btn_default_small_selected" />
+    <item android:state_enabled="true"
+        android:drawable="@drawable/btn_default_small_normal" />
+    <item android:state_focused="true"
+        android:drawable="@drawable/btn_default_small_normal_disable_focused" />
+    <item
+         android:drawable="@drawable/btn_default_small_normal_disable" />
+</selector>
+
diff --git a/res/mdpi/btn_default_small_normal.9.png b/res/mdpi/btn_default_small_normal.9.png
new file mode 100644
index 0000000000000000000000000000000000000000..5dddd4640d9c603443dc3fc2ac3dc01e82a92c91
GIT binary patch
literal 1508
zcmeAS@N?(olHy`uVBq!ia0vp^VnD3M!3HD`oXegDq$EpRBT9nv(@M${i&7aJQ}UBi
z6+Ckj(^G>|6H_V+Po~;1FfgZOhD4M^`1)8S=jZArg4F0$<Q4#RGcefLR}>^BXQ!4Z
zB&DWj=GiK}-@RW+Av48RDcsc8z_-9TH6zobswg$M$}c3jDm&RSMakYy!KT6rXh3di
zNuokUZcbjYRfVk**jy_h8zii+qySb@l5ML5aa4qFfP!;=QL2Keo`G(%fti7VnW3Jc
zv5C34xsHO7fuVuEfswwUk*=Y+m9dePfq?=PC;@FNN=dT{a&d#&1?1T(Wt5Z@Sn2DR
zmzV368|&p4rRy77T3Uk4Ff!5ws?aU2%qvN((9J7WhMC}!TAW;zSx}OhpQivaF)=B>
zw8T~k=u(ImatnNY;RfdwLjwvJWcn3}1^R}12Ku?VAb){X7ncO3BAkI$b#X{#L8^XG
zYH@yPQ8F+-(v)GEfnkT=B9KC$Ypnc>GJ%njnB$V4T$-DjSK{ens|1wMOUX>JGIVls
zHa9e}FflYTFtac;bTYHFbT)K#a&vYxbv83JgqeX&ucd{#rK6*%nUjm5xuKzpn}w5w
znT5Hble3eVo1vkh2~4kNUU5lcUUDkT-b|pqK)r?*ZcYYx^;$U>r6!i-7lq{K=fF}x
zKt_H^esM;Afr4|ese*4}a%LWg4~ia$Z(UN0GRsm^+=}vZ6~N(Zm5IfEXG0@%b2D>e
zBNHQI?EZ%6O(7@D^ns4i2PF=q!~zonra%xAp7ennc;-pX1Ll|_VAl9psb<W;z$D`7
z;uunKYfD(H-(druyhCn{o*cdGGb|3X$TZ$<6)xcOJCOK-mmyR9#$kzBEiDxZIe!o9
z8GZ^<_dPE5^75{0KOfa>OkRD$_LN4B#`Dvgbd*_{<#sm&AK-Lg7ZPvaKVYrEx=v`N
z)ftA)bCXndO7pdEuh(uCWD-|be=*%au3z45@kQG`b*xXe9XGUzJNNvvyB@Q;TF`8X
zvuw==dH?J?@PFxBL%Z)?i!?H=UCx}$3ECEMzCfq5=a^iA{rS)Fv0LIMJA4vx{AO{z
zAS7)?_Q^e$%mTYNmfev3y?Ae|Uh5OX;v?Q`c5HMxb@HK&#l|d^ZJrs7|N1<dJCBNp
z9P;_GZri$zGauXA+)?u9n}1Bj^TNZQDIZjh=Plvj+y3fQlWx#6&%lF;``RmY?g~ya
zSvYg~KgHJj$@3UuxWaXIn_OGh^~ig|1R>Qd9T}guAKuJmJ-$)JQM!Mtww9EK+3x<O
zH%eXGyt&>^Ui5NVc5y}5yT~<_Wt(<HH7N8(-#h51moKyXOR0`T*sPjaZD9hcqL+0}
zXMJy3qF?G|*8jF}%CgKYQER8@x4m_eK3;fs*<qdy$7=l4CTH%yzdu2?Z@S3hbhFvM
zh3j70Waqw7w25O}$N0RV`9RNawmppV7|tKqy5dRVkr%d;<eB??)~}KBxcFD(L#j<2
zLwK5Sws2kS<vUw5_Y`kt*tY7Z)JHS-rL|iY3od%-_PL%#f??hkuO(YnSEPf=T~Aj(
Jmvv4FO#obYCF}qI

literal 0
HcmV?d00001

diff --git a/res/mdpi/btn_default_small_normal_disable.9.png b/res/mdpi/btn_default_small_normal_disable.9.png
new file mode 100644
index 0000000000000000000000000000000000000000..6ab5c4a20e03d8c0381da37d2213c0a69f6b7c5e
GIT binary patch
literal 1487
zcmbVMZA=qq96zK1LQuiMm<+qynJf@#@18<?ZJ|T1FVeB{Qiu{!@n{cvz}?mBA*Goc
z(I8o~D1I2?kZr);*rM?znuV#bxe3?+0*iAD2pfz^Cg7Z#F4MRxP__@m54+^<dH&Dy
z|NZ{&_hxSPwzc7L;SdC^HD?;E;`*%kz7!TBe!B-M8pUO!U@R2!xl*B;<S8h_&XrKG
znIX$4D@EFC_VrO|5ENWW7ZeJGmRGelj**jo9l4uvif9N*OLsd-TLmS+B~%&B>X3ym
zx)7MQ>yRBv3ubZZDF>bD;i-I2c7e@PVbj==^q1i@w^k%zD1n6COeO2mx^>7RuU6dq
zk5L3(R1qq4$dXfqmRwlR@f56-V=^12!eAAUgX9z($CF?M1~3#;pg<u5xHeg##V~mJ
zK}2i3y;N&8n3ipck`8eQf>Vp4)z#JV>SQ^`m!UwT(fBzO3Yn-ObJef{>6Wps%>f1j
z<+Aa#Q=mB(_A`<tT$P|hL{FDOV4N1q3Nh<iPL!B3)J-~3AjeRK@yE5O?GmiizizCm
z?JB5oQmB=3aaFuctVikQ09ef3(+&9*MQ^k@JS`T5tTb@8Du!YOvq6W5H*z~|*8+mj
z;~+(y0u-1^4FI9iX!O8HnDiM+y$XN;$7)!OTCG&+lQF^oZ~zP@HKA6iaX{z^l?ecl
z62O{Smq4;MDqxou?Ji*zrl(@HdY&Q$jxXT2%0LI?IyixIIXEY*&sV}0(nhm>*<YTe
zXbluk@2Bi0o@3y}{A%e{?CXI7$5nW;B1MtBdfZD``~S%qDmsJuo8v#t5;zh&(7#=2
zeNk8$9*Pwwh8IV}TC92mK|xQ<hKvID=-1m**{fK@+tF7Oh7)@mwg_RK`a3%I!B}hW
z4P|~qtIhk>*;C$i8*iLT7z*-+M^+wqXwX@Q&zs~Ex4ki`m%GTqiro*>>UL&@91OO6
zk{<N&w)p(<9kq?X_xSZEo#Slr`!&BR+EI1Jk*%|TPsFdQ9~@^NL|sFrp$pRb0pr8n
z?+yNfSLW0%$PrAoW~#Y=MkgGo+mGLSF1Dld^r1d!NQ>+6oI~=;1afz;Z+5D?_NUQ{
zN=f+PX-(*~^lChtlRw#W{#1FeYG(pf^v$8M=6J`htnT*OyH}c~Bxmw!BF2ISC7({Z
zr_VO`P1(z=dHZ&?5ix0_aiQLhNcGLUV?UJN`J>B9mP}f=XG&9N<77r1cz6At>j@X2
z{?^ytXz~u9L}JE@5*vu(zczTk{BUw;sAtpcQ6W4bHa8|_*zCLC-%k8K^P{q1(0fZ=
zmE%Y_0gVKk*VBpPh0^0Wd**MKk60u(r=9besr|lVS@>_Gm*{6w%`ZmEJKoBjx!59E
z@LYc9BRVNHuWdsc{9RQ`fB2ctrAK>w<rh4q_ssX3qDN+h&RW^=D~&&INji6}Goq?B
z+SvU)9!5mh&A~o|xwMtpMK45UKF%%rws(_bI;e^9m0VXp8i<;Z#@_v)VD9AeSw$i9
k{V&9Pc56oeXV;ntD94j_Vj<mF?*GM_joF6N#NLDd0HO;a=Kufz

literal 0
HcmV?d00001

diff --git a/res/mdpi/btn_default_small_normal_disable_focused.9.png b/res/mdpi/btn_default_small_normal_disable_focused.9.png
new file mode 100644
index 0000000000000000000000000000000000000000..c65bace36acdbcc35db309ccf74eed118979d273
GIT binary patch
literal 1506
zcmeAS@N?(olHy`uVBq!ia0vp^VnD3M!3HD`oXegDq$EpRBT9nv(@M${i&7aJQ}UBi
z6+Ckj(^G>|6H_V+Po~;1FfgZOhD4M^`1)8S=jZArg4F0$<Q4#RGcefLR}>^BXQ!4Z
zB&DWj=GiK}-@RW+Av48RDcsc8z_-9TH6zobswg$M$}c3jDm&RSMakYy!KT6rXh3di
zNuokUZcbjYRfVk**jy_h8zii+qySb@l5ML5aa4qFfP!;=QL2Keo`G(%fti7VnW3Jc
zv5C34xsHO7fuVuEfswwUk*=Y+m9dePfq?=PC;@FNN=dT{a&d#&1?1T(Wt5Z@Sn2DR
zmzV368|&p4rRy77T3Uk4Ff!5ws?aU2%qvN((9J7WhMC}!TAW;zSx}OhpQivaF)=B>
zw8T~k=u(ImatnNY;RfdwLjwvJWcn3}1^R}12Ku?VAb){X7ncO3BAkI$b#X{#L8^XG
zYH@yPQ8F+-(v)GEfnkT=B9KC$Ypnc>GJ%njnB$V4T$-DjSK{ens|1wMOUX>JGIVls
zHa9e}FflYTFtac;bTYHFbT)K#a&vYxbv83JgqeX&ucd{BshP8}g_Db+xuKzpn}w5w
znT5Hble3eVo1vkh2~4kNUU5lcUUDkT-b|pqP`&1O^;$U>r6!i-7lq{K=fF}xKt_H^
zesM;Afr4|ese*4}a%LWg4~ia$Z(UN0GRsm^+=}vZ6~N(Zm5IfEXG0@%b2D>eBNHQI
z?EZ%6O(7@D^ns4i2PF=q!~zonra%xAp7ennc;-pX1Ll|_VAlBN#TdfCz$E19;uunK
z>rS|}H?yO_JzM?2jtoVyrm#h=98F?fVgDKv^l$viIC4l{p{c3&#*d7n@&Zk3Q&n!T
zHnsNq>4*qkbm%F!es}L~oY}p!P30GlH7ai2dGr0gzhz}J7qggWe4Mk0pW)BXDM7D)
zCZuRy_M9FpThb<RV><WIQ|fcRtiM06NJi|zmByb9VSR4JnX}V8nO0j)IKSqePJxcV
zfoU}d9K|O$aS0c*w#nQ!df**Dv2bg?)_0?wlfO@E*|A~XqcpRxHCq=X%;~g9IrI5h
z${&-BbHzW+_;LG7TwF6Flf;wT^_O#Uds%b-i5CPcVmt6wfMtrIgmK@d)@KK_0%Ceg
zca|RG>2XxLWyJI2cyy^w$$~h!BNLn$Ui>p&tKiBtZI6~o@x`-~*BEYNn|)kL&A=`!
z-)M{1BZItB!<CjBC1%SB=%1fbc*pAB*Hs*hfya34FWq2cs{0h7!zp#F!6<KEWyI8q
zoijvsb-QGyeBez<h!b75^^baJ<Jqk)?jmuujolkx?-DGRahG0QoGN&;bmoFH;&$)0
zdY3<UUeElQ-+s58>9oi9j&~mn5OO*ts43Cx#QwFB-FtWRoyXctO9Z?Wo4;#Wv)%V_
zVs8{&=k~Vuz4<P+U6)JFZabm#-B5TMpYruP`u14`7JK4#JLPUp_F-J+sGP^Te;KbV
z%eg<NKL1X>m8ZS*ua7|Q_xO@oM-p~++UM<Km0*zB{YHB742j#IQrFYf&t;ucLK6V=
C_aXKG

literal 0
HcmV?d00001

diff --git a/res/mdpi/btn_default_small_pressed.9.png b/res/mdpi/btn_default_small_pressed.9.png
new file mode 100644
index 0000000000000000000000000000000000000000..43e82f977878dff5b8501a8916d69fc8fa89d639
GIT binary patch
literal 1413
zcmeAS@N?(olHy`uVBq!ia0vp^VnD3M!3HD`oXegDq$EpRBT9nv(@M${i&7aJQ}UBi
z6+Ckj(^G>|6H_V+Po~;1FfgZOhD4M^`1)8S=jZArg4F0$<Q4#RGcefLR}>^BXQ!4Z
zB&DWj=GiK}-@RW+Av48RDcsc8z_-9TH6zobswg$M$}c3jDm&RSMakYy!KT6rXh3di
zNuokUZcbjYRfVk**jy_h8zii+qySb@l5ML5aa4qFfP!;=QL2Keo`G(%fti7VnW3Jc
zv5C34xsHO7fuVuEfswwUk*=Y+m9dePfq?=PC;@FNN=dT{a&d#&1?1T(Wt5Z@Sn2DR
zmzV368|&p4rRy77T3Uk4Ff!5ws?aU2%qvN((9J7WhMC}!TAW;zSx}OhpQivaF)=B>
zw8T~k=u(ImatnNY;RfdwLjwvJWcn3}1^R}12Ku?VAb){X7ncO3BAkI$b#X{#L8^XG
zYH@yPQ8F+-(v)GEfnkT=B9KC$Ypnc>GJ%njnB$V4T$-DjSK{ens|1wMOUX>JGIVls
zHa9e}FflYTFtac;bTYHFbT)K#a&vYxbv83JgqeX&ucd{#rK6*%tCNeNxuKzpn}w5w
znT5Hble3eVo1vkh2~4kNUU5lcUUDkT-b|pqP`xgA^;$U>r6!i-7lq{K=fF}xKt_H^
zesM;Afr4|ese*4}a%LWg4~ia$Z(UN0GRsm^+=}vZ6~N(Zm5IfEXG0@%b2D>eBNHQI
z?EZ%6O(7@D^ns4i2PF=q!~zonra%xAp7ennc;-pX1Ll|_VAjYMi95%@z_``Z#WAGf
z)|D_{Z)QV*xt`^&t}OgbB6~PQTWme|de%>3ukT>5pH#8>xRY(Apcnrlg`=x-jr}%7
zzOCCb*JXOr14ps6xO@A2)EFo5f9hb}BI(j#Q?cu$MUsq>lvAJN<B1;+RGepVDQ4^{
zytCJ8XP!E{tbA$t-iK#zrv7hQZo568@5Y(vw<-IboAnyzornnKdHZBa>zC+1rTaT7
zx+NJi-}p?juxFh-`B_w7ztYB!Cfgjnn(uG;`0>uZfRK(1hC+c!^7@$?dE2x?Ie)JD
zt9EXt_|((>r~jUf-Z&*<>FVT;q^&W979XudlV!UU9Sl80vd_jI>6h4gB6*d@`_Qm}
zrAf1*d2{(cezkaOt$K7ua8CW{;Ei!_3wvj~Gj^#yb=#(uwb|*fvUZN|qx)A1Jq<ha
z>!!P0E#>)9tPoS)Z7b@p($(DmWz{<|2c9kaR&59q_;-tI&5otbYa>?N_;dd914;L@
zdmr}kzkT4^>yuZ;(!F}d_Uk*HCvQ}I^4H`}Pjq|X#2dC;iPJZ}?UH1bo1s*fyvOR2
z=9F1AU2h7P+gM#Vqq{(RzU$Mo-)dx@`4`$cKS~VvH=Ti*A-8+6=F5ChW>CTC>FVdQ
I&MBb@0Bgbd7XSbN

literal 0
HcmV?d00001

diff --git a/res/mdpi/btn_default_small_selected.9.png b/res/mdpi/btn_default_small_selected.9.png
new file mode 100644
index 0000000000000000000000000000000000000000..7a376a97f0d695fb8dedaf5f7dc8708276f8a174
GIT binary patch
literal 1391
zcmeAS@N?(olHy`uVBq!ia0vp^VnD3M!3HD`oXegDq$EpRBT9nv(@M${i&7aJQ}UBi
z6+Ckj(^G>|6H_V+Po~;1FfgZOhD4M^`1)8S=jZArg4F0$<Q4#RGcefLR}>^BXQ!4Z
zB&DWj=GiK}-@RW+Av48RDcsc8z_-9TH6zobswg$M$}c3jDm&RSMakYy!KT6rXh3di
zNuokUZcbjYRfVk**jy_h8zii+qySb@l5ML5aa4qFfP!;=QL2Keo`G(%fti7VnW3Jc
zv5C34xsHO7fuVuEfswwUk*=Y+m9dePfq?=PC;@FNN=dT{a&d#&1?1T(Wt5Z@Sn2DR
zmzV368|&p4rRy77T3Uk4Ff!5ws?aU2%qvN((9J7WhMC}!TAW;zSx}OhpQivaF)=B>
zw8T~k=u(ImatnNY;RfdwLjwvJWcn3}1^R}12Ku?VAb){X7ncO3BAkI$b#X{#L8^XG
zYH@yPQ8F+-(v)GEfnkT=B9KC$Ypnc>GJ%njnB$V4T$-DjSK{ens|1wMOUX>JGIVls
zHa9e}FflYTFtac;bTYHFbT)K#a&vYxbv83JgqeX&ucd{Bft#VRsgsMLxuKzpn}w5w
znT5Hble3eVo1vkh2~4kNUU5lcUUDkT-b|pqP`xI2^;$U>r6!i-7lq{K=fF}xKt_H^
zesM;Afr4|ese*4}a%LWg4~ia$Z(UN0GRsm^+=}vZ6~N(Zm5IfEXG0@%b2D>eBNHQI
z?EZ%6O(7@D^ns4i2PF=q!~zonra%xAp7ennc;-pX1Ll|_VAkmW$CAy!z&PL2#WAGf
z)}1hGKW0M#*=lnRjUOc(6WJKK+&f&HXFm=oVmKAk7=7WALLsAygNI4yihw2!j>a|Z
z>FXVTpKQwA<e0QX-7@O@=iBC+g@t#rSHDqBG@9Y5upx$JTcetQWs!;l{|@%JgGXm+
zoy(bU;NH)R*F3B1*FIkS?$fWAZ#LYvKlbUA+!W<&ukV&+Z#CYxSJ_W!dw8Ex*7h=m
zCG}nN%|nlPyKpTLE`NMB&NXx0LkaQDt;>0Xl^y3LzPfedO6%i8eHwR<C?46FbUJAI
z2Br5N#Yes$xtFzH<atMTn1_jJ<?NiIPfE)p1Q*Qs>8`ybSN43se}_Vw*o`Y33wMaB
z9*yigv}YQ}rbSndC&|^ur0+hP7B<B$^RM~Qd0`u7iym}-(Z6Oz{;sn{a?8WJZi%vg
z|M(~Es7U|O^|tG>FLZMqEmFIz`}@mA^MJ$8*w+8P@Quyp%FZb(Yrcupez)3igGEfH
z@_qyR11IL<<R!Pmr|7qCVbZz1dq%;ETh<?#Y?ze={JV}^di1$S-ImAi4I|f)RUR>>
ne0!aRy;o_**zlbCFOb0CD713zv9QicP!Z_q>gTe~DWM4fzpL@p

literal 0
HcmV?d00001

diff --git a/src/dlangui/widgets/controls.d b/src/dlangui/widgets/controls.d
index e0b97828..51bbee49 100644
--- a/src/dlangui/widgets/controls.d
+++ b/src/dlangui/widgets/controls.d
@@ -261,9 +261,26 @@ class RadioButton : ImageTextButton {
         styleId = "TRANSPARENT_BUTTON_BACKGROUND";
         checkable = true;
     }
+
+	void uncheckSiblings() {
+		Widget p = parent;
+		if (!p)
+			return;
+		for (int i = 0; i < p.childCount; i++) {
+			Widget child = p.child(i);
+			if (child is this)
+				continue;
+			RadioButton rb = cast(RadioButton)child;
+			if (rb)
+				rb.checked = false;
+		}
+	}
+
     // called to process click and notify listeners
     override protected bool handleClick() {
+		uncheckSiblings();
         checked = true;
+
         return super.handleClick();
     }
 
@@ -279,6 +296,7 @@ class Button : Widget {
     this(string ID = null) {
 		super(ID);
         styleId = "BUTTON";
+		clickable = true;
         focusable = true;
         trackHover = true;
     }
@@ -399,6 +417,7 @@ class ScrollBar : AbstractSlider, OnClickHandler {
             super(ID);
             styleId = "PAGE_SCROLL";
             trackHover = true;
+            clickable = true;
         }
     }
 
diff --git a/src/dlangui/widgets/lists.d b/src/dlangui/widgets/lists.d
index ce8be428..66cf932e 100644
--- a/src/dlangui/widgets/lists.d
+++ b/src/dlangui/widgets/lists.d
@@ -236,7 +236,7 @@ class ListWidget : WidgetGroup, OnScrollHandler {
     }
 
     /// override to handle focus changes
-    override protected void onFocusChange(bool focused) {
+    override protected void handleFocusChange(bool focused) {
         updateSelectedItemFocus();
     }
 
diff --git a/src/dlangui/widgets/menu.d b/src/dlangui/widgets/menu.d
index abfd671c..99431e57 100644
--- a/src/dlangui/widgets/menu.d
+++ b/src/dlangui/widgets/menu.d
@@ -90,6 +90,7 @@ class MenuItemWidget : HorizontalLayout {
         _label.text = _item.label;
         addChild(_label);
         trackHover = true;
+		clickable = true;
     }
 }
 
diff --git a/src/dlangui/widgets/tabs.d b/src/dlangui/widgets/tabs.d
index f5509454..79c6e2e3 100644
--- a/src/dlangui/widgets/tabs.d
+++ b/src/dlangui/widgets/tabs.d
@@ -20,6 +20,7 @@ Authors:   $(WEB coolreader.org, Vadim Lopatin)
 */
 module dlangui.widgets.tabs;
 
+import dlangui.core.signals;
 import dlangui.widgets.layouts;
 import dlangui.widgets.controls;
 
@@ -82,6 +83,7 @@ class TabItemWidget : HorizontalLayout {
         addChild(_label);
         addChild(_closeButton);
         setItem(item);
+		clickable = true;
         trackHover = true;
     }
     protected bool onClick(Widget source) {
diff --git a/src/dlangui/widgets/widget.d b/src/dlangui/widgets/widget.d
index c0635dfc..7b297b41 100644
--- a/src/dlangui/widgets/widget.d
+++ b/src/dlangui/widgets/widget.d
@@ -52,10 +52,21 @@ enum Orientation : ubyte {
     Horizontal
 }
 
+/// interface - slot for onClick
 interface OnClickHandler {
     bool onClick(Widget source);
 }
 
+/// interface - slot for onCheckChanged
+interface OnCheckHandler {
+    bool onCheckChanged(Widget source, bool checked);
+}
+
+/// interface - slot for onFocusChanged
+interface OnFocusHandler {
+    bool onFocusChanged(Widget source, bool focused);
+}
+
 class Widget {
     /// widget id
     protected string _id;
@@ -152,10 +163,15 @@ class Widget {
     @property uint state() const {
         if ((_state & State.Parent) != 0 && _parent !is null)
             return _parent.state;
-        return _state;
+        return _state | State.WindowFocused; // TODO:
     }
     /// override to handle focus changes
-    protected void onFocusChange(bool focused) {
+    protected void handleFocusChange(bool focused) {
+		onFocusChangeListener(this, checked);
+    }
+    /// override to handle check changes
+    protected void handleCheckChange(bool checked) {
+		onCheckChangeListener(this, checked);
     }
     /// set new widget state (set of flags from State enum)
     @property Widget state(uint newState) {
@@ -166,9 +182,14 @@ class Widget {
             invalidate();
             // notify focus changes
             if ((oldState & State.Focused) && !(newState & State.Focused))
-                onFocusChange(false);
+                handleFocusChange(false);
             else if (!(oldState & State.Focused) && (newState & State.Focused))
-                onFocusChange(true);
+                handleFocusChange(true);
+            // notify checked changes
+            if ((oldState & State.Checked) && !(newState & State.Checked))
+                handleCheckChange(false);
+            else if (!(oldState & State.Checked) && (newState & State.Checked))
+                handleCheckChange(true);
         }
         return this;
     }
@@ -337,13 +358,23 @@ class Widget {
         return _pos.isPointInside(x, y);
     }
 
+	/// return true if state has State.Enabled flag set
+    @property bool enabled() { return (state & State.Enabled) != 0; }
+	/// change enabled state
+    @property Widget enabled(bool flg) { flg ? setState(State.Enabled) : resetState(State.Enabled); return this; }
+
     protected bool _clickable;
+	/// when true, user can click this control, and get onClick listeners called
     @property bool clickable() { return _clickable; }
     @property Widget clickable(bool flg) { _clickable = flg; return this; }
+    @property bool canClick() { return _clickable && enabled && visible; }
 
     protected bool _checkable;
+	/// when true, control supports Checked state
     @property bool checkable() { return _checkable; }
     @property Widget checkable(bool flg) { _checkable = flg; return this; }
+    @property bool canCheck() { return _checkable && enabled && visible; }
+
 
     protected bool _checked;
     /// get checked state
@@ -425,7 +456,7 @@ class Widget {
 
     /// process key event, return true if event is processed.
     bool onKeyEvent(KeyEvent event) {
-		if (clickable) {
+		if (canClick) {
             // support onClick event initiated by Space or Return keys
             if (event.action == KeyAction.KeyDown) {
                 if (event.keyCode == KeyCode.SPACE || event.keyCode == KeyCode.RETURN) {
@@ -448,7 +479,7 @@ class Widget {
     bool onMouseEvent(MouseEvent event) {
         //Log.d("onMouseEvent ", id, " ", event.action, "  (", event.x, ",", event.y, ")");
 		// support onClick
-		if (clickable) {
+		if (canClick) {
 	        if (event.action == MouseAction.ButtonDown && event.button == MouseButton.Left) {
 	            setState(State.Pressed);
                 if (focusable)
@@ -497,10 +528,16 @@ class Widget {
 	    return false;
     }
 
+    // =======================================================
+    // Signals
 
 	/// on click event listener (bool delegate(Widget))
     Signal!OnClickHandler onClickListener;
-	
+	/// checked state change event listener (bool delegate(Widget, bool))
+    Signal!OnCheckHandler onCheckChangeListener;
+	/// focus state change event listener (bool delegate(Widget, bool))
+    Signal!OnFocusHandler onFocusChangeListener;
+
     // =======================================================
     // Layout and measurement methods