bugfix
This commit is contained in:
parent
5e32fe3083
commit
586f6a46db
@ -273,6 +273,8 @@ class Modal(VBox):
|
|||||||
self.pos = w.pos[0] + x, w.pos[1] + y
|
self.pos = w.pos[0] + x, w.pos[1] + y
|
||||||
|
|
||||||
def open(self, widget=None):
|
def open(self, widget=None):
|
||||||
|
if self.parent:
|
||||||
|
return
|
||||||
self.dispatch('on_pre_open')
|
self.dispatch('on_pre_open')
|
||||||
if widget is None:
|
if widget is None:
|
||||||
widget = Window
|
widget = Window
|
||||||
|
1829
kivyblocks/blocks.c
1829
kivyblocks/blocks.c
File diff suppressed because it is too large
Load Diff
@ -461,6 +461,7 @@ class Blocks(EventDispatcher):
|
|||||||
data.update(rt)
|
data.update(rt)
|
||||||
if desc.get('keymapping'):
|
if desc.get('keymapping'):
|
||||||
data = keyMapping(data, desc.get('keymapping'))
|
data = keyMapping(data, desc.get('keymapping'))
|
||||||
|
Logger.info('getActionData():rtdesc=%s, data=%s', rtdesc, data)
|
||||||
return data
|
return data
|
||||||
|
|
||||||
def registedfunctionAction(self, widget:Widget, desc, *args):
|
def registedfunctionAction(self, widget:Widget, desc, *args):
|
||||||
|
@ -126,6 +126,8 @@ class ClickableIconText(_IconBehavior, _TextBehavior, ClickableBox):
|
|||||||
|
|
||||||
class CommandBox(ClickableIconText):
|
class CommandBox(ClickableIconText):
|
||||||
value = DictProperty(None)
|
value = DictProperty(None)
|
||||||
|
params = DictProperty(None)
|
||||||
|
conform = DictProperty(None)
|
||||||
target = StringProperty(None)
|
target = StringProperty(None)
|
||||||
datawidget = StringProperty(None)
|
datawidget = StringProperty(None)
|
||||||
datamethod = StringProperty(None)
|
datamethod = StringProperty(None)
|
||||||
@ -143,6 +145,8 @@ class CommandBox(ClickableIconText):
|
|||||||
datawidget = self.datawidget,
|
datawidget = self.datawidget,
|
||||||
datamethod = self.method,
|
datamethod = self.method,
|
||||||
url=self.url,
|
url=self.url,
|
||||||
|
params=self.params,
|
||||||
|
conform = self.conform,
|
||||||
rfname = self.rfname,
|
rfname = self.rfname,
|
||||||
method = self.method,
|
method = self.method,
|
||||||
script = self.script,
|
script = self.script,
|
||||||
@ -325,9 +329,15 @@ def build_cmdbox_view(desc, rec=None):
|
|||||||
kw = {
|
kw = {
|
||||||
'text' : desc.get('label'),
|
'text' : desc.get('label'),
|
||||||
'source' : desc.get('icon'),
|
'source' : desc.get('icon'),
|
||||||
'img_kw' : desc.get('img_kw'),
|
'img_kw' : desc.get('img_kw',{
|
||||||
|
'size_hint':[None, None],
|
||||||
|
'height':CSize(1),
|
||||||
|
'width':CSize(1)
|
||||||
|
}),
|
||||||
'rfname':desc.get('rfname'),
|
'rfname':desc.get('rfname'),
|
||||||
'script':desc.get('script'),
|
'script':desc.get('script'),
|
||||||
|
'params':desc.get('params'),
|
||||||
|
'conform':desc.get('conform'),
|
||||||
'target':desc.get('target'),
|
'target':desc.get('target'),
|
||||||
'datawidget':desc.get('datawidget'),
|
'datawidget':desc.get('datawidget'),
|
||||||
'datamethod':desc.get('datamethod'),
|
'datamethod':desc.get('datamethod'),
|
||||||
|
@ -7,6 +7,7 @@ def get_cmd_desc(cmd_desc):
|
|||||||
desc = {
|
desc = {
|
||||||
}
|
}
|
||||||
v = cmd_desc
|
v = cmd_desc
|
||||||
|
desc['conform'] = v.get('conform')
|
||||||
desc['target'] = v.get('target')
|
desc['target'] = v.get('target')
|
||||||
if v.get('datawidget'):
|
if v.get('datawidget'):
|
||||||
desc['datawidget'] = v.get('datawidget')
|
desc['datawidget'] = v.get('datawidget')
|
||||||
@ -14,6 +15,7 @@ def get_cmd_desc(cmd_desc):
|
|||||||
desc['datamethod'] = v['datamethod']
|
desc['datamethod'] = v['datamethod']
|
||||||
keys = v.keys()
|
keys = v.keys()
|
||||||
if 'url' in keys:
|
if 'url' in keys:
|
||||||
|
Logger.info('get_cmd_desc():cmd_desc=%s', cmd_desc)
|
||||||
desc['actiontype'] = 'urlwidget'
|
desc['actiontype'] = 'urlwidget'
|
||||||
desc['mode'] = 'replace'
|
desc['mode'] = 'replace'
|
||||||
options = {
|
options = {
|
||||||
@ -43,14 +45,22 @@ def get_cmd_desc(cmd_desc):
|
|||||||
def cmd_action(cmd_desc, widget):
|
def cmd_action(cmd_desc, widget):
|
||||||
desc = get_cmd_desc(cmd_desc)
|
desc = get_cmd_desc(cmd_desc)
|
||||||
if desc is None:
|
if desc is None:
|
||||||
Logger.error('CommandAction: cmd_desc=%s error')
|
Logger.error('CommandAction: desc is None cmd_desc=%s error', \
|
||||||
|
cmd_desc)
|
||||||
return
|
return
|
||||||
blocks = Factory.Blocks()
|
blocks = Factory.Blocks()
|
||||||
if 'conform' in cmd_desc:
|
conform_desc = desc.get('conform')
|
||||||
options = cmd_desc['conform']
|
if conform_desc is None:
|
||||||
w = Factory.Conform(**options)
|
Logger.info('cmd_action():desc=%s', desc)
|
||||||
f = partial(blocks.uniaction, widget, desc)
|
blocks.uniaction(widget, desc)
|
||||||
w.bind(on_conform=f)
|
|
||||||
return
|
return
|
||||||
blocks.uniaction(widget, desc)
|
|
||||||
|
w = blocks.widgetBuild({
|
||||||
|
"widgettype":"Conform",
|
||||||
|
"options":conform_desc
|
||||||
|
})
|
||||||
|
w.bind(on_conform=partial(blocks.uniaction, widget, desc))
|
||||||
|
w.open()
|
||||||
|
Logger.info('cmd_action():desc=%s, conform and action', desc)
|
||||||
|
|
||||||
|
|
||||||
|
@ -2,12 +2,79 @@ from appPublic.timeUtils import curDateString, monthMaxDay
|
|||||||
|
|
||||||
from kivy.clock import Clock
|
from kivy.clock import Clock
|
||||||
from kivy.factory import Factory
|
from kivy.factory import Factory
|
||||||
from kivy.properties import NumericProperty
|
from kivy.properties import NumericProperty, BooleanProperty, \
|
||||||
|
OptionProperty
|
||||||
from kivy.uix.boxlayout import BoxLayout
|
from kivy.uix.boxlayout import BoxLayout
|
||||||
from kivy.uix.label import Label
|
from kivy.uix.label import Label
|
||||||
from kivyblocks.baseWidget import SelectInput, HBox
|
from kivyblocks.baseWidget import SelectInput, HBox
|
||||||
from kivyblocks.utils import CSize
|
from kivyblocks.utils import CSize
|
||||||
|
|
||||||
|
date_format_info = {
|
||||||
|
'yyyy-mm-dd':{
|
||||||
|
'ystart':0,
|
||||||
|
'mstart':5,
|
||||||
|
'dstart':8
|
||||||
|
},
|
||||||
|
'mm-dd-yyyy':{
|
||||||
|
'ystart':6,
|
||||||
|
'mstart':0,
|
||||||
|
'dstart':3
|
||||||
|
},
|
||||||
|
'dd-mm-yyyy':{
|
||||||
|
'ystart':6,
|
||||||
|
'mstart':3,
|
||||||
|
'dstart':0
|
||||||
|
},
|
||||||
|
'yyyymmdd':{
|
||||||
|
'ystart':0,
|
||||||
|
'mstart':4,
|
||||||
|
'dstart':6
|
||||||
|
},
|
||||||
|
'mmddyyyy':{
|
||||||
|
'ystart':4,
|
||||||
|
'mstart':0,
|
||||||
|
'dstart':2
|
||||||
|
},
|
||||||
|
'ddmmyyyy':{
|
||||||
|
'ystart':4,
|
||||||
|
'mstart':2,
|
||||||
|
'dstart':0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
def get_datestr(y, m, d, dateformat):
|
||||||
|
def replace_slice(s, v, pos,length):
|
||||||
|
x = '%0' + str(length) + 'd'
|
||||||
|
sv = x % v
|
||||||
|
d = ''
|
||||||
|
if pos > 0:
|
||||||
|
d = s[0:pos]
|
||||||
|
d = d + sv
|
||||||
|
d = d + s[pos+length:]
|
||||||
|
return d
|
||||||
|
if y is None:
|
||||||
|
return None
|
||||||
|
|
||||||
|
info = date_format_info.get(dateformat)
|
||||||
|
if info is None:
|
||||||
|
return None
|
||||||
|
ret = dateformat
|
||||||
|
ret = replace_slice(ret, y, info['ystart'], 4)
|
||||||
|
ret = replace_slice(ret, m, info['mstart'], 4)
|
||||||
|
ret = replace_slice(ret, d, info['dstart'], 4)
|
||||||
|
return ret
|
||||||
|
|
||||||
|
def get_ymd(dstr, dateformat):
|
||||||
|
if not dstr:
|
||||||
|
return None, None, None
|
||||||
|
|
||||||
|
info = date_format_info.get(dateformat)
|
||||||
|
if info is None:
|
||||||
|
return None, None, None
|
||||||
|
y = int(dstr[info['ystart'], info['ystart']+4])
|
||||||
|
m = int(dstr[info['mstart'], info['mstart']+2])
|
||||||
|
d = int(dstr[info['dstart'], info['dstart']+2])
|
||||||
|
return y, m, d
|
||||||
|
|
||||||
class YearInput(SelectInput):
|
class YearInput(SelectInput):
|
||||||
nullable = BooleanProperty(True)
|
nullable = BooleanProperty(True)
|
||||||
e_year = NumericProperty(None)
|
e_year = NumericProperty(None)
|
||||||
@ -87,27 +154,16 @@ class YearInput(SelectInput):
|
|||||||
|
|
||||||
class DateInput(HBox):
|
class DateInput(HBox):
|
||||||
default_date = OptionProperty(None, options=[None, 'today'])
|
default_date = OptionProperty(None, options=[None, 'today'])
|
||||||
date_format = OptionProperty('yyyy-mm-dd', options=[
|
date_format = OptionProperty('yyyy-mm-dd', options=[k for k in date_format_info.keys() ])
|
||||||
'yyyy-mm-dd',
|
|
||||||
'dd-mm-yyyy',
|
|
||||||
'mm-dd-yyyy',
|
|
||||||
'yyyymmdd',
|
|
||||||
'mmddyyyy',
|
|
||||||
'ddmmyyyy']
|
|
||||||
def __init__(self, allow_copy=True, **kw):
|
def __init__(self, allow_copy=True, **kw):
|
||||||
print('DateInput():kw=', kw)
|
print('DateInput():kw=', kw)
|
||||||
self.datetype = 'yyyy-mm-dd'
|
|
||||||
kw['size_hint_x'] = None
|
kw['size_hint_x'] = None
|
||||||
kw['width'] = 10
|
kw['width'] = 10
|
||||||
super(DateInput, self).__init__(**kw)
|
super(DateInput, self).__init__(**kw)
|
||||||
self.register_event_type('on_changed')
|
self.register_event_type('on_changed')
|
||||||
self.old_datestr = None
|
self.old_datestr = None
|
||||||
value = kw.get('value',self.defaultdate())
|
value = kw.get('value',self.defaultdate())
|
||||||
if value:
|
y, m, d = get_ymd(value, 'yyyy-mm-dd')
|
||||||
y, m, d = self.str2ymd(value)
|
|
||||||
else:
|
|
||||||
y, m, d = None, None, None
|
|
||||||
|
|
||||||
months_data = []
|
months_data = []
|
||||||
days_data = []
|
days_data = []
|
||||||
for i in range(12):
|
for i in range(12):
|
||||||
@ -162,26 +218,13 @@ class DateInput(HBox):
|
|||||||
def str2ymd(self, datestr):
|
def str2ymd(self, datestr):
|
||||||
if datestr is None or datestr == '':
|
if datestr is None or datestr == '':
|
||||||
return None, None, None
|
return None, None, None
|
||||||
|
return get_ymd(datestr, self.dateformat)
|
||||||
if len(datestr) == 8:
|
|
||||||
self.datetype = 'yyyymmdd'
|
|
||||||
y = int(datestr[:4])
|
|
||||||
m = int(datestr[4:6])
|
|
||||||
d = int(datestr[6:8])
|
|
||||||
return y, m, d
|
|
||||||
self.datetype = 'yyyy-mm-dd'
|
|
||||||
y = int(datestr[:4])
|
|
||||||
m = int(datestr[5:7])
|
|
||||||
d = int(datestr[8:10])
|
|
||||||
return y, m, d
|
|
||||||
|
|
||||||
def ymd2str(self, y, m, d):
|
def ymd2str(self, y, m, d):
|
||||||
if y is None:
|
if y is None:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
if self.datetype == 'yyyymmdd':
|
return get_datestr(y, m, d, self.dateformat)
|
||||||
return '%4d%02d%02d' % (y,m,d)
|
|
||||||
return '%04d-%02d-%02d' % (y, m, d)
|
|
||||||
|
|
||||||
def data_changed(self, o, d):
|
def data_changed(self, o, d):
|
||||||
datestr = None
|
datestr = None
|
||||||
|
@ -13,10 +13,11 @@ from .ready import WidgetReady
|
|||||||
class VResponsiveLayout(WidgetCSS, WidgetReady, ScrollView):
|
class VResponsiveLayout(WidgetCSS, WidgetReady, ScrollView):
|
||||||
box_width = NumericProperty(None)
|
box_width = NumericProperty(None)
|
||||||
box_width_c = NumericProperty(None)
|
box_width_c = NumericProperty(None)
|
||||||
|
columes = NumericProperty(None)
|
||||||
def __init__(self, **kw):
|
def __init__(self, **kw):
|
||||||
self._inner = None
|
self._inner = None
|
||||||
self.col_width = None
|
self.col_width = None
|
||||||
self.col_width_hint = False
|
self.cols = None
|
||||||
super(VResponsiveLayout, self).__init__(**kw)
|
super(VResponsiveLayout, self).__init__(**kw)
|
||||||
self.options = kw
|
self.options = kw
|
||||||
self._inner = GridLayout(cols=1, padding=2,
|
self._inner = GridLayout(cols=1, padding=2,
|
||||||
@ -26,45 +27,28 @@ class VResponsiveLayout(WidgetCSS, WidgetReady, ScrollView):
|
|||||||
super(VResponsiveLayout,self).add_widget(self._inner)
|
super(VResponsiveLayout,self).add_widget(self._inner)
|
||||||
self._inner.bind(
|
self._inner.bind(
|
||||||
minimum_height=self._inner.setter('height'))
|
minimum_height=self._inner.setter('height'))
|
||||||
self.bind(pos=self.set_col_width_cnt,size=self.set_col_width_cnt)
|
self.bind(size=self.set_col_width)
|
||||||
|
|
||||||
def on_box_width_c(self, *args):
|
def calculate_col_width(self, cnt):
|
||||||
print('on_box_width_c fire......')
|
|
||||||
if self.box_width_c is None:
|
|
||||||
return
|
|
||||||
if self.box_width:
|
|
||||||
return
|
|
||||||
if not self._inner:
|
|
||||||
return
|
|
||||||
self.col_width = CSize(self.box_width)
|
|
||||||
self.col_width_hint = False
|
|
||||||
self.set_col_width_cnt()
|
|
||||||
|
|
||||||
def on_box_width(self, *args):
|
|
||||||
print('on_box_width fire......')
|
|
||||||
if not self._inner:
|
|
||||||
return
|
|
||||||
if self.box_width is None:
|
|
||||||
return
|
|
||||||
if self.box_width <= 1:
|
|
||||||
self.col_width = self.box_width
|
|
||||||
self.col_width_hint = True
|
|
||||||
else:
|
|
||||||
self.col_width = self.box_width
|
|
||||||
self.col_width_hint = False
|
|
||||||
self.set_col_width_cnt()
|
|
||||||
|
|
||||||
def calculate_col_width(self):
|
|
||||||
# cnt * col_width + 2*padding + (cnt-1) * spacing = width
|
# cnt * col_width + 2*padding + (cnt-1) * spacing = width
|
||||||
w = self._inner
|
w = self._inner
|
||||||
if len(w.padding) == 1:
|
padding = 0
|
||||||
width = w.width - 2 * w.padding
|
if isinstance(w.padding, float):
|
||||||
return width * self.col_width
|
padding = w.padding
|
||||||
if len(w.padding) == 2:
|
elif len(w.padding) < 4:
|
||||||
width = w.width - 2 * w.padding[0]
|
padding = w.padding[0]
|
||||||
return width * self.col_width
|
else:
|
||||||
width = w.width - w.padding[0] - w.padding[2]
|
padding = (w.padding[0] + w.padding[2])/2
|
||||||
return width * self.col_width
|
spacing = 0
|
||||||
|
if isinstance(w.spacing, float):
|
||||||
|
spacing = w.spacing
|
||||||
|
elif len(w.spacing) < 4:
|
||||||
|
spacing = w.spacing[0]
|
||||||
|
else:
|
||||||
|
spacing = (w.spacing[0] + w.spacing[2]) / 2
|
||||||
|
print('w.padding=', w.padding, 'w.spacing=', w.spacing, 'padding=', padding)
|
||||||
|
width = (w.width - 2 * padding - cnt * spacing) / cnt
|
||||||
|
return width
|
||||||
|
|
||||||
def get_col_width(self):
|
def get_col_width(self):
|
||||||
return self._inner.col_default_width
|
return self._inner.col_default_width
|
||||||
@ -72,40 +56,42 @@ class VResponsiveLayout(WidgetCSS, WidgetReady, ScrollView):
|
|||||||
def get_cols(self):
|
def get_cols(self):
|
||||||
return self._inner.cols
|
return self._inner.cols
|
||||||
|
|
||||||
def set_col_width(self):
|
def set_col_width(self, o, s):
|
||||||
if self.box_width_c is not None:
|
if self._inner is None:
|
||||||
self.col_width = CSize(self.box_width)
|
|
||||||
self.col_width_hint = False
|
|
||||||
return
|
return
|
||||||
|
self._inner.size = self.size
|
||||||
|
if isHandHold() and self.width < self.height:
|
||||||
|
self.cols = 1
|
||||||
|
self.col_width = self.calculate_col_width(self.cols)
|
||||||
|
return self.setCols()
|
||||||
|
|
||||||
|
if self.columes is not None and self.columes > 0:
|
||||||
|
self.cols = self.columes
|
||||||
|
self.col_width = self.calculate_col_width(self.cols)
|
||||||
|
return self.setCols()
|
||||||
|
|
||||||
|
if self.box_width_c is not None:
|
||||||
|
self.cols = floor(self._inner.width / CSize(self.box_width_c))
|
||||||
|
if self.cols < 1:
|
||||||
|
self.cols = 1
|
||||||
|
self.col_width = self.calculate_col_width(self.cols)
|
||||||
|
return self.setCols()
|
||||||
|
|
||||||
if self.box_width is not None:
|
if self.box_width is not None:
|
||||||
if self.box_width <= 1:
|
if self.box_width <= 1:
|
||||||
self.col_width = self.box_width
|
sef.cols = floor(1 / self.box)
|
||||||
self.col_width_hint = True
|
self.col_width = self.calculate_col_width(self.cols)
|
||||||
|
return self.setCols()
|
||||||
else:
|
else:
|
||||||
self.col_width = self.box_width
|
self.cols = floor(self._inner.width / self.box_width)
|
||||||
self.col_width_hint = False
|
if self.cols < 1:
|
||||||
|
self.cols = 1
|
||||||
|
self.col_width = self.calculate_col_width(self.cols)
|
||||||
|
return self.setCols()
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
||||||
def set_col_width_cnt(self, *args):
|
|
||||||
print('set_col_width_cnt() called .....')
|
|
||||||
if self.col_width is None:
|
|
||||||
self.set_col_width()
|
|
||||||
if self.col_width is None:
|
|
||||||
return
|
|
||||||
|
|
||||||
if self.col_width_hint:
|
|
||||||
self._inner.col_default_width = \
|
|
||||||
self.calculate_col_width()
|
|
||||||
else:
|
|
||||||
self._inner.col_default_width = self.col_width
|
|
||||||
self.setCols()
|
|
||||||
for w in self._inner.children:
|
|
||||||
w.size_hint_x = None
|
|
||||||
w.width = self._inner.col_default_width
|
|
||||||
|
|
||||||
def on_orientation(self,o):
|
def on_orientation(self,o):
|
||||||
self.set_col_width_cnt()
|
self.set_col_width()
|
||||||
|
|
||||||
def add_widget(self,widget,**kw):
|
def add_widget(self,widget,**kw):
|
||||||
a = self._inner.add_widget(widget,**kw)
|
a = self._inner.add_widget(widget,**kw)
|
||||||
@ -119,8 +105,9 @@ class VResponsiveLayout(WidgetCSS, WidgetReady, ScrollView):
|
|||||||
return a
|
return a
|
||||||
|
|
||||||
def setCols(self,*args):
|
def setCols(self,*args):
|
||||||
cols = floor(self.width / self._inner.col_default_width)
|
self._inner.cols = self.cols
|
||||||
if cols < 1:
|
self._inner.col_default_width = self.col_width
|
||||||
cols = 1
|
for w in self._inner.children:
|
||||||
self._inner.cols = cols
|
w.size_hint_x = None
|
||||||
|
w.width = self._inner.col_default_width
|
||||||
|
|
||||||
|
19
setup.py
19
setup.py
@ -1,22 +1,22 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
|
||||||
from distutils.core import setup
|
try:
|
||||||
from setuptools import setup, find_packages
|
from setuptools import setup
|
||||||
|
except ImportError:
|
||||||
|
from distutils.core import setup
|
||||||
from Cython.Build import cythonize
|
from Cython.Build import cythonize
|
||||||
from version import version
|
|
||||||
|
|
||||||
# usage:
|
# usage:
|
||||||
# python setup.py bdist_wininst generate a window executable file
|
# python setup.py bdist_wininst generate a window executable file
|
||||||
# python setup.py bdist_egg generate a egg file
|
# python setup.py bdist_egg generate a egg file
|
||||||
# Release information about eway
|
# Release information about eway
|
||||||
|
|
||||||
# version = "0.0.4"
|
version = "0.1.1"
|
||||||
description = "kivy blocks is a tool to build kivy ui with json format uidesc files"
|
description = "kivy blocks is a tool to build kivy ui with json format uidesc files"
|
||||||
author = "yumoqing"
|
author = "yumoqing"
|
||||||
email = "yumoqing@icloud.com"
|
email = "yumoqing@icloud.com"
|
||||||
|
|
||||||
packages=find_packages()
|
|
||||||
package_data = {
|
package_data = {
|
||||||
"kivyblocks":[
|
"kivyblocks":[
|
||||||
"blocks.pyx",
|
"blocks.pyx",
|
||||||
@ -52,7 +52,14 @@ setup(
|
|||||||
"appPublic",
|
"appPublic",
|
||||||
"sqlor"
|
"sqlor"
|
||||||
],
|
],
|
||||||
packages=packages,
|
packages=[
|
||||||
|
'kivyblocks',
|
||||||
|
'kivyblocks.image_processing',
|
||||||
|
'kivyblocks.mapview',
|
||||||
|
'kivyblocks.uitype',
|
||||||
|
'kivyblocks.widgetExt',
|
||||||
|
'kivyblocks.xcamera'
|
||||||
|
],
|
||||||
package_data=package_data,
|
package_data=package_data,
|
||||||
keywords = [
|
keywords = [
|
||||||
],
|
],
|
||||||
|
@ -1 +1 @@
|
|||||||
version = '0.0.5'
|
version = '0.1.0'
|
||||||
|
Loading…
Reference in New Issue
Block a user