diff --git a/web/py_lib/flutter.py b/web/py_lib/flutter.py index 6d7463fe2c79bf1eef4680a5f3efeef8460fbf07..a5570f2a6f12dc13dc3751feeafadfdd93e9f2c3 100755 --- a/web/py_lib/flutter.py +++ b/web/py_lib/flutter.py @@ -524,21 +524,309 @@ from . import flutter_icons Icons = flutter_icons.Icons -# TODO -# https://material.io/components/banners -# https://material.io/components/buttons -# https://material.io/components/cards -# https://material.io/components/checkboxes -# https://material.io/components/chips -# https://material.io/components/data-tables -# https://material.io/components/dialogs -# https://material.io/components/lists -# https://material.io/components/menus -# https://material.io/components/navigation-drawer -# https://material.io/components/radio-buttons -# https://material.io/components/sheets-side -# https://material.io/components/snackbars -# https://material.io/components/switches -# https://material.io/components/tabs -# https://material.io/components/text-fields -# https://material.io/components/tooltips +def make_svg(tag, attrs={}, style={}): + xmlns = 'http://www.w3.org/2000/svg' + elt = javascript.document.createElementNS(xmlns, tag) + for k, v in attrs.items(): + elt.setAttribute(k, v) + if len(style) > 0: + style = ';'.join([f'{k}:{v}'for k, v in style.items()]) + elt.setAttribute('style', style) + return elt + + +# Implementation of https://material.io/components/buttons +# Including: TextButton, OutlinedButton, ContainedButton +class TextButton: + def __init__(self, child=None, icon=None, onPressed=None, trailing=False, disabled=False): + if child is not None: + assert hasattr(child, 'render') and callable(child.render) + if onPressed is not None: + assert callable(onPressed) + self.child = child + self.icon = icon + self.onPressed = onPressed + self.trailing = trailing + self.disabled = disabled + if self.icon is None: + self.trailing = False + + def render(self, parent): + e = maketag('button', {'class': 'mdc-button'}) + parent.appendChild(e) + ripple = maketag('span', {'class': 'mdc-button__ripple'}) + e.appendChild(ripple) + if self.icon is not None: + if self.trailing: + add_class(e, 'mdc-button--icon-trailing') + label = maketag('span', {'class': 'mdc-button__label'}) + label.appendChild(javascript.document.createTextNode(self.child.text)) + e.appendChild(label) + else: + add_class(e, 'mdc-button--icon-leading') + if isinstance(self.icon, str): + # TODO: check + i = maketag('i', {"class": "material-icons mdc-button__icon", "aria-hidden": "true"}) + i.appendChild(javascript.document.createTextNode(self.icon)) + else: + # TODO: check whether self.icon is an instance of svg (class: JSObject) + i = make_svg('svg', {"class": "mdc-button__icon", "aria-hidden": "true", "xmlns": "http://www.w3.org/2000/svg", "viewBox": "0 0 10 10"}) + i.appendChild(self.icon) + e.appendChild(i) + if not self.trailing: + label = maketag('span', {'class': 'mdc-button__label'}) + label.appendChild(javascript.document.createTextNode(self.child.text)) + e.appendChild(label) + if self.disabled: + e.setAttribute('disabled', '') + if self.onPressed is not None: + e.bind('click', self.onPressed) + return e + + +class OutlinedButton: + def __init__(self, child=None, icon=None, onPressed=None, trailing=False, disabled=False): + if child is not None: + assert hasattr(child, 'render') and callable(child.render) + if onPressed is not None: + assert callable(onPressed) + self.child = child + self.onPressed = onPressed + self.icon = icon + self.trailing = trailing + self.disabled = disabled + if self.icon is None: + self.trailing = False + + def render(self, parent): + # e = maketag('button', {'class': 'mdc-button mdc-button--outlined'}, {'border': 'solid 1px #590DE4'}) + e = maketag('button', {'class': 'mdc-button mdc-button--outlined'}) + parent.appendChild(e) + ripple = maketag('span', {'class': 'mdc-button__ripple'}) + e.appendChild(ripple) + if self.icon is not None: + if self.trailing: + add_class(e, 'mdc-button--icon-trailing') + label = maketag('span', {'class': 'mdc-button__label'}) + label.appendChild(javascript.document.createTextNode(self.child.text)) + e.appendChild(label) + else: + add_class(e, 'mdc-button--icon-leading') + if isinstance(self.icon, str): + # TODO: check + i = maketag('i', {"class": "material-icons mdc-button__icon", "aria-hidden": "true"}) + i.appendChild(javascript.document.createTextNode(self.icon)) + else: + # TODO: check whether self.icon is an instance of svg (class: JSObject) + i = make_svg('svg', {"class": "mdc-button__icon", "aria-hidden": "true", "xmlns": "http://www.w3.org/2000/svg", "viewBox": "0 0 10 10"}) + i.appendChild(self.icon) + e.appendChild(i) + if not self.trailing: + label = maketag('span', {'class': 'mdc-button__label'}) + label.appendChild(javascript.document.createTextNode(self.child.text)) + e.appendChild(label) + if self.disabled: + e.setAttribute('disabled', '') + if self.onPressed is not None: + e.bind('click', self.onPressed) + return e + + +class ContainedButton: + def __init__(self, child=None, icon=None, onPressed=None, trailing=False, disabled=False): + if child is not None: + assert hasattr(child, 'render') and callable(child.render) + if onPressed is not None: + assert callable(onPressed) + self.child = child + self.icon = icon + self.onPressed = onPressed + self.trailing = trailing + # TODO: trailing is not suitable for this class + self.disabled = disabled + if self.icon is None: + self.trailing = False + + def render(self, parent): + e = maketag('button', {'class': 'mdc-button mdc-button--raised'}) + parent.append(e) + if self.icon is not None: + if self.trailing: + # add_class(e, 'mdc-button--icon-trailing') + add_class(e, 'mdc-button--trailing') + label = maketag('span', {'class': 'mdc-button__label'}) + label.appendChild(javascript.document.createTextNode(self.child.text)) + e.appendChild(label) + else: + add_class(e, 'mdc-button--leading') + ripple = maketag('span', {'class': 'mdc-button__ripple'}) + e.appendChild(ripple) + if isinstance(self.icon, str): + # TODO: check + i = maketag('i', {"class": "material-icons mdc-button__icon", "aria-hidden": "true"}) + i.appendChild(javascript.document.createTextNode(self.icon)) + else: + # TODO: check whether self.icon is an instance of svg (class: JSObject) + i = make_svg('svg', + {"class": "mdc-button__icon", "aria-hidden": "true", "xmlns": "http://www.w3.org/2000/svg", + "viewBox": "0 0 10 10"}) + i.appendChild(self.icon) + e.appendChild(i) + if not self.trailing: + label = maketag('span', {'class': 'mdc-button__label'}) + label.appendChild(javascript.document.createTextNode(self.child.text)) + e.appendChild(label) + if self.disabled: + e.setAttribute('disabled', '') + if self.onPressed is not None: + e.bind('click', self.onPressed) + return e + + +# Implementation of https://material.io/components/sliders +# Including: Slider and RangeSlider +class Slider: + def __init__(self, value, min, max, label, divisions=None, onChanged=None, tick_mark=False, disabled=False): + if onChanged is not None: + assert callable(onChanged) + self.value = value + self.min = min + self.max = max + self.label = label + self.divisions = divisions + self.onChanged = onChanged + self.tick_mark = tick_mark + self.disabled = disabled + if self.divisions is None: + self.tick_mark = False + def render(self, parent): + e = maketag('div', {'class': 'mdc-slider'}, style={'width': '200px'}) + parent.appendChild(e) + i = maketag('input', {'class': 'mdc-slider__input', 'type': 'range', 'min': f'{self.min}', 'max': f'{self.max}', 'value': f'{self.value}', 'name': 'volume', 'aria-label': 'Continuous slider demo'}) + if self.divisions is not None: + add_class(e, 'mdc-slider--discrete') + i = maketag('input', {'class': 'mdc-slider__input', 'type': 'range', 'min': f'{self.min}', 'max': f'{self.max}', 'value': f'{self.value}', 'name': 'volume', 'step': f'{self.divisions}', 'aria-label': 'Discrete slider demo'}) + if self.tick_mark: + add_class(e, 'mdc-slider--tick-marks') + i = maketag('input', {'class': 'mdc-slider__input', 'type': 'range', 'min': f'{self.min}', 'max': f'{self.max}', 'value': f'{self.value}', 'name': 'volume', 'step': f'{self.divisions}', 'aria-label': 'Discrete slider with tick marks demo'}) + e.appendChild(i) + track = maketag('div', {'class': 'mdc-slider__track'}) + e.appendChild(track) + inactive = maketag('div', {'class': 'mdc-slider__track--inactive'}) + active = maketag('div', {'class': 'mdc-slider__track--active'}) + track.appendChild(inactive) + track.appendChild(active) + active_fill = maketag('div', {'class': 'mdc-slider__track--active_fill'}) + active.appendChild(active_fill) + if self.tick_mark: + tick_marks = maketag('div', {'class': 'mdc-slider__tick-marks'}) + track.appendChild(tick_marks) + for _ in range(6): + tick_marks.appendChild(maketag('div', {'class': 'mdc-slider__tick-mark--active'})) + for _ in range(5): + tick_marks.appendChild(maketag('div', {'class': 'mdc-slider__tick-mark--inactive'})) + + thumb = maketag('div', {'class': 'mdc-slider__thumb'}) + e.appendChild(thumb) + if self.divisions is not None: + value_indicator_container = maketag('div', {'class': 'mdc-slider__value-indicator-container', 'aria-hidden': 'true'}) + thumb.appendChild(value_indicator_container) + value_indicator = maketag('div', {'class': 'mdc-slider__value-indicator'}) + value_indicator_container.appendChild(value_indicator) + value_indicator_text = maketag('span', {'class': 'mdc-slider__value-indicator-text'}) + value_indicator.appendChild(value_indicator_text) + value_indicator_text.appendChild(javascript.document.createTextNode('50')) + thumb_knob = maketag('div', {'class': 'mdc-slider__thumb-knob'}) + thumb.appendChild(thumb_knob) + if self.disabled: + add_class(e, 'mdc-slider--disabled') + javascript.mdc.slider.MDCSlider.new(e) + return e + + +class RangeSlider: + def __init__(self, values, min, max, labels, divisions=None, onChanged=None, tick_mark=False, disabled=False): + if onChanged is not None: + assert callable(onChanged) + self.values = values + self.min = min + self.max = max + self.labels = labels + self.divisions = divisions + self.onChanged = onChanged + self.tick_mark = tick_mark + self.disabled = disabled + if self.divisions is None: + self.tick_mark = False + def render(self, parent): + e = maketag('div', {'class': 'mdc-slider mdc-slider--range'}, style={'width': '200px'}) + parent.appendChild(e) + start = maketag('input', {'class': 'mdc-slider__input', 'type': "range", 'min': f'{self.min}', 'max': f'{self.max}', 'value': f'{self.values[0]}', 'name': 'rangeStart', 'aria-label': 'Continuous range slider demo'}) + end = maketag('input', {'class': 'mdc-slider__input', 'type': "range", 'min': f'{self.min}', 'max': f'{self.max}', 'value': f'{self.values[1]}', 'name': 'rangeEnd', 'aria-label': 'Continuous range slider demo'}) + if self.divisions is not None: + add_class(e, 'mdc-slider--discrete') + start = maketag('input', {'class': 'mdc-slider__input', 'type': "range", 'min': f'{self.min}', 'max': f'{self.max}', 'value': f'{self.values[0]}', + 'step': f'{self.divisions}', 'name': 'rangeStart', 'aria-label': 'Discrete range slider demo'}) + end = maketag('input', {'class': 'mdc-slider__input', 'type': "range", 'min': f'{self.min}', 'max': f'{self.max}', 'value': f'{self.values[1]}', + 'step': f'{self.divisions}', 'name': 'rangeEnd', 'aria-label': 'Discrete range slider demo'}) + if self.tick_mark: + add_class(e, 'mdc-slider--tick-marks') + start = maketag('input', {'class': 'mdc-slider__input', 'type': "range", 'min': f'{self.min}', 'max': f'{self.max}', + 'value': f'{self.values[0]}', + 'step': f'{self.divisions}', 'name': 'rangeStart', + 'aria-label': 'Discrete range slider with tick marks demo'}) + end = maketag('input', {'class': 'mdc-slider__input', 'type': "range", 'min': f'{self.min}', 'max': f'{self.max}', + 'value': f'{self.values[1]}', + 'step': f'{self.divisions}', 'name': 'rangeEnd', + 'aria-label': 'Discrete range slider with tick marks demo'}) + e.appendChild(start) + e.appendChild(end) + track = maketag('div', {'class': 'mdc-slider__track'}) + e.appendChild(track) + inactive = maketag('div', {'class': 'mdc-slider__track--inactive'}) + active = maketag('div', {'class': 'mdc-slider__track--active'}) + track.appendChild(inactive) + track.appendChild(active) + active_fill = maketag('div', {'class': 'mdc-slider__track--active_fill'}) + active.appendChild(active_fill) + + if self.tick_mark: + tick_marks = maketag('div', {'class': 'mdc-slider__tick-marks'}) + track.appendChild(tick_marks) + for _ in range(6): + tick_marks.appendChild(maketag('div', {'class': 'mdc-slider__tick-mark--active'})) + for _ in range(5): + tick_marks.appendChild(maketag('div', {'class': 'mdc-slider__tick-mark--inactive'})) + + thumb = maketag('div', {'class': 'mdc-slider__thumb'}) + e.appendChild(thumb) + + if self.divisions is not None: + value_indicator_container = maketag('div', {'class': 'mdc-slider__value-indicator-container', 'aria-hidden': 'true'}) + thumb.appendChild(value_indicator_container) + value_indicator = maketag('div', {'class': 'mdc-slider__value-indicator'}) + value_indicator_container.appendChild(value_indicator) + value_indicator_text = maketag('span', {'class': 'mdc-slider__value-indicator-text'}) + value_indicator.appendChild(value_indicator_text) + value_indicator_text.appendChild(javascript.document.createTextNode('20')) + + thumb_knob = maketag('div', {'class': 'mdc-slider__thumb-knob'}) + thumb.appendChild(thumb_knob) + thumb2 = maketag('div', {'class': 'mdc-slider__thumb'}) + e.appendChild(thumb2) + + if self.divisions is not None: + value_indicator_container2 = maketag('div', {'class': 'mdc-slider__value-indicator-container', 'aria-hidden': 'true'}) + thumb2.appendChild(value_indicator_container2) + value_indicator2 = maketag('div', {'class': 'mdc-slider__value-indicator'}) + value_indicator_container2.appendChild(value_indicator2) + value_indicator_text2 = maketag('span', {'class': 'mdc-slider__value-indicator-text'}) + value_indicator2.appendChild(value_indicator_text2) + value_indicator_text2.appendChild(javascript.document.createTextNode('50')) + + thumb_knob2 = maketag('div', {'class': 'mdc-slider__thumb-knob'}) + thumb2.appendChild(thumb_knob2) + if self.disabled: + add_class(e, 'mdc-slider--disabled') + javascript.mdc.slider.MDCSlider.new(e) + return e diff --git a/web/py_lib/flutter_test_3.py b/web/py_lib/flutter_test_3.py new file mode 100755 index 0000000000000000000000000000000000000000..9dfcbe428ed5b87b0d74315e86020d943ac33816 --- /dev/null +++ b/web/py_lib/flutter_test_3.py @@ -0,0 +1,93 @@ + +from . import flutter +TextStyle = flutter.TextStyle +Container = flutter.Container +Center = flutter.Center +Text = flutter.Text +Stack = flutter.Stack +Positioned = flutter.Positioned +Transform = flutter.Transform +LinearGradient = flutter.LinearGradient +Colors = flutter.Colors +TextAlign = flutter.TextAlign +EdgeInsets = flutter.EdgeInsets +FontWeight = flutter.FontWeight +FontStyle = flutter.FontStyle +BoxShadow = flutter.BoxShadow +BoxShape = flutter.BoxShape +RichText = flutter.RichText +TextSpan = flutter.TextSpan +TextOverflow = flutter.TextOverflow +BorderRadius = flutter.BorderRadius +Offset = flutter.Offset +Scaffold = flutter.Scaffold +AppBar = flutter.AppBar +IconButton = flutter.IconButton +Icon = flutter.Icon +Icons = flutter.Icons +TextButton = flutter.TextButton +OutlinedButton = flutter.OutlinedButton +ContainedButton = flutter.ContainedButton +Column = flutter.Column +MainAxisAlignment = flutter.MainAxisAlignment +Slider = flutter.Slider +RangeSlider = flutter.RangeSlider +_sliderValue = 20. +_sliderDiscreteValue = 20. +_rangeSliderDiscreteValues = [40., 80.] + + +# https://flutter.cn/docs/development/ui/widgets-intro +def 使用Material组件(): + # test buttons + return Scaffold( + appBar=AppBar( + leading=IconButton( + icon=Icon(Icons.menu), + onPressed=None, + ), + title=Text('Example title'), + actions=[ + IconButton( + icon=Icon(Icons.search), + onPressed=None, + ), + ], + ), + # body is the majority of the screen. + body=Container( + Center( + child=Column( + mainAxisAlignment=MainAxisAlignment.center, + children=[ + TextButton( + child=Text('TEXT'), + icon=flutter.make_svg('rect', {'x': '0', 'y': '0', 'width': '10', 'height': '10', 'fill': 'green'}), + onPressed=None, + trailing=True, + disabled=True, + ), + OutlinedButton( + child=Text('OUTLINED'), + icon=Icons.add, + onPressed=None, + trailing=True, + disabled=True, + ), + ContainedButton( + child=Text('CONTAINED'), + icon=Icons.bookmark, + onPressed=None, + trailing=False, + disabled=True, + ), + ], + ), + ), + color=Colors.white, + ), + ) + +def run_app(): + widget = 使用Material组件() + widget.render(parent=javascript.document.body) \ No newline at end of file diff --git a/web/py_lib/flutter_test_4.py b/web/py_lib/flutter_test_4.py new file mode 100755 index 0000000000000000000000000000000000000000..dfc56209d45ceff142b71ec23a5cc2328c1e1ef0 --- /dev/null +++ b/web/py_lib/flutter_test_4.py @@ -0,0 +1,90 @@ + +from . import flutter +TextStyle = flutter.TextStyle +Container = flutter.Container +Center = flutter.Center +Text = flutter.Text +Stack = flutter.Stack +Positioned = flutter.Positioned +Transform = flutter.Transform +LinearGradient = flutter.LinearGradient +Colors = flutter.Colors +TextAlign = flutter.TextAlign +EdgeInsets = flutter.EdgeInsets +FontWeight = flutter.FontWeight +FontStyle = flutter.FontStyle +BoxShadow = flutter.BoxShadow +BoxShape = flutter.BoxShape +RichText = flutter.RichText +TextSpan = flutter.TextSpan +TextOverflow = flutter.TextOverflow +BorderRadius = flutter.BorderRadius +Offset = flutter.Offset +Scaffold = flutter.Scaffold +AppBar = flutter.AppBar +IconButton = flutter.IconButton +Icon = flutter.Icon +Icons = flutter.Icons +TextButton = flutter.TextButton +OutlinedButton = flutter.OutlinedButton +ContainedButton = flutter.ContainedButton +Column = flutter.Column +MainAxisAlignment = flutter.MainAxisAlignment +Slider = flutter.Slider +RangeSlider = flutter.RangeSlider +_sliderValue = 20. +_sliderDiscreteValue = 20. +_rangeSliderDiscreteValues = [40., 80.] + + +def 使用Material组件(): + # test sliders + return Scaffold( + appBar=AppBar( + leading=IconButton( + icon=Icon(Icons.menu), + onPressed=None, + ), + title=Text('Example title'), + actions=[ + IconButton( + icon=Icon(Icons.search), + onPressed=None, + ), + ], + ), + # body is the majority of the screen. + body=Container( + Center( + child=Column( + children=[ + Slider( + value=_sliderDiscreteValue, + min=0, + max=100, + divisions=10, + label=str(int(_sliderDiscreteValue)), + onChanged=None, + tick_mark=True, + disabled=False, + ), + RangeSlider( + values=_rangeSliderDiscreteValues, + min=0, + max=100, + divisions=5, + labels=(str(int(_rangeSliderDiscreteValues[0])), str(int(_rangeSliderDiscreteValues[1]))), + onChanged=None, + tick_mark=True, + disabled=False, + ), + ], + ), + ), + color=Colors.white, + ), + ) + +def run_app(): + widget = 使用Material组件() + widget.render(parent=javascript.document.body) \ No newline at end of file