kivyblocks/kivyblocks/clickable.py
2022-10-09 14:09:31 +08:00

412 lines
9.9 KiB
Python

from functools import partial
from kivy.logger import Logger
from kivy.uix.behaviors import TouchRippleButtonBehavior
from kivy.graphics import Color, Rectangle
from kivy.uix.boxlayout import BoxLayout
from kivy.factory import Factory
from kivy.uix.image import AsyncImage
from kivy.properties import NumericProperty, DictProperty, \
ObjectProperty, StringProperty, BooleanProperty
from kivyblocks.ready import WidgetReady
from kivyblocks.bgcolorbehavior import BGColorBehavior
from kivyblocks.utils import CSize, SUPER, blockImage
from kivyblocks.baseWidget import Box, Text
from kivyblocks.widget_css import WidgetCSS
from .uitype.factory import UiFactory, get_value
from .command_action import cmd_action
def on_disabled(self, *args):
pass
TouchRippleButtonBehavior.on_disabled = on_disabled
class TinyText(Text):
def __init__(self, **kw):
SUPER(TinyText, self, kw)
self.size_hint = (None, None)
self.on_texture_size(self, (1,1))
def on_texture_size(self, o, ts):
self.texture_update()
self.size = self.texture_size
class PressableBox(TouchRippleButtonBehavior, Box):
normal_css = StringProperty("default")
actived_css = StringProperty("default")
box_actived = BooleanProperty(False)
def __init__(self,
border_width=1,
user_data=None,
radius=[],
**kw):
super(PressableBox, self).__init__(
padding=[border_width,
border_width,
border_width,
border_width],
radius=radius,
**kw)
self.border_width = border_width
self.user_data = user_data
self.actived = False
self.csscls = self.normal_css
def active(self, flag):
self.box_actived = flag
def on_box_actived(self, o, v):
if self.box_actived:
self.csscls = self.actived_css
else:
self.csscls = self.normal_css
def on_press(self,o=None):
self.box_actived = True
def setValue(self,d):
self.user_data = d
def getValue(self):
return self.user_data
class ClickableBox(TouchRippleButtonBehavior, Box):
def __init__(self,
border_width=1,
**kw):
SUPER(ClickableBox, self, kw)
self.size_hint = [None, None]
self.border_width = border_width
# self.bind(minimum_height=self.setter('height'))
# self.bind(minimum_width=self.setter('width'))
self.bind(children=self.reset_size)
def reset_size(self, o, s):
self.size_hint = (None,None)
if self.orientation == 'horizontal':
self.height = max([c.height for c in self.children])
self.width = sum([c.width for c in self.children])
else:
self.height = sum([c.height for c in self.children])
self.width = max([c.width for c in self.children])
def on_press(self,o=None):
pass
class _IconBehavior:
source=StringProperty(None)
img_kw = DictProperty(None)
def on_source(self, o, source):
if self.source is None:
try:
self.remove_widget(self.img_w)
except:
pass
self.img_w = None
return
self.build_icon()
def on_img_kw(self, o, img_kw):
self.build_icon()
def build_icon(self):
if not self.source:
return
if not self.img_kw:
self.img_kw = {}
if self.img_w:
self.img_w.source = self.source
for k,v in self.img_kw.items():
setattr(self.img_w, k,v)
return
self.img_w = AsyncImage(source=self.source, **self.img_kw)
self.add_widget(self.img_w)
class _TextBehavior:
text = StringProperty(None)
fontsize = NumericProperty(1)
def create_text_widget(self):
if self.text is None:
return
self.txt_w = TinyText(otext=self.text,
i18n=True,
font_size=CSize(self.fontsize))
self.txt_w.font_size = CSize(self.fontsize)
self.txt_w.bind(texture_size=self.reset_size)
self.txt_w.size_hint = (None, None)
self.txt_w.size = self.txt_w.texture_size
self.add_widget(self.txt_w)
def on_fontsize(self, o, fs):
if self.txt_w:
self.txt_w.font_size = CSize(self.fontsize)
self.txt_w.texture_update()
def on_text(self, o, txt):
if self.text is None:
return
if not self.txt_w:
return
if self.txt_w:
self.txt_w.text = self.text
self.txt_w.texture_update()
class ClickableText(_TextBehavior, ClickableBox):
def __init__(self, **kw):
self.txt_w = None
SUPER(ClickableText, self, kw)
self.create_text_widget()
class ClickableIconText(_IconBehavior, _TextBehavior, ClickableBox):
def __init__(self, **kw):
self.txt_w = None
self.img_w = None
SUPER(ClickableIconText, self, kw)
self.build_icon()
self.create_text_widget()
class CommandBox(ClickableIconText):
value = DictProperty(None)
params = DictProperty(None)
conform = DictProperty(None)
target = StringProperty(None)
datawidget = StringProperty(None)
datamethod = StringProperty(None)
url = StringProperty(None)
rfname = StringProperty(None)
method = StringProperty(None)
script = StringProperty(None)
actions = DictProperty(None)
def on_press(self, *args):
if self.datawidget is None:
self.datawidget = 'self'
self.datamethod = 'getValue'
desc = dict(target=self.target,
datawidget = self.datawidget,
datamethod = self.method,
url=self.url,
params=self.params,
conform = self.conform,
rfname = self.rfname,
method = self.method,
script = self.script,
actions = self.actions
)
cmd_action(desc, self)
def setValue(self, value:dict):
self.value = value
def getValue(self):
return self.value
class ToggleText(ClickableText):
"""
construct commad
ToggleText(**{
"text":"test",
"css_on":"selected",
"css_off":"normal"
})
"""
select_state = BooleanProperty(False)
css_on = StringProperty('default')
css_off = StringProperty('default')
def __init__(self, **kw):
SUPER(ToggleText, self, kw)
def toggle(self):
self.select_state = False if self.select_state else True
def select(self, flag):
if flag:
self.select_state = True
else:
self.select_state = False
def state(self):
return self.select_state
def on_select_state(self, o, f):
self.csscls = self.css_on if f else self.css_off
class ToggleIconText(ToggleText):
"""
{
"orientation",
"text",
"css_on",
"css_off",
"source_on",
"source_off"
}
"""
source_on = StringProperty(None)
source_off = StringProperty(None)
source = StringProperty(None)
img_kw = DictProperty({})
def __init__(self, **kw):
self.img_w = None
SUPER(ToggleIconText, self, kw)
self.source = self.source_off
self.img_w = AsyncImage(source=self.source, **self.img_kw)
self.add_widget(self.img_w, index=len(self.children))
def on_select_state(self, o, f):
super(ToggleIconText, self).on_select_state(o,f)
self.source = self.source_on if f else self.source_off
def on_source(self, o, source):
if source is None:
return
if self.img_w:
self.img_w.source = self.source
class ClickableImage(_IconBehavior, ClickableBox):
def __init__(self, **kw):
self.img_w = None
SUPER(ClickableImage, self, kw)
self.build_icon()
class ToggleImage(ClickableImage):
source_on = StringProperty(None)
source_off = StringProperty(None)
select_state = BooleanProperty(False)
def __init__(self, **kw):
SUPER(ToggleImage, self, kw)
self.source = self.source_on
def toggle(self):
self.select_state = False if self.select_state else True
def select(self, flag):
if flag:
self.select_state = True
else:
self.select_state = False
def state(self):
return self.select_state
def on_select_state(self, o, f):
if f:
self.source = self.source_on
else:
self.source = self.source_off
class SingleCheckBox(ClickableBox):
otext = StringProperty(None)
select_state = BooleanProperty(False)
def __init__(self, **kw):
self.old_state = None
self.register_event_type('on_changed')
SUPER(SingleCheckBox, self, kw)
self.source_on = blockImage('checkbox-on.png')
self.source_off = blockImage('checkbox-off.png')
self.select(False)
self.create_img_w()
if self.otext:
self.txt_w = TinyText(otext=self.otext, i18n=True)
self.txt_w.bind(size=self.reset_size)
self.add_widget(self.txt_w)
self.reset_size(self, None)
def on_changed(self, o=None):
pass
def on_press(self, o=None):
self.toggle()
def select(self, f):
self.select_state = f
def change_img_source(self):
self.img_w.source = self.source_on if self.select_state else \
self.source_off
def toggle(self):
self.select_state = False if self.select_state else True
def state(self):
return self.select_state
def on_select_state(self, o, f):
self.change_img_source()
self.dispatch('on_changed', self.select_state)
def getValue(self):
return self.state()
def setValue(self, v):
self.select(True if v else False)
def create_img_w(self):
if self.select_state:
self.img_w = AsyncImage(source=self.source_on,
size_hint=(None, None),
size=CSize(1, 1)
)
else:
self.img_w = AsyncImage(source=self.source_off,
size_hint=(None, None),
size=CSize(1, 1)
)
self.add_widget(self.img_w)
self.img_w.bind(size=self.reset_size)
def build_checkbox(desc, rec=None):
v = get_value(desc, rec)
if v is None:
v = False
x = SingleCheckBox(select_state=v)
return x
def build_cmdbox_view(desc, rec=None):
vd = None
if rec is not None:
vd = {f:rec.get(f) for f in desc.get('data') }
kw = {
'text' : desc.get('label'),
'source' : desc.get('icon'),
'img_kw' : desc.get('img_kw',{
'size_hint':[None, None],
'height':CSize(1),
'width':CSize(1)
}),
'rfname':desc.get('rfname'),
'script':desc.get('script'),
'params':desc.get('params'),
'conform':desc.get('conform'),
'target':desc.get('target'),
'datawidget':desc.get('datawidget'),
'datamethod':desc.get('datamethod'),
'method':desc.get('method'),
'url':desc.get('url')
}
if desc.get('uiparams'):
kw.update(desc.get('uiparams'))
x = CommandBox(**kw)
x.setValue(vd)
return x
UiFactory.register('checkbox', build_checkbox, build_checkbox)
UiFactory.register('cmdbox', build_cmdbox_view, build_cmdbox_view)
r = Factory.register
r('CommandBox', CommandBox)
r('TinyText', TinyText)
r('SingleCheckBox', SingleCheckBox)
r('ClickableBox', ClickableBox)
r('ClickableText',ClickableText)
r('ClickableIconText',ClickableIconText)
r('ToggleText',ToggleText)
r('ToggleIconText',ToggleIconText)
r('ClickableImage',ClickableImage)
r('ToggleImage',ToggleImage)