├── __init__.py ├── flat_kivy ├── __init__.py ├── settings.json ├── README.md ├── data │ ├── font │ │ ├── Roboto-Black.ttf │ │ ├── Roboto-Bold.ttf │ │ ├── Roboto-Light.ttf │ │ ├── Roboto-Thin.ttf │ │ ├── Roboto-Italic.ttf │ │ ├── Roboto-Medium.ttf │ │ ├── Roboto-Regular.ttf │ │ ├── Roboto-BoldItalic.ttf │ │ ├── Roboto-ThinItalic.ttf │ │ ├── Roboto-BlackItalic.ttf │ │ ├── Roboto-LightItalic.ttf │ │ ├── Roboto-MediumItalic.ttf │ │ ├── RobotoCondensed-Bold.ttf │ │ ├── fontawesome-webfont.ttf │ │ ├── RobotoCondensed-Italic.ttf │ │ ├── RobotoCondensed-Light.ttf │ │ ├── RobotoCondensed-Regular.ttf │ │ ├── RobotoCondensed-BoldItalic.ttf │ │ └── RobotoCondensed-LightItalic.ttf │ ├── images │ │ ├── button_raised.png │ │ └── button_raised.xcf │ └── ui_elements.kv ├── uix │ ├── flatoptionbutton.py │ ├── flaticon.py │ ├── styles.py │ ├── flatslider.py │ ├── flattextinput.py │ ├── flattogglebutton.py │ ├── flatcard.py │ ├── flatbutton.py │ ├── flaticonbutton.py │ ├── flatimagebutton.py │ ├── flatlabel.py │ ├── __init__.py │ ├── flatcheckbox.py │ └── behaviors.py ├── .gitignore ├── logmanager.py ├── utils.py ├── flat.kv ├── numpad.py ├── dbinterface.py ├── font_definitions.py ├── numpad.kv ├── color_definitions.py ├── flatapp.py └── fa_icon_definitions.py ├── README.md ├── .gitignore └── demo.py /__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /flat_kivy/__init__.py: -------------------------------------------------------------------------------- 1 | __version__ = '0.0.1' 2 | -------------------------------------------------------------------------------- /flat_kivy/settings.json: -------------------------------------------------------------------------------- 1 | {"settings": {"device_id": {"value": {"value": 536}}}} -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | FlatKivy 2 | ======== 3 | 4 | A collection of experimental flat/material design widgets for Kivy. 5 | -------------------------------------------------------------------------------- /flat_kivy/README.md: -------------------------------------------------------------------------------- 1 | FlatKivy 2 | ======== 3 | 4 | A collection of experimental flat/material design widgets for Kivy. 5 | -------------------------------------------------------------------------------- /flat_kivy/data/font/Roboto-Black.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kovak/FlatKivy/HEAD/flat_kivy/data/font/Roboto-Black.ttf -------------------------------------------------------------------------------- /flat_kivy/data/font/Roboto-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kovak/FlatKivy/HEAD/flat_kivy/data/font/Roboto-Bold.ttf -------------------------------------------------------------------------------- /flat_kivy/data/font/Roboto-Light.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kovak/FlatKivy/HEAD/flat_kivy/data/font/Roboto-Light.ttf -------------------------------------------------------------------------------- /flat_kivy/data/font/Roboto-Thin.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kovak/FlatKivy/HEAD/flat_kivy/data/font/Roboto-Thin.ttf -------------------------------------------------------------------------------- /flat_kivy/data/font/Roboto-Italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kovak/FlatKivy/HEAD/flat_kivy/data/font/Roboto-Italic.ttf -------------------------------------------------------------------------------- /flat_kivy/data/font/Roboto-Medium.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kovak/FlatKivy/HEAD/flat_kivy/data/font/Roboto-Medium.ttf -------------------------------------------------------------------------------- /flat_kivy/data/font/Roboto-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kovak/FlatKivy/HEAD/flat_kivy/data/font/Roboto-Regular.ttf -------------------------------------------------------------------------------- /flat_kivy/data/images/button_raised.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kovak/FlatKivy/HEAD/flat_kivy/data/images/button_raised.png -------------------------------------------------------------------------------- /flat_kivy/data/images/button_raised.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kovak/FlatKivy/HEAD/flat_kivy/data/images/button_raised.xcf -------------------------------------------------------------------------------- /flat_kivy/data/font/Roboto-BoldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kovak/FlatKivy/HEAD/flat_kivy/data/font/Roboto-BoldItalic.ttf -------------------------------------------------------------------------------- /flat_kivy/data/font/Roboto-ThinItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kovak/FlatKivy/HEAD/flat_kivy/data/font/Roboto-ThinItalic.ttf -------------------------------------------------------------------------------- /flat_kivy/data/font/Roboto-BlackItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kovak/FlatKivy/HEAD/flat_kivy/data/font/Roboto-BlackItalic.ttf -------------------------------------------------------------------------------- /flat_kivy/data/font/Roboto-LightItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kovak/FlatKivy/HEAD/flat_kivy/data/font/Roboto-LightItalic.ttf -------------------------------------------------------------------------------- /flat_kivy/data/font/Roboto-MediumItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kovak/FlatKivy/HEAD/flat_kivy/data/font/Roboto-MediumItalic.ttf -------------------------------------------------------------------------------- /flat_kivy/data/font/RobotoCondensed-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kovak/FlatKivy/HEAD/flat_kivy/data/font/RobotoCondensed-Bold.ttf -------------------------------------------------------------------------------- /flat_kivy/data/font/fontawesome-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kovak/FlatKivy/HEAD/flat_kivy/data/font/fontawesome-webfont.ttf -------------------------------------------------------------------------------- /flat_kivy/data/font/RobotoCondensed-Italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kovak/FlatKivy/HEAD/flat_kivy/data/font/RobotoCondensed-Italic.ttf -------------------------------------------------------------------------------- /flat_kivy/data/font/RobotoCondensed-Light.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kovak/FlatKivy/HEAD/flat_kivy/data/font/RobotoCondensed-Light.ttf -------------------------------------------------------------------------------- /flat_kivy/data/font/RobotoCondensed-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kovak/FlatKivy/HEAD/flat_kivy/data/font/RobotoCondensed-Regular.ttf -------------------------------------------------------------------------------- /flat_kivy/data/font/RobotoCondensed-BoldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kovak/FlatKivy/HEAD/flat_kivy/data/font/RobotoCondensed-BoldItalic.ttf -------------------------------------------------------------------------------- /flat_kivy/data/font/RobotoCondensed-LightItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kovak/FlatKivy/HEAD/flat_kivy/data/font/RobotoCondensed-LightItalic.ttf -------------------------------------------------------------------------------- /flat_kivy/uix/flatoptionbutton.py: -------------------------------------------------------------------------------- 1 | 2 | from kivy.properties import StringProperty 3 | 4 | from flat_kivy.uix.flattogglebutton import FlatToggleButton 5 | 6 | 7 | class FlatOptionButton(FlatToggleButton): 8 | key = StringProperty(None) -------------------------------------------------------------------------------- /flat_kivy/uix/flaticon.py: -------------------------------------------------------------------------------- 1 | 2 | from kivy.properties import ListProperty, StringProperty 3 | 4 | from flat_kivy.uix.flatlabel import FlatLabel 5 | 6 | 7 | class FlatIcon(FlatLabel): 8 | color_tuple = ListProperty(['Grey', '0000']) 9 | icon = StringProperty('') 10 | 11 | -------------------------------------------------------------------------------- /flat_kivy/uix/styles.py: -------------------------------------------------------------------------------- 1 | 2 | from kivy.properties import ListProperty, StringProperty 3 | 4 | from flat_kivy.utils import construct_data_resource 5 | 6 | 7 | class RaisedStyle(object): 8 | border = ListProperty([16, 16, 16, 16]) 9 | border_size = ListProperty([2, 3, 4, 3]) 10 | border_image = StringProperty( 11 | construct_data_resource('images/button_raised.png')) 12 | -------------------------------------------------------------------------------- /flat_kivy/uix/flatslider.py: -------------------------------------------------------------------------------- 1 | 2 | 3 | from kivy.uix.slider import Slider 4 | from kivy.properties import ListProperty 5 | 6 | from behaviors import GrabBehavior, SliderTouchRippleBehavior, ThemeBehavior 7 | 8 | 9 | class FlatSlider(GrabBehavior, SliderTouchRippleBehavior, ThemeBehavior, 10 | Slider): 11 | color_tuple = ListProperty(['Blue', '500']) 12 | slider_color_tuple = ListProperty(['Orange', '300']) 13 | outline_color_tuple = ListProperty(['Blue', '600']) 14 | slider_outline_color_tuple = ListProperty(['Orange', '500']) 15 | ripple_color_tuple = ListProperty(['Grey', '0000']) 16 | -------------------------------------------------------------------------------- /flat_kivy/uix/flattextinput.py: -------------------------------------------------------------------------------- 1 | 2 | from kivy.uix.textinput import TextInput 3 | from kivy.uix.stacklayout import StackLayout 4 | from kivy.properties import ListProperty, ObjectProperty, StringProperty 5 | 6 | from flat_kivy.uix.behaviors import (GrabBehavior, TouchRippleBehavior) 7 | 8 | 9 | class FlatTextInput(GrabBehavior, TouchRippleBehavior, TextInput): 10 | ripple_color_tuple = ListProperty(['Grey', '1000']) 11 | 12 | def on_touch_down(self, touch): 13 | TextInput.on_touch_down(self, touch) 14 | super(FlatTextInput, self).on_touch_down(touch) 15 | 16 | 17 | class TextInputFocus(StackLayout): 18 | close_callback = ObjectProperty(None) 19 | text = StringProperty(None, allownone=True) 20 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | 5 | # C extensions 6 | *.so 7 | 8 | # Distribution / packaging 9 | .Python 10 | env/ 11 | bin/ 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | eggs/ 16 | lib/ 17 | lib64/ 18 | parts/ 19 | sdist/ 20 | var/ 21 | *.egg-info/ 22 | .installed.cfg 23 | *.egg 24 | 25 | # Installer logs 26 | pip-log.txt 27 | pip-delete-this-directory.txt 28 | 29 | # Unit test / coverage reports 30 | htmlcov/ 31 | .tox/ 32 | .coverage 33 | .cache 34 | nosetests.xml 35 | coverage.xml 36 | 37 | # Translations 38 | *.mo 39 | 40 | # Mr Developer 41 | .mr.developer.cfg 42 | .project 43 | .pydevproject 44 | 45 | # Rope 46 | .ropeproject 47 | 48 | # Django stuff: 49 | *.log 50 | *.pot 51 | 52 | # Sphinx documentation 53 | docs/_build/ 54 | 55 | -------------------------------------------------------------------------------- /flat_kivy/.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | 5 | # C extensions 6 | *.so 7 | 8 | # Distribution / packaging 9 | .Python 10 | env/ 11 | bin/ 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | eggs/ 16 | lib/ 17 | lib64/ 18 | parts/ 19 | sdist/ 20 | var/ 21 | *.egg-info/ 22 | .installed.cfg 23 | *.egg 24 | 25 | # Installer logs 26 | pip-log.txt 27 | pip-delete-this-directory.txt 28 | 29 | # Unit test / coverage reports 30 | htmlcov/ 31 | .tox/ 32 | .coverage 33 | .cache 34 | nosetests.xml 35 | coverage.xml 36 | 37 | # Translations 38 | *.mo 39 | 40 | # Mr Developer 41 | .mr.developer.cfg 42 | .project 43 | .pydevproject 44 | 45 | # Rope 46 | .ropeproject 47 | 48 | # Django stuff: 49 | *.log 50 | *.pot 51 | 52 | # Sphinx documentation 53 | docs/_build/ 54 | 55 | -------------------------------------------------------------------------------- /flat_kivy/uix/flattogglebutton.py: -------------------------------------------------------------------------------- 1 | 2 | from kivy.uix.anchorlayout import AnchorLayout 3 | from kivy.properties import BooleanProperty 4 | 5 | from flat_kivy.uix.behaviors import ToggleButtonBehavior 6 | from flat_kivy.uix.flatbutton import FlatButtonBase 7 | from flat_kivy.uix.styles import RaisedStyle 8 | 9 | 10 | class FlatToggleButton(FlatButtonBase, ToggleButtonBehavior, AnchorLayout): 11 | no_up = BooleanProperty(False) 12 | 13 | def on_touch_down(self, touch): 14 | if self.no_up: 15 | if self.collide_point(touch.x, touch.y) and self.state == 'normal': 16 | super(FlatToggleButton, self).on_touch_down(touch) 17 | else: 18 | super(FlatToggleButton, self).on_touch_down(touch) 19 | 20 | 21 | class RaisedFlatToggleButton(RaisedStyle, FlatToggleButton): 22 | pass -------------------------------------------------------------------------------- /flat_kivy/uix/flatcard.py: -------------------------------------------------------------------------------- 1 | 2 | from kivy.uix.boxlayout import BoxLayout 3 | from kivy.properties import StringProperty, ListProperty 4 | 5 | from flat_kivy.uix.behaviors import (GrabBehavior, ThemeBehavior, LogBehavior, 6 | TouchRippleBehavior, ButtonBehavior) 7 | 8 | 9 | class FlatCard(GrabBehavior, ThemeBehavior, LogBehavior, TouchRippleBehavior, 10 | ButtonBehavior, BoxLayout): 11 | image_source = StringProperty(None) 12 | style = StringProperty(None, allownone=True) 13 | color_tuple = ListProperty(['Grey', '500']) 14 | font_color_tuple = ListProperty(['Grey', '1000']) 15 | font_ramp_tuple = ListProperty(None) 16 | ripple_color_tuple = ListProperty(['Grey', '1000']) 17 | text = StringProperty(None) 18 | color = ListProperty([1., 1., 1.]) 19 | color_down = ListProperty([.7, .7, .7]) 20 | 21 | def on_color(self, instance, value): 22 | self.color_down = [x*.7 for x in value] -------------------------------------------------------------------------------- /flat_kivy/logmanager.py: -------------------------------------------------------------------------------- 1 | 2 | from kivy.event import EventDispatcher 3 | from kivy.properties import NumericProperty, BooleanProperty, StringProperty 4 | 5 | from flat_kivy.dbinterface import DBInterface 6 | 7 | 8 | class LogManager(EventDispatcher): 9 | device_id = NumericProperty(None) 10 | do_logging = BooleanProperty(False) 11 | do_label_logging = BooleanProperty(False) 12 | do_image_logging = BooleanProperty(False) 13 | do_screen_logging = BooleanProperty(False) 14 | touch_id = NumericProperty(0) 15 | hour = NumericProperty(None) 16 | log_path = StringProperty('default_log_dir') 17 | 18 | def __init__(self, log_path, **kwargs): 19 | super(LogManager, self).__init__(**kwargs) 20 | self.log_path = log_path 21 | self.log_interface = log_interface = DBInterface( 22 | log_path, 'log', do_date=True, do_hour=True) 23 | touch_id = log_interface.get_entry('touches', 'last_touch_id', 'value') 24 | if touch_id is None: 25 | touch_id = 0 26 | self.touch_id = touch_id 27 | 28 | def on_device_id(self, instance, value): 29 | print('in on device id', value) -------------------------------------------------------------------------------- /flat_kivy/uix/flatbutton.py: -------------------------------------------------------------------------------- 1 | 2 | from kivy.uix.anchorlayout import AnchorLayout 3 | from kivy.properties import (ListProperty, StringProperty, NumericProperty, 4 | BooleanProperty) 5 | 6 | from flat_kivy.uix.behaviors import (GrabBehavior, ThemeBehavior, LogBehavior, 7 | TouchRippleBehavior, ButtonBehavior) 8 | from flat_kivy.uix.styles import RaisedStyle 9 | 10 | 11 | class FlatButtonBase(GrabBehavior, LogBehavior, TouchRippleBehavior, 12 | ThemeBehavior): 13 | color = ListProperty([1., 1., 1.]) 14 | color_down = ListProperty([.7, .7, .7]) 15 | border_size = ListProperty([0, 0, 0, 0]) 16 | text = StringProperty('') 17 | alpha = NumericProperty(1.0) 18 | style = StringProperty(None, allownone=True) 19 | color_tuple = ListProperty(['Grey', '500']) 20 | font_color_tuple = ListProperty(['Grey', '1000']) 21 | ripple_color_tuple = ListProperty(['Grey', '1000']) 22 | font_ramp_tuple = ListProperty(None) 23 | font_size = NumericProperty(12) 24 | eat_touch = BooleanProperty(False) 25 | 26 | def on_color(self, instance, value): 27 | self.color_down = [x*.7 for x in value] 28 | 29 | 30 | class FlatButton(FlatButtonBase, ButtonBehavior, AnchorLayout): 31 | pass 32 | 33 | 34 | class RaisedFlatButton(RaisedStyle, FlatButton): 35 | pass 36 | -------------------------------------------------------------------------------- /flat_kivy/uix/flaticonbutton.py: -------------------------------------------------------------------------------- 1 | 2 | from kivy.uix.anchorlayout import AnchorLayout 3 | from kivy.properties import (ListProperty, StringProperty, 4 | VariableListProperty, NumericProperty) 5 | 6 | from flat_kivy.uix.behaviors import (GrabBehavior, LogBehavior, 7 | ButtonBehavior, TouchRippleBehavior, 8 | ThemeBehavior) 9 | 10 | 11 | class FlatIconButton(GrabBehavior, LogBehavior, ButtonBehavior, 12 | TouchRippleBehavior, ThemeBehavior, AnchorLayout): 13 | color = ListProperty([1., 1., 1.]) 14 | color_down = ListProperty([.7, .7, .7]) 15 | text = StringProperty('') 16 | icon = StringProperty('') 17 | style = StringProperty(None, allownone=True) 18 | font_size = NumericProperty(12) 19 | icon_color_tuple = ListProperty(['Grey', '1000']) 20 | color_tuple = ListProperty(['Blue', '500']) 21 | font_color_tuple = ListProperty(['Grey', '1000']) 22 | font_ramp_tuple = ListProperty(None) 23 | ripple_color_tuple = ListProperty(['Grey', '1000']) 24 | content_padding = VariableListProperty([0., 0., 0., 0.]) 25 | content_spacing = VariableListProperty([0., 0.], length=2) 26 | 27 | def on_color(self, instance, value): 28 | self.color_down = [x*.7 for x in value] 29 | 30 | 31 | class FlatIconButtonLeft(FlatIconButton): 32 | pass 33 | -------------------------------------------------------------------------------- /flat_kivy/uix/flatimagebutton.py: -------------------------------------------------------------------------------- 1 | 2 | from kivy.uix.anchorlayout import AnchorLayout 3 | from kivy.properties import (ListProperty, StringProperty, 4 | VariableListProperty, NumericProperty) 5 | 6 | from flat_kivy.uix.behaviors import (GrabBehavior, LogBehavior, 7 | ButtonBehavior, TouchRippleBehavior, 8 | ThemeBehavior) 9 | 10 | 11 | class FlatImageButton(GrabBehavior, LogBehavior, ButtonBehavior, 12 | TouchRippleBehavior, ThemeBehavior, AnchorLayout): 13 | color = ListProperty([1., 1., 1.]) 14 | color_down = ListProperty([.7, .7, .7]) 15 | text = StringProperty('') 16 | icon_source = StringProperty('') 17 | style = StringProperty(None, allownone=True) 18 | font_size = NumericProperty(12) 19 | image_color_tuple = ListProperty(['Grey', '1000']) 20 | color_tuple = ListProperty(['Blue', '500']) 21 | font_color_tuple = ListProperty(['Grey', '1000']) 22 | font_ramp_tuple = ListProperty(None) 23 | ripple_color_tuple = ListProperty(['Grey', '1000']) 24 | content_padding = VariableListProperty([0., 0., 0., 0.]) 25 | content_spacing = VariableListProperty([0., 0.], length=2) 26 | 27 | def on_color(self, instance, value): 28 | self.color_down = [x*.7 for x in value] 29 | 30 | class FlatImageButtonLeft(FlatImageButton): 31 | pass -------------------------------------------------------------------------------- /flat_kivy/utils.py: -------------------------------------------------------------------------------- 1 | from __future__ import unicode_literals, print_function 2 | from os import path 3 | from kivy.utils import platform, get_color_from_hex 4 | from color_definitions import colors 5 | from fa_icon_definitions import fa_icons 6 | import kivy.metrics 7 | 8 | def get_metric_conversion(metric_tuple): 9 | return getattr(kivy.metrics, metric_tuple[1])(metric_tuple[0]) 10 | 11 | 12 | def construct_target_file_name(target_file_name, pyfile): 13 | '''Returns the correct file path relative to the __file__ 14 | passed as pyfile''' 15 | if pyfile is None: 16 | pyfile = __file__ 17 | return path.join(path.dirname(path.abspath(pyfile)), target_file_name) 18 | 19 | 20 | def construct_data_resource(res_path): 21 | path_to_this_file = path.abspath(__file__) 22 | data_path = path.join(path.dirname(path_to_this_file), 23 | 'data') 24 | return path.join(data_path, res_path) 25 | 26 | 27 | def get_icon_char(icon): 28 | if icon != '': 29 | try: 30 | return fa_icons[icon] 31 | except: 32 | print('icon: ' + icon + ' not in fa_icons') 33 | return '' 34 | else: 35 | return '' 36 | 37 | 38 | def get_rgba_color(color_tuple, control_alpha=None): 39 | color, weight = color_tuple 40 | try: 41 | color = get_color_from_hex(colors[color][weight]) 42 | except: 43 | print('Error: ' + color + weight + ' not found, set to default') 44 | return (1., 1., 1., 1.) 45 | if control_alpha is None: 46 | return color 47 | else: 48 | color[3] = control_alpha 49 | return color 50 | 51 | 52 | -------------------------------------------------------------------------------- /flat_kivy/uix/flatlabel.py: -------------------------------------------------------------------------------- 1 | 2 | from kivy.uix.label import Label 3 | from kivy.properties import (BooleanProperty, ListProperty, StringProperty, 4 | ObjectProperty) 5 | 6 | from flat_kivy.uix.behaviors import (GrabBehavior, ThemeBehavior, LogBehavior) 7 | from flat_kivy.utils import construct_data_resource, get_metric_conversion 8 | 9 | 10 | class FlatLabel(GrabBehavior, ThemeBehavior, LogBehavior, Label): 11 | text = StringProperty(None, allownone=True) 12 | color_tuple = ListProperty(['Grey', '0000']) 13 | style = StringProperty(None, allownone=True) 14 | style_dict = ObjectProperty(None, allownone=True) 15 | do_resize = BooleanProperty(True) 16 | ramp_group = ObjectProperty(None, allownone=True) 17 | font_ramp_tuple = ListProperty(None) 18 | 19 | def __init__(self, **kwargs): 20 | self._do_check_adjustments = True 21 | super(FlatLabel, self).__init__(**kwargs) 22 | 23 | def on_style_dict(self, instance, value): 24 | if value is not None: 25 | self.font_name = construct_data_resource( 26 | 'font/' + value.font_file) 27 | self.font_size = font_size = get_metric_conversion( 28 | value.size_mobile) 29 | self.color[3] = value.alpha 30 | #self.shorten = not value['wrap'] 31 | 32 | def on_font_ramp_tuple(self, instance, value): 33 | if self.ramp_group is not None: 34 | self.ramp_group.remove_widget(self) 35 | 36 | def on_ramp_group(self, instance, value): 37 | if value is not None: 38 | value.add_label(self) 39 | value.trigger_fit_check() 40 | 41 | def on_touch_down(self, touch): 42 | log_manager = self.log_manager 43 | if log_manager.do_label_logging: 44 | super(FlatLabel, self).on_touch_down(touch) 45 | if self in touch.ud: 46 | touch_id = touch.ud['log_id'] 47 | log_manager.log_interface.set_entry( 48 | 'touches', touch_id, 'label_touched', self.text, 49 | do_timestamp=True) 50 | 51 | def on_texture(self, instance, value): 52 | ramp_group = self.ramp_group 53 | if ramp_group is not None and self._do_check_adjustments: 54 | ramp_group.trigger_fit_check() 55 | 56 | def on_size(self, instance, value): 57 | ramp_group = self.ramp_group 58 | if ramp_group is not None and self._do_check_adjustments: 59 | ramp_group.trigger_fit_check() -------------------------------------------------------------------------------- /flat_kivy/flat.kv: -------------------------------------------------------------------------------- 1 | ScreenManager: 2 | title: 'test' 3 | desc: 'test2' 4 | size: root.size 5 | FlatScreen: 6 | name: 'test_2' 7 | FlatButton: 8 | text: 'to other screen' 9 | on_release: root.current = 'test_screen' 10 | FlatScreen: 11 | name: 'test_screen' 12 | canvas.before: 13 | Color: 14 | rgb: (1., 1., 1.) 15 | Rectangle: 16 | size: self.size 17 | pos: self.pos 18 | Widget: 19 | 20 | FlatTextInput: 21 | pos: 20, 440 22 | size: 200, 100 23 | FlatIconButton: 24 | color_tuple: ('Purple', '600') 25 | font_color_tuple: ('Grey', '0000') 26 | icon_color_tuple: ('Grey', '0000') 27 | text: 'Icon Button' 28 | style: 'Display 2' 29 | icon: 'fa-home' 30 | #theme: ('blue', 'variant_1') 31 | pos: (20, 320) 32 | size: (200, 100) 33 | FlatIconButtonLeft: 34 | color_tuple: ('Cyan', '300') 35 | ripple_color_tuple: ('Red', '100') 36 | icon_color_tuple: ('Yellow', '100') 37 | font_color_tuple: ('Yellow', '100') 38 | text: 'Colored Icon' 39 | icon: 'fa-chevron-left' 40 | style: 'Button' 41 | #theme: ('blue', 'variant_1') 42 | pos: (500, 320) 43 | size: (200, 100) 44 | FlatButton: 45 | color_tuple: ('Red', '500') 46 | ripple_color_tuple: ('Yellow', '100') 47 | ripple_scale: 1.0 48 | style: 'Display 2' 49 | text: 'Only Built 4 Cuban Linx' 50 | on_release: root.current = 'test_2' 51 | pos: (240, 320) 52 | #theme: ('blue', 'variant_2') 53 | size: (200, 100) 54 | FlatCheckBox: 55 | ripple_color_tuple: ('Orange', '100') 56 | check_color_tuple: ('Orange', '500') 57 | size: (100, 100) 58 | pos: (140, 200) 59 | #theme: ('blue', 'variant_1') 60 | ripple_duration_in: .2 61 | FlatCheckBox: 62 | ripple_color_tuple: ('LightBlue', '100') 63 | check_color_tuple: ('LightBlue', '500') 64 | outline_size: 10 65 | outline_color_tuple: ('LightBlue', '900') 66 | size: (50, 50) 67 | pos: (20, 200) 68 | #theme: ('blue', 'variant_1') 69 | ripple_duration_in: .2 70 | check_scale: .6 71 | FlatLabel: 72 | size: 300, 200 73 | pos: 100, 25 74 | style: 'Display 1' 75 | color_tuple: ('Grey', '1000') 76 | text: 'test test test test test test test test test test just got here test test test test test test test test test test' 77 | valign: 'middle' 78 | halign: 'center' 79 | font_ramp_tuple: ('test', '1') 80 | text_size: self.size 81 | FlatSlider: 82 | size: 400, sp(20) 83 | pos: 250, 500 84 | 85 | FlatCard: 86 | pos: 400, 25 87 | size: root.size[0]*.4, root.size[1]*.4 88 | text: 'other' 89 | color_tuple: ('Purple', '800') 90 | image_source: 'AstroPic1.jpg' 91 | font_ramp_tuple: ('test2', '1') -------------------------------------------------------------------------------- /flat_kivy/uix/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | from kivy.uix.gridlayout import GridLayout 3 | from kivy.properties import (ListProperty, DictProperty, StringProperty, 4 | ObjectProperty, NumericProperty) 5 | from kivy.lang import Builder 6 | 7 | from flat_kivy.utils import construct_data_resource 8 | 9 | 10 | Builder.load_file( 11 | construct_data_resource('ui_elements.kv')) 12 | 13 | 14 | class ErrorContent(GridLayout): 15 | error_text = StringProperty('Default Error Text') 16 | 17 | 18 | class OptionContent(GridLayout): 19 | option_text = StringProperty('Default Option Text') 20 | options_dict = DictProperty(None) 21 | callback = ObjectProperty(None) 22 | dismiss_func = ObjectProperty(None) 23 | 24 | def __init__(self, options_dict, **kwargs): 25 | super(OptionContent, self).__init__(**kwargs) 26 | self.options_dict = options_dict 27 | self.populate_options(options_dict) 28 | 29 | def populate_options(self, options_dict): 30 | rem_wid = self.remove_widget 31 | add_wid = self.add_widget 32 | for child in self.children: 33 | if isinstance(child, OptionButton): 34 | rem_wid(child) 35 | for key in options_dict: 36 | button = OptionButton(key=key, text=options_dict[key], 37 | on_release=self.option_callback) 38 | add_wid(button) 39 | 40 | def option_callback(self, instance): 41 | callback = self.callback 42 | if callback is not None: 43 | self.callback(instance.key) 44 | self.dismiss_func() 45 | 46 | 47 | # Fake a number of modules that have a small amount of code. 48 | 49 | import sys 50 | 51 | from kivy.uix.popup import Popup 52 | from kivy.uix.screenmanager import Screen 53 | from kivy.uix.scrollview import ScrollView 54 | 55 | from flat_kivy.uix.behaviors import GrabBehavior, LogNoTouchBehavior 56 | 57 | 58 | class FlatPopup(Popup): 59 | popup_color = ListProperty([1., 1., 1., 1.]) 60 | title_color_tuple = ListProperty(['Gray', '0000']) 61 | 62 | 63 | class FlatScreen(GrabBehavior, LogNoTouchBehavior, Screen): 64 | 65 | def on_enter(self, *args): 66 | super(FlatScreen, self).on_enter(*args) 67 | print('in enter screen') 68 | log_manager = self.log_manager 69 | if log_manager.do_screen_logging: 70 | print('logging screen') 71 | log_manager.log_interface.set_entry('events', 'screen_events', 72 | 'enter', self.name, do_history=True) 73 | 74 | def on_leave(self, *args): 75 | super(FlatScreen, self).on_leave(*args) 76 | log_manager = self.log_manager 77 | if log_manager.do_screen_logging: 78 | log_manager.log_interface.set_entry('events', 'screen_events', 79 | 'exit', self.name, do_history=True) 80 | 81 | 82 | class FlatScrollView(ScrollView): 83 | 84 | def scroll_to_top(self): 85 | self.scroll_y = 1.0 86 | 87 | 88 | class flatpopup(object): 89 | FlatPopup = FlatPopup 90 | 91 | 92 | class flatscreemanager(object): 93 | FlatScreen = FlatScreen 94 | 95 | 96 | class flatscrollview(object): 97 | FlatScrollView = FlatScrollView 98 | 99 | 100 | sys.modules['flat_kivy.uix.flatpopup'] = flatpopup 101 | sys.modules['flat_kivy.uix.flatscreemanager'] = flatscreemanager 102 | sys.modules['flat_kivy.uix.flatscrollview'] = flatscrollview 103 | -------------------------------------------------------------------------------- /flat_kivy/numpad.py: -------------------------------------------------------------------------------- 1 | from __future__ import unicode_literals, print_function 2 | from kivy.uix.widget import Widget 3 | from kivy.properties import (StringProperty, 4 | NumericProperty, ObjectProperty) 5 | from kivy.lang import Builder 6 | 7 | from utils import construct_target_file_name 8 | 9 | Builder.load_file(construct_target_file_name('numpad.kv', __file__)) 10 | 11 | 12 | class NumPad(Widget): 13 | display_text = StringProperty("0") 14 | display_value = NumericProperty(0) 15 | init_value = NumericProperty(100) 16 | maximum_value = NumericProperty(None, allownone=True) 17 | minimum_value = NumericProperty(None, allownone=True) 18 | return_callback = ObjectProperty(None, allownone=True) 19 | units = StringProperty(None, allownone=True) 20 | 21 | def __init__(self, **kwargs): 22 | super(NumPad, self).__init__(**kwargs) 23 | 24 | def check_minimum_value(self): 25 | if self.minimum_value != None: 26 | if self.display_value < self.minimum_value: 27 | self.display_text = str(self.minimum_value) 28 | 29 | def button_callback(self, button_str): 30 | if button_str in [str(x) for x in range(10)]: 31 | if self.display_text == '0': 32 | self.display_text = button_str 33 | else: 34 | self.display_text = self.display_text + button_str 35 | maximum_value = self.maximum_value 36 | if maximum_value != None: 37 | if self.display_value > maximum_value: 38 | self.display_value = maximum_value 39 | elif button_str == 'del': 40 | self.display_text = self.display_text[:-1] 41 | elif button_str == 'ret': 42 | self.check_minimum_value() 43 | self.return_callback(self.display_value, True) 44 | 45 | def on_display_text(self, instance, value): 46 | if value == '': 47 | self.display_text = '0' 48 | return 49 | if int(value) > self.maximum_value and self.maximum_value != None: 50 | self.display_text = str(self.maximum_value) 51 | return 52 | self.display_value = int(value) 53 | if self.return_callback is not None: 54 | self.return_callback(self.display_value, False) 55 | 56 | 57 | class DecimalNumPad(NumPad): 58 | 59 | def button_callback(self, button_str): 60 | if button_str in [str(x) for x in range(10)] or button_str == '.' and \ 61 | '.' not in self.display_text: 62 | if self.display_text == '0': 63 | self.display_text = button_str 64 | else: 65 | self.display_text = self.display_text + button_str 66 | maximum_value = self.maximum_value 67 | if maximum_value != None: 68 | if self.display_value > maximum_value: 69 | self.display_value = maximum_value 70 | elif button_str == 'del': 71 | self.display_text = self.display_text[:-1] 72 | elif button_str == 'ret': 73 | self.check_minimum_value() 74 | self.return_callback(self.display_value, True) 75 | 76 | def on_display_text(self, instance, value): 77 | if value == '': 78 | self.display_text = '0' 79 | return 80 | if value == '.': 81 | self.display_text = '0.' 82 | return 83 | if float(value) > self.maximum_value and self.maximum_value != None: 84 | self.display_text = str(self.maximum_value) 85 | return 86 | self.display_value = float(value) 87 | if self.return_callback is not None: 88 | self.return_callback(self.display_value, False) 89 | -------------------------------------------------------------------------------- /flat_kivy/uix/flatcheckbox.py: -------------------------------------------------------------------------------- 1 | 2 | from kivy.uix.widget import Widget 3 | from kivy.uix.boxlayout import BoxLayout 4 | from kivy.properties import (BooleanProperty, ListProperty, StringProperty, 5 | NumericProperty, ObjectProperty) 6 | 7 | from flat_kivy.uix.behaviors import (ToggleButtonBehavior, GrabBehavior, 8 | ThemeBehavior, TouchRippleBehavior, 9 | LogBehavior) 10 | from flat_kivy.uix.flaticon import FlatIcon 11 | 12 | 13 | class Check(FlatIcon): 14 | scale = NumericProperty(1.0) 15 | 16 | 17 | class CheckBox(ToggleButtonBehavior, Widget): 18 | '''CheckBox class, see module documentation for more information. 19 | ''' 20 | 21 | active = BooleanProperty(False) 22 | '''Indicates if the switch is active or inactive. 23 | 24 | :attr:`active` is a :class:`~kivy.properties.BooleanProperty` and defaults 25 | to False. 26 | ''' 27 | 28 | def __init__(self, **kwargs): 29 | self._previous_group = None 30 | super(CheckBox, self).__init__(**kwargs) 31 | 32 | def on_state(self, instance, value): 33 | if value == 'down': 34 | self.active = True 35 | else: 36 | self.active = False 37 | 38 | def _toggle_active(self): 39 | self._do_press() 40 | 41 | 42 | class FlatCheckBox(GrabBehavior, TouchRippleBehavior, 43 | LogBehavior, ThemeBehavior, CheckBox): 44 | check = ObjectProperty(None) 45 | no_interact = BooleanProperty(False) 46 | check_scale = NumericProperty(.5) 47 | outline_size = NumericProperty(5) 48 | color_tuple = ListProperty(['Grey', '0000']) 49 | check_color_tuple = ListProperty(['Grey', '1000']) 50 | outline_color_tuple = ListProperty(['Grey', '1000']) 51 | ripple_color_tuple = ListProperty(['Grey', '1000']) 52 | 53 | def __init__(self, **kwargs): 54 | super(FlatCheckBox, self).__init__(**kwargs) 55 | self.check = check = Check(scale=self.check_scale, 56 | color_tuple=self.check_color_tuple) 57 | self.bind(pos=check.setter('pos'), 58 | size=check.setter('size'), 59 | check_scale=check.setter('scale'), 60 | check_color_tuple=check.setter('color_tuple')) 61 | 62 | def on_active(self, instance, value): 63 | check = self.check 64 | if value and check not in self.children: 65 | self.add_widget(check) 66 | elif not value and check in self.children: 67 | self.remove_widget(check) 68 | 69 | def on_touch_down(self, touch): 70 | if self.no_interact: 71 | if self.collide_point(touch.x, touch.y): 72 | return False 73 | else: 74 | super(FlatCheckBox, self).on_touch_down(touch) 75 | 76 | def on_touch_move(self, touch): 77 | if self.no_interact: 78 | if self.collide_point(touch.x, touch.y): 79 | return False 80 | else: 81 | super(FlatCheckBox, self).on_touch_move(touch) 82 | 83 | def on_touch_up(self, touch): 84 | if self.no_interact: 85 | if self.collide_point(touch.x, touch.y): 86 | return False 87 | else: 88 | super(FlatCheckBox, self).on_touch_up(touch) 89 | 90 | 91 | class FlatCheckBoxListItem(GrabBehavior, TouchRippleBehavior, 92 | ThemeBehavior, BoxLayout): 93 | text = StringProperty(None) 94 | group = StringProperty(None) 95 | outline_size = NumericProperty(5) 96 | style = StringProperty(None, allownone=True) 97 | check_scale = NumericProperty(.7) 98 | font_color_tuple = ListProperty(['Grey', '1000']) 99 | color_tuple = ListProperty(['Grey', '500']) 100 | check_color_tuple = ListProperty(['Grey', '1000']) 101 | checkbox_color_tuple = ListProperty(['Grey', '0000']) 102 | outline_color_tuple = ListProperty(['Grey', '1000']) 103 | ripple_color_tuple = ListProperty(['Grey', '1000']) 104 | font_ramp_tuple = ListProperty(['default', '1']) 105 | halign = StringProperty('left') 106 | valign = StringProperty('bottom') 107 | alpha = NumericProperty(None, allownone=True) 108 | 109 | def on_touch_down(self, touch): 110 | if self.collide_point(touch.x, touch.y): 111 | self.toggle_checkbox() 112 | super(FlatCheckBoxListItem, self).on_touch_down(touch) 113 | 114 | def on_touch_up(self, touch): 115 | super(FlatCheckBoxListItem, self).on_touch_up(touch) 116 | 117 | def toggle_checkbox(self): 118 | self.ids.checkbox._toggle_active() 119 | -------------------------------------------------------------------------------- /flat_kivy/dbinterface.py: -------------------------------------------------------------------------------- 1 | from __future__ import unicode_literals, print_function 2 | from datetime import datetime, timedelta, date 3 | from kivy.storage.jsonstore import JsonStore 4 | from kivy.clock import Clock 5 | import os 6 | 7 | class DBInterface(object): 8 | 9 | def __init__(self, data_dir, name, do_date=False, do_hour=False, **kwargs): 10 | super(DBInterface, self).__init__(**kwargs) 11 | 12 | self.ensure_dir(data_dir) 13 | if do_date: 14 | if do_hour: 15 | date = self.convert_time_to_json_ymdh(self.get_time()) 16 | else: 17 | date = self.convert_time_to_json_ymd(self.get_time()) 18 | json_name = data_dir + name + '-' + date + '.json' 19 | reset_json_name = ( 20 | data_dir + name + '-' + date + '-reset_timers.json') 21 | else: 22 | json_name = data_dir + name + '.json' 23 | reset_json_name = data_dir + name + '-reset_timers.json' 24 | self.data = data = JsonStore(json_name) 25 | self.reset_timers = reset_timers = JsonStore(reset_json_name) 26 | self.sync = Clock.create_trigger(self.trigger_sync) 27 | self.check_reset(0.) 28 | Clock.schedule_interval(self.check_reset, 60.) 29 | 30 | def ensure_dir(self, f): 31 | d = os.path.dirname(f) 32 | if not os.path.exists(d): 33 | os.makedirs(d) 34 | 35 | def check_reset(self, dt): 36 | reset_timers = self.reset_timers 37 | current_time = self.get_time() 38 | keys_to_del = [] 39 | for each in reset_timers: 40 | expire_time = self.convert_time_from_json(each) 41 | if expire_time < current_time: 42 | data = reset_timers[each] 43 | self.set_entry(data['table'], data['row'], 44 | data['name'], None) 45 | keys_to_del.append(each) 46 | for key in keys_to_del: 47 | reset_timers.delete(key) 48 | 49 | 50 | def trigger_sync(self, dt): 51 | data = self.data 52 | print('syncing') 53 | data._is_changed = True 54 | data.store_sync() 55 | 56 | 57 | def get_entry(self, table, row, name): 58 | data = self.data 59 | try: 60 | return data[table][row][name]['value'] 61 | except: 62 | return None 63 | 64 | def get_row(self, table, row): 65 | data = self.data 66 | try: 67 | return data[table][row] 68 | except: 69 | return None 70 | 71 | def get_table(self, table): 72 | data = self.data 73 | try: 74 | return data[table] 75 | except: 76 | return None 77 | 78 | def remove_entry(self, table, row, name, value): 79 | data = self.data 80 | try: 81 | name_data = data[table][row][name] 82 | except: 83 | print('no entry: ', table, row, name) 84 | try: 85 | name_data['value'].remove(value) 86 | except: 87 | print(value, 'not found in: ', table, row, name) 88 | self.sync() 89 | 90 | def append_entry(self, table, row, name, value, do_timestamp=False): 91 | data = self.data 92 | try: 93 | table_data = data[table] 94 | except: 95 | data[table] = table_data = {} 96 | try: 97 | row_data = table_data[row] 98 | except: 99 | table_data[row] = row_data = {} 100 | try: 101 | name_data = row_data[name] 102 | except: 103 | name_data = {'value': []} 104 | row_data[name] = name_data 105 | if do_timestamp: 106 | time = self.get_time() 107 | time_stamp = self.convert_time_to_json(time) 108 | value = (value, time_stamp) 109 | name_data['value'].append(value) 110 | self.sync() 111 | 112 | def set_entry(self, table, row, name, value, do_history=False, 113 | reset_in_hours=None, do_timestamp=False): 114 | data = self.data 115 | print('set_entry', table, row, name, value) 116 | try: 117 | table_data = data[table] 118 | except: 119 | data[table] = table_data = {} 120 | try: 121 | row_data = table_data[row] 122 | except: 123 | table_data[row] = row_data = {} 124 | try: 125 | name_data = row_data[name] 126 | except: 127 | name_data = {'value': None} 128 | row_data[name] = name_data 129 | if do_history and 'history' not in name_data: 130 | name_data['history'] = {} 131 | if name_data['value'] != value: 132 | 133 | name_data['value'] = value 134 | if do_timestamp: 135 | time = self.get_time() 136 | time_stamp = self.convert_time_to_json(time) 137 | name_data['time_stamp'] = time_stamp 138 | if do_history: 139 | time = self.get_time() 140 | time_stamp = self.convert_time_to_json(time) 141 | name_data['history'][time_stamp] = value 142 | if reset_in_hours is not None: 143 | timed = timedelta(hours=reset_in_hours) 144 | expire_time = time + timed 145 | expires_at = self.convert_time_to_json(expire_time) 146 | reset_timers = self.reset_timers 147 | reset_timers[expires_at] = { 148 | 'table': table, 149 | 'row': row, 150 | 'name': name, 151 | } 152 | 153 | self.sync() 154 | if self.data[table] == {}: 155 | self.data[table] = table_data 156 | self.sync() 157 | 158 | 159 | 160 | def get_time(self): 161 | return datetime.utcnow() 162 | 163 | def convert_time_to_json_ymd(self, datetime): 164 | if datetime is not None: 165 | return datetime.strftime('%Y-%m-%d') 166 | else: 167 | return None 168 | 169 | def convert_time_to_json_ymdh(self, datetime): 170 | if datetime is not None: 171 | return datetime.strftime('%Y-%m-%dT%H') 172 | else: 173 | return None 174 | 175 | def convert_time_to_json(self, datetime): 176 | if datetime is not None: 177 | return datetime.strftime('%Y-%m-%dT%H:%M:%S') 178 | else: 179 | return None 180 | 181 | def convert_time_from_json(self, jsontime): 182 | if jsontime is not None: 183 | return datetime.strptime(jsontime, '%Y-%m-%dT%H:%M:%S') 184 | else: 185 | return None -------------------------------------------------------------------------------- /flat_kivy/font_definitions.py: -------------------------------------------------------------------------------- 1 | from __future__ import unicode_literals, print_function 2 | from kivy.uix.label import Label 3 | from kivy.event import EventDispatcher 4 | from flat_kivy.uix.flatlabel import FlatLabel 5 | from kivy.clock import Clock 6 | from kivy.properties import ListProperty 7 | import operator 8 | 9 | 10 | def get_style(style): 11 | if style is not None: 12 | try: 13 | return style_manager.styles[style] 14 | except: 15 | print('font style: ' + style + ' not defined.') 16 | return None 17 | else: 18 | return None 19 | 20 | def get_font_ramp_group(font_ramp_tuple): 21 | if font_ramp_tuple != []: 22 | group_name, ramp_name = font_ramp_tuple 23 | font_ramp = style_manager.get_font_ramp(ramp_name) 24 | if style_manager.check_ramp_group_exists(group_name): 25 | return style_manager.get_ramp_group(group_name) 26 | else: 27 | return style_manager.create_ramp_group(group_name, font_ramp) 28 | 29 | class FontStyle(object): 30 | 31 | def __init__(self, font_file, name, size_mobile, size_desktop, alpha, 32 | **kwargs): 33 | super(FontStyle, self).__init__(**kwargs) 34 | self.font_file = font_file 35 | self.name = name 36 | self.size_mobile = size_mobile 37 | self.size_desktop = size_desktop 38 | self.alpha = alpha 39 | 40 | class RampGroup(EventDispatcher): 41 | ignore_list = ListProperty(['default']) 42 | 43 | def __init__(self, font_ramp, name, **kwargs): 44 | super(RampGroup, self).__init__(**kwargs) 45 | self.tracked_labels = [] 46 | self.font_ramp = font_ramp 47 | self.name = name 48 | self.current_style = font_ramp[-1] 49 | self._test_label = FlatLabel() 50 | self._cache = {} 51 | self.max_iterations = 5 52 | self.trigger_fit_check = Clock.create_trigger( 53 | self.check_fit_for_all_labels) 54 | 55 | def copy_label_to_test_label(self, label, style): 56 | test_label = self._test_label 57 | test_label.size = label.size 58 | test_label.style = style 59 | test_label.text = label.text 60 | test_label.halign = label.halign 61 | test_label.valign = label.valign 62 | test_label.max_lines = label.max_lines 63 | test_label.texture_update() 64 | return test_label 65 | 66 | def check_fit_for_all_labels(self, dt): 67 | if self.name in self.ignore_list: 68 | return 69 | tracked_labels = self.tracked_labels 70 | font_ramp = self.font_ramp 71 | return_counts = {} 72 | 73 | for each in font_ramp: 74 | return_counts[each] = {'fit_count': 0, 'big_count': 0, 75 | 'small_count': 0} 76 | for label in tracked_labels: 77 | for style in font_ramp: 78 | return_count = return_counts[style] 79 | 80 | fit = self.get_fit(label, style) 81 | if fit == 'fits': 82 | return_count['fit_count'] += 1 83 | elif fit == 'toobig': 84 | return_count['big_count'] += 1 85 | elif fit =='toosmall': 86 | return_count['small_count'] += 1 87 | fit_counts = [] 88 | fit_a = fit_counts.append 89 | for style in return_counts: 90 | if return_counts[style]['big_count'] == 0: 91 | fit_a(( 92 | style, return_counts[style]['fit_count'], 93 | return_counts[style]['big_count'], 94 | return_counts[style]['small_count'], 95 | font_ramp.index(style))) 96 | sorted_fit = sorted(fit_counts, key=lambda x: ( 97 | x[1], -x[2], -x[3], -x[4])) 98 | if len(sorted_fit) > 0: 99 | last = sorted_fit[-1] 100 | else: 101 | last = [font_ramp[-1], 0, 0, 0] 102 | if last[2] > 0 and last[1] == 0 and last[3] == 0: 103 | style = font_ramp[-1] 104 | else: 105 | style = last[0] 106 | self.set_style(style) 107 | 108 | def set_style(self, style): 109 | for tracked_label in self.tracked_labels: 110 | tracked_label._do_check_adjustments = True 111 | tracked_label.style = style 112 | 113 | def reset_track_adjustments(self, dt): 114 | for tracked_label in self.tracked_labels: 115 | tracked_label._do_check_adjustments = True 116 | 117 | def calculate_fit(self, label): 118 | actual_size = label._label._internal_size 119 | size = label.size 120 | style = label.style 121 | status = 'fits' 122 | if actual_size[0] > size[0] or actual_size[1] > size[1]: 123 | status = 'toobig' 124 | elif actual_size[0] < size[0]*.5 and actual_size[1] < size[1] *.5: 125 | status = 'toosmall' 126 | return status 127 | 128 | def get_fit(self, label, style): 129 | key = (label.text, (label.width, label.height), style) 130 | if key not in self._cache: 131 | test_label = self.copy_label_to_test_label(label, style) 132 | self._cache[key] = self.calculate_fit(test_label) 133 | return self._cache[key] 134 | 135 | def add_label(self, label): 136 | tracked_labels = self.tracked_labels 137 | if label not in tracked_labels and isinstance(label, Label): 138 | tracked_labels.append(label) 139 | label.style = self.current_style 140 | else: 141 | print('Label already added or not instance of Label') 142 | 143 | def remove_widget(self, label): 144 | try: 145 | self.tracked_labels.remove(label) 146 | except: 147 | print('widget not removed, maybe it is not there') 148 | 149 | 150 | class StyleManager(object): 151 | 152 | def __init__(self, **kwargs): 153 | super(StyleManager, self).__init__(**kwargs) 154 | self.styles = {} 155 | self.ramp_groups = {} 156 | self.font_ramps = {} 157 | 158 | def add_style(self, font_file, name, size_mobile, size_desktop, alpha): 159 | style = FontStyle(font_file, name, size_mobile, size_desktop, alpha) 160 | self.styles[name] = style 161 | 162 | def add_font_ramp(self, name, ramp): 163 | styles = self.styles 164 | for style in ramp: 165 | assert(style in styles) 166 | self.font_ramps[name] = ramp 167 | 168 | def create_ramp_group(self, name, ramp): 169 | ramp_group = RampGroup(ramp, name) 170 | self.ramp_groups[name] = ramp_group 171 | return ramp_group 172 | 173 | def check_ramp_group_exists(self, name): 174 | return name in self.ramp_groups 175 | 176 | def get_font_ramp(self, ramp_name): 177 | return self.font_ramps[ramp_name] 178 | 179 | def get_ramp_group(self, group_name): 180 | return self.ramp_groups[group_name] 181 | 182 | style_manager = StyleManager() 183 | 184 | 185 | 186 | -------------------------------------------------------------------------------- /demo.py: -------------------------------------------------------------------------------- 1 | 2 | 3 | from kivy.uix.widget import Widget 4 | from kivy.lang import Builder 5 | 6 | from flat_kivy.flatapp import FlatApp 7 | from flat_kivy.uix.flattextinput import FlatTextInput 8 | 9 | Builder.load_string(''' 10 | : 11 | GridLayout: 12 | cols: 3 13 | pos: root.pos 14 | size: root.size 15 | padding: '5dp' 16 | spacing: '5dp' 17 | canvas.before: 18 | Color: 19 | rgb: 1,1,1 20 | Rectangle: 21 | pos: self.pos 22 | size: self.size 23 | 24 | FlatButton: 25 | text: 'button' 26 | theme: ('green', 'accent') 27 | 28 | FlatIconButton: 29 | text: 'icon button' 30 | icon: 'fa-tree' 31 | theme: ('green', 'accent') 32 | 33 | FlatToggleButton: 34 | text: 'toggle button' 35 | group: 'toggle' 36 | theme: ('green', 'accent') 37 | 38 | RaisedFlatToggleButton: 39 | text: 'raised toggle button' 40 | group: 'toggle' 41 | theme: ('green', 'accent') 42 | 43 | FlatCheckBoxListItem: 44 | text: 'check 1' 45 | group: 'check' 46 | theme: ('green', 'accent') 47 | 48 | FlatCheckBoxListItem: 49 | text: 'check 2' 50 | group: 'check' 51 | theme: ('green', 'accent') 52 | 53 | FlatCard: 54 | image_source: 'flat_kivy/AstroPic1.jpg' 55 | text: 'the card' 56 | color_tuple: ('Gray', '0000') 57 | 58 | FlatTextInput: 59 | 60 | BoxLayout: 61 | orientation: 'vertical' 62 | FlatLabel: 63 | text: 'FlatScrollView' 64 | size_hint_y: None 65 | height: '35dp' 66 | theme: ('green', 'main') 67 | 68 | FlatScrollView: 69 | do_scroll_x: False 70 | 71 | BoxLayout: 72 | orientation: 'vertical' 73 | height: '400dp' 74 | size_hint_y: None 75 | FlatLabel: 76 | text: '1' 77 | FlatLabel: 78 | text: '2' 79 | FlatLabel: 80 | text: '3' 81 | FlatLabel: 82 | text: '4' 83 | FlatLabel: 84 | text: '5' 85 | FlatLabel: 86 | text: '6' 87 | 88 | RaisedFlatButton: 89 | text: 'popup' 90 | theme: ('green', 'accent') 91 | on_release: popup_demo.open() 92 | popup_demo: popup_demo.__self__ 93 | 94 | FlatPopup: 95 | id: popup_demo 96 | title: 'Flat Popup Demo' 97 | size_hint: .6,.6 98 | on_parent: if self.parent: self.parent.remove_widget(self) 99 | 100 | BoxLayout: 101 | spacing: '5dp' 102 | padding: '5dp' 103 | RaisedFlatButton: 104 | text: 'just a button in here' 105 | theme: ('green', 'main') 106 | RaisedFlatButton: 107 | text: 'just a button in here' 108 | theme: ('green', 'main') 109 | 110 | FlatSlider: 111 | id: hor_slider 112 | orientation: 'horizontal' 113 | min: 10 114 | value: ver_slider.value 115 | theme: ('green', 'main') 116 | 117 | FlatSlider: 118 | id: ver_slider 119 | orientation: 'vertical' 120 | min: 10 121 | value: hor_slider.value 122 | theme: ('green', 'main') 123 | 124 | FlatSlider: 125 | value: hor_slider.value 126 | orientation: 'horizontal' 127 | disabled: True 128 | theme: ('green', 'main') 129 | ''') 130 | 131 | 132 | class RootWidget(Widget): 133 | pass 134 | 135 | 136 | class MyFlatApp(FlatApp): 137 | def build(self): 138 | return RootWidget() 139 | 140 | def setup_themes(self): 141 | main = { 142 | 'FlatButton': { 143 | 'color_tuple': ('Gray', '0000'), 144 | 'font_color_tuple': ('LightGreen', '800'), 145 | 'style': 'Button', 146 | }, 147 | 'RaisedFlatButton': { 148 | 'color_tuple': ('Gray', '0000'), 149 | 'font_color_tuple': ('LightGreen', '800'), 150 | 'style': 'Button', 151 | }, 152 | 'FlatLabel': { 153 | 'style': 'Button', 154 | }, 155 | 'FlatSlider': { 156 | 'bar_fill_color_tuple': ('LightGreen', '500'), 157 | 'handle_accent_color_tuple': ('LightGreen', '200'), 158 | } 159 | } 160 | 161 | accent = { 162 | 'FlatButton': { 163 | 'color_tuple': ('LightGreen', '500'), 164 | 'font_color_tuple': ('Gray', '1000'), 165 | 'style': 'Button', 166 | }, 167 | 'RaisedFlatButton': { 168 | 'color_tuple': ('LightGreen', '500'), 169 | 'font_color_tuple': ('Gray', '1000'), 170 | 'style': 'Button', 171 | }, 172 | 'FlatIconButton': { 173 | 'color_tuple': ('LightGreen', '500'), 174 | 'font_color_tuple': ('Gray', '1000'), 175 | 'style': 'Button', 176 | 'icon_color_tuple': ('Gray', '1000') 177 | }, 178 | 'FlatToggleButton': { 179 | 'color_tuple': ('LightGreen', '500'), 180 | 'font_color_tuple': ('Gray', '1000'), 181 | 'style': 'Button', 182 | }, 183 | 'RaisedFlatToggleButton': { 184 | 'color_tuple': ('LightGreen', '500'), 185 | 'font_color_tuple': ('Gray', '1000'), 186 | 'style': 'Button', 187 | }, 188 | 'FlatCheckBox': { 189 | 'color_tuple': ('Gray', '0000'), 190 | 'check_color_tuple': ('LightGreen', '500'), 191 | 'outline_color_tuple': ('Gray', '1000'), 192 | 'style': 'Button', 193 | 'check_scale': .7, 194 | 'outline_size': '10dp', 195 | }, 196 | 'FlatCheckBoxListItem': { 197 | 'font_color_tuple': ('Gray', '1000'), 198 | 'check_color_tuple': ('LightGreen', '500'), 199 | 'outline_color_tuple': ('Gray', '800'), 200 | 'style': 'Button', 201 | 'check_scale': .7, 202 | 'outline_size': '10dp', 203 | }, 204 | } 205 | 206 | self.theme_manager.add_theme('green', 'main', main) 207 | self.theme_manager.add_theme('green', 'accent', accent) 208 | 209 | 210 | if __name__ == '__main__': 211 | MyFlatApp().run() 212 | -------------------------------------------------------------------------------- /flat_kivy/numpad.kv: -------------------------------------------------------------------------------- 1 | : 2 | BoxLayout: 3 | size: root.size 4 | pos: root.pos 5 | orientation: 'vertical' 6 | padding: 10 7 | FlatLabel: 8 | size_hint_y: .2 9 | text: root.display_text + root.units if root.units != None else root.display_text 10 | color: (0., 0., 0., 1.) 11 | text_size: self.size 12 | #theme: ('blue', 'variant_2') 13 | halign: 'right' 14 | font_size: self.height * .75 15 | GridLayout: 16 | cols:3 17 | size_hint_y: .8 18 | padding: 5 19 | spacing: 5 20 | FlatButton: 21 | text: "7" 22 | size_hint: (.33,.25) 23 | on_release: root.button_callback("7") 24 | font_size: self.height * .4 25 | #theme: ('blue', 'numpad') 26 | FlatButton: 27 | text: "8" 28 | size_hint: (.33,.25) 29 | on_release: root.button_callback("8") 30 | font_size: self.height * .4 31 | #theme: ('blue', 'numpad') 32 | FlatButton: 33 | text: "9" 34 | size_hint: (.33,.25) 35 | on_release: root.button_callback("9") 36 | font_size: self.height * .4 37 | #theme: ('blue', 'numpad') 38 | FlatButton: 39 | text: "4" 40 | size_hint: (.33,.25) 41 | on_release: root.button_callback("4") 42 | font_size: self.height * .4 43 | #theme: ('blue', 'numpad') 44 | FlatButton: 45 | text: "5" 46 | size_hint: (.33,.25) 47 | on_release: root.button_callback("5") 48 | font_size: self.height * .4 49 | #theme: ('blue', 'numpad') 50 | FlatButton: 51 | text: "6" 52 | size_hint: (.33,.25) 53 | on_release: root.button_callback("6") 54 | font_size: self.height * .4 55 | #theme: ('blue', 'numpad') 56 | FlatButton: 57 | text: "1" 58 | size_hint: (.33,.25) 59 | on_release: root.button_callback("1") 60 | font_size: self.height * .4 61 | #theme: ('blue', 'numpad') 62 | FlatButton: 63 | text: "2" 64 | size_hint: (.33,.25) 65 | on_release: root.button_callback("2") 66 | font_size: self.height * .4 67 | #theme: ('blue', 'numpad') 68 | FlatButton: 69 | text: "3" 70 | size_hint: (.33,.25) 71 | on_release: root.button_callback("3") 72 | font_size: self.height * .4 73 | #theme: ('blue', 'numpad') 74 | FlatButton: 75 | text: "del" 76 | size_hint: (.33,.25) 77 | on_release: root.button_callback("del") 78 | font_size: self.height * .4 79 | #theme: ('blue', 'numpad') 80 | FlatButton: 81 | text: "0" 82 | size_hint: (.33,.25) 83 | on_release: root.button_callback("0") 84 | font_size: self.height * .4 85 | #theme: ('blue', 'numpad') 86 | FlatButton: 87 | text: "enter" 88 | size_hint: (.33,.25) 89 | on_release: root.button_callback("ret") 90 | font_size: self.height * .4 91 | style: 'Display 2' 92 | color: (6./255., 114./255., 0.) 93 | 94 | 95 | <-DecimalNumPad>: 96 | BoxLayout: 97 | size: root.size 98 | pos: root.pos 99 | orientation: 'vertical' 100 | padding: 10 101 | FlatLabel: 102 | size_hint_y: .2 103 | text: root.display_text + root.units if root.units != None else root.display_text 104 | text_size: self.size 105 | halign: 'right' 106 | color: (0., 0., 0., 1.) 107 | #theme: ('blue', 'variant_2') 108 | font_size: self.height * .75 109 | GridLayout: 110 | cols:3 111 | size_hint_y: .8 112 | padding: 5 113 | spacing: 5 114 | FlatButton: 115 | #theme: ('blue', 'numpad') 116 | text: "7" 117 | size_hint: (.33,.20) 118 | on_release: root.button_callback("7") 119 | font_size: self.height * .4 120 | FlatButton: 121 | #theme: ('blue', 'numpad') 122 | text: "8" 123 | size_hint: (.33,.20) 124 | on_release: root.button_callback("8") 125 | font_size: self.height * .4 126 | FlatButton: 127 | #theme: ('blue', 'numpad') 128 | text: "9" 129 | size_hint: (.33,.20) 130 | on_release: root.button_callback("9") 131 | font_size: self.height * .4 132 | FlatButton: 133 | #theme: ('blue', 'numpad') 134 | text: "4" 135 | size_hint: (.33,.20) 136 | on_release: root.button_callback("4") 137 | font_size: self.height * .4 138 | FlatButton: 139 | #theme: ('blue', 'numpad') 140 | text: "5" 141 | size_hint: (.33,.20) 142 | on_release: root.button_callback("5") 143 | font_size: self.height * .4 144 | FlatButton: 145 | #theme: ('blue', 'numpad') 146 | text: "6" 147 | size_hint: (.33,.20) 148 | on_release: root.button_callback("6") 149 | font_size: self.height * .4 150 | FlatButton: 151 | #theme: ('blue', 'numpad') 152 | text: "1" 153 | size_hint: (.33,.20) 154 | on_release: root.button_callback("1") 155 | font_size: self.height * .4 156 | FlatButton: 157 | #theme: ('blue', 'numpad') 158 | text: "2" 159 | size_hint: (.33,.20) 160 | on_release: root.button_callback("2") 161 | font_size: self.height * .4 162 | FlatButton: 163 | #theme: ('blue', 'numpad') 164 | text: "3" 165 | size_hint: (.33,.20) 166 | on_release: root.button_callback("3") 167 | font_size: self.height * .4 168 | FlatButton: 169 | #theme: ('blue', 'numpad') 170 | text: "del" 171 | size_hint: (.33,.20) 172 | on_release: root.button_callback("del") 173 | font_size: self.height * .4 174 | FlatButton: 175 | #theme: ('blue', 'numpad') 176 | text: "0" 177 | size_hint: (.33,.20) 178 | on_release: root.button_callback("0") 179 | font_size: self.height * .4 180 | FlatButton: 181 | #theme: ('blue', 'numpad') 182 | text: '.' 183 | size_hint: (.33, .20) 184 | on_release: root.button_callback(".") 185 | font_size: self.height * .4 186 | Widget: 187 | size_hint: (.33, .20) 188 | Widget: 189 | size_hint: (.33, .20) 190 | FlatButton: 191 | color: (6./255., 114./255., 0.) 192 | text: "enter" 193 | size_hint: (.33,.20) 194 | on_release: root.button_callback("ret") 195 | font_size: self.height * .4 196 | style: 'Display 2' -------------------------------------------------------------------------------- /flat_kivy/color_definitions.py: -------------------------------------------------------------------------------- 1 | 2 | colors = { 3 | 'Red': { 4 | '500': 'e51c23', 5 | '50': 'fde0dc', 6 | '100': 'f9bdbb', 7 | '200': 'f69988', 8 | '300': 'f36c60', 9 | '400': 'e84e40', 10 | '500': 'e51c23', 11 | '600': 'dd191d', 12 | '700': 'd01716', 13 | '800': 'c41411', 14 | '900': 'b0120a', 15 | 'A100': 'ff7997', 16 | 'A200': 'ff5177', 17 | 'A400': 'ff2d6f', 18 | 'A700': 'e00032', 19 | }, 20 | 21 | 'Pink': { 22 | '500': 'e91e63', 23 | '50': 'fce4ec', 24 | '100': 'f8bbd0', 25 | '200': 'f48fb1', 26 | '300': 'f06292', 27 | '400': 'ec407a', 28 | '500': 'e91e63', 29 | '600': 'd81b60', 30 | '700': 'c2185b', 31 | '800': 'ad1457', 32 | '900': '880e4f', 33 | 'A100': 'ff80ab', 34 | 'A200': 'ff4081', 35 | 'A400': 'f50057', 36 | 'A700': 'c51162', 37 | }, 38 | 39 | 'Purple': { 40 | '500': '9c27b0', 41 | '50': 'f3e5f5', 42 | '100': 'e1bee7', 43 | '200': 'ce93d8', 44 | '300': 'ba68c8', 45 | '400': 'ab47bc', 46 | '500': '9c27b0', 47 | '600': '8e24aa', 48 | '700': '7b1fa2', 49 | '800': '6a1b9a', 50 | '900': '4a148c', 51 | 'A100': 'ea80fc', 52 | 'A200': 'e040fb', 53 | 'A400': 'd500f9', 54 | 'A700': 'aa00ff', 55 | }, 56 | 57 | 'DeepPurple': { 58 | '500': '673ab7', 59 | '50': 'ede7f6', 60 | '100': 'd1c4e9', 61 | '200': 'b39ddb', 62 | '300': '9575cd', 63 | '400': '7e57c2', 64 | '500': '673ab7', 65 | '600': '5e35b1', 66 | '700': '512da8', 67 | '800': '4527a0', 68 | '900': '311b92', 69 | 'A100': 'b388ff', 70 | 'A200': '7c4dff', 71 | 'A400': '651fff', 72 | 'A700': '6200ea', 73 | }, 74 | 75 | 'Indigo': { 76 | '500': '3f51b5', 77 | '50': 'e8eaf6', 78 | '100': 'c5cae9', 79 | '200': '9fa8da', 80 | '300': '7986cb', 81 | '400': '5c6bc0', 82 | '500': '3f51b5', 83 | '600': '3949ab', 84 | '700': '303f9f', 85 | '800': '283593', 86 | '900': '1a237e', 87 | 'A100': '8c9eff', 88 | 'A200': '536dfe', 89 | 'A400': '3d5afe', 90 | 'A700': '304ffe', 91 | }, 92 | 93 | 'Blue': { 94 | '500': '5677fc', 95 | '50': 'e7e9fd', 96 | '100': 'd0d9ff', 97 | '200': 'afbfff', 98 | '300': '91a7ff', 99 | '400': '738ffe', 100 | '500': '5677fc', 101 | '600': '4e6cef', 102 | '700': '455ede', 103 | '800': '3b50ce', 104 | '900': '2a36b1', 105 | 'A100': 'a6baff', 106 | 'A200': '6889ff', 107 | 'A400': '4d73ff', 108 | 'A700': '4d69ff', 109 | }, 110 | 111 | 'LightBlue': { 112 | '500': '03a9f4', 113 | '50': 'e1f5fe', 114 | '100': 'b3e5fc', 115 | '200': '81d4fa', 116 | '300': '4fc3f7', 117 | '400': '29b6f6', 118 | '500': '03a9f4', 119 | '600': '039be5', 120 | '700': '0288d1', 121 | '800': '0277bd', 122 | '900': '01579b', 123 | 'A100': '80d8ff', 124 | 'A200': '40c4ff', 125 | 'A400': '00b0ff', 126 | 'A700': '0091ea', 127 | }, 128 | 129 | 'Cyan': { 130 | '500': '00bcd4', 131 | '50': 'e0f7fa', 132 | '100': 'b2ebf2', 133 | '200': '80deea', 134 | '300': '4dd0e1', 135 | '400': '26c6da', 136 | '500': '00bcd4', 137 | '600': '00acc1', 138 | '700': '0097a7', 139 | '800': '00838f', 140 | '900': '006064', 141 | 'A100': '84ffff', 142 | 'A200': '18ffff', 143 | 'A400': '00e5ff', 144 | 'A700': '00b8d4', 145 | }, 146 | 147 | 'Teal': { 148 | '500': '009688', 149 | '50': 'e0f2f1', 150 | '100': 'b2dfdb', 151 | '200': '80cbc4', 152 | '300': '4db6ac', 153 | '400': '26a69a', 154 | '500': '009688', 155 | '600': '00897b', 156 | '700': '00796b', 157 | '800': '00695c', 158 | '900': '004d40', 159 | 'A100': 'a7ffeb', 160 | 'A200': '64ffda', 161 | 'A400': '1de9b6', 162 | 'A700': '00bfa5', 163 | }, 164 | 165 | 'Green': { 166 | '500': '259b24', 167 | '50': 'd0f8ce', 168 | '100': 'a3e9a4', 169 | '200': '72d572', 170 | '300': '42bd41', 171 | '400': '2baf2b', 172 | '500': '259b24', 173 | '600': '0a8f08', 174 | '700': '0a7e07', 175 | '800': '056f00', 176 | '900': '0d5302', 177 | 'A100': 'a2f78d', 178 | 'A200': '5af158', 179 | 'A400': '14e715', 180 | 'A700': '12c700', 181 | }, 182 | 183 | 'LightGreen': { 184 | '500': '8bc34a', 185 | '50': 'f1f8e9', 186 | '100': 'dcedc8', 187 | '200': 'c5e1a5', 188 | '300': 'aed581', 189 | '400': '9ccc65', 190 | '500': '8bc34a', 191 | '600': '7cb342', 192 | '700': '689f38', 193 | '800': '558b2f', 194 | '900': '33691e', 195 | 'A100': 'ccff90', 196 | 'A200': 'b2ff59', 197 | 'A400': '76ff03', 198 | 'A700': '64dd17', 199 | }, 200 | 201 | 'Lime': { 202 | '500': 'cddc39', 203 | '50': 'f9fbe7', 204 | '100': 'f0f4c3', 205 | '200': 'e6ee9c', 206 | '300': 'dce775', 207 | '400': 'd4e157', 208 | '500': 'cddc39', 209 | '600': 'c0ca33', 210 | '700': 'afb42b', 211 | '800': '9e9d24', 212 | '900': '827717', 213 | 'A100': 'f4ff81', 214 | 'A200': 'eeff41', 215 | 'A400': 'c6ff00', 216 | 'A700': 'aeea00', 217 | }, 218 | 219 | 220 | 'Yellow': { 221 | '500': 'ffeb3b', 222 | '50': 'fffde7', 223 | '100': 'fff9c4', 224 | '200': 'fff59d', 225 | '300': 'fff176', 226 | '400': 'ffee58', 227 | '500': 'ffeb3b', 228 | '600': 'fdd835', 229 | '700': 'fbc02d', 230 | '800': 'f9a825', 231 | '900': 'f57f17', 232 | 'A100': 'ffff8d', 233 | 'A200': 'ffff00', 234 | 'A400': 'ffea00', 235 | 'A700': 'ffd600', 236 | }, 237 | 238 | 'Amber': { 239 | '500': 'ffc107', 240 | '50': 'fff8e1', 241 | '100': 'ffecb3', 242 | '200': 'ffe082', 243 | '300': 'ffd54f', 244 | '400': 'ffca28', 245 | '500': 'ffc107', 246 | '600': 'ffb300', 247 | '700': 'ffa000', 248 | '800': 'ff8f00', 249 | '900': 'ff6f00', 250 | 'A100': 'ffe57f', 251 | 'A200': 'ffd740', 252 | 'A400': 'ffc400', 253 | 'A700': 'ffab00', 254 | }, 255 | 256 | 'Orange': { 257 | '500': 'ff9800', 258 | '50': 'fff3e0', 259 | '100': 'ffe0b2', 260 | '200': 'ffcc80', 261 | '300': 'ffb74d', 262 | '400': 'ffa726', 263 | '500': 'ff9800', 264 | '600': 'fb8c00', 265 | '700': 'f57c00', 266 | '800': 'ef6c00', 267 | '900': 'e65100', 268 | 'A100': 'ffd180', 269 | 'A200': 'ffab40', 270 | 'A400': 'ff9100', 271 | 'A700': 'ff6d00', 272 | }, 273 | 'DeepOrange': { 274 | '500': 'ff5722', 275 | '50': 'fbe9e7', 276 | '100': 'ffccbc', 277 | '200': 'ffab91', 278 | '300': 'ff8a65', 279 | '400': 'ff7043', 280 | '500': 'ff5722', 281 | '600': 'f4511e', 282 | '700': 'e64a19', 283 | '800': 'd84315', 284 | '900': 'bf360c', 285 | 'A100': 'ff9e80', 286 | 'A200': 'ff6e40', 287 | 'A400': 'ff3d00', 288 | 'A700': 'dd2c00', 289 | }, 290 | 'Brown': { 291 | '500': '795548', 292 | '50': 'efebe9', 293 | '100': 'd7ccc8', 294 | '200': 'bcaaa4', 295 | '300': 'a1887f', 296 | '400': '8d6e63', 297 | '500': '795548', 298 | '600': '6d4c41', 299 | '700': '5d4037', 300 | '800': '4e342e', 301 | '900': '3e2723', 302 | }, 303 | 'Grey': { 304 | '500': '9e9e9e', 305 | '50': 'fafafa', 306 | '100': 'f5f5f5', 307 | '200': 'eeeeee', 308 | '300': 'e0e0e0', 309 | '400': 'bdbdbd', 310 | '500': '9e9e9e', 311 | '600': '757575', 312 | '700': '616161', 313 | '800': '424242', 314 | '900': '212121', 315 | '1000': '000000', 316 | '0000': 'ffffff', 317 | }, 318 | 'BlueGrey': { 319 | '500': '607d8b', 320 | '50': 'eceff1', 321 | '100': 'cfd8dc', 322 | '200': 'b0bec5', 323 | '300': '90a4ae', 324 | '400': '78909c', 325 | '500': '607d8b', 326 | '600': '546e7a', 327 | '700': '455a64', 328 | '800': '37474f', 329 | '900': '263238', 330 | }, 331 | 'Gray': { 332 | '500': '9e9e9e', 333 | '50': 'fafafa', 334 | '100': 'f5f5f5', 335 | '200': 'eeeeee', 336 | '300': 'e0e0e0', 337 | '400': 'bdbdbd', 338 | '500': '9e9e9e', 339 | '600': '757575', 340 | '700': '616161', 341 | '800': '424242', 342 | '900': '212121', 343 | '1000': '000000', 344 | '0000': 'ffffff', 345 | }, 346 | 'BlueGray': { 347 | '500': '607d8b', 348 | '50': 'eceff1', 349 | '100': 'cfd8dc', 350 | '200': 'b0bec5', 351 | '300': '90a4ae', 352 | '400': '78909c', 353 | '500': '607d8b', 354 | '600': '546e7a', 355 | '700': '455a64', 356 | '800': '37474f', 357 | '900': '263238', 358 | }, 359 | } 360 | 361 | 362 | 363 | -------------------------------------------------------------------------------- /flat_kivy/flatapp.py: -------------------------------------------------------------------------------- 1 | from __future__ import unicode_literals, print_function 2 | from kivy.app import App 3 | from kivy.properties import ObjectProperty, NumericProperty, BooleanProperty 4 | from kivy.clock import Clock 5 | 6 | from flat_kivy.uix.behaviors import LogBehavior 7 | from flat_kivy.uix.flatbutton import FlatButton, RaisedFlatButton 8 | from flat_kivy.uix.flattogglebutton import (FlatToggleButton, 9 | RaisedFlatToggleButton) 10 | from flat_kivy.uix.flatpopup import FlatPopup 11 | from flat_kivy.uix import ErrorContent, OptionContent 12 | from flat_kivy.uix.flaticonbutton import FlatIconButton 13 | from flat_kivy.uix.flatcheckbox import FlatCheckBox, FlatCheckBoxListItem 14 | from flat_kivy.uix.flatlabel import FlatLabel 15 | from flat_kivy.uix.flatslider import FlatSlider 16 | from flat_kivy.uix.flatcard import FlatCard 17 | from flat_kivy.uix.flattextinput import FlatTextInput 18 | 19 | from numpad import DecimalNumPad, NumPad 20 | from utils import get_icon_char, get_rgba_color, construct_target_file_name 21 | from font_definitions import get_font_ramp_group, get_style, style_manager 22 | from dbinterface import DBInterface 23 | 24 | 25 | def style_default(style_name): 26 | return None 27 | 28 | def color_default(color_tuple): 29 | return (1., 1., 1., 1.) 30 | 31 | def icon_default(icon_name): 32 | return '' 33 | 34 | def ramp_default(ramp_group_tuple): 35 | return None 36 | 37 | 38 | class ThemeManager(object): 39 | 40 | types_to_theme = { 41 | 'FlatButton': FlatButton, 'FlatIconButton': FlatIconButton, 42 | 'FlatToggleButton': FlatToggleButton, 43 | 'RaisedFlatButton': RaisedFlatButton, 44 | 'RaisedFlatToggleButton': RaisedFlatToggleButton, 45 | 'FlatLabel': FlatLabel, 46 | 'FlatCheckBox': FlatCheckBox, 47 | 'FlatCheckBoxListItem': FlatCheckBoxListItem, 48 | 'FlatSlider': FlatSlider, 49 | 'FlatCard': FlatCard, 50 | } 51 | 52 | themes = {} 53 | 54 | def get_theme(self, theme_name, variant_name): 55 | try: 56 | return self.themes[theme_name][variant_name] 57 | except KeyError: 58 | print(theme_name, variant_name, 'not in theme') 59 | return None 60 | 61 | def get_theme_types(self): 62 | return self.types_to_theme 63 | 64 | def add_theme_type(self, type_name, theme_type): 65 | self.types_to_theme[type_name] = theme_type 66 | 67 | def add_theme(self, theme, variant, theme_dict): 68 | themes = self.themes 69 | if theme not in themes: 70 | themes[theme] = {} 71 | if variant not in themes[theme]: 72 | themes[theme][variant] = {} 73 | self.themes[theme][variant] = theme_dict 74 | 75 | 76 | class FlatApp(App): 77 | get_color = ObjectProperty(color_default) 78 | get_icon = ObjectProperty(icon_default) 79 | get_style = ObjectProperty(style_default) 80 | get_ramp_group = ObjectProperty() 81 | device_id = NumericProperty(None) 82 | do_device_id = BooleanProperty(True) 83 | 84 | def __init__(self, **kwargs): 85 | self.theme_manager = ThemeManager() 86 | self.setup_font_ramps() 87 | self.get_color = get_rgba_color 88 | self.get_icon = get_icon_char 89 | self.get_style = get_style 90 | self.get_ramp_group = get_font_ramp_group 91 | super(FlatApp, self).__init__(**kwargs) 92 | self.setup_themes() 93 | self.numpads = numpads = {} 94 | numpads['decimal'] = DecimalNumPad() 95 | numpads['regular'] = NumPad() 96 | if self.do_device_id: 97 | log_behavior = LogBehavior() 98 | self.log_manager = log_manager = log_behavior.log_manager 99 | self.settings_interface = settings_interface = DBInterface( 100 | construct_target_file_name('', __file__), 'settings') 101 | self.device_id = device_id = settings_interface.get_entry( 102 | 'settings', 'device_id', 'value') 103 | 104 | self.bind(device_id=log_manager.setter('device_id')) 105 | if device_id is None: 106 | Clock.schedule_once(self.register_device_id) 107 | 108 | def _register_device_id(self, value, is_return): 109 | if is_return: 110 | self.device_id = value 111 | self.settings_interface.set_entry('settings', 'device_id', 'value', 112 | value) 113 | 114 | def register_device_id(self, dt): 115 | self.raise_numpad('Enter Device ID', self._register_device_id, 116 | auto_dismiss=False) 117 | 118 | def get_font(self, font_file): 119 | return construct_target_file_name(font_file, None) 120 | 121 | def raise_error(self, error_title, error_text, auto_dismiss=True, timeout=None): 122 | error_content = ErrorContent() 123 | error_popup = FlatPopup( 124 | content=error_content, size_hint=(.6, .4), 125 | auto_dismiss=auto_dismiss) 126 | error_content.error_text = error_text 127 | error_popup.title = error_title 128 | dismiss_button = error_content.dismiss_button 129 | dismiss_button.bind(on_release=error_popup.dismiss) 130 | error_popup.open() 131 | if timeout is not None: 132 | def close_popup(dt): 133 | error_popup.dismiss() 134 | Clock.schedule_once(close_popup, timeout) 135 | 136 | def raise_option_dialogue(self, option_title, option_text, options, 137 | callback, auto_dismiss=True): 138 | option_content = OptionContent(options, option_text=option_text, 139 | callback=callback) 140 | option_popup = FlatPopup(content=option_content, size_hint=(.6,.4), 141 | auto_dismiss=auto_dismiss) 142 | option_popup.title = option_title 143 | option_content.dismiss_func = option_popup.dismiss 144 | option_popup.open() 145 | 146 | def raise_numpad(self, numpad_title, callback, units=None, 147 | minimum=None, maximum=None, do_decimal=False, auto_dismiss=True): 148 | if do_decimal: 149 | numpad = self.numpads['decimal'] 150 | else: 151 | numpad = self.numpads['regular'] 152 | numpad.units = units 153 | numpad.minimum_value = minimum 154 | numpad.maximum_value = maximum 155 | if numpad.parent is not None: 156 | numpad.parent.remove_widget(numpad) 157 | numpad_popup = FlatPopup( 158 | title=numpad_title, content=numpad, size_hint=(.8, .8), 159 | auto_dismiss=auto_dismiss) 160 | def return_callback(value, is_return): 161 | if callback is not None: 162 | callback(value, is_return) 163 | if is_return: 164 | numpad_popup.dismiss(force=True) 165 | numpad_popup.ids.container.remove_widget(numpad) 166 | numpad.return_callback = None 167 | numpad.display_text = '0' 168 | numpad.return_callback = return_callback 169 | numpad_popup.open() 170 | 171 | def setup_themes(self): 172 | variant_1 = { 173 | 'FlatButton':{ 174 | 'color_tuple': ('LightBlue', '500'), 175 | 'ripple_color_tuple': ('Cyan', '100'), 176 | 'font_color_tuple': ('Gray', '1000'), 177 | 'style': 'Display 2', 178 | 'ripple_scale': 2.0, 179 | }, 180 | 'FlatIconButton':{ 181 | 'color_tuple': ('LightBlue', '500'), 182 | 'ripple_color_tuple': ('Cyan', '100'), 183 | 'font_color_tuple': ('Gray', '1000'), 184 | 'style': 'Display 2', 185 | 'ripple_scale': 2.0, 186 | 'icon_color_tuple': ('Gray', '1000') 187 | }, 188 | 'FlatToggleButton':{ 189 | 'color_tuple': ('LightBlue', '500'), 190 | 'ripple_color_tuple': ('Cyan', '100'), 191 | 'font_color_tuple': ('Gray', '1000'), 192 | 'style': 'Display 2', 193 | 'ripple_scale': 2.0, 194 | }, 195 | 'FlatCheckBox':{ 196 | 'color_tuple': ('Gray', '0000'), 197 | 'ripple_color_tuple': ('Cyan', '100'), 198 | 'check_color_tuple': ('LightBlue', '500'), 199 | 'outline_color_tuple': ('Gray', '1000'), 200 | 'style': 'Display 2', 201 | 'ripple_scale': 2.0, 202 | 'check_scale': .7, 203 | 'outline_size': '10dp', 204 | }, 205 | 'FlatCheckBoxListItem':{ 206 | 'color_tuple': ('Gray', '0000'), 207 | 'ripple_color_tuple': ('Cyan', '100'), 208 | 'check_color_tuple': ('LightBlue', '500'), 209 | 'outline_color_tuple': ('Gray', '1000'), 210 | 'style': 'Display 2', 211 | 'ripple_scale': 2.0, 212 | 'check_scale': .7, 213 | 'outline_size': '10dp', 214 | }, 215 | } 216 | 217 | variant_2 = { 218 | 'FlatButton':{ 219 | 'color_tuple': ('Green', '500'), 220 | 'ripple_color_tuple': ('Cyan', '100'), 221 | 'font_color_tuple': ('Gray', '1000'), 222 | 'style': 'Display 2', 223 | 'ripple_scale': 2.0, 224 | }, 225 | 'FlatIconButton':{ 226 | 'color_tuple': ('Green', '500'), 227 | 'ripple_color_tuple': ('Cyan', '100'), 228 | 'font_color_tuple': ('Gray', '1000'), 229 | 'style': 'Display 2', 230 | 'ripple_scale': 2.0, 231 | 'icon_color_tuple': ('Gray', '1000') 232 | }, 233 | } 234 | 235 | self.theme_manager.add_theme('blue', 'variant_1', variant_1) 236 | self.theme_manager.add_theme('blue', 'variant_2', variant_2) 237 | 238 | def setup_font_ramps(self): 239 | font_styles = { 240 | 'Display 4': { 241 | 'font': 'Roboto-Light.ttf', 242 | 'sizings': {'mobile': (112, 'sp'), 'desktop': (112, 'sp')}, 243 | 'alpha': .65, 244 | 'wrap': False, 245 | }, 246 | 'Display 3': { 247 | 'font': 'Roboto-Regular.ttf', 248 | 'sizings': {'mobile': (56, 'sp'), 'desktop': (56, 'sp')}, 249 | 'alpha': .65, 250 | 'wrap': False, 251 | }, 252 | 'Display 2': { 253 | 'font': 'Roboto-Regular.ttf', 254 | 'sizings': {'mobile': (45, 'sp'), 'desktop': (45, 'sp')}, 255 | 'alpha': .65, 256 | 'wrap': True, 257 | 'wrap_id': '1', 258 | 'leading': (48, 'pt'), 259 | }, 260 | 'Display 1': { 261 | 'font': 'Roboto-Regular.ttf', 262 | 'sizings': {'mobile': (34, 'sp'), 'desktop': (34, 'sp')}, 263 | 'alpha': .65, 264 | 'wrap': True, 265 | 'wrap_id': '2', 266 | 'leading': (40, 'pt'), 267 | }, 268 | 'Headline': { 269 | 'font': 'Roboto-Regular.ttf', 270 | 'sizings': {'mobile': (24, 'sp'), 'desktop': (24, 'sp')}, 271 | 'alpha': .87, 272 | 'wrap': True, 273 | 'wrap_id': '3', 274 | 'leading': (32, 'pt'), 275 | }, 276 | 'Title': { 277 | 'font': 'Roboto-Medium.ttf', 278 | 'sizings': {'mobile': (20, 'sp'), 'desktop': (20, 'sp')}, 279 | 'alpha': .87, 280 | 'wrap': False, 281 | }, 282 | 'Subhead': { 283 | 'font': 'Roboto-Regular.ttf', 284 | 'sizings': {'mobile': (16, 'sp'), 'desktop': (15, 'sp')}, 285 | 'alpha': .87, 286 | 'wrap': True, 287 | 'wrap_id': '4', 288 | 'leading': (28, 'pt'), 289 | }, 290 | 'Body 2': { 291 | 'font': 'Roboto-Medium.ttf', 292 | 'sizings': {'mobile': (14, 'sp'), 'desktop': (13, 'sp')}, 293 | 'alpha': .87, 294 | 'wrap': True, 295 | 'wrap_id': '5', 296 | 'leading': (24, 'pt'), 297 | }, 298 | 'Body 1': { 299 | 'font': 'Roboto-Regular.ttf', 300 | 'sizings': {'mobile': (14, 'sp'), 'desktop': (13, 'sp')}, 301 | 'alpha': .87, 302 | 'wrap': True, 303 | 'wrap_id': '6', 304 | 'leading': (20, 'pt'), 305 | }, 306 | 'Caption': { 307 | 'font': 'Roboto-Regular.ttf', 308 | 'sizings': {'mobile': (12, 'sp'), 'desktop': (12, 'sp')}, 309 | 'alpha': .65, 310 | 'wrap': False, 311 | }, 312 | 'Menu': { 313 | 'font': 'Roboto-Medium.ttf', 314 | 'sizings': {'mobile': (14, 'sp'), 'desktop': (13, 'sp')}, 315 | 'alpha': .87, 316 | 'wrap': False, 317 | }, 318 | 'Button': { 319 | 'font': 'Roboto-Medium.ttf', 320 | 'sizings': {'mobile': (14, 'sp'), 'desktop': (14, 'sp')}, 321 | 'alpha': .87, 322 | 'wrap': False, 323 | }, 324 | } 325 | for each in font_styles: 326 | style = font_styles[each] 327 | sizings = style['sizings'] 328 | style_manager.add_style(style['font'], each, sizings['mobile'], 329 | sizings['desktop'], style['alpha']) 330 | 331 | style_manager.add_font_ramp('1', ['Display 2', 'Display 1', 332 | 'Headline', 'Subhead', 'Body 2', 'Body 1']) 333 | 334 | 335 | if __name__ == '__main__': 336 | FlatApp().run() -------------------------------------------------------------------------------- /flat_kivy/data/ui_elements.kv: -------------------------------------------------------------------------------- 1 | #:import ScrollEffect kivy.effects.scroll.ScrollEffect 2 | #:import randint random.randint 3 | 4 | : 5 | text: texti.text 6 | texti: texti 7 | orientation: 'tb-lr' 8 | padding: 15 9 | spacing: 15 10 | FlatTextInput: 11 | id: texti 12 | height: 50 13 | size_hint: (1.0, None) 14 | FlatButton: 15 | text: 'Submit' 16 | on_release: root.close_callback() 17 | size_hint: (1.0, .1) 18 | color: (43./255., 153./255., 1.0) 19 | 20 | 21 | <-FlatTextInput>: 22 | ripple_color: app.get_color(self.ripple_color_tuple) 23 | canvas.before: 24 | Color: 25 | rgba: self.background_color 26 | Rectangle: 27 | size: self.size 28 | pos: self.pos 29 | Color: 30 | rgba: self.cursor_color 31 | Rectangle: 32 | size: (self.size[0], sp(2)) 33 | pos: self.pos[0], self.cursor_pos[1] - self.line_height - sp(2) 34 | Color: 35 | rgba: (self.cursor_color if self.focus and not self.cursor_blink else (0, 0, 0, 0)) 36 | Rectangle: 37 | pos: [int(x) for x in self.cursor_pos] 38 | size: sp(2), -self.line_height 39 | Color: 40 | rgba: self.disabled_foreground_color if self.disabled else (self.hint_text_color if not self.text and not self.focus else self.foreground_color) 41 | 42 | 43 | 44 | : 45 | font_size: '20dp' 46 | color: app.get_color(self.color_tuple) 47 | style_dict: app.get_style(self.style) 48 | ramp_group: app.get_ramp_group(self.font_ramp_tuple) 49 | disabled_color: (.3, .3, .3, .5) 50 | 51 | 52 | : 53 | scroll_timeout: 75 54 | scroll_distance: 10 55 | effect_cls: ScrollEffect 56 | 57 | 58 | : 59 | color: app.get_color(self.color_tuple) 60 | ripple_color: app.get_color(self.ripple_color_tuple) 61 | canvas.before: 62 | Color: 63 | rgba: self.color_down[0:3] + [self.alpha] if self.state == 'down' or root.disabled else self.color[0:3] + [self.alpha] 64 | Rectangle: 65 | size: (self.width - self.border_size[1] - self.border_size[3], self.height - self.border_size[0] - self.border_size[2]) 66 | pos: (self.x + self.border_size[3], self.y + self.border_size[2]) 67 | 68 | FlatLabel: 69 | size_hint: (1.0, 1.0) 70 | color_tuple: root.font_color_tuple 71 | text: root.text 72 | style: root.style 73 | text_size: root.size 74 | font_ramp_tuple: root.font_ramp_tuple 75 | valign: 'middle' 76 | halign: 'center' 77 | font_size: root.font_size 78 | 79 | 80 | : 81 | canvas.before: 82 | BorderImage: 83 | border: self.border 84 | pos: self.pos 85 | size: self.size 86 | source: self.border_image 87 | 88 | 89 | : 90 | text: app.get_icon(self.icon) 91 | valign: 'middle' 92 | font_name: app.get_font('data/font/fontawesome-webfont.ttf') 93 | halign: 'center' 94 | padding: ('5dp', '5dp') 95 | color: app.get_color(root.color_tuple) 96 | font_size: '64dp' 97 | 98 | 99 | : 100 | color: app.get_color(self.color_tuple) 101 | ripple_color: app.get_color(self.ripple_color_tuple) 102 | canvas.before: 103 | Color: 104 | rgb: root.color_down if root.state == 'down' or root.disabled else root.color 105 | Rectangle: 106 | size: self.size 107 | pos: self.pos 108 | StackLayout: 109 | orientation: 'tb-lr' 110 | id: label_space 111 | padding: root.content_padding 112 | spacing: root.content_spacing 113 | Image: 114 | id: icon 115 | size_hint: (.25, 1.0) 116 | source: root.icon_source 117 | opacity: label.color[3] 118 | FlatLabel: 119 | size_hint: (.75, 1.0) 120 | text: root.text 121 | style: root.style 122 | id: label 123 | color_tuple: root.font_color_tuple 124 | font_ramp_tuple: root.font_ramp_tuple 125 | valign: 'middle' 126 | halign: 'left' 127 | text_size: self.size 128 | font_size: root.font_size 129 | 130 | 131 | <-FlatImageButtonLeft>: 132 | color: app.get_color(self.color_tuple) 133 | ripple_color: app.get_color(self.ripple_color_tuple) 134 | canvas.before: 135 | Color: 136 | rgb: root.color_down if root.state == 'down' or root.disabled else root.color 137 | Rectangle: 138 | size: self.size 139 | pos: self.pos 140 | StackLayout: 141 | orientation: 'tb-lr' 142 | id: label_space 143 | padding: root.content_padding 144 | spacing: root.content_spacing 145 | FlatLabel: 146 | size_hint: (.75, 1.0) 147 | text: root.text 148 | style: root.style 149 | id: label 150 | color_tuple: root.font_color_tuple 151 | font_ramp_tuple: root.font_ramp_tuple 152 | valign: 'middle' 153 | halign: 'right' 154 | text_size: self.size 155 | font_size: root.font_size 156 | Image: 157 | id: icon 158 | size_hint: (.25, 1.0) 159 | source: root.icon_source 160 | opacity: label.color[3] 161 | 162 | 163 | : 164 | color: app.get_color(self.color_tuple) 165 | ripple_color: app.get_color(self.ripple_color_tuple) 166 | canvas.before: 167 | Color: 168 | rgb: root.color_down if root.state == 'down' or root.disabled else root.color 169 | Rectangle: 170 | size: self.size 171 | pos: self.pos 172 | StackLayout: 173 | orientation: 'tb-lr' 174 | id: label_space 175 | padding: root.content_padding 176 | spacing: root.content_spacing 177 | FlatIcon: 178 | id: icon 179 | size_hint: (None, 1.0) 180 | width: self.texture_size[0] 181 | icon: root.icon 182 | valign: 'middle' 183 | halign: 'right' 184 | opacity: label.color[3] 185 | color_tuple: root.icon_color_tuple 186 | text_size: self.size 187 | FlatLabel: 188 | width: root.size[0] - icon.size[0] 189 | size_hint: (None, 1.0) 190 | text: root.text 191 | style: root.style 192 | id: label 193 | color_tuple: root.font_color_tuple 194 | font_ramp_tuple: root.font_ramp_tuple 195 | valign: 'middle' 196 | halign: 'left' 197 | text_size: self.size 198 | font_size: root.font_size 199 | 200 | <-FlatIconButtonLeft>: 201 | color: app.get_color(self.color_tuple) 202 | ripple_color: app.get_color(self.ripple_color_tuple) 203 | canvas.before: 204 | Color: 205 | rgb: root.color_down if root.state == 'down' or root.disabled else root.color 206 | Rectangle: 207 | size: self.size 208 | pos: self.pos 209 | StackLayout: 210 | orientation: 'tb-lr' 211 | id: label_space 212 | FlatLabel: 213 | width: root.size[0] - icon.size[0] 214 | size_hint: (None, 1.0) 215 | text: root.text 216 | style: root.style 217 | id: label 218 | color_tuple: root.font_color_tuple 219 | valign: 'middle' 220 | font_ramp_tuple: root.font_ramp_tuple 221 | halign: 'right' 222 | text_size: self.size 223 | font_size: root.font_size 224 | FlatIcon: 225 | id: icon 226 | size_hint: (None, 1.0) 227 | width: self.texture_size[0] 228 | icon: root.icon 229 | valign: 'middle' 230 | halign: 'left' 231 | opacity: label.color[3] 232 | color_tuple: root.icon_color_tuple 233 | text_size: self.size 234 | 235 | 236 | : 237 | orientation: 'tb-lr' 238 | size_hint: (1.0, 1.0) 239 | cols: 1 240 | padding: 5 241 | spacing: 5 242 | FlatLabel: 243 | text_size: self.size 244 | size_hint: (1.0, .8) 245 | font_size: '14sp' 246 | valign: 'top' 247 | text: root.option_text 248 | 249 | 250 | : 251 | orientation: 'tb-lr' 252 | size_hint: (1.0, 1.0) 253 | dismiss_button: dismiss_button 254 | cols: 1 255 | padding: 5 256 | spacing: 5 257 | FlatLabel: 258 | text_size: self.size 259 | size_hint: (1.0, .8) 260 | font_ramp_tuple: ('error_content' + str(randint(1,10000)), '1') 261 | valign: 'top' 262 | text: root.error_text 263 | color_tuple: ('Gray', '1000') 264 | FlatButton: 265 | id: dismiss_button 266 | text: 'Ok' 267 | size_hint: (1.0, .2) 268 | 269 | 270 | <-FlatPopup>: 271 | _container: container 272 | 273 | canvas.before: 274 | Color: 275 | rgba: root.background_color[:3] + [root.background_color[-1] * self._anim_alpha] 276 | Rectangle: 277 | size: self._window.size if self._window else (0, 0) 278 | Color: 279 | rgba: root.popup_color 280 | Rectangle: 281 | size: self.size 282 | pos: self.pos 283 | 284 | GridLayout: 285 | padding: 12 286 | cols: 1 287 | size_hint: None, None 288 | pos: root.pos 289 | size: root.size 290 | 291 | FlatLabel: 292 | text: root.title 293 | color: app.get_color(root.title_color_tuple) 294 | size_hint_y: None 295 | height: self.texture_size[1] + 16 296 | text_size: self.width - 16, None 297 | font_size: root.title_size 298 | 299 | Widget: 300 | size_hint_y: None 301 | height: 4 302 | canvas: 303 | Color: 304 | rgba: root.separator_color 305 | Rectangle: 306 | pos: self.x, self.y + root.separator_height / 2. 307 | size: self.width, root.separator_height 308 | 309 | BoxLayout: 310 | id: container 311 | 312 | 313 | <-FlatCheckBox>: 314 | ripple_color: app.get_color(self.ripple_color_tuple) 315 | canvas.before: 316 | Color: 317 | rgba: app.get_color(self.outline_color_tuple) 318 | Rectangle: 319 | size: self.size 320 | pos: self.pos 321 | Color: 322 | rgba: app.get_color(self.color_tuple) 323 | Rectangle: 324 | size: self.size[0] - sp(self.outline_size), self.size[1] - sp(self.outline_size) 325 | pos: self.pos[0] + sp(self.outline_size/2.), self.pos[1] + sp(self.outline_size/2.) 326 | 327 | 328 | : 329 | icon: 'fa-check' 330 | halign: 'center' 331 | font_size: (self.size[0] + self.size[1]/2.)*self.scale 332 | 333 | 334 | : 335 | orientation: 'horizontal' 336 | active: checkbox.active 337 | ripple_color: app.get_color(self.ripple_color_tuple) 338 | spacing: '5dp' 339 | FlatLabel: 340 | id: label 341 | text: root.text 342 | size_hint: (.8, 1.0) 343 | color: app.get_color(root.font_color_tuple) 344 | style: root.style 345 | halign: root.halign 346 | font_ramp_tuple: root.font_ramp_tuple 347 | valign: root.valign 348 | text_size: self.size 349 | FlatCheckBox: 350 | group: root.group 351 | id: checkbox 352 | no_interact: True 353 | size_hint: (None, 1.0) 354 | width: self.height 355 | alpha: root.alpha 356 | outline_color_tuple: root.outline_color_tuple 357 | outline_size: root.outline_size 358 | check_color_tuple: root.check_color_tuple 359 | color_tuple: root.checkbox_color_tuple 360 | check_scale: root.check_scale 361 | 362 | 363 | : 364 | orientation: 'vertical' 365 | padding: [dp(5)] 366 | color: app.get_color(self.color_tuple) 367 | ripple_color: app.get_color(self.ripple_color_tuple) 368 | canvas.before: 369 | Color: 370 | rgb: root.color_down if root.state == 'down' or root.disabled else root.color 371 | Rectangle: 372 | size: self.size 373 | pos: self.pos 374 | Image: 375 | source: root.image_source 376 | id: image 377 | keep_ratio: True 378 | allow_stretch: True 379 | size_hint: (1., .7) 380 | FlatLabel: 381 | style: root.style 382 | id: label 383 | size_hint: (1., .3) 384 | font_ramp_tuple: root.font_ramp_tuple 385 | color_tuple: root.font_color_tuple 386 | text_size: self.size 387 | halign: 'center' 388 | valign: 'top' 389 | text: root.text 390 | 391 | 392 | <-FlatSlider>: 393 | ripple_color: app.get_color(self.ripple_color_tuple) 394 | canvas: 395 | Color: 396 | rgba: app.get_color(self.color_tuple) 397 | Rectangle: 398 | pos: (self.x + self.padding, self.center_y - sp(8)) if self.orientation == 'horizontal' else (self.center_x - sp(8), self.y + self.padding) 399 | size: (self.width - self.padding * 2, sp(16)) if self.orientation == 'horizontal' else (sp(16), self.height - self.padding * 2) 400 | Color: 401 | rgba: app.get_color(self.outline_color_tuple) 402 | Line: 403 | rounded_rectangle: [self.x + self.padding, self.center_y - sp(8), self.width - self.padding * 2, sp(16), sp(4)] if self.orientation == 'horizontal' else [self.center_x - sp(8), self.y + self.padding, sp(16), self.height - self.padding * 2, sp(4)] 404 | width: sp(2) 405 | Color: 406 | rgba: app.get_color(self.slider_color_tuple) 407 | Ellipse: 408 | pos: (self.value_pos[0] - sp(16), self.center_y - sp(17)) if self.orientation == 'horizontal' else (self.center_x - sp(16), self.value_pos[1] - sp(16)) 409 | size: (sp(32), sp(32)) 410 | Color: 411 | rgba: app.get_color(self.slider_outline_color_tuple) 412 | Line: 413 | ellipse: [self.value_pos[0] - sp(16), self.center_y - sp(17), sp(32), sp(32)] if self.orientation == 'horizontal' else [self.center_x - sp(17), self.value_pos[1] - sp(16), sp(32), sp(32)] 414 | width: sp(2) -------------------------------------------------------------------------------- /flat_kivy/fa_icon_definitions.py: -------------------------------------------------------------------------------- 1 | fa_icons = { 2 | 'fa-glass': u"\uf000", 3 | 'fa-music': u"\uf001", 4 | 'fa-search': u"\uf002", 5 | 'fa-envelope-o': u"\uf003", 6 | 'fa-heart': u"\uf004", 7 | 'fa-star': u"\uf005", 8 | 'fa-star-o': u"\uf006", 9 | 'fa-user': u"\uf007", 10 | 'fa-film': u"\uf008", 11 | 'fa-th-large': u"\uf009", 12 | 'fa-th': u"\uf00a", 13 | 'fa-th-list': u"\uf00b", 14 | 'fa-check': u"\uf00c", 15 | 'fa-times': u"\uf00d", 16 | 'fa-search-plus': u"\uf00e", 17 | 'fa-search-minus': u"\uf010", 18 | 'fa-power-off': u"\uf011", 19 | 'fa-signal': u"\uf012", 20 | 'fa-gear': u"\uf013", 21 | 'fa-cog': u"\uf013", 22 | 'fa-trash-o': u"\uf014", 23 | 'fa-home': u"\uf015", 24 | 'fa-file-o': u"\uf016", 25 | 'fa-clock-o': u"\uf017", 26 | 'fa-road': u"\uf018", 27 | 'fa-download': u"\uf019", 28 | 'fa-arrow-circle-o-down': u"\uf01a", 29 | 'fa-arrow-circle-o-up': u"\uf01b", 30 | 'fa-inbox': u"\uf01c", 31 | 'fa-play-circle-o': u"\uf01d", 32 | 'fa-rotate-right': u"\uf01e", 33 | 'fa-repeat': u"\uf01e", 34 | 'fa-refresh': u"\uf021", 35 | 'fa-list-alt': u"\uf022", 36 | 'fa-lock': u"\uf023", 37 | 'fa-flag': u"\uf024", 38 | 'fa-headphones': u"\uf025", 39 | 'fa-volume-off': u"\uf026", 40 | 'fa-volume-down': u"\uf027", 41 | 'fa-volume-up': u"\uf028", 42 | 'fa-qrcode': u"\uf029", 43 | 'fa-barcode': u"\uf02a", 44 | 'fa-tag': u"\uf02b", 45 | 'fa-tags': u"\uf02c", 46 | 'fa-book': u"\uf02d", 47 | 'fa-bookmark': u"\uf02e", 48 | 'fa-print': u"\uf02f", 49 | 'fa-camera': u"\uf030", 50 | 'fa-font': u"\uf031", 51 | 'fa-bold': u"\uf032", 52 | 'fa-italic': u"\uf033", 53 | 'fa-text-height': u"\uf034", 54 | 'fa-text-width': u"\uf035", 55 | 'fa-align-left': u"\uf036", 56 | 'fa-align-center': u"\uf037", 57 | 'fa-align-right': u"\uf038", 58 | 'fa-align-justify': u"\uf039", 59 | 'fa-list': u"\uf03a", 60 | 'fa-dedent': u"\uf03b", 61 | 'fa-outdent': u"\uf03b", 62 | 'fa-indent': u"\uf03c", 63 | 'fa-video-camera': u"\uf03d", 64 | 'fa-photo': u"\uf03e", 65 | 'fa-image': u"\uf03e", 66 | 'fa-picture-o': u"\uf03e", 67 | 'fa-pencil': u"\uf040", 68 | 'fa-map-marker': u"\uf041", 69 | 'fa-adjust': u"\uf042", 70 | 'fa-tint': u"\uf043", 71 | 'fa-edit': u"\uf044", 72 | 'fa-pencil-square-o': u"\uf044", 73 | 'fa-share-square-o': u"\uf045", 74 | 'fa-check-square-o': u"\uf046", 75 | 'fa-arrows': u"\uf047", 76 | 'fa-step-backward': u"\uf048", 77 | 'fa-fast-backward': u"\uf049", 78 | 'fa-backward': u"\uf04a", 79 | 'fa-play': u"\uf04b", 80 | 'fa-pause': u"\uf04c", 81 | 'fa-stop': u"\uf04d", 82 | 'fa-forward': u"\uf04e", 83 | 'fa-fast-forward': u"\uf050", 84 | 'fa-step-forward': u"\uf051", 85 | 'fa-eject': u"\uf052", 86 | 'fa-chevron-left': u"\uf053", 87 | 'fa-chevron-right': u"\uf054", 88 | 'fa-plus-circle': u"\uf055", 89 | 'fa-minus-circle': u"\uf056", 90 | 'fa-times-circle': u"\uf057", 91 | 'fa-check-circle': u"\uf058", 92 | 'fa-question-circle': u"\uf059", 93 | 'fa-info-circle': u"\uf05a", 94 | 'fa-crosshairs': u"\uf05b", 95 | 'fa-times-circle-o': u"\uf05c", 96 | 'fa-check-circle-o': u"\uf05d", 97 | 'fa-ban': u"\uf05e", 98 | 'fa-arrow-left': u"\uf060", 99 | 'fa-arrow-right': u"\uf061", 100 | 'fa-arrow-up': u"\uf062", 101 | 'fa-arrow-down': u"\uf063", 102 | 'fa-mail-forward': u"\uf064", 103 | 'fa-share': u"\uf064", 104 | 'fa-expand': u"\uf065", 105 | 'fa-compress': u"\uf066", 106 | 'fa-plus': u"\uf067", 107 | 'fa-minus': u"\uf068", 108 | 'fa-asterisk': u"\uf069", 109 | 'fa-exclamation-circle': u"\uf06a", 110 | 'fa-gift': u"\uf06b", 111 | 'fa-leaf': u"\uf06c", 112 | 'fa-fire': u"\uf06d", 113 | 'fa-eye': u"\uf06e", 114 | 'fa-eye-slash': u"\uf070", 115 | 'fa-warning': u"\uf071", 116 | 'fa-exclamation-triangle': u"\uf071", 117 | 'fa-plane': u"\uf072", 118 | 'fa-calendar': u"\uf073", 119 | 'fa-random': u"\uf074", 120 | 'fa-comment': u"\uf075", 121 | 'fa-magnet': u"\uf076", 122 | 'fa-chevron-up': u"\uf077", 123 | 'fa-chevron-down': u"\uf078", 124 | 'fa-retweet': u"\uf079", 125 | 'fa-shopping-cart': u"\uf07a", 126 | 'fa-folder': u"\uf07b", 127 | 'fa-folder-open': u"\uf07c", 128 | 'fa-arrows-v': u"\uf07d", 129 | 'fa-arrows-h': u"\uf07e", 130 | 'fa-bar-chart-o': u"\uf080", 131 | 'fa-twitter-square': u"\uf081", 132 | 'fa-facebook-square': u"\uf082", 133 | 'fa-camera-retro': u"\uf083", 134 | 'fa-key': u"\uf084", 135 | 'fa-gears': u"\uf085", 136 | 'fa-cogs': u"\uf085", 137 | 'fa-comments': u"\uf086", 138 | 'fa-thumbs-o-up': u"\uf087", 139 | 'fa-thumbs-o-down': u"\uf088", 140 | 'fa-star-half': u"\uf089", 141 | 'fa-heart-o': u"\uf08a", 142 | 'fa-sign-out': u"\uf08b", 143 | 'fa-linkedin-square': u"\uf08c", 144 | 'fa-thumb-tack': u"\uf08d", 145 | 'fa-external-link': u"\uf08e", 146 | 'fa-sign-in': u"\uf090", 147 | 'fa-trophy': u"\uf091", 148 | 'fa-github-square': u"\uf092", 149 | 'fa-upload': u"\uf093", 150 | 'fa-lemon-o': u"\uf094", 151 | 'fa-phone': u"\uf095", 152 | 'fa-square-o': u"\uf096", 153 | 'fa-bookmark-o': u"\uf097", 154 | 'fa-phone-square': u"\uf098", 155 | 'fa-twitter': u"\uf099", 156 | 'fa-facebook': u"\uf09a", 157 | 'fa-github': u"\uf09b", 158 | 'fa-unlock': u"\uf09c", 159 | 'fa-credit-card': u"\uf09d", 160 | 'fa-rss': u"\uf09e", 161 | 'fa-hdd-o': u"\uf0a0", 162 | 'fa-bullhorn': u"\uf0a1", 163 | 'fa-bell': u"\uf0f3", 164 | 'fa-certificate': u"\uf0a3", 165 | 'fa-hand-o-right': u"\uf0a4", 166 | 'fa-hand-o-left': u"\uf0a5", 167 | 'fa-hand-o-up': u"\uf0a6", 168 | 'fa-hand-o-down': u"\uf0a7", 169 | 'fa-arrow-circle-left': u"\uf0a8", 170 | 'fa-arrow-circle-right': u"\uf0a9", 171 | 'fa-arrow-circle-up': u"\uf0aa", 172 | 'fa-arrow-circle-down': u"\uf0ab", 173 | 'fa-globe': u"\uf0ac", 174 | 'fa-wrench': u"\uf0ad", 175 | 'fa-tasks': u"\uf0ae", 176 | 'fa-filter': u"\uf0b0", 177 | 'fa-briefcase': u"\uf0b1", 178 | 'fa-arrows-alt': u"\uf0b2", 179 | 'fa-group': u"\uf0c0", 180 | 'fa-users': u"\uf0c0", 181 | 'fa-chain': u"\uf0c1", 182 | 'fa-link': u"\uf0c1", 183 | 'fa-cloud': u"\uf0c2", 184 | 'fa-flask': u"\uf0c3", 185 | 'fa-cut': u"\uf0c4", 186 | 'fa-scissors': u"\uf0c4", 187 | 'fa-copy': u"\uf0c5", 188 | 'fa-files-o': u"\uf0c5", 189 | 'fa-paperclip': u"\uf0c6", 190 | 'fa-save': u"\uf0c7", 191 | 'fa-floppy-o': u"\uf0c7", 192 | 'fa-square': u"\uf0c8", 193 | 'fa-navicon': u"\uf0c9", 194 | 'fa-reorder': u"\uf0c9", 195 | 'fa-bars': u"\uf0c9", 196 | 'fa-list-ul': u"\uf0ca", 197 | 'fa-list-ol': u"\uf0cb", 198 | 'fa-strikethrough': u"\uf0cc", 199 | 'fa-underline': u"\uf0cd", 200 | 'fa-table': u"\uf0ce", 201 | 'fa-magic': u"\uf0d0", 202 | 'fa-truck': u"\uf0d1", 203 | 'fa-pinterest': u"\uf0d2", 204 | 'fa-pinterest-square': u"\uf0d3", 205 | 'fa-google-plus-square': u"\uf0d4", 206 | 'fa-google-plus': u"\uf0d5", 207 | 'fa-money': u"\uf0d6", 208 | 'fa-caret-down': u"\uf0d7", 209 | 'fa-caret-up': u"\uf0d8", 210 | 'fa-caret-left': u"\uf0d9", 211 | 'fa-caret-right': u"\uf0da", 212 | 'fa-columns': u"\uf0db", 213 | 'fa-unsorted': u"\uf0dc", 214 | 'fa-sort': u"\uf0dc", 215 | 'fa-sort-down': u"\uf0dd", 216 | 'fa-sort-desc': u"\uf0dd", 217 | 'fa-sort-up': u"\uf0de", 218 | 'fa-sort-asc': u"\uf0de", 219 | 'fa-envelope': u"\uf0e0", 220 | 'fa-linkedin': u"\uf0e1", 221 | 'fa-rotate-left': u"\uf0e2", 222 | 'fa-undo': u"\uf0e2", 223 | 'fa-legal': u"\uf0e3", 224 | 'fa-gavel': u"\uf0e3", 225 | 'fa-dashboard': u"\uf0e4", 226 | 'fa-tachometer': u"\uf0e4", 227 | 'fa-comment-o': u"\uf0e5", 228 | 'fa-comments-o': u"\uf0e6", 229 | 'fa-flash': u"\uf0e7", 230 | 'fa-bolt': u"\uf0e7", 231 | 'fa-sitemap': u"\uf0e8", 232 | 'fa-umbrella': u"\uf0e9", 233 | 'fa-paste': u"\uf0ea", 234 | 'fa-clipboard': u"\uf0ea", 235 | 'fa-lightbulb-o': u"\uf0eb", 236 | 'fa-exchange': u"\uf0ec", 237 | 'fa-cloud-download': u"\uf0ed", 238 | 'fa-cloud-upload': u"\uf0ee", 239 | 'fa-user-md': u"\uf0f0", 240 | 'fa-stethoscope': u"\uf0f1", 241 | 'fa-suitcase': u"\uf0f2", 242 | 'fa-bell-o': u"\uf0a2", 243 | 'fa-coffee': u"\uf0f4", 244 | 'fa-cutlery': u"\uf0f5", 245 | 'fa-file-text-o': u"\uf0f6", 246 | 'fa-building-o': u"\uf0f7", 247 | 'fa-hospital-o': u"\uf0f8", 248 | 'fa-ambulance': u"\uf0f9", 249 | 'fa-medkit': u"\uf0fa", 250 | 'fa-fighter-jet': u"\uf0fb", 251 | 'fa-beer': u"\uf0fc", 252 | 'fa-h-square': u"\uf0fd", 253 | 'fa-plus-square': u"\uf0fe", 254 | 'fa-angle-double-left': u"\uf100", 255 | 'fa-angle-double-right': u"\uf101", 256 | 'fa-angle-double-up': u"\uf102", 257 | 'fa-angle-double-down': u"\uf103", 258 | 'fa-angle-left': u"\uf104", 259 | 'fa-angle-right': u"\uf105", 260 | 'fa-angle-up': u"\uf106", 261 | 'fa-angle-down': u"\uf107", 262 | 'fa-desktop': u"\uf108", 263 | 'fa-laptop': u"\uf109", 264 | 'fa-tablet': u"\uf10a", 265 | 'fa-mobile-phone': u"\uf10b", 266 | 'fa-mobile': u"\uf10b", 267 | 'fa-circle-o': u"\uf10c", 268 | 'fa-quote-left': u"\uf10d", 269 | 'fa-quote-right': u"\uf10e", 270 | 'fa-spinner': u"\uf110", 271 | 'fa-circle': u"\uf111", 272 | 'fa-mail-reply': u"\uf112", 273 | 'fa-reply': u"\uf112", 274 | 'fa-github-alt': u"\uf113", 275 | 'fa-folder-o': u"\uf114", 276 | 'fa-folder-open-o': u"\uf115", 277 | 'fa-smile-o': u"\uf118", 278 | 'fa-frown-o': u"\uf119", 279 | 'fa-meh-o': u"\uf11a", 280 | 'fa-gamepad': u"\uf11b", 281 | 'fa-keyboard-o': u"\uf11c", 282 | 'fa-flag-o': u"\uf11d", 283 | 'fa-flag-checkered': u"\uf11e", 284 | 'fa-terminal': u"\uf120", 285 | 'fa-code': u"\uf121", 286 | 'fa-mail-reply-all': u"\uf122", 287 | 'fa-reply-all': u"\uf122", 288 | 'fa-star-half-empty': u"\uf123", 289 | 'fa-star-half-full': u"\uf123", 290 | 'fa-star-half-o': u"\uf123", 291 | 'fa-location-arrow': u"\uf124", 292 | 'fa-crop': u"\uf125", 293 | 'fa-code-fork': u"\uf126", 294 | 'fa-unlink': u"\uf127", 295 | 'fa-chain-broken': u"\uf127", 296 | 'fa-question': u"\uf128", 297 | 'fa-info': u"\uf129", 298 | 'fa-exclamation': u"\uf12a", 299 | 'fa-superscript': u"\uf12b", 300 | 'fa-subscript': u"\uf12c", 301 | 'fa-eraser': u"\uf12d", 302 | 'fa-puzzle-piece': u"\uf12e", 303 | 'fa-microphone': u"\uf130", 304 | 'fa-microphone-slash': u"\uf131", 305 | 'fa-shield': u"\uf132", 306 | 'fa-calendar-o': u"\uf133", 307 | 'fa-fire-extinguisher': u"\uf134", 308 | 'fa-rocket': u"\uf135", 309 | 'fa-maxcdn': u"\uf136", 310 | 'fa-chevron-circle-left': u"\uf137", 311 | 'fa-chevron-circle-right': u"\uf138", 312 | 'fa-chevron-circle-up': u"\uf139", 313 | 'fa-chevron-circle-down': u"\uf13a", 314 | 'fa-html5': u"\uf13b", 315 | 'fa-css3': u"\uf13c", 316 | 'fa-anchor': u"\uf13d", 317 | 'fa-unlock-alt': u"\uf13e", 318 | 'fa-bullseye': u"\uf140", 319 | 'fa-ellipsis-h': u"\uf141", 320 | 'fa-ellipsis-v': u"\uf142", 321 | 'fa-rss-square': u"\uf143", 322 | 'fa-play-circle': u"\uf144", 323 | 'fa-ticket': u"\uf145", 324 | 'fa-minus-square': u"\uf146", 325 | 'fa-minus-square-o': u"\uf147", 326 | 'fa-level-up': u"\uf148", 327 | 'fa-level-down': u"\uf149", 328 | 'fa-check-square': u"\uf14a", 329 | 'fa-pencil-square': u"\uf14b", 330 | 'fa-external-link-square': u"\uf14c", 331 | 'fa-share-square': u"\uf14d", 332 | 'fa-compass': u"\uf14e", 333 | 'fa-toggle-down': u"\uf150", 334 | 'fa-caret-square-o-down': u"\uf150", 335 | 'fa-toggle-up': u"\uf151", 336 | 'fa-caret-square-o-up': u"\uf151", 337 | 'fa-toggle-right': u"\uf152", 338 | 'fa-caret-square-o-right': u"\uf152", 339 | 'fa-euro': u"\uf153", 340 | 'fa-eur': u"\uf153", 341 | 'fa-gbp': u"\uf154", 342 | 'fa-dollar': u"\uf155", 343 | 'fa-usd': u"\uf155", 344 | 'fa-rupee': u"\uf156", 345 | 'fa-inr': u"\uf156", 346 | 'fa-cny': u"\uf157", 347 | 'fa-rmb': u"\uf157", 348 | 'fa-yen': u"\uf157", 349 | 'fa-jpy': u"\uf157", 350 | 'fa-ruble': u"\uf158", 351 | 'fa-rouble': u"\uf158", 352 | 'fa-rub': u"\uf158", 353 | 'fa-won': u"\uf159", 354 | 'fa-krw': u"\uf159", 355 | 'fa-bitcoin': u"\uf15a", 356 | 'fa-btc': u"\uf15a", 357 | 'fa-file': u"\uf15b", 358 | 'fa-file-text': u"\uf15c", 359 | 'fa-sort-alpha-asc': u"\uf15d", 360 | 'fa-sort-alpha-desc': u"\uf15e", 361 | 'fa-sort-amount-asc': u"\uf160", 362 | 'fa-sort-amount-desc': u"\uf161", 363 | 'fa-sort-numeric-asc': u"\uf162", 364 | 'fa-sort-numeric-desc': u"\uf163", 365 | 'fa-thumbs-up': u"\uf164", 366 | 'fa-thumbs-down': u"\uf165", 367 | 'fa-youtube-square': u"\uf166", 368 | 'fa-youtube': u"\uf167", 369 | 'fa-xing': u"\uf168", 370 | 'fa-xing-square': u"\uf169", 371 | 'fa-youtube-play': u"\uf16a", 372 | 'fa-dropbox': u"\uf16b", 373 | 'fa-stack-overflow': u"\uf16c", 374 | 'fa-instagram': u"\uf16d", 375 | 'fa-flickr': u"\uf16e", 376 | 'fa-adn': u"\uf170", 377 | 'fa-bitbucket': u"\uf171", 378 | 'fa-bitbucket-square': u"\uf172", 379 | 'fa-tumblr': u"\uf173", 380 | 'fa-tumblr-square': u"\uf174", 381 | 'fa-long-arrow-down': u"\uf175", 382 | 'fa-long-arrow-up': u"\uf176", 383 | 'fa-long-arrow-left': u"\uf177", 384 | 'fa-long-arrow-right': u"\uf178", 385 | 'fa-apple': u"\uf179", 386 | 'fa-windows': u"\uf17a", 387 | 'fa-android': u"\uf17b", 388 | 'fa-linux': u"\uf17c", 389 | 'fa-dribbble': u"\uf17d", 390 | 'fa-skype': u"\uf17e", 391 | 'fa-foursquare': u"\uf180", 392 | 'fa-trello': u"\uf181", 393 | 'fa-female': u"\uf182", 394 | 'fa-male': u"\uf183", 395 | 'fa-gittip': u"\uf184", 396 | 'fa-sun-o': u"\uf185", 397 | 'fa-moon-o': u"\uf186", 398 | 'fa-archive': u"\uf187", 399 | 'fa-bug': u"\uf188", 400 | 'fa-vk': u"\uf189", 401 | 'fa-weibo': u"\uf18a", 402 | 'fa-renren': u"\uf18b", 403 | 'fa-pagelines': u"\uf18c", 404 | 'fa-stack-exchange': u"\uf18d", 405 | 'fa-arrow-circle-o-right': u"\uf18e", 406 | 'fa-arrow-circle-o-left': u"\uf190", 407 | 'fa-toggle-left': u"\uf191", 408 | 'fa-caret-square-o-left': u"\uf191", 409 | 'fa-dot-circle-o': u"\uf192", 410 | 'fa-wheelchair': u"\uf193", 411 | 'fa-vimeo-square': u"\uf194", 412 | 'fa-turkish-lira': u"\uf195", 413 | 'fa-try': u"\uf195", 414 | 'fa-plus-square-o': u"\uf196", 415 | 'fa-space-shuttle': u"\uf197", 416 | 'fa-slack': u"\uf198", 417 | 'fa-envelope-square': u"\uf199", 418 | 'fa-wordpress': u"\uf19a", 419 | 'fa-openid': u"\uf19b", 420 | 'fa-institution': u"\uf19c", 421 | 'fa-bank': u"\uf19c", 422 | 'fa-university': u"\uf19c", 423 | 'fa-mortar-board': u"\uf19d", 424 | 'fa-graduation-cap': u"\uf19d", 425 | 'fa-yahoo': u"\uf19e", 426 | 'fa-google': u"\uf1a0", 427 | 'fa-reddit': u"\uf1a1", 428 | 'fa-reddit-square': u"\uf1a2", 429 | 'fa-stumbleupon-circle': u"\uf1a3", 430 | 'fa-stumbleupon': u"\uf1a4", 431 | 'fa-delicious': u"\uf1a5", 432 | 'fa-digg': u"\uf1a6", 433 | 'fa-pied-piper-square': u"\uf1a7", 434 | 'fa-pied-piper': u"\uf1a7", 435 | 'fa-pied-piper-alt': u"\uf1a8", 436 | 'fa-drupal': u"\uf1a9", 437 | 'fa-joomla': u"\uf1aa", 438 | 'fa-language': u"\uf1ab", 439 | 'fa-fax': u"\uf1ac", 440 | 'fa-building': u"\uf1ad", 441 | 'fa-child': u"\uf1ae", 442 | 'fa-paw': u"\uf1b0", 443 | 'fa-spoon': u"\uf1b1", 444 | 'fa-cube': u"\uf1b2", 445 | 'fa-cubes': u"\uf1b3", 446 | 'fa-behance': u"\uf1b4", 447 | 'fa-behance-square': u"\uf1b5", 448 | 'fa-steam': u"\uf1b6", 449 | 'fa-steam-square': u"\uf1b7", 450 | 'fa-recycle': u"\uf1b8", 451 | 'fa-automobile': u"\uf1b9", 452 | 'fa-car': u"\uf1b9", 453 | 'fa-cab': u"\uf1ba", 454 | 'fa-taxi': u"\uf1ba", 455 | 'fa-tree': u"\uf1bb", 456 | 'fa-spotify': u"\uf1bc", 457 | 'fa-deviantart': u"\uf1bd", 458 | 'fa-soundcloud': u"\uf1be", 459 | 'fa-database': u"\uf1c0", 460 | 'fa-file-pdf-o': u"\uf1c1", 461 | 'fa-file-word-o': u"\uf1c2", 462 | 'fa-file-excel-o': u"\uf1c3", 463 | 'fa-file-powerpoint-o': u"\uf1c4", 464 | 'fa-file-photo-o': u"\uf1c5", 465 | 'fa-file-picture-o': u"\uf1c5", 466 | 'fa-file-image-o': u"\uf1c5", 467 | 'fa-file-zip-o': u"\uf1c6", 468 | 'fa-file-archive-o': u"\uf1c6", 469 | 'fa-file-sound-o': u"\uf1c7", 470 | 'fa-file-audio-o': u"\uf1c7", 471 | 'fa-file-movie-o': u"\uf1c8", 472 | 'fa-file-video-o': u"\uf1c8", 473 | 'fa-file-code-o': u"\uf1c9", 474 | 'fa-vine': u"\uf1ca", 475 | 'fa-codepen': u"\uf1cb", 476 | 'fa-jsfiddle': u"\uf1cc", 477 | 'fa-life-bouy': u"\uf1cd", 478 | 'fa-life-saver': u"\uf1cd", 479 | 'fa-support': u"\uf1cd", 480 | 'fa-life-ring': u"\uf1cd", 481 | 'fa-circle-o-notch': u"\uf1ce", 482 | 'fa-ra': u"\uf1d0", 483 | 'fa-rebel': u"\uf1d0", 484 | 'fa-ge': u"\uf1d1", 485 | 'fa-empire': u"\uf1d1", 486 | 'fa-git-square': u"\uf1d2", 487 | 'fa-git': u"\uf1d3", 488 | 'fa-hacker-news': u"\uf1d4", 489 | 'fa-tencent-weibo': u"\uf1d5", 490 | 'fa-qq': u"\uf1d6", 491 | 'fa-wechat': u"\uf1d7", 492 | 'fa-weixin': u"\uf1d7", 493 | 'fa-send': u"\uf1d8", 494 | 'fa-paper-plane': u"\uf1d8", 495 | 'fa-send-o': u"\uf1d9", 496 | 'fa-paper-plane-o': u"\uf1d9", 497 | 'fa-history': u"\uf1da", 498 | 'fa-circle-thin': u"\uf1db", 499 | 'fa-header': u"\uf1dc", 500 | 'fa-paragraph': u"\uf1dd", 501 | 'fa-sliders': u"\uf1de", 502 | 'fa-share-alt': u"\uf1e0", 503 | 'fa-share-alt-square': u"\uf1e1", 504 | 'fa-bomb': u"\uf1e2", 505 | } -------------------------------------------------------------------------------- /flat_kivy/uix/behaviors.py: -------------------------------------------------------------------------------- 1 | 2 | from weakref import ref 3 | 4 | from kivy.app import App 5 | from kivy.clock import Clock 6 | from kivy.properties import (ObjectProperty, OptionProperty, NumericProperty, 7 | ListProperty, StringProperty) 8 | from kivy.metrics import sp 9 | from kivy.animation import Animation 10 | from kivy.graphics import Color, Ellipse, Rectangle 11 | from kivy.graphics import (StencilPush, StencilPop, StencilUse, 12 | StencilUnUse, Color, Rectangle) 13 | try: 14 | from kivy.graphics import (ScissorPush, ScissorPop) 15 | except ImportError: 16 | _has_scissor_instr = False 17 | else: 18 | _has_scissor_instr = True 19 | 20 | from flat_kivy.utils import construct_data_resource 21 | from flat_kivy.logmanager import LogManager 22 | 23 | 24 | class ThemeBehavior(object): 25 | theme = ListProperty([]) 26 | 27 | def on_theme(self, instance, value): 28 | if value != []: 29 | app = App.get_running_app() 30 | theme = app.theme_manager.get_theme(value[0], value[1]) 31 | types = app.theme_manager.get_theme_types() 32 | for each in types: 33 | if isinstance(self, types[each]): 34 | try: 35 | theme_def = theme[each] 36 | except: 37 | print(each, 'not in theme', value[0], value[1], self) 38 | continue 39 | for propname in theme_def: 40 | setattr(self, propname, theme_def[propname]) 41 | 42 | 43 | class GrabBehavior(object): 44 | last_touch = ObjectProperty(None) 45 | 46 | def on_touch_down(self, touch): 47 | if touch.is_mouse_scrolling: 48 | return False 49 | if self.disabled: 50 | return False 51 | if not self.collide_point(touch.x, touch.y): 52 | return False 53 | if self in touch.ud: 54 | return False 55 | touch.grab(self) 56 | touch.ud[self] = True 57 | self.last_touch = touch 58 | return super(GrabBehavior, self).on_touch_down(touch) 59 | 60 | def on_touch_move(self, touch): 61 | if super(GrabBehavior, self).on_touch_move(touch): 62 | return True 63 | if touch.grab_current is self: 64 | return True 65 | return self in touch.ud 66 | 67 | def on_touch_up(self, touch): 68 | if touch.grab_current is self: 69 | result = super(GrabBehavior, self).on_touch_up(touch) 70 | touch.ungrab(self) 71 | self.last_touch = touch 72 | return result 73 | 74 | 75 | class LogBehavior(object): 76 | log_manager = LogManager( 77 | construct_data_resource('logs/')) 78 | 79 | def on_touch_down(self, touch): 80 | log_manager = self.log_manager 81 | if self in touch.ud and log_manager.do_logging: 82 | print(self, 'in on touch dwon') 83 | coords = (touch.x, touch.y) 84 | log_interface = log_manager.log_interface 85 | touch_id = log_manager.touch_id 86 | touch.ud['log_id'] = touch_id 87 | log_interface.set_entry( 88 | 'touches', touch_id, 'touch_down_at', coords, 89 | do_timestamp=True) 90 | log_manager.touch_id += 1 91 | log_interface.set_entry( 92 | 'touches', 'last_touch_id', 'value', touch_id) 93 | return super(LogBehavior, self).on_touch_down(touch) 94 | 95 | def on_touch_move(self, touch): 96 | log_manager = self.log_manager 97 | if self in touch.ud and log_manager.do_logging: 98 | coords = (touch.x, touch.y) 99 | touch_id = touch.ud['log_id'] 100 | log_manager.log_interface.append_entry('touches', touch_id, 101 | 'touch_moves_at', coords, do_timestamp=True) 102 | return super(LogBehavior, self).on_touch_move(touch) 103 | 104 | def on_touch_up(self, touch): 105 | log_manager = self.log_manager 106 | if self in touch.ud and log_manager.do_logging: 107 | coords = (touch.x, touch.y) 108 | touch_id = touch.ud['log_id'] 109 | log_manager.log_interface.set_entry( 110 | 'touches', touch_id, 'touch_up_at', coords, do_timestamp=True) 111 | return super(LogBehavior, self).on_touch_up(touch) 112 | 113 | 114 | class LogNoTouchBehavior(object): 115 | log_manager = LogManager( 116 | construct_data_resource('logs/')) 117 | 118 | 119 | class ButtonBehavior(object): 120 | '''Button behavior. 121 | 122 | :Events: 123 | `on_press` 124 | Fired when the button is pressed. 125 | `on_release` 126 | Fired when the button is released (i.e. the touch/click that 127 | pressed the button goes away). 128 | ''' 129 | 130 | state = OptionProperty('normal', options=('normal', 'down')) 131 | '''State of the button, must be one of 'normal' or 'down'. 132 | The state is 'down' only when the button is currently touched/clicked, 133 | otherwise 'normal'. 134 | 135 | :attr:`state` is an :class:`~kivy.properties.OptionProperty`. 136 | ''' 137 | 138 | 139 | def __init__(self, **kwargs): 140 | self.register_event_type(b'on_press') 141 | self.register_event_type(b'on_release') 142 | super(ButtonBehavior, self).__init__(**kwargs) 143 | 144 | def _do_press(self): 145 | self.state = 'down' 146 | 147 | def _do_release(self): 148 | self.state = 'normal' 149 | 150 | def on_touch_down(self, touch): 151 | if self in touch.ud: 152 | if isinstance(self, LogBehavior): 153 | log_manager = self.log_manager 154 | if log_manager.do_logging: 155 | if isinstance(self, CheckBox): 156 | touch_id = touch.ud['log_id'] 157 | log_manager.log_interface.set_entry( 158 | 'touches', touch_id, 159 | 'checkbox_pressed_down', self.state, 160 | do_timestamp=True) 161 | else: 162 | touch_id = touch.ud['log_id'] 163 | log_manager.log_interface.set_entry( 164 | 'touches', touch_id, 165 | 'button_pressed', self.text, do_timestamp=True) 166 | self._do_press() 167 | self.dispatch(b'on_press') 168 | return super(ButtonBehavior, self).on_touch_down(touch) 169 | 170 | def on_touch_move(self, touch): 171 | return super(ButtonBehavior, self).on_touch_move(touch) 172 | 173 | def on_touch_up(self, touch): 174 | if self in touch.ud: 175 | if isinstance(self, LogBehavior): 176 | log_manager = self.log_manager 177 | if log_manager.do_logging: 178 | if isinstance(self, CheckBox): 179 | touch_id = touch.ud['log_id'] 180 | log_manager.log_interface.set_entry( 181 | 'touches', touch_id, 182 | 'checkbox_released', self.state, 183 | do_timestamp=True) 184 | else: 185 | touch_id = touch.ud['log_id'] 186 | log_manager.log_interface.set_entry( 187 | 'touches', touch_id, 'button_released', 188 | self.text, do_timestamp=True) 189 | self._do_release() 190 | self.dispatch(b'on_release') 191 | return super(ButtonBehavior, self).on_touch_up(touch) 192 | 193 | def on_press(self): 194 | pass 195 | 196 | def on_release(self): 197 | pass 198 | 199 | def trigger_action(self, duration=0.1): 200 | '''Trigger whatever action(s) have been bound to the button by calling 201 | both the on_press and on_release callbacks. 202 | 203 | This simulates a quick button press without using any touch events. 204 | 205 | Duration is the length of the press in seconds. Pass 0 if you want 206 | the action to happen instantly. 207 | 208 | .. versionadded:: 1.8.0 209 | ''' 210 | self._do_press() 211 | self.dispatch('on_press') 212 | 213 | def trigger_release(dt): 214 | self._do_release() 215 | self.dispatch('on_release') 216 | if not duration: 217 | trigger_release(0) 218 | else: 219 | Clock.schedule_once(trigger_release, duration) 220 | 221 | 222 | class ToggleButtonBehavior(ButtonBehavior): 223 | '''ToggleButton behavior, see ToggleButton module documentation for more 224 | information. 225 | 226 | .. versionadded:: 1.8.0 227 | ''' 228 | 229 | __groups = {} 230 | 231 | group = ObjectProperty(None, allownone=True) 232 | '''Group of the button. If None, no group will be used (button is 233 | independent). If specified, :attr:`group` must be a hashable object, like 234 | a string. Only one button in a group can be in 'down' state. 235 | 236 | :attr:`group` is a :class:`~kivy.properties.ObjectProperty` 237 | ''' 238 | 239 | def __init__(self, **kwargs): 240 | self._previous_group = None 241 | super(ToggleButtonBehavior, self).__init__(**kwargs) 242 | 243 | def on_group(self, *largs): 244 | groups = ToggleButtonBehavior.__groups 245 | if self._previous_group: 246 | group = groups[self._previous_group] 247 | for item in group[:]: 248 | if item() is self: 249 | group.remove(item) 250 | break 251 | group = self._previous_group = self.group 252 | if group not in groups: 253 | groups[group] = [] 254 | r = ref(self, ToggleButtonBehavior._clear_groups) 255 | groups[group].append(r) 256 | 257 | def _release_group(self, current): 258 | if self.group is None: 259 | return 260 | group = self.__groups[self.group] 261 | for item in group[:]: 262 | widget = item() 263 | if widget is None: 264 | group.remove(item) 265 | if widget is current: 266 | continue 267 | widget.state = 'normal' 268 | 269 | def _do_press(self): 270 | self._release_group(self) 271 | self.state = 'normal' if self.state == 'down' else 'down' 272 | 273 | def _do_release(self): 274 | pass 275 | 276 | @staticmethod 277 | def _clear_groups(wk): 278 | # auto flush the element when the weak reference have been deleted 279 | groups = ToggleButtonBehavior.__groups 280 | for group in list(groups.values()): 281 | if wk in group: 282 | group.remove(wk) 283 | break 284 | 285 | @staticmethod 286 | def get_widgets(groupname): 287 | '''Return the widgets contained in a specific group. If the group 288 | doesn't exist, an empty list will be returned. 289 | 290 | .. important:: 291 | 292 | Always release the result of this method! In doubt, do:: 293 | 294 | l = ToggleButtonBehavior.get_widgets('mygroup') 295 | # do your job 296 | del l 297 | 298 | .. warning:: 299 | 300 | It's possible that some widgets that you have previously 301 | deleted are still in the list. Garbage collector might need 302 | more elements before flushing it. The return of this method 303 | is informative, you've been warned! 304 | ''' 305 | groups = ToggleButtonBehavior.__groups 306 | if groupname not in groups: 307 | return [] 308 | return [x() for x in groups[groupname] if x()][:] 309 | 310 | 311 | class TouchRippleBehavior(object): 312 | ripple_rad = NumericProperty(10) 313 | ripple_pos = ListProperty([0, 0]) 314 | ripple_color = ListProperty((0., 0., 0., 1.)) 315 | ripple_duration_in = NumericProperty(.7) 316 | ripple_duration_out = NumericProperty(.3) 317 | fade_to_alpha = NumericProperty(.12) 318 | ripple_scale = NumericProperty(4.0) 319 | ripple_func_in = StringProperty('in_cubic') 320 | ripple_func_out = StringProperty('out_quad') 321 | 322 | def on_touch_down(self, touch): 323 | if self in touch.ud: 324 | self.anim_complete(self, self) 325 | self.ripple_pos = ripple_pos = (touch.x, touch.y) 326 | Animation.cancel_all(self, 'ripple_rad', 'ripple_color') 327 | rc = self.ripple_color 328 | ripple_rad = self.ripple_rad 329 | self.ripple_color = [rc[0], rc[1], rc[2], .16] 330 | anim = Animation( 331 | ripple_rad=max(self.width, self.height) * self.ripple_scale, 332 | t=self.ripple_func_in, 333 | ripple_color=[rc[0], rc[1], rc[2], self.fade_to_alpha], 334 | duration=self.ripple_duration_in) 335 | anim.start(self) 336 | with self.canvas.after: 337 | x,y = self.to_window(*self.pos) 338 | width, height = self.size 339 | #In python 3 the int cast will be unnecessary 340 | pos = (int(round(x)), int(round(y))) 341 | size = (int(round(width)), int(round(height))) 342 | 343 | if _has_scissor_instr: 344 | ScissorPush(x=pos[0], y=pos[1], 345 | width=size[0], height=size[1]) 346 | else: 347 | StencilPush() 348 | Rectangle(pos=(int(round(x)), int(round(y))), 349 | size=(int(round(width)), int(round(height)))) 350 | 351 | StencilUse() 352 | 353 | self.col_instruction = Color(rgba=self.ripple_color) 354 | self.ellipse = Ellipse(size=(ripple_rad, ripple_rad), 355 | pos=(ripple_pos[0] - ripple_rad/2., 356 | ripple_pos[1] - ripple_rad/2.)) 357 | 358 | if _has_scissor_instr: 359 | ScissorPop() 360 | else: 361 | StencilUnUse() 362 | Rectangle(pos=(int(round(x)), int(round(y))), 363 | size=(int(round(width)), int(round(height)))) 364 | StencilPop() 365 | 366 | self.bind(ripple_color=self.set_color, ripple_pos=self.set_ellipse, 367 | ripple_rad=self.set_ellipse) 368 | return super(TouchRippleBehavior, self).on_touch_down(touch) 369 | 370 | def set_ellipse(self, instance, value): 371 | ellipse = self.ellipse 372 | ripple_pos = self.ripple_pos 373 | ripple_rad = self.ripple_rad 374 | ellipse.size = (ripple_rad, ripple_rad) 375 | ellipse.pos = (ripple_pos[0] - ripple_rad/2., 376 | ripple_pos[1] - ripple_rad/2.) 377 | 378 | def set_color(self, instance, value): 379 | self.col_instruction.rgba = value 380 | 381 | def on_touch_up(self, touch): 382 | if self in touch.ud: 383 | rc = self.ripple_color 384 | anim = Animation(ripple_color=[rc[0], rc[1], rc[2], 0.], 385 | t=self.ripple_func_out, duration=self.ripple_duration_out) 386 | anim.bind(on_complete=self.anim_complete) 387 | anim.start(self) 388 | return super(TouchRippleBehavior, self).on_touch_up(touch) 389 | 390 | def anim_complete(self, anim, instance): 391 | self.ripple_rad = 10 392 | self.canvas.after.clear() 393 | 394 | 395 | class SliderTouchRippleBehavior(object): 396 | ripple_rad = NumericProperty(10) 397 | ripple_pos = ListProperty([0, 0]) 398 | ripple_color = ListProperty((1., 1., 1., 1.)) 399 | ripple_duration_in = NumericProperty(.2) 400 | ripple_duration_out = NumericProperty(.5) 401 | fade_to_alpha = NumericProperty(.75) 402 | ripple_scale = NumericProperty(2.0) 403 | ripple_func_in = StringProperty('in_cubic') 404 | ripple_func_out = StringProperty('out_quad') 405 | 406 | def __init__(self, **kwargs): 407 | super(SliderTouchRippleBehavior, self).__init__(**kwargs) 408 | self.slider_stencil = None 409 | self.slider_stencil_unuse = None 410 | self.slider_line_stencil = None 411 | self.slider_line_stencil_unuse = None 412 | 413 | def on_touch_down(self, touch): 414 | if self in touch.ud: 415 | self.anim_complete(self, self) 416 | self.ripple_pos = ripple_pos = (touch.x, touch.y) 417 | Animation.cancel_all(self, 'ripple_rad', 'ripple_color') 418 | rc = self.ripple_color 419 | ripple_rad = self.ripple_rad 420 | self.ripple_color = [rc[0], rc[1], rc[2], 1.] 421 | anim = Animation( 422 | ripple_rad=max(self.width, self.height) * self.ripple_scale, 423 | t=self.ripple_func_in, 424 | ripple_color=[rc[0], rc[1], rc[2], self.fade_to_alpha], 425 | duration=self.ripple_duration_in) 426 | anim.start(self) 427 | with self.canvas.after: 428 | x,y = self.to_window(*self.pos) 429 | width, height = self.size 430 | 431 | if self.orientation == 'horizontal': 432 | ellipse_pos = (self.value_pos[0] - sp(16), self.center_y - sp(17)) 433 | stencil_pos = (self.x + self.padding + sp(2), self.center_y - sp(7)) 434 | stencil_size = (self.width - self.padding * 2 - sp(4), sp(14)) 435 | else: 436 | ellipse_pos = (self.center_x - sp(17), self.value_pos[1] - sp(16)) 437 | stencil_pos = (self.center_x - sp(7), self.y + self.padding + sp(2)) 438 | stencil_size = (sp(14), self.height - self.padding * 2 - sp(4)) 439 | 440 | StencilPush() 441 | Rectangle( 442 | pos=stencil_pos, 443 | size=stencil_size) 444 | self.slider_stencil = Ellipse( 445 | pos=ellipse_pos, 446 | size=(sp(32), sp(32))) 447 | StencilUse(op='lequal') 448 | self.col_instruction = Color(rgba=self.ripple_color) 449 | self.ellipse = Ellipse(size=(ripple_rad, ripple_rad), 450 | pos=(ripple_pos[0] - ripple_rad/2., 451 | ripple_pos[1] - ripple_rad/2.)) 452 | StencilUnUse() 453 | Rectangle( 454 | pos=stencil_pos, 455 | size=stencil_size) 456 | self.slider_stencil_unuse = Ellipse( 457 | pos=ellipse_pos, 458 | size=(sp(32), sp(32))) 459 | 460 | StencilPop() 461 | self.bind(ripple_color=self.set_color, ripple_pos=self.set_ellipse, 462 | ripple_rad=self.set_ellipse) 463 | return super(SliderTouchRippleBehavior, self).on_touch_down(touch) 464 | 465 | def update_stencil(self): 466 | if self.orientation == 'horizontal': 467 | pos = [self.value_pos[0] - sp(16), 468 | self.center_y - sp(17)] 469 | ellipse = [self.value_pos[0] - sp(16), 470 | self.center_y - sp(17), sp(32), sp(32)] 471 | else: 472 | pos = [self.center_x - sp(17), 473 | self.value_pos[1] - sp(16)] 474 | ellipse = [self.center_x - sp(17), 475 | self.value_pos[1] - sp(16), sp(32), sp(32)] 476 | 477 | if self.slider_stencil is not None: 478 | self.slider_stencil.pos = pos 479 | if self.slider_stencil_unuse is not None: 480 | self.slider_stencil_unuse.pos = pos 481 | if self.slider_line_stencil is not None: 482 | self.slider_line_stencil.ellipse = ellipse 483 | if self.slider_line_stencil_unuse is not None: 484 | self.slider_line_stencil_unuse.ellipse = ellipse 485 | 486 | def on_value_pos(self, instance, value): 487 | self.update_stencil() 488 | 489 | def set_ellipse(self, instance, value): 490 | ellipse = self.ellipse 491 | ripple_pos = self.ripple_pos 492 | ripple_rad = self.ripple_rad 493 | ellipse.size = (ripple_rad, ripple_rad) 494 | ellipse.pos = (ripple_pos[0] - ripple_rad/2., 495 | ripple_pos[1] - ripple_rad/2.) 496 | 497 | def set_color(self, instance, value): 498 | self.col_instruction.rgba = value 499 | 500 | def on_touch_up(self, touch): 501 | if self in touch.ud: 502 | rc = self.ripple_color 503 | anim = Animation(ripple_color=[rc[0], rc[1], rc[2], 0.], 504 | t=self.ripple_func_out, duration=self.ripple_duration_out) 505 | anim.bind(on_complete=self.anim_complete) 506 | anim.start(self) 507 | return super(SliderTouchRippleBehavior, self).on_touch_up(touch) 508 | 509 | def anim_complete(self, anim, instance): 510 | self.ripple_rad = 10 511 | self.canvas.after.clear() 512 | self.slider_stencil = None 513 | self.slider_stencil_unuse = None 514 | --------------------------------------------------------------------------------