detach kivy version from upstream
|
@ -1,45 +0,0 @@
|
||||||
from helper_sql import *
|
|
||||||
|
|
||||||
|
|
||||||
def search_sql(xAddress="toaddress", account=None, folder="inbox", where=None, what=None, unreadOnly=False):
|
|
||||||
if what is not None and what != "":
|
|
||||||
what = "%" + what + "%"
|
|
||||||
else:
|
|
||||||
what = None
|
|
||||||
|
|
||||||
if folder == "sent":
|
|
||||||
sqlStatementBase = '''
|
|
||||||
SELECT toaddress, fromaddress, subject, status, ackdata, lastactiontime
|
|
||||||
FROM sent '''
|
|
||||||
else:
|
|
||||||
sqlStatementBase = '''SELECT folder, msgid, toaddress, fromaddress, subject, received, read
|
|
||||||
FROM inbox '''
|
|
||||||
sqlStatementParts = []
|
|
||||||
sqlArguments = []
|
|
||||||
if account is not None:
|
|
||||||
if xAddress == 'both':
|
|
||||||
sqlStatementParts.append("(fromaddress = ? OR toaddress = ?)")
|
|
||||||
sqlArguments.append(account)
|
|
||||||
sqlArguments.append(account)
|
|
||||||
else:
|
|
||||||
sqlStatementParts.append(xAddress + " = ? ")
|
|
||||||
sqlArguments.append(account)
|
|
||||||
if folder is not None:
|
|
||||||
if folder == "new":
|
|
||||||
folder = "inbox"
|
|
||||||
unreadOnly = True
|
|
||||||
sqlStatementParts.append("folder = ? ")
|
|
||||||
sqlArguments.append(folder)
|
|
||||||
else:
|
|
||||||
sqlStatementParts.append("folder != ?")
|
|
||||||
sqlArguments.append("trash")
|
|
||||||
if what is not None:
|
|
||||||
sqlStatementParts.append("%s LIKE ?" % (where))
|
|
||||||
sqlArguments.append(what)
|
|
||||||
if unreadOnly:
|
|
||||||
sqlStatementParts.append("read = 0")
|
|
||||||
if len(sqlStatementParts) > 0:
|
|
||||||
sqlStatementBase += "WHERE " + " AND ".join(sqlStatementParts)
|
|
||||||
if folder == "sent":
|
|
||||||
sqlStatementBase += " ORDER BY lastactiontime"
|
|
||||||
return sqlQuery(sqlStatementBase, sqlArguments)
|
|
|
@ -1,354 +0,0 @@
|
||||||
#:import la kivy.adapters.listadapter
|
|
||||||
#:import factory kivy.factory
|
|
||||||
#:import mpybit bitmessagekivy.mpybit
|
|
||||||
#:import C kivy.utils.get_color_from_hex
|
|
||||||
|
|
||||||
<Navigator>:
|
|
||||||
id: nav_drawer
|
|
||||||
NavigationDrawerIconButton:
|
|
||||||
Spinner:
|
|
||||||
pos_hint:{"x":0,"y":.3}
|
|
||||||
id: btn
|
|
||||||
background_color: app.theme_cls.primary_dark
|
|
||||||
text: app.showmeaddresses(name='text')
|
|
||||||
values: app.showmeaddresses(name='values')
|
|
||||||
on_text:app.getCurrentAccountData(self.text)
|
|
||||||
|
|
||||||
NavigationDrawerIconButton:
|
|
||||||
icon: 'email-open'
|
|
||||||
text: "inbox"
|
|
||||||
on_release: app.root.ids.scr_mngr.current = 'inbox'
|
|
||||||
NavigationDrawerIconButton:
|
|
||||||
icon: 'mail-send'
|
|
||||||
text: "sent"
|
|
||||||
on_release: app.root.ids.scr_mngr.current = 'sent'
|
|
||||||
NavigationDrawerIconButton:
|
|
||||||
icon: 'dropbox'
|
|
||||||
text: "trash"
|
|
||||||
on_release: app.root.ids.scr_mngr.current = 'trash'
|
|
||||||
NavigationDrawerIconButton:
|
|
||||||
icon: 'email'
|
|
||||||
text: "drafts"
|
|
||||||
on_release: app.root.ids.scr_mngr.current = 'dialog'
|
|
||||||
NavigationDrawerIconButton:
|
|
||||||
icon: 'markunread-mailbox'
|
|
||||||
text: "test"
|
|
||||||
on_release: app.root.ids.scr_mngr.current = 'test'
|
|
||||||
NavigationDrawerIconButton:
|
|
||||||
text: "new identity"
|
|
||||||
icon:'accounts-add'
|
|
||||||
on_release: app.root.ids.scr_mngr.current = 'newidentity'
|
|
||||||
|
|
||||||
BoxLayout:
|
|
||||||
orientation: 'vertical'
|
|
||||||
Toolbar:
|
|
||||||
id: toolbar
|
|
||||||
title: app.getCurrentAccount()
|
|
||||||
background_color: app.theme_cls.primary_dark
|
|
||||||
left_action_items: [['menu', lambda x: app.nav_drawer.toggle()]]
|
|
||||||
Button:
|
|
||||||
text:"EXIT"
|
|
||||||
color: 0,0,0,1
|
|
||||||
background_color: (0,0,0,0)
|
|
||||||
size_hint_y: 0.4
|
|
||||||
size_hint_x: 0.1
|
|
||||||
pos_hint: {'x': 0.8, 'y':0.4}
|
|
||||||
on_press: app.say_exit()
|
|
||||||
|
|
||||||
|
|
||||||
ScreenManager:
|
|
||||||
id: scr_mngr
|
|
||||||
Inbox:
|
|
||||||
id:sc1
|
|
||||||
Sent:
|
|
||||||
id:sc2
|
|
||||||
Trash:
|
|
||||||
id:sc3
|
|
||||||
Dialog:
|
|
||||||
id:sc4
|
|
||||||
Test:
|
|
||||||
id:sc5
|
|
||||||
Create:
|
|
||||||
id:sc6
|
|
||||||
NewIdentity:
|
|
||||||
id:sc7
|
|
||||||
Page:
|
|
||||||
id:sc8
|
|
||||||
AddressSuccessful:
|
|
||||||
id:sc9
|
|
||||||
|
|
||||||
Button:
|
|
||||||
id:create
|
|
||||||
height:100
|
|
||||||
size_hint_y: 0.13
|
|
||||||
size_hint_x: 0.1
|
|
||||||
pos_hint: {'x': 0.85, 'y': 0.5}
|
|
||||||
background_color: (0,0,0,0)
|
|
||||||
on_press: scr_mngr.current = 'create'
|
|
||||||
Image:
|
|
||||||
source: 'images/plus.png'
|
|
||||||
y: self.parent.y - 7.5
|
|
||||||
x: self.parent.x + self.parent.width - 50
|
|
||||||
size: 70, 70
|
|
||||||
|
|
||||||
<SwipeButton@Carousel>:
|
|
||||||
text: ''
|
|
||||||
size_hint_y: None
|
|
||||||
height: 48
|
|
||||||
ignore_perpendicular_swipes: True
|
|
||||||
data_index: 0
|
|
||||||
min_move: 20 / self.width
|
|
||||||
|
|
||||||
on__offset: app.update_index(root.data_index, self.index)
|
|
||||||
|
|
||||||
canvas.before:
|
|
||||||
Color:
|
|
||||||
rgba: C('FFFFFF33')
|
|
||||||
|
|
||||||
Rectangle:
|
|
||||||
pos: self.pos
|
|
||||||
size: self.size
|
|
||||||
|
|
||||||
Line:
|
|
||||||
rectangle: self.pos + self.size
|
|
||||||
|
|
||||||
Button:
|
|
||||||
text: 'delete ({}:{})'.format(root.text, root.data_index)
|
|
||||||
on_press: app.delete(root.data_index)
|
|
||||||
|
|
||||||
Button:
|
|
||||||
text: root.text
|
|
||||||
on_press: app.getInboxMessageDetail(self.text)
|
|
||||||
|
|
||||||
Button:
|
|
||||||
text: 'archive'
|
|
||||||
on_press: app.archive(root.data_index)
|
|
||||||
|
|
||||||
<Inbox>:
|
|
||||||
name: 'inbox'
|
|
||||||
RecycleView:
|
|
||||||
data: root.data
|
|
||||||
viewclass: 'SwipeButton'
|
|
||||||
do_scroll_x: False
|
|
||||||
scroll_timeout: 100
|
|
||||||
|
|
||||||
RecycleBoxLayout:
|
|
||||||
id:rc
|
|
||||||
orientation: 'vertical'
|
|
||||||
size_hint_y: None
|
|
||||||
height: self.minimum_height
|
|
||||||
default_size_hint: 1, None
|
|
||||||
canvas.before:
|
|
||||||
Color:
|
|
||||||
rgba: 0,0,0, 1
|
|
||||||
Rectangle:
|
|
||||||
pos: self.pos
|
|
||||||
size: self.size
|
|
||||||
|
|
||||||
<Sent>:
|
|
||||||
name: 'sent'
|
|
||||||
RecycleView:
|
|
||||||
data: root.data
|
|
||||||
viewclass: 'SwipeButton'
|
|
||||||
do_scroll_x: False
|
|
||||||
scroll_timeout: 100
|
|
||||||
|
|
||||||
RecycleBoxLayout:
|
|
||||||
id:rc
|
|
||||||
orientation: 'vertical'
|
|
||||||
size_hint_y: None
|
|
||||||
height: self.minimum_height
|
|
||||||
default_size_hint: 1, None
|
|
||||||
canvas.before:
|
|
||||||
Color:
|
|
||||||
rgba: 0,0,0, 1
|
|
||||||
Rectangle:
|
|
||||||
pos: self.pos
|
|
||||||
size: self.size
|
|
||||||
|
|
||||||
<Trash>:
|
|
||||||
name: 'trash'
|
|
||||||
RecycleView:
|
|
||||||
data: root.data
|
|
||||||
viewclass: 'SwipeButton'
|
|
||||||
do_scroll_x: False
|
|
||||||
scroll_timeout: 100
|
|
||||||
|
|
||||||
RecycleBoxLayout:
|
|
||||||
id:rc
|
|
||||||
orientation: 'vertical'
|
|
||||||
size_hint_y: None
|
|
||||||
height: self.minimum_height
|
|
||||||
default_size_hint: 1, None
|
|
||||||
canvas.before:
|
|
||||||
Color:
|
|
||||||
rgba: 0,0,0, 1
|
|
||||||
Rectangle:
|
|
||||||
pos: self.pos
|
|
||||||
size: self.size
|
|
||||||
|
|
||||||
<Dialog>:
|
|
||||||
name: 'dialog'
|
|
||||||
Label:
|
|
||||||
text:"I have a good dialox box"
|
|
||||||
color: 0,0,0,1
|
|
||||||
<Test>:
|
|
||||||
name: 'test'
|
|
||||||
Label:
|
|
||||||
text:"I am in test"
|
|
||||||
color: 0,0,0,1
|
|
||||||
|
|
||||||
<Create>:
|
|
||||||
name: 'create'
|
|
||||||
GridLayout:
|
|
||||||
rows: 5
|
|
||||||
cols: 1
|
|
||||||
padding: 60,60,60,60
|
|
||||||
spacing: 50
|
|
||||||
BoxLayout:
|
|
||||||
size_hint_y: None
|
|
||||||
height: '32dp'
|
|
||||||
Label:
|
|
||||||
text: 'FROM'
|
|
||||||
color: 0,0,0,1
|
|
||||||
Spinner:
|
|
||||||
size_hint: 1,1
|
|
||||||
pos_hint: {"x":0,"top":1.}
|
|
||||||
pos: 10,10
|
|
||||||
id: spinner_id
|
|
||||||
text: app.showmeaddresses(name='text')
|
|
||||||
values: app.showmeaddresses(name='values')
|
|
||||||
|
|
||||||
BoxLayout:
|
|
||||||
size_hint_y: None
|
|
||||||
height: '32dp'
|
|
||||||
Label:
|
|
||||||
text: 'TO'
|
|
||||||
color: 0,0,0,1
|
|
||||||
TextInput:
|
|
||||||
id: recipent
|
|
||||||
hint_text: 'To'
|
|
||||||
|
|
||||||
BoxLayout:
|
|
||||||
size_hint_y: None
|
|
||||||
height: '32dp'
|
|
||||||
Label:
|
|
||||||
text: 'SUBJECT'
|
|
||||||
color: 0,0,0,1
|
|
||||||
TextInput:
|
|
||||||
id: subject
|
|
||||||
hint_text: 'SUBJECT'
|
|
||||||
|
|
||||||
BoxLayout:
|
|
||||||
size_hint_y: None
|
|
||||||
height: '32dp'
|
|
||||||
Label:
|
|
||||||
text: 'BODY'
|
|
||||||
color: 0,0,0,1
|
|
||||||
TextInput:
|
|
||||||
id: message
|
|
||||||
multiline:True
|
|
||||||
size_hint: 1,2
|
|
||||||
|
|
||||||
Button:
|
|
||||||
text: 'send'
|
|
||||||
size_hint_y: 0.1
|
|
||||||
size_hint_x: 0.2
|
|
||||||
height: '32dp'
|
|
||||||
pos_hint: {'x': .5, 'y': 0.1}
|
|
||||||
on_press: root.send()
|
|
||||||
Button:
|
|
||||||
text: 'cancel'
|
|
||||||
size_hint_y: 0.1
|
|
||||||
size_hint_x: 0.2
|
|
||||||
height: '32dp'
|
|
||||||
pos_hint: {'x': .72, 'y': 0.1}
|
|
||||||
on_press: root.cancel()
|
|
||||||
|
|
||||||
<NewIdentity>:
|
|
||||||
name: 'newidentity'
|
|
||||||
GridLayout:
|
|
||||||
padding: '120dp'
|
|
||||||
cols: 1
|
|
||||||
Label:
|
|
||||||
text:"""Here you may generate as many addresses as you like. Indeed, creating and abandoning addresses is encouraged."""
|
|
||||||
line_height:1.5
|
|
||||||
text_size:(700,None)
|
|
||||||
color: 0,0,0,1
|
|
||||||
BoxLayout:
|
|
||||||
CheckBox:
|
|
||||||
canvas.before:
|
|
||||||
Color:
|
|
||||||
rgb: 1,0,0
|
|
||||||
Ellipse:
|
|
||||||
pos:self.center_x-8, self.center_y-8
|
|
||||||
size:[16,16]
|
|
||||||
group: "money"
|
|
||||||
id:chk
|
|
||||||
text:"use a random number generator to make an address"
|
|
||||||
on_active:
|
|
||||||
root.checked = self.text
|
|
||||||
active:root.is_active
|
|
||||||
|
|
||||||
Label:
|
|
||||||
text: "use a random number generator to make an address"
|
|
||||||
color: 0,0,0,1
|
|
||||||
BoxLayout:
|
|
||||||
CheckBox:
|
|
||||||
canvas.before:
|
|
||||||
Color:
|
|
||||||
rgb: 1,0,0
|
|
||||||
Ellipse:
|
|
||||||
pos:self.center_x-8, self.center_y-8
|
|
||||||
size:[16,16]
|
|
||||||
group: "money"
|
|
||||||
id:chk
|
|
||||||
text:"use a pseudo number generator to make an address"
|
|
||||||
on_active:
|
|
||||||
root.checked = self.text
|
|
||||||
active:not root.is_active
|
|
||||||
Label:
|
|
||||||
text: "use a pseudo number generator to make an address"
|
|
||||||
color: 0,0,0,1
|
|
||||||
Label:
|
|
||||||
color: 0,0,0,1
|
|
||||||
size_hint_x: .35
|
|
||||||
markup: True
|
|
||||||
text: "[b]{}[/b]".format("Randomly generated addresses")
|
|
||||||
BoxLayout:
|
|
||||||
size_hint_y: None
|
|
||||||
height: '32dp'
|
|
||||||
Label:
|
|
||||||
text: "Label (not shown to anyone except you)"
|
|
||||||
color: 0,0,0,1
|
|
||||||
BoxLayout:
|
|
||||||
size_hint_y: None
|
|
||||||
height: '32dp'
|
|
||||||
TextInput:
|
|
||||||
id: label
|
|
||||||
|
|
||||||
Button:
|
|
||||||
text: 'Cancel'
|
|
||||||
size_hint_y: 0.1
|
|
||||||
size_hint_x: 0.3
|
|
||||||
height: '32dp'
|
|
||||||
pos_hint: {'x': .1, 'y': 0.1}
|
|
||||||
Button:
|
|
||||||
text: 'Ok'
|
|
||||||
size_hint_y: 0.1
|
|
||||||
size_hint_x: 0.3
|
|
||||||
height: '32dp'
|
|
||||||
pos_hint: {'x': .5, 'y': 0.1}
|
|
||||||
on_press: root.generateaddress()
|
|
||||||
|
|
||||||
<Page>:
|
|
||||||
name: 'page'
|
|
||||||
Label:
|
|
||||||
text: 'I am on description of my email yooooo'
|
|
||||||
color: 0,0,0,1
|
|
||||||
|
|
||||||
<AddressSuccessful>:
|
|
||||||
name: 'add_sucess'
|
|
||||||
Label:
|
|
||||||
text: 'Successfully created a new bit address'
|
|
||||||
color: 0,0,0,1
|
|
|
@ -1,393 +0,0 @@
|
||||||
import kivy_helper_search
|
|
||||||
import os
|
|
||||||
import queues
|
|
||||||
import shutdown
|
|
||||||
import state
|
|
||||||
import time
|
|
||||||
|
|
||||||
from kivy.app import App
|
|
||||||
from kivy.lang import Builder
|
|
||||||
from kivy.properties import BooleanProperty
|
|
||||||
from kivy.clock import Clock
|
|
||||||
from navigationdrawer import NavigationDrawer
|
|
||||||
from kivy.properties import ObjectProperty, StringProperty, ListProperty
|
|
||||||
from kivy.uix.screenmanager import Screen
|
|
||||||
from kivy.uix.textinput import TextInput
|
|
||||||
from kivymd.theming import ThemeManager
|
|
||||||
from kivymd.toolbar import Toolbar
|
|
||||||
from bmconfigparser import BMConfigParser
|
|
||||||
from helper_ackPayload import genAckPayload
|
|
||||||
from addresses import decodeAddress, addBMIfNotPresent
|
|
||||||
from helper_sql import sqlExecute
|
|
||||||
|
|
||||||
statusIconColor = 'red'
|
|
||||||
|
|
||||||
|
|
||||||
class NavigateApp(App, TextInput):
|
|
||||||
"""Application uses kivy in which base Class of Navigate App inherits from the App class."""
|
|
||||||
|
|
||||||
theme_cls = ThemeManager()
|
|
||||||
nav_drawer = ObjectProperty()
|
|
||||||
|
|
||||||
def build(self):
|
|
||||||
"""Return a main_widget as a root widget.
|
|
||||||
|
|
||||||
An application can be built if you return a widget on build(), or if you set
|
|
||||||
self.root.
|
|
||||||
"""
|
|
||||||
main_widget = Builder.load_file(
|
|
||||||
os.path.join(os.path.dirname(__file__), 'main.kv'))
|
|
||||||
self.nav_drawer = Navigator()
|
|
||||||
return main_widget
|
|
||||||
|
|
||||||
def getCurrentAccountData(self, text):
|
|
||||||
"""Get Current Address Account Data."""
|
|
||||||
state.association = text
|
|
||||||
self.root.ids.sc1.clear_widgets()
|
|
||||||
self.root.ids.sc2.clear_widgets()
|
|
||||||
self.root.ids.sc3.clear_widgets()
|
|
||||||
self.root.ids.sc1.add_widget(Inbox())
|
|
||||||
self.root.ids.sc2.add_widget(Sent())
|
|
||||||
self.root.ids.sc3.add_widget(Trash())
|
|
||||||
self.root.ids.toolbar.title = BMConfigParser().get(
|
|
||||||
state.association, 'label') + '({})'.format(state.association)
|
|
||||||
Inbox()
|
|
||||||
Sent()
|
|
||||||
Trash()
|
|
||||||
|
|
||||||
def say_exit(self):
|
|
||||||
"""Exit the application as uses shutdown PyBitmessage."""
|
|
||||||
print("**************************EXITING FROM APPLICATION*****************************")
|
|
||||||
App.get_running_app().stop()
|
|
||||||
shutdown.doCleanShutdown()
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def showmeaddresses(name="text"):
|
|
||||||
"""Show the addresses in spinner to make as dropdown."""
|
|
||||||
if name == "text":
|
|
||||||
return BMConfigParser().addresses()[0]
|
|
||||||
elif name == "values":
|
|
||||||
return BMConfigParser().addresses()
|
|
||||||
|
|
||||||
def update_index(self, data_index, index):
|
|
||||||
"""Update index after archieve message to trash."""
|
|
||||||
if self.root.ids.scr_mngr.current == 'inbox':
|
|
||||||
self.root.ids.sc1.data[data_index]['index'] = index
|
|
||||||
elif self.root.ids.scr_mngr.current == 'sent':
|
|
||||||
self.root.ids.sc2.data[data_index]['index'] = index
|
|
||||||
elif self.root.ids.scr_mngr.current == 'trash':
|
|
||||||
self.root.ids.sc3.data[data_index]['index'] = index
|
|
||||||
|
|
||||||
def delete(self, data_index):
|
|
||||||
"""It will make delete using remove function."""
|
|
||||||
print("delete {}".format(data_index))
|
|
||||||
self._remove(data_index)
|
|
||||||
|
|
||||||
def archive(self, data_index):
|
|
||||||
"""It will make archieve using remove function."""
|
|
||||||
print("archive {}".format(data_index))
|
|
||||||
self._remove(data_index)
|
|
||||||
|
|
||||||
def _remove(self, data_index):
|
|
||||||
"""It will remove message by resetting the values in recycleview data."""
|
|
||||||
if self.root.ids.scr_mngr.current == 'inbox':
|
|
||||||
self.root.ids.sc1.data.pop(data_index)
|
|
||||||
self.root.ids.sc1.data = [{
|
|
||||||
'data_index': i,
|
|
||||||
'index': d['index'],
|
|
||||||
'height': d['height'],
|
|
||||||
'text': d['text']}
|
|
||||||
for i, d in enumerate(self.root.ids.sc1.data)
|
|
||||||
]
|
|
||||||
elif self.root.ids.scr_mngr.current == 'sent':
|
|
||||||
self.root.ids.sc2.data.pop(data_index)
|
|
||||||
self.root.ids.sc2.data = [{
|
|
||||||
'data_index': i,
|
|
||||||
'index': d['index'],
|
|
||||||
'height': d['height'],
|
|
||||||
'text': d['text']}
|
|
||||||
for i, d in enumerate(self.root.ids.sc2.data)
|
|
||||||
]
|
|
||||||
elif self.root.ids.scr_mngr.current == 'trash':
|
|
||||||
self.root.ids.sc3.data.pop(data_index)
|
|
||||||
self.root.ids.sc3.data = [{
|
|
||||||
'data_index': i,
|
|
||||||
'index': d['index'],
|
|
||||||
'height': d['height'],
|
|
||||||
'text': d['text']}
|
|
||||||
for i, d in enumerate(self.root.ids.sc3.data)
|
|
||||||
]
|
|
||||||
|
|
||||||
def getInboxMessageDetail(self, instance):
|
|
||||||
"""It will get message detail after make selected message description."""
|
|
||||||
try:
|
|
||||||
self.root.ids.scr_mngr.current = 'page'
|
|
||||||
except AttributeError:
|
|
||||||
self.parent.manager.current = 'page'
|
|
||||||
print('Message Clicked {}'.format(instance))
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def getCurrentAccount():
|
|
||||||
"""It uses to get current account label."""
|
|
||||||
return BMConfigParser().get(state.association, 'label') + '({})'.format(state.association)
|
|
||||||
|
|
||||||
|
|
||||||
class Navigator(NavigationDrawer):
|
|
||||||
"""Navigator class uses NavigationDrawer.
|
|
||||||
|
|
||||||
It is an UI panel that shows our app's main navigation menu
|
|
||||||
It is hidden when not in use, but appears when the user swipes
|
|
||||||
a finger from the left edge of the screen or, when at the top
|
|
||||||
level of the app, the user touches the drawer icon in the app bar
|
|
||||||
"""
|
|
||||||
|
|
||||||
image_source = StringProperty('images/qidenticon_two.png')
|
|
||||||
title = StringProperty('Navigation')
|
|
||||||
|
|
||||||
|
|
||||||
class Inbox(Screen):
|
|
||||||
"""Inbox Screen uses screen to show widgets of screens."""
|
|
||||||
|
|
||||||
data = ListProperty()
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
super(Inbox, self).__init__(*args, **kwargs)
|
|
||||||
if state.association == '':
|
|
||||||
state.association = Navigator().ids.btn.text
|
|
||||||
Clock.schedule_once(self.init_ui, 0)
|
|
||||||
|
|
||||||
def init_ui(self, dt=0):
|
|
||||||
"""Clock Schdule for method inbox accounts."""
|
|
||||||
self.inboxaccounts()
|
|
||||||
print(dt)
|
|
||||||
|
|
||||||
def inboxaccounts(self):
|
|
||||||
"""Load inbox accounts."""
|
|
||||||
account = state.association
|
|
||||||
self.loadMessagelist(account, 'All', '')
|
|
||||||
|
|
||||||
def loadMessagelist(self, account, where="", what=""):
|
|
||||||
"""Load Inbox list for inbox messages."""
|
|
||||||
xAddress = "toaddress"
|
|
||||||
queryreturn = kivy_helper_search.search_sql(
|
|
||||||
xAddress, account, 'inbox', where, what, False)
|
|
||||||
if queryreturn:
|
|
||||||
self.data = [{
|
|
||||||
'data_index': i,
|
|
||||||
'index': 1,
|
|
||||||
'height': 48,
|
|
||||||
'text': row[4]}
|
|
||||||
for i, row in enumerate(queryreturn)
|
|
||||||
]
|
|
||||||
else:
|
|
||||||
self.data = [{
|
|
||||||
'data_index': 1,
|
|
||||||
'index': 1,
|
|
||||||
'height': 48,
|
|
||||||
'text': "yet no message for this account!!!!!!!!!!!!!"}
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class Page(Screen):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class AddressSuccessful(Screen):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class Sent(Screen):
|
|
||||||
"""Sent Screen uses screen to show widgets of screens."""
|
|
||||||
|
|
||||||
data = ListProperty()
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
super(Sent, self).__init__(*args, **kwargs)
|
|
||||||
if state.association == '':
|
|
||||||
state.association = Navigator().ids.btn.text
|
|
||||||
Clock.schedule_once(self.init_ui, 0)
|
|
||||||
|
|
||||||
def init_ui(self, dt=0):
|
|
||||||
"""Clock Schdule for method sent accounts."""
|
|
||||||
self.sentaccounts()
|
|
||||||
print(dt)
|
|
||||||
|
|
||||||
def sentaccounts(self):
|
|
||||||
"""Load sent accounts."""
|
|
||||||
account = state.association
|
|
||||||
self.loadSent(account, 'All', '')
|
|
||||||
|
|
||||||
def loadSent(self, account, where="", what=""):
|
|
||||||
"""Load Sent list for Sent messages."""
|
|
||||||
xAddress = 'fromaddress'
|
|
||||||
queryreturn = kivy_helper_search.search_sql(
|
|
||||||
xAddress, account, "sent", where, what, False)
|
|
||||||
if queryreturn:
|
|
||||||
self.data = [{
|
|
||||||
'data_index': i,
|
|
||||||
'index': 1,
|
|
||||||
'height': 48,
|
|
||||||
'text': row[2]}
|
|
||||||
for i, row in enumerate(queryreturn)
|
|
||||||
]
|
|
||||||
else:
|
|
||||||
self.data = [{
|
|
||||||
'data_index': 1,
|
|
||||||
'index': 1,
|
|
||||||
'height': 48,
|
|
||||||
'text': "yet no message for this account!!!!!!!!!!!!!"}
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class Trash(Screen):
|
|
||||||
"""Trash Screen uses screen to show widgets of screens."""
|
|
||||||
|
|
||||||
data = ListProperty()
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
super(Trash, self).__init__(*args, **kwargs)
|
|
||||||
if state.association == '':
|
|
||||||
state.association = Navigator().ids.btn.text
|
|
||||||
Clock.schedule_once(self.init_ui, 0)
|
|
||||||
|
|
||||||
def init_ui(self, dt=0):
|
|
||||||
"""Clock Schdule for method inbox accounts."""
|
|
||||||
self.inboxaccounts()
|
|
||||||
print(dt)
|
|
||||||
|
|
||||||
def inboxaccounts(self):
|
|
||||||
"""Load inbox accounts."""
|
|
||||||
account = state.association
|
|
||||||
self.loadTrashlist(account, 'All', '')
|
|
||||||
|
|
||||||
def loadTrashlist(self, account, where="", what=""):
|
|
||||||
"""Load Trash list for trashed messages."""
|
|
||||||
xAddress = "toaddress"
|
|
||||||
queryreturn = kivy_helper_search.search_sql(
|
|
||||||
xAddress, account, 'trash', where, what, False)
|
|
||||||
if queryreturn:
|
|
||||||
self.data = [{
|
|
||||||
'data_index': i,
|
|
||||||
'index': 1,
|
|
||||||
'height': 48,
|
|
||||||
'text': row[4]}
|
|
||||||
for i, row in enumerate(queryreturn)
|
|
||||||
]
|
|
||||||
else:
|
|
||||||
self.data = [{
|
|
||||||
'data_index': 1,
|
|
||||||
'index': 1,
|
|
||||||
'height': 48,
|
|
||||||
'text': "yet no message for this account!!!!!!!!!!!!!"}
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class Dialog(Screen):
|
|
||||||
"""Dialog Screen uses screen to show widgets of screens."""
|
|
||||||
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class Test(Screen):
|
|
||||||
"""Test Screen uses screen to show widgets of screens."""
|
|
||||||
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class Create(Screen):
|
|
||||||
"""Create Screen uses screen to show widgets of screens."""
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
super(Create, self).__init__(*args, **kwargs)
|
|
||||||
|
|
||||||
def send(self):
|
|
||||||
"""Send message from one address to another."""
|
|
||||||
fromAddress = self.ids.spinner_id.text
|
|
||||||
# For now we are using static address i.e we are not using recipent field value.
|
|
||||||
toAddress = "BM-2cWyUfBdY2FbgyuCb7abFZ49JYxSzUhNFe"
|
|
||||||
message = self.ids.message.text
|
|
||||||
subject = self.ids.subject.text
|
|
||||||
encoding = 3
|
|
||||||
print("message: ", self.ids.message.text)
|
|
||||||
sendMessageToPeople = True
|
|
||||||
if sendMessageToPeople:
|
|
||||||
if toAddress != '':
|
|
||||||
status, addressVersionNumber, streamNumber, ripe = decodeAddress(
|
|
||||||
toAddress)
|
|
||||||
if status == 'success':
|
|
||||||
toAddress = addBMIfNotPresent(toAddress)
|
|
||||||
|
|
||||||
if addressVersionNumber > 4 or addressVersionNumber <= 1:
|
|
||||||
print("addressVersionNumber > 4 or addressVersionNumber <= 1")
|
|
||||||
if streamNumber > 1 or streamNumber == 0:
|
|
||||||
print("streamNumber > 1 or streamNumber == 0")
|
|
||||||
if statusIconColor == 'red':
|
|
||||||
print("shared.statusIconColor == 'red'")
|
|
||||||
stealthLevel = BMConfigParser().safeGetInt(
|
|
||||||
'bitmessagesettings', 'ackstealthlevel')
|
|
||||||
ackdata = genAckPayload(streamNumber, stealthLevel)
|
|
||||||
t = ()
|
|
||||||
sqlExecute(
|
|
||||||
'''INSERT INTO sent VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)''',
|
|
||||||
'',
|
|
||||||
toAddress,
|
|
||||||
ripe,
|
|
||||||
fromAddress,
|
|
||||||
subject,
|
|
||||||
message,
|
|
||||||
ackdata,
|
|
||||||
int(time.time()),
|
|
||||||
int(time.time()),
|
|
||||||
0,
|
|
||||||
'msgqueued',
|
|
||||||
0,
|
|
||||||
'sent',
|
|
||||||
encoding,
|
|
||||||
BMConfigParser().getint('bitmessagesettings', 'ttl'))
|
|
||||||
toLabel = ''
|
|
||||||
queues.workerQueue.put(('sendmessage', toAddress))
|
|
||||||
print("sqlExecute successfully ##### ##################")
|
|
||||||
self.ids.message.text = ''
|
|
||||||
self.ids.spinner_id.text = '<select>'
|
|
||||||
self.ids.subject.text = ''
|
|
||||||
self.ids.recipent.text = ''
|
|
||||||
return None
|
|
||||||
|
|
||||||
def cancel(self):
|
|
||||||
"""Reset values for send message."""
|
|
||||||
self.ids.message.text = ''
|
|
||||||
self.ids.spinner_id.text = '<select>'
|
|
||||||
self.ids.subject.text = ''
|
|
||||||
self.ids.recipent.text = ''
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
class NewIdentity(Screen):
|
|
||||||
"""Create new address for PyBitmessage."""
|
|
||||||
|
|
||||||
is_active = BooleanProperty(False)
|
|
||||||
checked = StringProperty("")
|
|
||||||
# self.manager.parent.ids.create.children[0].source = 'images/plus-4-xxl.png'
|
|
||||||
|
|
||||||
def generateaddress(self):
|
|
||||||
"""Generate new address."""
|
|
||||||
if self.checked == 'use a random number generator to make an address':
|
|
||||||
queues.apiAddressGeneratorReturnQueue.queue.clear()
|
|
||||||
streamNumberForAddress = 1
|
|
||||||
label = self.ids.label.text
|
|
||||||
eighteenByteRipe = False
|
|
||||||
nonceTrialsPerByte = 1000
|
|
||||||
payloadLengthExtraBytes = 1000
|
|
||||||
|
|
||||||
queues.addressGeneratorQueue.put((
|
|
||||||
'createRandomAddress',
|
|
||||||
4, streamNumberForAddress,
|
|
||||||
label, 1, "", eighteenByteRipe,
|
|
||||||
nonceTrialsPerByte,
|
|
||||||
payloadLengthExtraBytes)
|
|
||||||
)
|
|
||||||
self.manager.current = 'add_sucess'
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
NavigateApp().run()
|
|
|
@ -353,8 +353,8 @@ class Main(object):
|
||||||
bitmessagecurses.runwrapper()
|
bitmessagecurses.runwrapper()
|
||||||
elif state.kivy:
|
elif state.kivy:
|
||||||
config.remove_option('bitmessagesettings', 'dontconnect')
|
config.remove_option('bitmessagesettings', 'dontconnect')
|
||||||
from bitmessagekivy.mpybit import NavigateApp
|
# from bitmessagekivy.mpybit import NavigateApp
|
||||||
NavigateApp().run()
|
# NavigateApp().run()
|
||||||
else:
|
else:
|
||||||
import bitmessageqt
|
import bitmessageqt
|
||||||
bitmessageqt.run()
|
bitmessageqt.run()
|
||||||
|
|
|
@ -1,21 +0,0 @@
|
||||||
The MIT License (MIT)
|
|
||||||
|
|
||||||
Copyright (c) 2015 Andrés Rodríguez and KivyMD contributors
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in
|
|
||||||
all copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
THE SOFTWARE.
|
|
|
@ -1,6 +0,0 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
import os
|
|
||||||
|
|
||||||
path = os.path.dirname(__file__)
|
|
||||||
fonts_path = os.path.join(path, "fonts/")
|
|
||||||
images_path = os.path.join(path, 'images/')
|
|
|
@ -1,254 +0,0 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
|
||||||
from kivy.lang import Builder
|
|
||||||
from kivy.properties import StringProperty, ListProperty, OptionProperty
|
|
||||||
from kivy.utils import get_color_from_hex
|
|
||||||
from kivymd.color_definitions import colors
|
|
||||||
from kivymd.theming import ThemableBehavior
|
|
||||||
from kivy.uix.accordion import Accordion, AccordionItem
|
|
||||||
from kivymd.backgroundcolorbehavior import BackgroundColorBehavior
|
|
||||||
from kivy.uix.boxlayout import BoxLayout
|
|
||||||
|
|
||||||
|
|
||||||
class MDAccordionItemTitleLayout(ThemableBehavior, BackgroundColorBehavior, BoxLayout):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class MDAccordion(ThemableBehavior, BackgroundColorBehavior, Accordion):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class MDAccordionItem(ThemableBehavior, AccordionItem):
|
|
||||||
title_theme_color = OptionProperty(None, allownone=True,
|
|
||||||
options=['Primary', 'Secondary', 'Hint',
|
|
||||||
'Error', 'Custom'])
|
|
||||||
''' Color theme for title text and icon '''
|
|
||||||
|
|
||||||
title_color = ListProperty(None, allownone=True)
|
|
||||||
''' Color for title text and icon if `title_theme_color` is Custom '''
|
|
||||||
|
|
||||||
background_color = ListProperty(None, allownone=True)
|
|
||||||
''' Color for the background of the accordian item title in rgba format.
|
|
||||||
'''
|
|
||||||
|
|
||||||
divider_color = ListProperty(None, allownone=True)
|
|
||||||
''' Color for dividers between different titles in rgba format
|
|
||||||
To remove the divider set a color with an alpha of 0.
|
|
||||||
'''
|
|
||||||
|
|
||||||
indicator_color = ListProperty(None, allownone=True)
|
|
||||||
''' Color for the indicator on the side of the active item in rgba format
|
|
||||||
To remove the indicator set a color with an alpha of 0.
|
|
||||||
'''
|
|
||||||
|
|
||||||
font_style = OptionProperty(
|
|
||||||
'Subhead', options=['Body1', 'Body2', 'Caption', 'Subhead', 'Title',
|
|
||||||
'Headline', 'Display1', 'Display2', 'Display3',
|
|
||||||
'Display4', 'Button', 'Icon'])
|
|
||||||
''' Font style to use for the title text '''
|
|
||||||
|
|
||||||
title_template = StringProperty('MDAccordionItemTitle')
|
|
||||||
''' Template to use for the title '''
|
|
||||||
|
|
||||||
icon = StringProperty(None,allownone=True)
|
|
||||||
''' Icon name to use when this item is expanded '''
|
|
||||||
|
|
||||||
icon_expanded = StringProperty('chevron-up')
|
|
||||||
''' Icon name to use when this item is expanded '''
|
|
||||||
|
|
||||||
icon_collapsed = StringProperty('chevron-down')
|
|
||||||
''' Icon name to use when this item is collapsed '''
|
|
||||||
|
|
||||||
|
|
||||||
Builder.load_string('''
|
|
||||||
#:import MDLabel kivymd.label.MDLabel
|
|
||||||
#:import md_icons kivymd.icon_definitions.md_icons
|
|
||||||
|
|
||||||
|
|
||||||
<MDAccordionItem>:
|
|
||||||
canvas.before:
|
|
||||||
Color:
|
|
||||||
rgba: self.background_color or self.theme_cls.primary_color
|
|
||||||
Rectangle:
|
|
||||||
size:self.size
|
|
||||||
pos:self.pos
|
|
||||||
|
|
||||||
PushMatrix
|
|
||||||
Translate:
|
|
||||||
xy: (dp(2),0) if self.orientation == 'vertical' else (0,dp(2))
|
|
||||||
canvas.after:
|
|
||||||
PopMatrix
|
|
||||||
Color:
|
|
||||||
rgba: self.divider_color or self.theme_cls.divider_color
|
|
||||||
Rectangle:
|
|
||||||
size:(dp(1),self.height) if self.orientation == 'horizontal' else (self.width,dp(1))
|
|
||||||
pos:self.pos
|
|
||||||
Color:
|
|
||||||
rgba: [0,0,0,0] if self.collapse else (self.indicator_color or self.theme_cls.accent_color)
|
|
||||||
Rectangle:
|
|
||||||
size:(dp(2),self.height) if self.orientation == 'vertical' else (self.width,dp(2))
|
|
||||||
pos:self.pos
|
|
||||||
|
|
||||||
[MDAccordionItemTitle@MDAccordionItemTitleLayout]:
|
|
||||||
padding: '12dp'
|
|
||||||
spacing: '12dp'
|
|
||||||
orientation: 'horizontal' if ctx.item.orientation=='vertical' else 'vertical'
|
|
||||||
canvas:
|
|
||||||
PushMatrix
|
|
||||||
Translate:
|
|
||||||
xy: (-dp(2),0) if ctx.item.orientation == 'vertical' else (0,-dp(2))
|
|
||||||
|
|
||||||
Color:
|
|
||||||
rgba: self.background_color or self.theme_cls.primary_color
|
|
||||||
Rectangle:
|
|
||||||
size:self.size
|
|
||||||
pos:self.pos
|
|
||||||
|
|
||||||
canvas.after:
|
|
||||||
Color:
|
|
||||||
rgba: [0,0,0,0] if ctx.item.collapse else (ctx.item.indicator_color or self.theme_cls.accent_color)
|
|
||||||
Rectangle:
|
|
||||||
size:(dp(2),self.height) if ctx.item.orientation == 'vertical' else (self.width,dp(2))
|
|
||||||
pos:self.pos
|
|
||||||
PopMatrix
|
|
||||||
MDLabel:
|
|
||||||
id:_icon
|
|
||||||
theme_text_color:ctx.item.title_theme_color if ctx.item.icon else 'Custom'
|
|
||||||
text_color:ctx.item.title_color if ctx.item.icon else [0,0,0,0]
|
|
||||||
text: md_icons[ctx.item.icon if ctx.item.icon else 'menu']
|
|
||||||
font_style:'Icon'
|
|
||||||
size_hint: (None,1) if ctx.item.orientation == 'vertical' else (1,None)
|
|
||||||
size: ((self.texture_size[0],1) if ctx.item.orientation == 'vertical' else (1,self.texture_size[1])) \
|
|
||||||
if ctx.item.icon else (0,0)
|
|
||||||
text_size: (self.width, None) if ctx.item.orientation=='vertical' else (None,self.width)
|
|
||||||
canvas.before:
|
|
||||||
PushMatrix
|
|
||||||
Rotate:
|
|
||||||
angle: 90 if ctx.item.orientation == 'horizontal' else 0
|
|
||||||
origin: self.center
|
|
||||||
canvas.after:
|
|
||||||
PopMatrix
|
|
||||||
MDLabel:
|
|
||||||
id:_label
|
|
||||||
theme_text_color:ctx.item.title_theme_color
|
|
||||||
text_color:ctx.item.title_color
|
|
||||||
text: ctx.item.title
|
|
||||||
font_style:ctx.item.font_style
|
|
||||||
text_size: (self.width, None) if ctx.item.orientation=='vertical' else (None,self.width)
|
|
||||||
canvas.before:
|
|
||||||
PushMatrix
|
|
||||||
Rotate:
|
|
||||||
angle: 90 if ctx.item.orientation == 'horizontal' else 0
|
|
||||||
origin: self.center
|
|
||||||
canvas.after:
|
|
||||||
PopMatrix
|
|
||||||
|
|
||||||
MDLabel:
|
|
||||||
id:_expand_icon
|
|
||||||
theme_text_color:ctx.item.title_theme_color
|
|
||||||
text_color:ctx.item.title_color
|
|
||||||
font_style:'Icon'
|
|
||||||
size_hint: (None,1) if ctx.item.orientation == 'vertical' else (1,None)
|
|
||||||
size: (self.texture_size[0],1) if ctx.item.orientation == 'vertical' else (1,self.texture_size[1])
|
|
||||||
text:md_icons[ctx.item.icon_collapsed if ctx.item.collapse else ctx.item.icon_expanded]
|
|
||||||
halign: 'right' if ctx.item.orientation=='vertical' else 'center'
|
|
||||||
#valign: 'middle' if ctx.item.orientation=='vertical' else 'bottom'
|
|
||||||
canvas.before:
|
|
||||||
PushMatrix
|
|
||||||
Rotate:
|
|
||||||
angle: 90 if ctx.item.orientation == 'horizontal' else 0
|
|
||||||
origin:self.center
|
|
||||||
canvas.after:
|
|
||||||
PopMatrix
|
|
||||||
|
|
||||||
''')
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
from kivy.app import App
|
|
||||||
from kivymd.theming import ThemeManager
|
|
||||||
|
|
||||||
class AccordionApp(App):
|
|
||||||
theme_cls = ThemeManager()
|
|
||||||
|
|
||||||
def build(self):
|
|
||||||
# self.theme_cls.primary_palette = 'Indigo'
|
|
||||||
return Builder.load_string("""
|
|
||||||
#:import MDLabel kivymd.label.MDLabel
|
|
||||||
#:import MDList kivymd.list.MDList
|
|
||||||
#:import OneLineListItem kivymd.list.OneLineListItem
|
|
||||||
BoxLayout:
|
|
||||||
spacing: '64dp'
|
|
||||||
MDAccordion:
|
|
||||||
orientation:'vertical'
|
|
||||||
MDAccordionItem:
|
|
||||||
title:'Item 1'
|
|
||||||
icon: 'home'
|
|
||||||
ScrollView:
|
|
||||||
MDList:
|
|
||||||
OneLineListItem:
|
|
||||||
text: "Subitem 1"
|
|
||||||
theme_text_color: 'Custom'
|
|
||||||
text_color: [1,1,1,1]
|
|
||||||
OneLineListItem:
|
|
||||||
text: "Subitem 2"
|
|
||||||
theme_text_color: 'Custom'
|
|
||||||
text_color: [1,1,1,1]
|
|
||||||
OneLineListItem:
|
|
||||||
text: "Subitem 3"
|
|
||||||
theme_text_color: 'Custom'
|
|
||||||
text_color: [1,1,1,1]
|
|
||||||
MDAccordionItem:
|
|
||||||
title:'Item 2'
|
|
||||||
icon: 'globe'
|
|
||||||
ScrollView:
|
|
||||||
MDList:
|
|
||||||
OneLineListItem:
|
|
||||||
text: "Subitem 4"
|
|
||||||
theme_text_color: 'Custom'
|
|
||||||
text_color: [1,1,1,1]
|
|
||||||
OneLineListItem:
|
|
||||||
text: "Subitem 5"
|
|
||||||
theme_text_color: 'Custom'
|
|
||||||
text_color: [1,1,1,1]
|
|
||||||
OneLineListItem:
|
|
||||||
text: "Subitem 6"
|
|
||||||
theme_text_color: 'Custom'
|
|
||||||
text_color: [1,1,1,1]
|
|
||||||
MDAccordionItem:
|
|
||||||
title:'Item 3'
|
|
||||||
ScrollView:
|
|
||||||
MDList:
|
|
||||||
OneLineListItem:
|
|
||||||
text: "Subitem 7"
|
|
||||||
theme_text_color: 'Custom'
|
|
||||||
text_color: [1,1,1,1]
|
|
||||||
OneLineListItem:
|
|
||||||
text: "Subitem 8"
|
|
||||||
theme_text_color: 'Custom'
|
|
||||||
text_color: [1,1,1,1]
|
|
||||||
OneLineListItem:
|
|
||||||
text: "Subitem 9"
|
|
||||||
theme_text_color: 'Custom'
|
|
||||||
text_color: [1,1,1,1]
|
|
||||||
MDAccordion:
|
|
||||||
orientation:'horizontal'
|
|
||||||
MDAccordionItem:
|
|
||||||
title:'Item 1'
|
|
||||||
icon: 'home'
|
|
||||||
MDLabel:
|
|
||||||
text:'Content 1'
|
|
||||||
theme_text_color:'Primary'
|
|
||||||
MDAccordionItem:
|
|
||||||
title:'Item 2'
|
|
||||||
MDLabel:
|
|
||||||
text:'Content 2'
|
|
||||||
theme_text_color:'Primary'
|
|
||||||
MDAccordionItem:
|
|
||||||
title:'Item 3'
|
|
||||||
MDLabel:
|
|
||||||
text:'Content 3'
|
|
||||||
theme_text_color:'Primary'
|
|
||||||
""")
|
|
||||||
|
|
||||||
|
|
||||||
AccordionApp().run()
|
|
|
@ -1,23 +0,0 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
from kivy.lang import Builder
|
|
||||||
from kivy.properties import BoundedNumericProperty, ReferenceListProperty
|
|
||||||
from kivy.uix.widget import Widget
|
|
||||||
|
|
||||||
Builder.load_string('''
|
|
||||||
<BackgroundColorBehavior>
|
|
||||||
canvas:
|
|
||||||
Color:
|
|
||||||
rgba: self.background_color
|
|
||||||
Rectangle:
|
|
||||||
size: self.size
|
|
||||||
pos: self.pos
|
|
||||||
''')
|
|
||||||
|
|
||||||
|
|
||||||
class BackgroundColorBehavior(Widget):
|
|
||||||
r = BoundedNumericProperty(1., min=0., max=1.)
|
|
||||||
g = BoundedNumericProperty(1., min=0., max=1.)
|
|
||||||
b = BoundedNumericProperty(1., min=0., max=1.)
|
|
||||||
a = BoundedNumericProperty(0., min=0., max=1.)
|
|
||||||
|
|
||||||
background_color = ReferenceListProperty(r, g, b, a)
|
|
|
@ -1,211 +0,0 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
'''
|
|
||||||
Bottom Sheets
|
|
||||||
=============
|
|
||||||
|
|
||||||
`Material Design spec Bottom Sheets page <http://www.google.com/design/spec/components/bottom-sheets.html>`_
|
|
||||||
|
|
||||||
In this module there's the :class:`MDBottomSheet` class which will let you implement your own Material Design Bottom Sheets, and there are two classes called :class:`MDListBottomSheet` and :class:`MDGridBottomSheet` implementing the ones mentioned in the spec.
|
|
||||||
|
|
||||||
Examples
|
|
||||||
--------
|
|
||||||
|
|
||||||
.. note::
|
|
||||||
|
|
||||||
These widgets are designed to be called from Python code only.
|
|
||||||
|
|
||||||
For :class:`MDListBottomSheet`:
|
|
||||||
|
|
||||||
.. code-block:: python
|
|
||||||
|
|
||||||
bs = MDListBottomSheet()
|
|
||||||
bs.add_item("Here's an item with text only", lambda x: x)
|
|
||||||
bs.add_item("Here's an item with an icon", lambda x: x, icon='md-cast')
|
|
||||||
bs.add_item("Here's another!", lambda x: x, icon='md-nfc')
|
|
||||||
bs.open()
|
|
||||||
|
|
||||||
For :class:`MDListBottomSheet`:
|
|
||||||
|
|
||||||
.. code-block:: python
|
|
||||||
|
|
||||||
bs = MDGridBottomSheet()
|
|
||||||
bs.add_item("Facebook", lambda x: x, icon_src='./assets/facebook-box.png')
|
|
||||||
bs.add_item("YouTube", lambda x: x, icon_src='./assets/youtube-play.png')
|
|
||||||
bs.add_item("Twitter", lambda x: x, icon_src='./assets/twitter.png')
|
|
||||||
bs.add_item("Da Cloud", lambda x: x, icon_src='./assets/cloud-upload.png')
|
|
||||||
bs.add_item("Camera", lambda x: x, icon_src='./assets/camera.png')
|
|
||||||
bs.open()
|
|
||||||
|
|
||||||
API
|
|
||||||
---
|
|
||||||
'''
|
|
||||||
from kivy.clock import Clock
|
|
||||||
from kivy.lang import Builder
|
|
||||||
from kivy.metrics import dp
|
|
||||||
from kivy.properties import ObjectProperty, StringProperty
|
|
||||||
from kivy.uix.behaviors import ButtonBehavior
|
|
||||||
from kivy.uix.boxlayout import BoxLayout
|
|
||||||
from kivy.uix.floatlayout import FloatLayout
|
|
||||||
from kivy.uix.gridlayout import GridLayout
|
|
||||||
from kivy.uix.modalview import ModalView
|
|
||||||
from kivy.uix.scrollview import ScrollView
|
|
||||||
from kivymd.backgroundcolorbehavior import BackgroundColorBehavior
|
|
||||||
from kivymd.label import MDLabel
|
|
||||||
from kivymd.list import MDList, OneLineListItem, ILeftBody, \
|
|
||||||
OneLineIconListItem
|
|
||||||
from kivymd.theming import ThemableBehavior
|
|
||||||
|
|
||||||
Builder.load_string('''
|
|
||||||
<MDBottomSheet>
|
|
||||||
background: 'atlas://data/images/defaulttheme/action_group_disabled'
|
|
||||||
background_color: 0,0,0,.8
|
|
||||||
sv: sv
|
|
||||||
upper_padding: upper_padding
|
|
||||||
gl_content: gl_content
|
|
||||||
ScrollView:
|
|
||||||
id: sv
|
|
||||||
do_scroll_x: False
|
|
||||||
BoxLayout:
|
|
||||||
size_hint_y: None
|
|
||||||
orientation: 'vertical'
|
|
||||||
padding: 0,1,0,0
|
|
||||||
height: upper_padding.height + gl_content.height + 1 # +1 to allow overscroll
|
|
||||||
BsPadding:
|
|
||||||
id: upper_padding
|
|
||||||
size_hint_y: None
|
|
||||||
height: root.height - min(root.width * 9 / 16, gl_content.height)
|
|
||||||
on_release: root.dismiss()
|
|
||||||
BottomSheetContent:
|
|
||||||
id: gl_content
|
|
||||||
size_hint_y: None
|
|
||||||
background_color: root.theme_cls.bg_normal
|
|
||||||
cols: 1
|
|
||||||
''')
|
|
||||||
|
|
||||||
|
|
||||||
class BsPadding(ButtonBehavior, FloatLayout):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class BottomSheetContent(BackgroundColorBehavior, GridLayout):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class MDBottomSheet(ThemableBehavior, ModalView):
|
|
||||||
sv = ObjectProperty()
|
|
||||||
upper_padding = ObjectProperty()
|
|
||||||
gl_content = ObjectProperty()
|
|
||||||
dismiss_zone_scroll = 1000 # Arbitrary high number
|
|
||||||
|
|
||||||
def open(self, *largs):
|
|
||||||
super(MDBottomSheet, self).open(*largs)
|
|
||||||
Clock.schedule_once(self.set_dismiss_zone, 0)
|
|
||||||
|
|
||||||
def set_dismiss_zone(self, *largs):
|
|
||||||
# Scroll to right below overscroll threshold:
|
|
||||||
self.sv.scroll_y = 1 - self.sv.convert_distance_to_scroll(0, 1)[1]
|
|
||||||
|
|
||||||
# This is a line where m (slope) is 1/6 and b (y-intercept) is 80:
|
|
||||||
self.dismiss_zone_scroll = self.sv.convert_distance_to_scroll(
|
|
||||||
0, (self.height - self.upper_padding.height) * (1 / 6.0) + 80)[
|
|
||||||
1]
|
|
||||||
# Uncomment next line if the limit should just be half of
|
|
||||||
# visible content on open (capped by specs to 16 units to width/9:
|
|
||||||
# self.dismiss_zone_scroll = (self.sv.convert_distance_to_scroll(
|
|
||||||
# 0, self.height - self.upper_padding.height)[1] * 0.50)
|
|
||||||
|
|
||||||
# Check if user has overscrolled enough to dismiss bottom sheet:
|
|
||||||
self.sv.bind(on_scroll_stop=self.check_if_scrolled_to_death)
|
|
||||||
|
|
||||||
def check_if_scrolled_to_death(self, *largs):
|
|
||||||
if self.sv.scroll_y >= 1 + self.dismiss_zone_scroll:
|
|
||||||
self.dismiss()
|
|
||||||
|
|
||||||
def add_widget(self, widget, index=0):
|
|
||||||
if type(widget) == ScrollView:
|
|
||||||
super(MDBottomSheet, self).add_widget(widget, index)
|
|
||||||
else:
|
|
||||||
self.gl_content.add_widget(widget,index)
|
|
||||||
|
|
||||||
|
|
||||||
Builder.load_string('''
|
|
||||||
#:import md_icons kivymd.icon_definitions.md_icons
|
|
||||||
<ListBSIconLeft>
|
|
||||||
font_style: 'Icon'
|
|
||||||
text: u"{}".format(md_icons[root.icon])
|
|
||||||
halign: 'center'
|
|
||||||
theme_text_color: 'Primary'
|
|
||||||
valign: 'middle'
|
|
||||||
''')
|
|
||||||
|
|
||||||
|
|
||||||
class ListBSIconLeft(ILeftBody, MDLabel):
|
|
||||||
icon = StringProperty()
|
|
||||||
|
|
||||||
|
|
||||||
class MDListBottomSheet(MDBottomSheet):
|
|
||||||
mlist = ObjectProperty()
|
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
|
||||||
super(MDListBottomSheet, self).__init__(**kwargs)
|
|
||||||
self.mlist = MDList()
|
|
||||||
self.gl_content.add_widget(self.mlist)
|
|
||||||
Clock.schedule_once(self.resize_content_layout, 0)
|
|
||||||
|
|
||||||
def resize_content_layout(self, *largs):
|
|
||||||
self.gl_content.height = self.mlist.height
|
|
||||||
|
|
||||||
def add_item(self, text, callback, icon=None):
|
|
||||||
if icon:
|
|
||||||
item = OneLineIconListItem(text=text, on_release=callback)
|
|
||||||
item.add_widget(ListBSIconLeft(icon=icon))
|
|
||||||
else:
|
|
||||||
item = OneLineListItem(text=text, on_release=callback)
|
|
||||||
|
|
||||||
item.bind(on_release=lambda x: self.dismiss())
|
|
||||||
self.mlist.add_widget(item)
|
|
||||||
|
|
||||||
|
|
||||||
Builder.load_string('''
|
|
||||||
<GridBSItem>
|
|
||||||
orientation: 'vertical'
|
|
||||||
padding: 0, dp(24), 0, 0
|
|
||||||
size_hint_y: None
|
|
||||||
size: dp(64), dp(96)
|
|
||||||
BoxLayout:
|
|
||||||
padding: dp(8), 0, dp(8), dp(8)
|
|
||||||
size_hint_y: None
|
|
||||||
height: dp(48)
|
|
||||||
Image:
|
|
||||||
source: root.source
|
|
||||||
MDLabel:
|
|
||||||
font_style: 'Caption'
|
|
||||||
theme_text_color: 'Secondary'
|
|
||||||
text: root.caption
|
|
||||||
halign: 'center'
|
|
||||||
''')
|
|
||||||
|
|
||||||
|
|
||||||
class GridBSItem(ButtonBehavior, BoxLayout):
|
|
||||||
source = StringProperty()
|
|
||||||
|
|
||||||
caption = StringProperty()
|
|
||||||
|
|
||||||
|
|
||||||
class MDGridBottomSheet(MDBottomSheet):
|
|
||||||
def __init__(self, **kwargs):
|
|
||||||
super(MDGridBottomSheet, self).__init__(**kwargs)
|
|
||||||
self.gl_content.padding = (dp(16), 0, dp(16), dp(24))
|
|
||||||
self.gl_content.height = dp(24)
|
|
||||||
self.gl_content.cols = 3
|
|
||||||
|
|
||||||
def add_item(self, text, callback, icon_src):
|
|
||||||
item = GridBSItem(
|
|
||||||
caption=text,
|
|
||||||
on_release=callback,
|
|
||||||
source=icon_src
|
|
||||||
)
|
|
||||||
item.bind(on_release=lambda x: self.dismiss())
|
|
||||||
if len(self.gl_content.children) % 3 == 0:
|
|
||||||
self.gl_content.height += dp(96)
|
|
||||||
self.gl_content.add_widget(item)
|
|
|
@ -1,453 +0,0 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
'''
|
|
||||||
Buttons
|
|
||||||
=======
|
|
||||||
|
|
||||||
`Material Design spec, Buttons page <https://www.google.com/design/spec/components/buttons.html>`_
|
|
||||||
|
|
||||||
`Material Design spec, Buttons: Floating Action Button page <https://www.google.com/design/spec/components/buttons-floating-action-button.html>`_
|
|
||||||
|
|
||||||
TO-DO: DOCUMENT MODULE
|
|
||||||
'''
|
|
||||||
from kivy.clock import Clock
|
|
||||||
from kivy.lang import Builder
|
|
||||||
from kivy.metrics import dp
|
|
||||||
from kivy.utils import get_color_from_hex
|
|
||||||
from kivy.properties import StringProperty, BoundedNumericProperty, \
|
|
||||||
ListProperty, AliasProperty, BooleanProperty, NumericProperty, \
|
|
||||||
OptionProperty
|
|
||||||
from kivy.uix.anchorlayout import AnchorLayout
|
|
||||||
from kivy.uix.behaviors import ButtonBehavior
|
|
||||||
from kivy.uix.boxlayout import BoxLayout
|
|
||||||
from kivy.animation import Animation
|
|
||||||
from kivymd.backgroundcolorbehavior import BackgroundColorBehavior
|
|
||||||
from kivymd.ripplebehavior import CircularRippleBehavior, \
|
|
||||||
RectangularRippleBehavior
|
|
||||||
from kivymd.elevationbehavior import ElevationBehavior, \
|
|
||||||
RoundElevationBehavior
|
|
||||||
from kivymd.theming import ThemableBehavior
|
|
||||||
from kivymd.color_definitions import colors
|
|
||||||
|
|
||||||
Builder.load_string('''
|
|
||||||
#:import md_icons kivymd.icon_definitions.md_icons
|
|
||||||
#:import colors kivymd.color_definitions.colors
|
|
||||||
#:import MDLabel kivymd.label.MDLabel
|
|
||||||
<MDIconButton>
|
|
||||||
size_hint: (None, None)
|
|
||||||
size: (dp(48), dp(48))
|
|
||||||
padding: dp(12)
|
|
||||||
theme_text_color: 'Primary'
|
|
||||||
MDLabel:
|
|
||||||
id: _label
|
|
||||||
font_style: 'Icon'
|
|
||||||
text: u"{}".format(md_icons[root.icon])
|
|
||||||
halign: 'center'
|
|
||||||
theme_text_color: root.theme_text_color
|
|
||||||
text_color: root.text_color
|
|
||||||
opposite_colors: root.opposite_colors
|
|
||||||
valign: 'middle'
|
|
||||||
|
|
||||||
<MDFlatButton>
|
|
||||||
canvas:
|
|
||||||
Color:
|
|
||||||
#rgba: self.background_color if self.state == 'normal' else self._bg_color_down
|
|
||||||
rgba: self._current_button_color
|
|
||||||
Rectangle:
|
|
||||||
size: self.size
|
|
||||||
pos: self.pos
|
|
||||||
size_hint: (None, None)
|
|
||||||
height: dp(36)
|
|
||||||
width: _label.texture_size[0] + dp(16)
|
|
||||||
padding: (dp(8), 0)
|
|
||||||
theme_text_color: 'Custom'
|
|
||||||
text_color: root.theme_cls.primary_color
|
|
||||||
MDLabel:
|
|
||||||
id: _label
|
|
||||||
text: root._text
|
|
||||||
font_style: 'Button'
|
|
||||||
size_hint_x: None
|
|
||||||
text_size: (None, root.height)
|
|
||||||
height: self.texture_size[1]
|
|
||||||
theme_text_color: root.theme_text_color
|
|
||||||
text_color: root.text_color
|
|
||||||
valign: 'middle'
|
|
||||||
halign: 'center'
|
|
||||||
opposite_colors: root.opposite_colors
|
|
||||||
|
|
||||||
<MDRaisedButton>:
|
|
||||||
canvas:
|
|
||||||
Clear
|
|
||||||
Color:
|
|
||||||
rgba: self.background_color_disabled if self.disabled else \
|
|
||||||
(self.background_color if self.state == 'normal' else self.background_color_down)
|
|
||||||
Rectangle:
|
|
||||||
size: self.size
|
|
||||||
pos: self.pos
|
|
||||||
|
|
||||||
anchor_x: 'center'
|
|
||||||
anchor_y: 'center'
|
|
||||||
background_color: root.theme_cls.primary_color
|
|
||||||
background_color_down: root.theme_cls.primary_dark
|
|
||||||
background_color_disabled: root.theme_cls.divider_color
|
|
||||||
theme_text_color: 'Primary'
|
|
||||||
MDLabel:
|
|
||||||
id: label
|
|
||||||
font_style: 'Button'
|
|
||||||
text: root._text
|
|
||||||
size_hint: None, None
|
|
||||||
width: root.width
|
|
||||||
text_size: self.width, None
|
|
||||||
height: self.texture_size[1]
|
|
||||||
theme_text_color: root.theme_text_color
|
|
||||||
text_color: root.text_color
|
|
||||||
opposite_colors: root.opposite_colors
|
|
||||||
disabled: root.disabled
|
|
||||||
halign: 'center'
|
|
||||||
valign: 'middle'
|
|
||||||
|
|
||||||
<MDFloatingActionButton>:
|
|
||||||
canvas:
|
|
||||||
Clear
|
|
||||||
Color:
|
|
||||||
rgba: self.background_color_disabled if self.disabled else \
|
|
||||||
(self.background_color if self.state == 'normal' else self.background_color_down)
|
|
||||||
Ellipse:
|
|
||||||
size: self.size
|
|
||||||
pos: self.pos
|
|
||||||
|
|
||||||
anchor_x: 'center'
|
|
||||||
anchor_y: 'center'
|
|
||||||
background_color: root.theme_cls.accent_color
|
|
||||||
background_color_down: root.theme_cls.accent_dark
|
|
||||||
background_color_disabled: root.theme_cls.divider_color
|
|
||||||
theme_text_color: 'Primary'
|
|
||||||
MDLabel:
|
|
||||||
id: label
|
|
||||||
font_style: 'Icon'
|
|
||||||
text: u"{}".format(md_icons[root.icon])
|
|
||||||
size_hint: None, None
|
|
||||||
size: dp(24), dp(24)
|
|
||||||
text_size: self.size
|
|
||||||
theme_text_color: root.theme_text_color
|
|
||||||
text_color: root.text_color
|
|
||||||
opposite_colors: root.opposite_colors
|
|
||||||
disabled: root.disabled
|
|
||||||
halign: 'center'
|
|
||||||
valign: 'middle'
|
|
||||||
''')
|
|
||||||
|
|
||||||
|
|
||||||
class MDIconButton(CircularRippleBehavior, ButtonBehavior, BoxLayout):
|
|
||||||
icon = StringProperty('circle')
|
|
||||||
theme_text_color = OptionProperty(None, allownone=True,
|
|
||||||
options=['Primary', 'Secondary', 'Hint',
|
|
||||||
'Error', 'Custom'])
|
|
||||||
text_color = ListProperty(None, allownone=True)
|
|
||||||
opposite_colors = BooleanProperty(False)
|
|
||||||
|
|
||||||
|
|
||||||
class MDFlatButton(ThemableBehavior, RectangularRippleBehavior,
|
|
||||||
ButtonBehavior, BackgroundColorBehavior, AnchorLayout):
|
|
||||||
width = BoundedNumericProperty(dp(64), min=dp(64), max=None,
|
|
||||||
errorhandler=lambda x: dp(64))
|
|
||||||
|
|
||||||
text_color = ListProperty()
|
|
||||||
|
|
||||||
text = StringProperty('')
|
|
||||||
theme_text_color = OptionProperty(None, allownone=True,
|
|
||||||
options=['Primary', 'Secondary', 'Hint',
|
|
||||||
'Error', 'Custom'])
|
|
||||||
text_color = ListProperty(None, allownone=True)
|
|
||||||
|
|
||||||
_text = StringProperty('')
|
|
||||||
_bg_color_down = ListProperty([0, 0, 0, 0])
|
|
||||||
_current_button_color = ListProperty([0, 0, 0, 0])
|
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
|
||||||
super(MDFlatButton, self).__init__(**kwargs)
|
|
||||||
self._current_button_color = self.background_color
|
|
||||||
self._bg_color_down = get_color_from_hex(
|
|
||||||
colors[self.theme_cls.theme_style]['FlatButtonDown'])
|
|
||||||
|
|
||||||
Clock.schedule_once(lambda x: self.ids._label.bind(
|
|
||||||
texture_size=self.update_width_on_label_texture))
|
|
||||||
|
|
||||||
def update_width_on_label_texture(self, instance, value):
|
|
||||||
self.ids._label.width = value[0]
|
|
||||||
|
|
||||||
def on_text(self, instance, value):
|
|
||||||
self._text = value.upper()
|
|
||||||
|
|
||||||
def on_touch_down(self, touch):
|
|
||||||
if touch.is_mouse_scrolling:
|
|
||||||
return False
|
|
||||||
elif not self.collide_point(touch.x, touch.y):
|
|
||||||
return False
|
|
||||||
elif self in touch.ud:
|
|
||||||
return False
|
|
||||||
elif self.disabled:
|
|
||||||
return False
|
|
||||||
else:
|
|
||||||
self.fade_bg = Animation(duration=.2, _current_button_color=get_color_from_hex(
|
|
||||||
colors[self.theme_cls.theme_style]['FlatButtonDown']))
|
|
||||||
self.fade_bg.start(self)
|
|
||||||
return super(MDFlatButton, self).on_touch_down(touch)
|
|
||||||
|
|
||||||
def on_touch_up(self, touch):
|
|
||||||
if touch.grab_current is self:
|
|
||||||
self.fade_bg.stop_property(self, '_current_button_color')
|
|
||||||
Animation(duration=.05, _current_button_color=self.background_color).start(self)
|
|
||||||
return super(MDFlatButton, self).on_touch_up(touch)
|
|
||||||
|
|
||||||
|
|
||||||
class MDRaisedButton(ThemableBehavior, RectangularRippleBehavior,
|
|
||||||
ElevationBehavior, ButtonBehavior,
|
|
||||||
AnchorLayout):
|
|
||||||
_bg_color_down = ListProperty([])
|
|
||||||
background_color = ListProperty()
|
|
||||||
background_color_down = ListProperty()
|
|
||||||
background_color_disabled = ListProperty()
|
|
||||||
theme_text_color = OptionProperty(None, allownone=True,
|
|
||||||
options=['Primary', 'Secondary', 'Hint',
|
|
||||||
'Error', 'Custom'])
|
|
||||||
text_color = ListProperty(None, allownone=True)
|
|
||||||
|
|
||||||
def _get_bg_color_down(self):
|
|
||||||
return self._bg_color_down
|
|
||||||
|
|
||||||
def _set_bg_color_down(self, color, alpha=None):
|
|
||||||
if len(color) == 2:
|
|
||||||
self._bg_color_down = get_color_from_hex(
|
|
||||||
colors[color[0]][color[1]])
|
|
||||||
if alpha:
|
|
||||||
self._bg_color_down[3] = alpha
|
|
||||||
elif len(color) == 4:
|
|
||||||
self._bg_color_down = color
|
|
||||||
|
|
||||||
background_color_down = AliasProperty(_get_bg_color_down,
|
|
||||||
_set_bg_color_down,
|
|
||||||
bind=('_bg_color_down',))
|
|
||||||
|
|
||||||
_bg_color_disabled = ListProperty([])
|
|
||||||
|
|
||||||
def _get_bg_color_disabled(self):
|
|
||||||
return self._bg_color_disabled
|
|
||||||
|
|
||||||
def _set_bg_color_disabled(self, color, alpha=None):
|
|
||||||
if len(color) == 2:
|
|
||||||
self._bg_color_disabled = get_color_from_hex(
|
|
||||||
colors[color[0]][color[1]])
|
|
||||||
if alpha:
|
|
||||||
self._bg_color_disabled[3] = alpha
|
|
||||||
elif len(color) == 4:
|
|
||||||
self._bg_color_disabled = color
|
|
||||||
|
|
||||||
background_color_disabled = AliasProperty(_get_bg_color_disabled,
|
|
||||||
_set_bg_color_disabled,
|
|
||||||
bind=('_bg_color_disabled',))
|
|
||||||
|
|
||||||
_elev_norm = NumericProperty(2)
|
|
||||||
|
|
||||||
def _get_elev_norm(self):
|
|
||||||
return self._elev_norm
|
|
||||||
|
|
||||||
def _set_elev_norm(self, value):
|
|
||||||
self._elev_norm = value if value <= 12 else 12
|
|
||||||
self._elev_raised = (value + 6) if value + 6 <= 12 else 12
|
|
||||||
self.elevation = self._elev_norm
|
|
||||||
|
|
||||||
elevation_normal = AliasProperty(_get_elev_norm, _set_elev_norm,
|
|
||||||
bind=('_elev_norm',))
|
|
||||||
|
|
||||||
_elev_raised = NumericProperty(8)
|
|
||||||
|
|
||||||
def _get_elev_raised(self):
|
|
||||||
return self._elev_raised
|
|
||||||
|
|
||||||
def _set_elev_raised(self, value):
|
|
||||||
self._elev_raised = value if value + self._elev_norm <= 12 else 12
|
|
||||||
|
|
||||||
elevation_raised = AliasProperty(_get_elev_raised, _set_elev_raised,
|
|
||||||
bind=('_elev_raised',))
|
|
||||||
|
|
||||||
text = StringProperty()
|
|
||||||
|
|
||||||
_text = StringProperty()
|
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
|
||||||
super(MDRaisedButton, self).__init__(**kwargs)
|
|
||||||
self.elevation_press_anim = Animation(elevation=self.elevation_raised,
|
|
||||||
duration=.2, t='out_quad')
|
|
||||||
self.elevation_release_anim = Animation(
|
|
||||||
elevation=self.elevation_normal, duration=.2, t='out_quad')
|
|
||||||
|
|
||||||
def on_disabled(self, instance, value):
|
|
||||||
if value:
|
|
||||||
self.elevation = 0
|
|
||||||
else:
|
|
||||||
self.elevation = self.elevation_normal
|
|
||||||
super(MDRaisedButton, self).on_disabled(instance, value)
|
|
||||||
|
|
||||||
def on_touch_down(self, touch):
|
|
||||||
if not self.disabled:
|
|
||||||
if touch.is_mouse_scrolling:
|
|
||||||
return False
|
|
||||||
if not self.collide_point(touch.x, touch.y):
|
|
||||||
return False
|
|
||||||
if self in touch.ud:
|
|
||||||
return False
|
|
||||||
Animation.cancel_all(self, 'elevation')
|
|
||||||
self.elevation_press_anim.start(self)
|
|
||||||
return super(MDRaisedButton, self).on_touch_down(touch)
|
|
||||||
|
|
||||||
def on_touch_up(self, touch):
|
|
||||||
if not self.disabled:
|
|
||||||
if touch.grab_current is not self:
|
|
||||||
return super(ButtonBehavior, self).on_touch_up(touch)
|
|
||||||
Animation.cancel_all(self, 'elevation')
|
|
||||||
self.elevation_release_anim.start(self)
|
|
||||||
else:
|
|
||||||
Animation.cancel_all(self, 'elevation')
|
|
||||||
self.elevation = 0
|
|
||||||
return super(MDRaisedButton, self).on_touch_up(touch)
|
|
||||||
|
|
||||||
def on_text(self, instance, text):
|
|
||||||
self._text = text.upper()
|
|
||||||
|
|
||||||
def on__elev_norm(self, instance, value):
|
|
||||||
self.elevation_release_anim = Animation(elevation=value,
|
|
||||||
duration=.2, t='out_quad')
|
|
||||||
|
|
||||||
def on__elev_raised(self, instance, value):
|
|
||||||
self.elevation_press_anim = Animation(elevation=value,
|
|
||||||
duration=.2, t='out_quad')
|
|
||||||
|
|
||||||
|
|
||||||
class MDFloatingActionButton(ThemableBehavior, CircularRippleBehavior,
|
|
||||||
RoundElevationBehavior, ButtonBehavior,
|
|
||||||
AnchorLayout):
|
|
||||||
_bg_color_down = ListProperty([])
|
|
||||||
background_color = ListProperty()
|
|
||||||
background_color_down = ListProperty()
|
|
||||||
background_color_disabled = ListProperty()
|
|
||||||
theme_text_color = OptionProperty(None, allownone=True,
|
|
||||||
options=['Primary', 'Secondary', 'Hint',
|
|
||||||
'Error', 'Custom'])
|
|
||||||
text_color = ListProperty(None, allownone=True)
|
|
||||||
|
|
||||||
def _get_bg_color_down(self):
|
|
||||||
return self._bg_color_down
|
|
||||||
|
|
||||||
def _set_bg_color_down(self, color, alpha=None):
|
|
||||||
if len(color) == 2:
|
|
||||||
self._bg_color_down = get_color_from_hex(
|
|
||||||
colors[color[0]][color[1]])
|
|
||||||
if alpha:
|
|
||||||
self._bg_color_down[3] = alpha
|
|
||||||
elif len(color) == 4:
|
|
||||||
self._bg_color_down = color
|
|
||||||
|
|
||||||
background_color_down = AliasProperty(_get_bg_color_down,
|
|
||||||
_set_bg_color_down,
|
|
||||||
bind=('_bg_color_down',))
|
|
||||||
|
|
||||||
_bg_color_disabled = ListProperty([])
|
|
||||||
|
|
||||||
def _get_bg_color_disabled(self):
|
|
||||||
return self._bg_color_disabled
|
|
||||||
|
|
||||||
def _set_bg_color_disabled(self, color, alpha=None):
|
|
||||||
if len(color) == 2:
|
|
||||||
self._bg_color_disabled = get_color_from_hex(
|
|
||||||
colors[color[0]][color[1]])
|
|
||||||
if alpha:
|
|
||||||
self._bg_color_disabled[3] = alpha
|
|
||||||
elif len(color) == 4:
|
|
||||||
self._bg_color_disabled = color
|
|
||||||
|
|
||||||
background_color_disabled = AliasProperty(_get_bg_color_disabled,
|
|
||||||
_set_bg_color_disabled,
|
|
||||||
bind=('_bg_color_disabled',))
|
|
||||||
icon = StringProperty('android')
|
|
||||||
|
|
||||||
_elev_norm = NumericProperty(6)
|
|
||||||
|
|
||||||
def _get_elev_norm(self):
|
|
||||||
return self._elev_norm
|
|
||||||
|
|
||||||
def _set_elev_norm(self, value):
|
|
||||||
self._elev_norm = value if value <= 12 else 12
|
|
||||||
self._elev_raised = (value + 6) if value + 6 <= 12 else 12
|
|
||||||
self.elevation = self._elev_norm
|
|
||||||
|
|
||||||
elevation_normal = AliasProperty(_get_elev_norm, _set_elev_norm,
|
|
||||||
bind=('_elev_norm',))
|
|
||||||
|
|
||||||
# _elev_raised = NumericProperty(12)
|
|
||||||
_elev_raised = NumericProperty(6)
|
|
||||||
|
|
||||||
def _get_elev_raised(self):
|
|
||||||
return self._elev_raised
|
|
||||||
|
|
||||||
def _set_elev_raised(self, value):
|
|
||||||
self._elev_raised = value if value + self._elev_norm <= 12 else 12
|
|
||||||
|
|
||||||
elevation_raised = AliasProperty(_get_elev_raised, _set_elev_raised,
|
|
||||||
bind=('_elev_raised',))
|
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
|
||||||
if self.elevation_raised == 0 and self.elevation_normal + 6 <= 12:
|
|
||||||
self.elevation_raised = self.elevation_normal + 6
|
|
||||||
elif self.elevation_raised == 0:
|
|
||||||
self.elevation_raised = 12
|
|
||||||
|
|
||||||
super(MDFloatingActionButton, self).__init__(**kwargs)
|
|
||||||
|
|
||||||
self.elevation_press_anim = Animation(elevation=self.elevation_raised,
|
|
||||||
duration=.2, t='out_quad')
|
|
||||||
self.elevation_release_anim = Animation(
|
|
||||||
elevation=self.elevation_normal, duration=.2, t='out_quad')
|
|
||||||
|
|
||||||
def _set_ellipse(self, instance, value):
|
|
||||||
ellipse = self.ellipse
|
|
||||||
ripple_rad = self.ripple_rad
|
|
||||||
|
|
||||||
ellipse.size = (ripple_rad, ripple_rad)
|
|
||||||
ellipse.pos = (self.center_x - ripple_rad / 2.,
|
|
||||||
self.center_y - ripple_rad / 2.)
|
|
||||||
|
|
||||||
def on_disabled(self, instance, value):
|
|
||||||
super(MDFloatingActionButton, self).on_disabled(instance, value)
|
|
||||||
if self.disabled:
|
|
||||||
self.elevation = 0
|
|
||||||
else:
|
|
||||||
self.elevation = self.elevation_normal
|
|
||||||
|
|
||||||
def on_touch_down(self, touch):
|
|
||||||
if not self.disabled:
|
|
||||||
if touch.is_mouse_scrolling:
|
|
||||||
return False
|
|
||||||
if not self.collide_point(touch.x, touch.y):
|
|
||||||
return False
|
|
||||||
if self in touch.ud:
|
|
||||||
return False
|
|
||||||
self.elevation_press_anim.stop(self)
|
|
||||||
self.elevation_press_anim.start(self)
|
|
||||||
return super(MDFloatingActionButton, self).on_touch_down(touch)
|
|
||||||
|
|
||||||
def on_touch_up(self, touch):
|
|
||||||
if not self.disabled:
|
|
||||||
if touch.grab_current is not self:
|
|
||||||
return super(ButtonBehavior, self).on_touch_up(touch)
|
|
||||||
self.elevation_release_anim.stop(self)
|
|
||||||
self.elevation_release_anim.start(self)
|
|
||||||
return super(MDFloatingActionButton, self).on_touch_up(touch)
|
|
||||||
|
|
||||||
def on_elevation_normal(self, instance, value):
|
|
||||||
self.elevation = value
|
|
||||||
|
|
||||||
def on_elevation_raised(self, instance, value):
|
|
||||||
if self.elevation_raised == 0 and self.elevation_normal + 6 <= 12:
|
|
||||||
self.elevation_raised = self.elevation_normal + 6
|
|
||||||
elif self.elevation_raised == 0:
|
|
||||||
self.elevation_raised = 12
|
|
|
@ -1,58 +0,0 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
from kivy.lang import Builder
|
|
||||||
from kivy.properties import BoundedNumericProperty, ReferenceListProperty, ListProperty,BooleanProperty
|
|
||||||
from kivy.uix.boxlayout import BoxLayout
|
|
||||||
from kivymd.elevationbehavior import ElevationBehavior
|
|
||||||
from kivymd.theming import ThemableBehavior
|
|
||||||
from kivy.metrics import dp
|
|
||||||
from kivy.uix.widget import Widget
|
|
||||||
|
|
||||||
Builder.load_string('''
|
|
||||||
<MDCard>
|
|
||||||
canvas:
|
|
||||||
Color:
|
|
||||||
rgba: self.background_color
|
|
||||||
RoundedRectangle:
|
|
||||||
size: self.size
|
|
||||||
pos: self.pos
|
|
||||||
radius: [self.border_radius]
|
|
||||||
Color:
|
|
||||||
rgba: self.theme_cls.divider_color
|
|
||||||
a: self.border_color_a
|
|
||||||
Line:
|
|
||||||
rounded_rectangle: (self.pos[0],self.pos[1],self.size[0],self.size[1],self.border_radius)
|
|
||||||
background_color: self.theme_cls.bg_light
|
|
||||||
|
|
||||||
<MDSeparator>
|
|
||||||
canvas:
|
|
||||||
Color:
|
|
||||||
rgba: self.theme_cls.divider_color
|
|
||||||
Rectangle:
|
|
||||||
size: self.size
|
|
||||||
pos: self.pos
|
|
||||||
''')
|
|
||||||
|
|
||||||
|
|
||||||
class MDSeparator(ThemableBehavior, BoxLayout):
|
|
||||||
""" A separator line """
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
super(MDSeparator, self).__init__(*args, **kwargs)
|
|
||||||
self.on_orientation()
|
|
||||||
|
|
||||||
def on_orientation(self,*args):
|
|
||||||
self.size_hint = (1, None) if self.orientation == 'horizontal' else (None, 1)
|
|
||||||
if self.orientation == 'horizontal':
|
|
||||||
self.height = dp(1)
|
|
||||||
else:
|
|
||||||
self.width = dp(1)
|
|
||||||
|
|
||||||
|
|
||||||
class MDCard(ThemableBehavior, ElevationBehavior, BoxLayout):
|
|
||||||
r = BoundedNumericProperty(1., min=0., max=1.)
|
|
||||||
g = BoundedNumericProperty(1., min=0., max=1.)
|
|
||||||
b = BoundedNumericProperty(1., min=0., max=1.)
|
|
||||||
a = BoundedNumericProperty(0., min=0., max=1.)
|
|
||||||
|
|
||||||
border_radius = BoundedNumericProperty(dp(3),min=0)
|
|
||||||
border_color_a = BoundedNumericProperty(0, min=0., max=1.)
|
|
||||||
background_color = ReferenceListProperty(r, g, b, a)
|
|
|
@ -1,360 +0,0 @@
|
||||||
colors = {
|
|
||||||
'Pink': {
|
|
||||||
'50': 'fce4ec',
|
|
||||||
'100': 'f8bbd0',
|
|
||||||
'200': 'f48fb1',
|
|
||||||
'300': 'f06292',
|
|
||||||
'400': 'ec407a',
|
|
||||||
'500': 'e91e63',
|
|
||||||
'600': 'd81b60',
|
|
||||||
'700': 'C2185B',
|
|
||||||
'800': 'ad1457',
|
|
||||||
'900': '88e4ff',
|
|
||||||
'A100': 'ff80ab',
|
|
||||||
'A400': 'F50057',
|
|
||||||
'A700': 'c51162',
|
|
||||||
'A200': 'ff4081'
|
|
||||||
},
|
|
||||||
|
|
||||||
'Blue': {
|
|
||||||
'200': '90caf9',
|
|
||||||
'900': '0D47A1',
|
|
||||||
'600': '1e88e5',
|
|
||||||
'A100': '82b1ff',
|
|
||||||
'300': '64b5f6',
|
|
||||||
'A400': '2979ff',
|
|
||||||
'700': '1976d2',
|
|
||||||
'50': 'e3f2fd',
|
|
||||||
'A700': '2962ff',
|
|
||||||
'400': '42a5f5',
|
|
||||||
'100': 'bbdefb',
|
|
||||||
'800': '1565c0',
|
|
||||||
'A200': '448aff',
|
|
||||||
'500': '2196f3'
|
|
||||||
},
|
|
||||||
|
|
||||||
'Indigo': {
|
|
||||||
'200': '9fa8da',
|
|
||||||
'900': '1a237e',
|
|
||||||
'600': '3949ab',
|
|
||||||
'A100': '8c9eff',
|
|
||||||
'300': '7986cb',
|
|
||||||
'A400': '3d5afe',
|
|
||||||
'700': '303f9f',
|
|
||||||
'50': 'e8eaf6',
|
|
||||||
'A700': '304ffe',
|
|
||||||
'400': '5c6bc0',
|
|
||||||
'100': 'c5cae9',
|
|
||||||
'800': '283593',
|
|
||||||
'A200': '536dfe',
|
|
||||||
'500': '3f51b5'
|
|
||||||
},
|
|
||||||
|
|
||||||
'BlueGrey': {
|
|
||||||
'200': 'b0bec5',
|
|
||||||
'900': '263238',
|
|
||||||
'600': '546e7a',
|
|
||||||
'300': '90a4ae',
|
|
||||||
'700': '455a64',
|
|
||||||
'50': 'eceff1',
|
|
||||||
'400': '78909c',
|
|
||||||
'100': 'cfd8dc',
|
|
||||||
'800': '37474f',
|
|
||||||
'500': '607d8b'
|
|
||||||
},
|
|
||||||
|
|
||||||
'Brown': {
|
|
||||||
'200': 'bcaaa4',
|
|
||||||
'900': '3e2723',
|
|
||||||
'600': '6d4c41',
|
|
||||||
'300': 'a1887f',
|
|
||||||
'700': '5d4037',
|
|
||||||
'50': 'efebe9',
|
|
||||||
'400': '8d6e63',
|
|
||||||
'100': 'd7ccc8',
|
|
||||||
'800': '4e342e',
|
|
||||||
'500': '795548'
|
|
||||||
},
|
|
||||||
|
|
||||||
'LightBlue': {
|
|
||||||
'200': '81d4fa',
|
|
||||||
'900': '01579B',
|
|
||||||
'600': '039BE5',
|
|
||||||
'A100': '80d8ff',
|
|
||||||
'300': '4fc3f7',
|
|
||||||
'A400': '00B0FF',
|
|
||||||
'700': '0288D1',
|
|
||||||
'50': 'e1f5fe',
|
|
||||||
'A700': '0091EA',
|
|
||||||
'400': '29b6f6',
|
|
||||||
'100': 'b3e5fc',
|
|
||||||
'800': '0277BD',
|
|
||||||
'A200': '40c4ff',
|
|
||||||
'500': '03A9F4'
|
|
||||||
},
|
|
||||||
|
|
||||||
'Purple': {
|
|
||||||
'200': 'ce93d8',
|
|
||||||
'900': '4a148c',
|
|
||||||
'600': '8e24aa',
|
|
||||||
'A100': 'ea80fc',
|
|
||||||
'300': 'ba68c8',
|
|
||||||
'A400': 'D500F9',
|
|
||||||
'700': '7b1fa2',
|
|
||||||
'50': 'f3e5f5',
|
|
||||||
'A700': 'AA00FF',
|
|
||||||
'400': 'ab47bc',
|
|
||||||
'100': 'e1bee7',
|
|
||||||
'800': '6a1b9a',
|
|
||||||
'A200': 'e040fb',
|
|
||||||
'500': '9c27b0'
|
|
||||||
},
|
|
||||||
|
|
||||||
'Grey': {
|
|
||||||
'200': 'eeeeee',
|
|
||||||
'900': '212121',
|
|
||||||
'600': '757575',
|
|
||||||
'300': 'e0e0e0',
|
|
||||||
'700': '616161',
|
|
||||||
'50': 'fafafa',
|
|
||||||
'400': 'bdbdbd',
|
|
||||||
'100': 'f5f5f5',
|
|
||||||
'800': '424242',
|
|
||||||
'500': '9e9e9e'
|
|
||||||
},
|
|
||||||
|
|
||||||
'Yellow': {
|
|
||||||
'200': 'fff59d',
|
|
||||||
'900': 'f57f17',
|
|
||||||
'600': 'fdd835',
|
|
||||||
'A100': 'ffff8d',
|
|
||||||
'300': 'fff176',
|
|
||||||
'A400': 'FFEA00',
|
|
||||||
'700': 'fbc02d',
|
|
||||||
'50': 'fffde7',
|
|
||||||
'A700': 'FFD600',
|
|
||||||
'400': 'ffee58',
|
|
||||||
'100': 'fff9c4',
|
|
||||||
'800': 'f9a825',
|
|
||||||
'A200': 'FFFF00',
|
|
||||||
'500': 'ffeb3b'
|
|
||||||
},
|
|
||||||
|
|
||||||
'LightGreen': {
|
|
||||||
'200': 'c5e1a5',
|
|
||||||
'900': '33691e',
|
|
||||||
'600': '7cb342',
|
|
||||||
'A100': 'ccff90',
|
|
||||||
'300': 'aed581',
|
|
||||||
'A400': '76FF03',
|
|
||||||
'700': '689f38',
|
|
||||||
'50': 'f1f8e9',
|
|
||||||
'A700': '64dd17',
|
|
||||||
'400': '9ccc65',
|
|
||||||
'100': 'dcedc8',
|
|
||||||
'800': '558b2f',
|
|
||||||
'A200': 'b2ff59',
|
|
||||||
'500': '8bc34a'
|
|
||||||
},
|
|
||||||
|
|
||||||
'DeepOrange': {
|
|
||||||
'200': 'ffab91',
|
|
||||||
'900': 'bf36c',
|
|
||||||
'600': 'f4511e',
|
|
||||||
'A100': 'ff9e80',
|
|
||||||
'300': 'ff8a65',
|
|
||||||
'A400': 'FF3D00',
|
|
||||||
'700': 'e64a19',
|
|
||||||
'50': 'fbe9e7',
|
|
||||||
'A700': 'DD2C00',
|
|
||||||
'400': 'ff7043',
|
|
||||||
'100': 'ffccbc',
|
|
||||||
'800': 'd84315',
|
|
||||||
'A200': 'ff6e40',
|
|
||||||
'500': 'ff5722'
|
|
||||||
},
|
|
||||||
|
|
||||||
'Green': {
|
|
||||||
'200': 'a5d6a7',
|
|
||||||
'900': '1b5e20',
|
|
||||||
'600': '43a047',
|
|
||||||
'A100': 'b9f6ca',
|
|
||||||
'300': '81c784',
|
|
||||||
'A400': '00E676',
|
|
||||||
'700': '388e3c',
|
|
||||||
'50': 'e8f5e9',
|
|
||||||
'A700': '00C853',
|
|
||||||
'400': '66bb6a',
|
|
||||||
'100': 'c8e6c9',
|
|
||||||
'800': '2e7d32',
|
|
||||||
'A200': '69f0ae',
|
|
||||||
'500': '4caf50'
|
|
||||||
},
|
|
||||||
|
|
||||||
'Red': {
|
|
||||||
'200': 'ef9a9a',
|
|
||||||
'900': 'b71c1c',
|
|
||||||
'600': 'e53935',
|
|
||||||
'A100': 'ff8a80',
|
|
||||||
'300': 'e57373',
|
|
||||||
'A400': 'ff1744',
|
|
||||||
'700': 'd32f2f',
|
|
||||||
'50': 'ffebee',
|
|
||||||
'A700': 'd50000',
|
|
||||||
'400': 'ef5350',
|
|
||||||
'100': 'ffcdd2',
|
|
||||||
'800': 'c62828',
|
|
||||||
'A200': 'ff5252',
|
|
||||||
'500': 'f44336'
|
|
||||||
},
|
|
||||||
|
|
||||||
'Teal': {
|
|
||||||
'200': '80cbc4',
|
|
||||||
'900': '004D40',
|
|
||||||
'600': '00897B',
|
|
||||||
'A100': 'a7ffeb',
|
|
||||||
'300': '4db6ac',
|
|
||||||
'A400': '1de9b6',
|
|
||||||
'700': '00796B',
|
|
||||||
'50': 'e0f2f1',
|
|
||||||
'A700': '00BFA5',
|
|
||||||
'400': '26a69a',
|
|
||||||
'100': 'b2dfdb',
|
|
||||||
'800': '00695C',
|
|
||||||
'A200': '64ffda',
|
|
||||||
'500': '009688'
|
|
||||||
},
|
|
||||||
|
|
||||||
'Orange': {
|
|
||||||
'200': 'ffcc80',
|
|
||||||
'900': 'E65100',
|
|
||||||
'600': 'FB8C00',
|
|
||||||
'A100': 'ffd180',
|
|
||||||
'300': 'ffb74d',
|
|
||||||
'A400': 'FF9100',
|
|
||||||
'700': 'F57C00',
|
|
||||||
'50': 'fff3e0',
|
|
||||||
'A700': 'FF6D00',
|
|
||||||
'400': 'ffa726',
|
|
||||||
'100': 'ffe0b2',
|
|
||||||
'800': 'EF6C00',
|
|
||||||
'A200': 'ffab40',
|
|
||||||
'500': 'FF9800'
|
|
||||||
},
|
|
||||||
|
|
||||||
'Cyan': {
|
|
||||||
'200': '80deea',
|
|
||||||
'900': '006064',
|
|
||||||
'600': '00ACC1',
|
|
||||||
'A100': '84ffff',
|
|
||||||
'300': '4dd0e1',
|
|
||||||
'A400': '00E5FF',
|
|
||||||
'700': '0097A7',
|
|
||||||
'50': 'e0f7fa',
|
|
||||||
'A700': '00B8D4',
|
|
||||||
'400': '26c6da',
|
|
||||||
'100': 'b2ebf2',
|
|
||||||
'800': '00838F',
|
|
||||||
'A200': '18ffff',
|
|
||||||
'500': '00BCD4'
|
|
||||||
},
|
|
||||||
|
|
||||||
'Amber': {
|
|
||||||
'200': 'ffe082',
|
|
||||||
'900': 'FF6F00',
|
|
||||||
'600': 'FFB300',
|
|
||||||
'A100': 'ffe57f',
|
|
||||||
'300': 'ffd54f',
|
|
||||||
'A400': 'FFC400',
|
|
||||||
'700': 'FFA000',
|
|
||||||
'50': 'fff8e1',
|
|
||||||
'A700': 'FFAB00',
|
|
||||||
'400': 'ffca28',
|
|
||||||
'100': 'ffecb3',
|
|
||||||
'800': 'FF8F00',
|
|
||||||
'A200': 'ffd740',
|
|
||||||
'500': 'FFC107'
|
|
||||||
},
|
|
||||||
|
|
||||||
'DeepPurple': {
|
|
||||||
'200': 'b39ddb',
|
|
||||||
'900': '311b92',
|
|
||||||
'600': '5e35b1',
|
|
||||||
'A100': 'b388ff',
|
|
||||||
'300': '9575cd',
|
|
||||||
'A400': '651fff',
|
|
||||||
'700': '512da8',
|
|
||||||
'50': 'ede7f6',
|
|
||||||
'A700': '6200EA',
|
|
||||||
'400': '7e57c2',
|
|
||||||
'100': 'd1c4e9',
|
|
||||||
'800': '4527a0',
|
|
||||||
'A200': '7c4dff',
|
|
||||||
'500': '673ab7'
|
|
||||||
},
|
|
||||||
|
|
||||||
'Lime': {
|
|
||||||
'200': 'e6ee9c',
|
|
||||||
'900': '827717',
|
|
||||||
'600': 'c0ca33',
|
|
||||||
'A100': 'f4ff81',
|
|
||||||
'300': 'dce775',
|
|
||||||
'A400': 'C6FF00',
|
|
||||||
'700': 'afb42b',
|
|
||||||
'50': 'f9fbe7',
|
|
||||||
'A700': 'AEEA00',
|
|
||||||
'400': 'd4e157',
|
|
||||||
'100': 'f0f4c3',
|
|
||||||
'800': '9e9d24',
|
|
||||||
'A200': 'eeff41',
|
|
||||||
'500': 'cddc39'
|
|
||||||
},
|
|
||||||
|
|
||||||
'Light': {
|
|
||||||
'StatusBar': 'E0E0E0',
|
|
||||||
'AppBar': 'F5F5F5',
|
|
||||||
'Background': 'FAFAFA',
|
|
||||||
'CardsDialogs': 'FFFFFF',
|
|
||||||
'FlatButtonDown': 'cccccc'
|
|
||||||
},
|
|
||||||
|
|
||||||
'Dark': {
|
|
||||||
'StatusBar': '000000',
|
|
||||||
'AppBar': '212121',
|
|
||||||
'Background': '303030',
|
|
||||||
'CardsDialogs': '424242',
|
|
||||||
'FlatButtonDown': '999999'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
light_colors = {
|
|
||||||
'Pink': ['50' '100', '200', 'A100'],
|
|
||||||
'Blue': ['50' '100', '200', '300', '400', 'A100'],
|
|
||||||
'Indigo': ['50' '100', '200', 'A100'],
|
|
||||||
'BlueGrey': ['50' '100', '200', '300'],
|
|
||||||
'Brown': ['50' '100', '200'],
|
|
||||||
'LightBlue': ['50' '100', '200', '300', '400', '500', 'A100', 'A200',
|
|
||||||
'A400'],
|
|
||||||
'Purple': ['50' '100', '200', 'A100'],
|
|
||||||
'Grey': ['50' '100', '200', '300', '400', '500'],
|
|
||||||
'Yellow': ['50' '100', '200', '300', '400', '500', '600', '700', '800',
|
|
||||||
'900', 'A100', 'A200', 'A400', 'A700'],
|
|
||||||
'LightGreen': ['50' '100', '200', '300', '400', '500', '600', 'A100',
|
|
||||||
'A200', 'A400', 'A700'],
|
|
||||||
'DeepOrange': ['50' '100', '200', '300', '400', 'A100', 'A200'],
|
|
||||||
'Green': ['50' '100', '200', '300', '400', '500', 'A100', 'A200', 'A400',
|
|
||||||
'A700'],
|
|
||||||
'Red': ['50' '100', '200', '300', 'A100'],
|
|
||||||
'Teal': ['50' '100', '200', '300', '400', 'A100', 'A200', 'A400', 'A700'],
|
|
||||||
'Orange': ['50' '100', '200', '300', '400', '500', '600', '700', 'A100',
|
|
||||||
'A200', 'A400', 'A700'],
|
|
||||||
'Cyan': ['50' '100', '200', '300', '400', '500', '600', 'A100', 'A200',
|
|
||||||
'A400', 'A700'],
|
|
||||||
'Amber': ['50' '100', '200', '300', '400', '500', '600', '700', '800',
|
|
||||||
'900', 'A100', 'A200', 'A400', 'A700'],
|
|
||||||
'DeepPurple': ['50' '100', '200', 'A100'],
|
|
||||||
'Lime': ['50' '100', '200', '300', '400', '500', '600', '700', '800',
|
|
||||||
'A100', 'A200', 'A400', 'A700'],
|
|
||||||
'Dark': [],
|
|
||||||
'Light': ['White', 'MainBackground', 'DialogBackground']
|
|
||||||
}
|
|
|
@ -1,325 +0,0 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
from kivy.lang import Builder
|
|
||||||
from kivy.uix.modalview import ModalView
|
|
||||||
from kivymd.label import MDLabel
|
|
||||||
from kivymd.theming import ThemableBehavior
|
|
||||||
from kivy.uix.floatlayout import FloatLayout
|
|
||||||
from kivymd.elevationbehavior import ElevationBehavior
|
|
||||||
import calendar
|
|
||||||
from datetime import date
|
|
||||||
import datetime
|
|
||||||
from kivy.properties import StringProperty, NumericProperty, ObjectProperty, \
|
|
||||||
BooleanProperty
|
|
||||||
from kivy.uix.anchorlayout import AnchorLayout
|
|
||||||
from kivy.uix.behaviors import ButtonBehavior
|
|
||||||
from kivymd.ripplebehavior import CircularRippleBehavior
|
|
||||||
from kivy.clock import Clock
|
|
||||||
from kivy.core.window import Window
|
|
||||||
|
|
||||||
Builder.load_string("""
|
|
||||||
#:import calendar calendar
|
|
||||||
<MDDatePicker>
|
|
||||||
cal_layout: cal_layout
|
|
||||||
|
|
||||||
size_hint: (None, None)
|
|
||||||
size: [dp(328), dp(484)] if self.theme_cls.device_orientation == 'portrait'\
|
|
||||||
else [dp(512), dp(304)]
|
|
||||||
pos_hint: {'center_x': .5, 'center_y': .5}
|
|
||||||
canvas:
|
|
||||||
Color:
|
|
||||||
rgb: app.theme_cls.primary_color
|
|
||||||
Rectangle:
|
|
||||||
size: [dp(328), dp(96)] if self.theme_cls.device_orientation == 'portrait'\
|
|
||||||
else [dp(168), dp(304)]
|
|
||||||
pos: [root.pos[0], root.pos[1] + root.height-dp(96)] if self.theme_cls.device_orientation == 'portrait'\
|
|
||||||
else [root.pos[0], root.pos[1] + root.height-dp(304)]
|
|
||||||
Color:
|
|
||||||
rgb: app.theme_cls.bg_normal
|
|
||||||
Rectangle:
|
|
||||||
size: [dp(328), dp(484)-dp(96)] if self.theme_cls.device_orientation == 'portrait'\
|
|
||||||
else [dp(344), dp(304)]
|
|
||||||
pos: [root.pos[0], root.pos[1] + root.height-dp(96)-(dp(484)-dp(96))]\
|
|
||||||
if self.theme_cls.device_orientation == 'portrait' else [root.pos[0]+dp(168), root.pos[1]] #+dp(334)
|
|
||||||
MDLabel:
|
|
||||||
id: label_full_date
|
|
||||||
font_style: 'Display1'
|
|
||||||
text_color: 1, 1, 1, 1
|
|
||||||
theme_text_color: 'Custom'
|
|
||||||
size_hint: (None, None)
|
|
||||||
size: [root.width, dp(30)] if root.theme_cls.device_orientation == 'portrait'\
|
|
||||||
else [dp(168), dp(30)]
|
|
||||||
pos: [root.pos[0]+dp(23), root.pos[1] + root.height - dp(74)] \
|
|
||||||
if root.theme_cls.device_orientation == 'portrait' \
|
|
||||||
else [root.pos[0]+dp(3), root.pos[1] + dp(214)]
|
|
||||||
line_height: 0.84
|
|
||||||
valign: 'middle'
|
|
||||||
text_size: [root.width, None] if root.theme_cls.device_orientation == 'portrait'\
|
|
||||||
else [dp(149), None]
|
|
||||||
bold: True
|
|
||||||
text: root.fmt_lbl_date(root.sel_year, root.sel_month, root.sel_day, root.theme_cls.device_orientation)
|
|
||||||
MDLabel:
|
|
||||||
id: label_year
|
|
||||||
font_style: 'Subhead'
|
|
||||||
text_color: 1, 1, 1, 1
|
|
||||||
theme_text_color: 'Custom'
|
|
||||||
size_hint: (None, None)
|
|
||||||
size: root.width, dp(30)
|
|
||||||
pos: (root.pos[0]+dp(23), root.pos[1]+root.height-dp(40)) if root.theme_cls.device_orientation == 'portrait'\
|
|
||||||
else (root.pos[0]+dp(16), root.pos[1]+root.height-dp(41))
|
|
||||||
valign: 'middle'
|
|
||||||
text: str(root.sel_year)
|
|
||||||
GridLayout:
|
|
||||||
id: cal_layout
|
|
||||||
cols: 7
|
|
||||||
size: (dp(44*7), dp(40*7)) if root.theme_cls.device_orientation == 'portrait'\
|
|
||||||
else (dp(46*7), dp(32*7))
|
|
||||||
col_default_width: dp(42) if root.theme_cls.device_orientation == 'portrait'\
|
|
||||||
else dp(39)
|
|
||||||
size_hint: (None, None)
|
|
||||||
padding: (dp(2), 0) if root.theme_cls.device_orientation == 'portrait'\
|
|
||||||
else (dp(7), 0)
|
|
||||||
spacing: (dp(2), 0) if root.theme_cls.device_orientation == 'portrait'\
|
|
||||||
else (dp(7), 0)
|
|
||||||
pos: (root.pos[0]+dp(10), root.pos[1]+dp(60)) if root.theme_cls.device_orientation == 'portrait'\
|
|
||||||
else (root.pos[0]+dp(168)+dp(8), root.pos[1]+dp(48))
|
|
||||||
MDLabel:
|
|
||||||
id: label_month_selector
|
|
||||||
font_style: 'Body2'
|
|
||||||
text: calendar.month_name[root.month].capitalize() + ' ' + str(root.year)
|
|
||||||
size_hint: (None, None)
|
|
||||||
size: root.width, dp(30)
|
|
||||||
pos: root.pos
|
|
||||||
theme_text_color: 'Primary'
|
|
||||||
pos_hint: {'center_x': 0.5, 'center_y': 0.75} if self.theme_cls.device_orientation == 'portrait'\
|
|
||||||
else {'center_x': 0.67, 'center_y': 0.915}
|
|
||||||
valign: "middle"
|
|
||||||
halign: "center"
|
|
||||||
MDIconButton:
|
|
||||||
icon: 'chevron-left'
|
|
||||||
theme_text_color: 'Secondary'
|
|
||||||
pos_hint: {'center_x': 0.09, 'center_y': 0.745} if root.theme_cls.device_orientation == 'portrait'\
|
|
||||||
else {'center_x': 0.39, 'center_y': 0.925}
|
|
||||||
on_release: root.change_month('prev')
|
|
||||||
MDIconButton:
|
|
||||||
icon: 'chevron-right'
|
|
||||||
theme_text_color: 'Secondary'
|
|
||||||
pos_hint: {'center_x': 0.92, 'center_y': 0.745} if root.theme_cls.device_orientation == 'portrait'\
|
|
||||||
else {'center_x': 0.94, 'center_y': 0.925}
|
|
||||||
on_release: root.change_month('next')
|
|
||||||
MDFlatButton:
|
|
||||||
pos: root.pos[0]+root.size[0]-dp(72)*2, root.pos[1] + dp(7)
|
|
||||||
text: "Cancel"
|
|
||||||
on_release: root.dismiss()
|
|
||||||
MDFlatButton:
|
|
||||||
pos: root.pos[0]+root.size[0]-dp(72), root.pos[1] + dp(7)
|
|
||||||
text: "OK"
|
|
||||||
on_release: root.ok_click()
|
|
||||||
|
|
||||||
<DayButton>
|
|
||||||
size_hint: None, None
|
|
||||||
size: (dp(40), dp(40)) if root.theme_cls.device_orientation == 'portrait'\
|
|
||||||
else (dp(32), dp(32))
|
|
||||||
MDLabel:
|
|
||||||
font_style: 'Caption'
|
|
||||||
theme_text_color: 'Custom' if root.is_today and not root.is_selected else 'Primary'
|
|
||||||
text_color: root.theme_cls.primary_color
|
|
||||||
opposite_colors: root.is_selected if root.owner.sel_month == root.owner.month \
|
|
||||||
and root.owner.sel_year == root.owner.year and str(self.text) == str(root.owner.sel_day) else False
|
|
||||||
size_hint_x: None
|
|
||||||
valign: 'middle'
|
|
||||||
halign: 'center'
|
|
||||||
text: root.text
|
|
||||||
|
|
||||||
<WeekdayLabel>
|
|
||||||
font_style: 'Caption'
|
|
||||||
theme_text_color: 'Secondary'
|
|
||||||
size: (dp(40), dp(40)) if root.theme_cls.device_orientation == 'portrait'\
|
|
||||||
else (dp(32), dp(32))
|
|
||||||
size_hint: None, None
|
|
||||||
text_size: self.size
|
|
||||||
valign: 'middle' if root.theme_cls.device_orientation == 'portrait' else 'bottom'
|
|
||||||
halign: 'center'
|
|
||||||
|
|
||||||
<DaySelector>
|
|
||||||
size: (dp(40), dp(40)) if root.theme_cls.device_orientation == 'portrait'\
|
|
||||||
else (dp(32), dp(32))
|
|
||||||
size_hint: (None, None)
|
|
||||||
canvas:
|
|
||||||
Color:
|
|
||||||
rgba: self.theme_cls.primary_color if self.shown else [0, 0, 0, 0]
|
|
||||||
Ellipse:
|
|
||||||
size: (dp(40), dp(40)) if root.theme_cls.device_orientation == 'portrait'\
|
|
||||||
else (dp(32), dp(32))
|
|
||||||
pos: self.pos if root.theme_cls.device_orientation == 'portrait'\
|
|
||||||
else [self.pos[0] + dp(3), self.pos[1]]
|
|
||||||
""")
|
|
||||||
|
|
||||||
|
|
||||||
class DaySelector(ThemableBehavior, AnchorLayout):
|
|
||||||
shown = BooleanProperty(False)
|
|
||||||
|
|
||||||
def __init__(self, parent):
|
|
||||||
super(DaySelector, self).__init__()
|
|
||||||
self.parent_class = parent
|
|
||||||
self.parent_class.add_widget(self, index=7)
|
|
||||||
self.selected_widget = None
|
|
||||||
Window.bind(on_resize=self.move_resize)
|
|
||||||
|
|
||||||
def update(self):
|
|
||||||
parent = self.parent_class
|
|
||||||
if parent.sel_month == parent.month and parent.sel_year == parent.year:
|
|
||||||
self.shown = True
|
|
||||||
else:
|
|
||||||
self.shown = False
|
|
||||||
|
|
||||||
def set_widget(self, widget):
|
|
||||||
self.selected_widget = widget
|
|
||||||
self.pos = widget.pos
|
|
||||||
self.move_resize(do_again=True)
|
|
||||||
self.update()
|
|
||||||
|
|
||||||
def move_resize(self, window=None, width=None, height=None, do_again=True):
|
|
||||||
self.pos = self.selected_widget.pos
|
|
||||||
if do_again:
|
|
||||||
Clock.schedule_once(lambda x: self.move_resize(do_again=False), 0.01)
|
|
||||||
|
|
||||||
|
|
||||||
class DayButton(ThemableBehavior, CircularRippleBehavior, ButtonBehavior,
|
|
||||||
AnchorLayout):
|
|
||||||
text = StringProperty()
|
|
||||||
owner = ObjectProperty()
|
|
||||||
is_today = BooleanProperty(False)
|
|
||||||
is_selected = BooleanProperty(False)
|
|
||||||
|
|
||||||
def on_release(self):
|
|
||||||
self.owner.set_selected_widget(self)
|
|
||||||
|
|
||||||
|
|
||||||
class WeekdayLabel(MDLabel):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class MDDatePicker(FloatLayout, ThemableBehavior, ElevationBehavior,
|
|
||||||
ModalView):
|
|
||||||
_sel_day_widget = ObjectProperty()
|
|
||||||
cal_list = None
|
|
||||||
cal_layout = ObjectProperty()
|
|
||||||
sel_year = NumericProperty()
|
|
||||||
sel_month = NumericProperty()
|
|
||||||
sel_day = NumericProperty()
|
|
||||||
day = NumericProperty()
|
|
||||||
month = NumericProperty()
|
|
||||||
year = NumericProperty()
|
|
||||||
today = date.today()
|
|
||||||
callback = ObjectProperty()
|
|
||||||
|
|
||||||
class SetDateError(Exception):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def __init__(self, callback, year=None, month=None, day=None,
|
|
||||||
firstweekday=0,
|
|
||||||
**kwargs):
|
|
||||||
self.callback = callback
|
|
||||||
self.cal = calendar.Calendar(firstweekday)
|
|
||||||
self.sel_year = year if year else self.today.year
|
|
||||||
self.sel_month = month if month else self.today.month
|
|
||||||
self.sel_day = day if day else self.today.day
|
|
||||||
self.month = self.sel_month
|
|
||||||
self.year = self.sel_year
|
|
||||||
self.day = self.sel_day
|
|
||||||
super(MDDatePicker, self).__init__(**kwargs)
|
|
||||||
self.selector = DaySelector(parent=self)
|
|
||||||
self.generate_cal_widgets()
|
|
||||||
self.update_cal_matrix(self.sel_year, self.sel_month)
|
|
||||||
self.set_month_day(self.sel_day)
|
|
||||||
self.selector.update()
|
|
||||||
|
|
||||||
def ok_click(self):
|
|
||||||
self.callback(date(self.sel_year, self.sel_month, self.sel_day))
|
|
||||||
self.dismiss()
|
|
||||||
|
|
||||||
def fmt_lbl_date(self, year, month, day, orientation):
|
|
||||||
d = datetime.date(int(year), int(month), int(day))
|
|
||||||
separator = '\n' if orientation == 'landscape' else ' '
|
|
||||||
return d.strftime('%a,').capitalize() + separator + d.strftime(
|
|
||||||
'%b').capitalize() + ' ' + str(day).lstrip('0')
|
|
||||||
|
|
||||||
def set_date(self, year, month, day):
|
|
||||||
try:
|
|
||||||
date(year, month, day)
|
|
||||||
except Exception as e:
|
|
||||||
print(e)
|
|
||||||
if str(e) == "day is out of range for month":
|
|
||||||
raise self.SetDateError(" Day %s day is out of range for month %s" % (day, month))
|
|
||||||
elif str(e) == "month must be in 1..12":
|
|
||||||
raise self.SetDateError("Month must be between 1 and 12, got %s" % month)
|
|
||||||
elif str(e) == "year is out of range":
|
|
||||||
raise self.SetDateError("Year must be between %s and %s, got %s" %
|
|
||||||
(datetime.MINYEAR, datetime.MAXYEAR, year))
|
|
||||||
else:
|
|
||||||
self.sel_year = year
|
|
||||||
self.sel_month = month
|
|
||||||
self.sel_day = day
|
|
||||||
self.month = self.sel_month
|
|
||||||
self.year = self.sel_year
|
|
||||||
self.day = self.sel_day
|
|
||||||
self.update_cal_matrix(self.sel_year, self.sel_month)
|
|
||||||
self.set_month_day(self.sel_day)
|
|
||||||
self.selector.update()
|
|
||||||
|
|
||||||
def set_selected_widget(self, widget):
|
|
||||||
if self._sel_day_widget:
|
|
||||||
self._sel_day_widget.is_selected = False
|
|
||||||
widget.is_selected = True
|
|
||||||
self.sel_month = int(self.month)
|
|
||||||
self.sel_year = int(self.year)
|
|
||||||
self.sel_day = int(widget.text)
|
|
||||||
self._sel_day_widget = widget
|
|
||||||
self.selector.set_widget(widget)
|
|
||||||
|
|
||||||
def set_month_day(self, day):
|
|
||||||
for idx in range(len(self.cal_list)):
|
|
||||||
if str(day) == str(self.cal_list[idx].text):
|
|
||||||
self._sel_day_widget = self.cal_list[idx]
|
|
||||||
self.sel_day = int(self.cal_list[idx].text)
|
|
||||||
if self._sel_day_widget:
|
|
||||||
self._sel_day_widget.is_selected = False
|
|
||||||
self._sel_day_widget = self.cal_list[idx]
|
|
||||||
self.cal_list[idx].is_selected = True
|
|
||||||
self.selector.set_widget(self.cal_list[idx])
|
|
||||||
|
|
||||||
def update_cal_matrix(self, year, month):
|
|
||||||
try:
|
|
||||||
dates = [x for x in self.cal.itermonthdates(year, month)]
|
|
||||||
except ValueError as e:
|
|
||||||
if str(e) == "year is out of range":
|
|
||||||
pass
|
|
||||||
else:
|
|
||||||
self.year = year
|
|
||||||
self.month = month
|
|
||||||
for idx in range(len(self.cal_list)):
|
|
||||||
if idx >= len(dates) or dates[idx].month != month:
|
|
||||||
self.cal_list[idx].disabled = True
|
|
||||||
self.cal_list[idx].text = ''
|
|
||||||
else:
|
|
||||||
self.cal_list[idx].disabled = False
|
|
||||||
self.cal_list[idx].text = str(dates[idx].day)
|
|
||||||
self.cal_list[idx].is_today = dates[idx] == self.today
|
|
||||||
self.selector.update()
|
|
||||||
|
|
||||||
def generate_cal_widgets(self):
|
|
||||||
cal_list = []
|
|
||||||
for i in calendar.day_abbr:
|
|
||||||
self.cal_layout.add_widget(WeekdayLabel(text=i[0].upper()))
|
|
||||||
for i in range(6 * 7): # 6 weeks, 7 days a week
|
|
||||||
db = DayButton(owner=self)
|
|
||||||
cal_list.append(db)
|
|
||||||
self.cal_layout.add_widget(db)
|
|
||||||
self.cal_list = cal_list
|
|
||||||
|
|
||||||
def change_month(self, operation):
|
|
||||||
op = 1 if operation is 'next' else -1
|
|
||||||
sl, sy = self.month, self.year
|
|
||||||
m = 12 if sl + op == 0 else 1 if sl + op == 13 else sl + op
|
|
||||||
y = sy - 1 if sl + op == 0 else sy + 1 if sl + op == 13 else sy
|
|
||||||
self.update_cal_matrix(y, m)
|
|
|
@ -1,176 +0,0 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
|
||||||
from kivy.lang import Builder
|
|
||||||
from kivy.properties import StringProperty, ObjectProperty, ListProperty
|
|
||||||
from kivy.metrics import dp
|
|
||||||
from kivy.uix.modalview import ModalView
|
|
||||||
from kivy.animation import Animation
|
|
||||||
from kivymd.theming import ThemableBehavior
|
|
||||||
from kivymd.elevationbehavior import ElevationBehavior
|
|
||||||
from kivymd.button import MDFlatButton
|
|
||||||
|
|
||||||
Builder.load_string('''
|
|
||||||
<MDDialog>:
|
|
||||||
canvas:
|
|
||||||
Color:
|
|
||||||
rgba: self.theme_cls.bg_light
|
|
||||||
Rectangle:
|
|
||||||
size: self.size
|
|
||||||
pos: self.pos
|
|
||||||
|
|
||||||
_container: container
|
|
||||||
_action_area: action_area
|
|
||||||
elevation: 12
|
|
||||||
GridLayout:
|
|
||||||
cols: 1
|
|
||||||
|
|
||||||
GridLayout:
|
|
||||||
cols: 1
|
|
||||||
padding: dp(24), dp(24), dp(24), 0
|
|
||||||
spacing: dp(20)
|
|
||||||
MDLabel:
|
|
||||||
text: root.title
|
|
||||||
font_style: 'Title'
|
|
||||||
theme_text_color: 'Primary'
|
|
||||||
halign: 'left'
|
|
||||||
valign: 'middle'
|
|
||||||
size_hint_y: None
|
|
||||||
text_size: self.width, None
|
|
||||||
height: self.texture_size[1]
|
|
||||||
|
|
||||||
BoxLayout:
|
|
||||||
id: container
|
|
||||||
|
|
||||||
AnchorLayout:
|
|
||||||
anchor_x: 'right'
|
|
||||||
anchor_y: 'center'
|
|
||||||
size_hint: 1, None
|
|
||||||
height: dp(48)
|
|
||||||
padding: dp(8), dp(8)
|
|
||||||
spacing: dp(4)
|
|
||||||
|
|
||||||
GridLayout:
|
|
||||||
id: action_area
|
|
||||||
rows: 1
|
|
||||||
size_hint: None, None if len(root._action_buttons) > 0 else 1
|
|
||||||
height: dp(36) if len(root._action_buttons) > 0 else 0
|
|
||||||
width: self.minimum_width
|
|
||||||
''')
|
|
||||||
|
|
||||||
|
|
||||||
class MDDialog(ThemableBehavior, ElevationBehavior, ModalView):
|
|
||||||
title = StringProperty('')
|
|
||||||
|
|
||||||
content = ObjectProperty(None)
|
|
||||||
|
|
||||||
background_color = ListProperty([0, 0, 0, .2])
|
|
||||||
|
|
||||||
_container = ObjectProperty()
|
|
||||||
_action_buttons = ListProperty([])
|
|
||||||
_action_area = ObjectProperty()
|
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
|
||||||
super(MDDialog, self).__init__(**kwargs)
|
|
||||||
self.bind(_action_buttons=self._update_action_buttons,
|
|
||||||
auto_dismiss=lambda *x: setattr(self.shadow, 'on_release',
|
|
||||||
self.shadow.dismiss if self.auto_dismiss else None))
|
|
||||||
|
|
||||||
def add_action_button(self, text, action=None):
|
|
||||||
"""Add an :class:`FlatButton` to the right of the action area.
|
|
||||||
|
|
||||||
:param icon: Unicode character for the icon
|
|
||||||
:type icon: str or None
|
|
||||||
:param action: Function set to trigger when on_release fires
|
|
||||||
:type action: function or None
|
|
||||||
"""
|
|
||||||
button = MDFlatButton(text=text,
|
|
||||||
size_hint=(None, None),
|
|
||||||
height=dp(36))
|
|
||||||
if action:
|
|
||||||
button.bind(on_release=action)
|
|
||||||
button.text_color = self.theme_cls.primary_color
|
|
||||||
button.background_color = self.theme_cls.bg_light
|
|
||||||
self._action_buttons.append(button)
|
|
||||||
|
|
||||||
def add_widget(self, widget):
|
|
||||||
if self._container:
|
|
||||||
if self.content:
|
|
||||||
raise PopupException(
|
|
||||||
'Popup can have only one widget as content')
|
|
||||||
self.content = widget
|
|
||||||
else:
|
|
||||||
super(MDDialog, self).add_widget(widget)
|
|
||||||
|
|
||||||
def open(self, *largs):
|
|
||||||
'''Show the view window from the :attr:`attach_to` widget. If set, it
|
|
||||||
will attach to the nearest window. If the widget is not attached to any
|
|
||||||
window, the view will attach to the global
|
|
||||||
:class:`~kivy.core.window.Window`.
|
|
||||||
'''
|
|
||||||
if self._window is not None:
|
|
||||||
Logger.warning('ModalView: you can only open once.')
|
|
||||||
return self
|
|
||||||
# search window
|
|
||||||
self._window = self._search_window()
|
|
||||||
if not self._window:
|
|
||||||
Logger.warning('ModalView: cannot open view, no window found.')
|
|
||||||
return self
|
|
||||||
self._window.add_widget(self)
|
|
||||||
self._window.bind(on_resize=self._align_center,
|
|
||||||
on_keyboard=self._handle_keyboard)
|
|
||||||
self.center = self._window.center
|
|
||||||
self.bind(size=self._align_center)
|
|
||||||
a = Animation(_anim_alpha=1., d=self._anim_duration)
|
|
||||||
a.bind(on_complete=lambda *x: self.dispatch('on_open'))
|
|
||||||
a.start(self)
|
|
||||||
return self
|
|
||||||
|
|
||||||
def dismiss(self, *largs, **kwargs):
|
|
||||||
'''Close the view if it is open. If you really want to close the
|
|
||||||
view, whatever the on_dismiss event returns, you can use the *force*
|
|
||||||
argument:
|
|
||||||
::
|
|
||||||
|
|
||||||
view = ModalView(...)
|
|
||||||
view.dismiss(force=True)
|
|
||||||
|
|
||||||
When the view is dismissed, it will be faded out before being
|
|
||||||
removed from the parent. If you don't want animation, use::
|
|
||||||
|
|
||||||
view.dismiss(animation=False)
|
|
||||||
|
|
||||||
'''
|
|
||||||
if self._window is None:
|
|
||||||
return self
|
|
||||||
if self.dispatch('on_dismiss') is True:
|
|
||||||
if kwargs.get('force', False) is not True:
|
|
||||||
return self
|
|
||||||
if kwargs.get('animation', True):
|
|
||||||
Animation(_anim_alpha=0., d=self._anim_duration).start(self)
|
|
||||||
else:
|
|
||||||
self._anim_alpha = 0
|
|
||||||
self._real_remove_widget()
|
|
||||||
return self
|
|
||||||
|
|
||||||
def on_content(self, instance, value):
|
|
||||||
if self._container:
|
|
||||||
self._container.clear_widgets()
|
|
||||||
self._container.add_widget(value)
|
|
||||||
|
|
||||||
def on__container(self, instance, value):
|
|
||||||
if value is None or self.content is None:
|
|
||||||
return
|
|
||||||
self._container.clear_widgets()
|
|
||||||
self._container.add_widget(self.content)
|
|
||||||
|
|
||||||
def on_touch_down(self, touch):
|
|
||||||
if self.disabled and self.collide_point(*touch.pos):
|
|
||||||
return True
|
|
||||||
return super(MDDialog, self).on_touch_down(touch)
|
|
||||||
|
|
||||||
def _update_action_buttons(self, *args):
|
|
||||||
self._action_area.clear_widgets()
|
|
||||||
for btn in self._action_buttons:
|
|
||||||
btn.ids._label.texture_update()
|
|
||||||
btn.width = btn.ids._label.texture_size[0] + dp(16)
|
|
||||||
self._action_area.add_widget(btn)
|
|
|
@ -1,187 +0,0 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
|
||||||
from kivy.app import App
|
|
||||||
from kivy.lang import Builder
|
|
||||||
from kivy.properties import (ListProperty, ObjectProperty, NumericProperty)
|
|
||||||
from kivy.properties import AliasProperty
|
|
||||||
from kivy.metrics import dp
|
|
||||||
|
|
||||||
Builder.load_string('''
|
|
||||||
<ElevationBehavior>
|
|
||||||
canvas.before:
|
|
||||||
Color:
|
|
||||||
a: self._soft_shadow_a
|
|
||||||
Rectangle:
|
|
||||||
texture: self._soft_shadow_texture
|
|
||||||
size: self._soft_shadow_size
|
|
||||||
pos: self._soft_shadow_pos
|
|
||||||
Color:
|
|
||||||
a: self._hard_shadow_a
|
|
||||||
Rectangle:
|
|
||||||
texture: self._hard_shadow_texture
|
|
||||||
size: self._hard_shadow_size
|
|
||||||
pos: self._hard_shadow_pos
|
|
||||||
Color:
|
|
||||||
a: 1
|
|
||||||
|
|
||||||
<RoundElevationBehavior>
|
|
||||||
canvas.before:
|
|
||||||
Color:
|
|
||||||
a: self._soft_shadow_a
|
|
||||||
Rectangle:
|
|
||||||
texture: self._soft_shadow_texture
|
|
||||||
size: self._soft_shadow_size
|
|
||||||
pos: self._soft_shadow_pos
|
|
||||||
Color:
|
|
||||||
a: self._hard_shadow_a
|
|
||||||
Rectangle:
|
|
||||||
texture: self._hard_shadow_texture
|
|
||||||
size: self._hard_shadow_size
|
|
||||||
pos: self._hard_shadow_pos
|
|
||||||
Color:
|
|
||||||
a: 1
|
|
||||||
''')
|
|
||||||
|
|
||||||
|
|
||||||
class ElevationBehavior(object):
|
|
||||||
_elevation = NumericProperty(1)
|
|
||||||
|
|
||||||
def _get_elevation(self):
|
|
||||||
return self._elevation
|
|
||||||
|
|
||||||
def _set_elevation(self, elevation):
|
|
||||||
try:
|
|
||||||
self._elevation = elevation
|
|
||||||
except:
|
|
||||||
self._elevation = 1
|
|
||||||
|
|
||||||
elevation = AliasProperty(_get_elevation, _set_elevation,
|
|
||||||
bind=('_elevation',))
|
|
||||||
|
|
||||||
_soft_shadow_texture = ObjectProperty()
|
|
||||||
_soft_shadow_size = ListProperty([0, 0])
|
|
||||||
_soft_shadow_pos = ListProperty([0, 0])
|
|
||||||
_soft_shadow_a = NumericProperty(0)
|
|
||||||
_hard_shadow_texture = ObjectProperty()
|
|
||||||
_hard_shadow_size = ListProperty([0, 0])
|
|
||||||
_hard_shadow_pos = ListProperty([0, 0])
|
|
||||||
_hard_shadow_a = NumericProperty(0)
|
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
|
||||||
super(ElevationBehavior, self).__init__(**kwargs)
|
|
||||||
self.bind(elevation=self._update_shadow,
|
|
||||||
pos=self._update_shadow,
|
|
||||||
size=self._update_shadow)
|
|
||||||
|
|
||||||
def _update_shadow(self, *args):
|
|
||||||
if self.elevation > 0:
|
|
||||||
ratio = self.width / (self.height if self.height != 0 else 1)
|
|
||||||
if ratio > -2 and ratio < 2:
|
|
||||||
self._shadow = App.get_running_app().theme_cls.quad_shadow
|
|
||||||
width = soft_width = self.width * 1.9
|
|
||||||
height = soft_height = self.height * 1.9
|
|
||||||
elif ratio <= -2:
|
|
||||||
self._shadow = App.get_running_app().theme_cls.rec_st_shadow
|
|
||||||
ratio = abs(ratio)
|
|
||||||
if ratio > 5:
|
|
||||||
ratio = ratio * 22
|
|
||||||
else:
|
|
||||||
ratio = ratio * 11.5
|
|
||||||
|
|
||||||
width = soft_width = self.width * 1.9
|
|
||||||
height = self.height + dp(ratio)
|
|
||||||
soft_height = self.height + dp(ratio) + dp(self.elevation) * .5
|
|
||||||
else:
|
|
||||||
self._shadow = App.get_running_app().theme_cls.quad_shadow
|
|
||||||
width = soft_width = self.width * 1.8
|
|
||||||
height = soft_height = self.height * 1.8
|
|
||||||
# self._shadow = App.get_running_app().theme_cls.rec_shadow
|
|
||||||
# ratio = abs(ratio)
|
|
||||||
# if ratio > 5:
|
|
||||||
# ratio = ratio * 22
|
|
||||||
# else:
|
|
||||||
# ratio = ratio * 11.5
|
|
||||||
#
|
|
||||||
# width = self.width + dp(ratio)
|
|
||||||
# soft_width = self.width + dp(ratio) + dp(self.elevation) * .9
|
|
||||||
# height = soft_height = self.height * 1.9
|
|
||||||
|
|
||||||
x = self.center_x - width / 2
|
|
||||||
soft_x = self.center_x - soft_width / 2
|
|
||||||
self._soft_shadow_size = (soft_width, soft_height)
|
|
||||||
self._hard_shadow_size = (width, height)
|
|
||||||
|
|
||||||
y = self.center_y - soft_height / 2 - dp(
|
|
||||||
.1 * 1.5 ** self.elevation)
|
|
||||||
self._soft_shadow_pos = (soft_x, y)
|
|
||||||
self._soft_shadow_a = 0.1 * 1.1 ** self.elevation
|
|
||||||
self._soft_shadow_texture = self._shadow.textures[
|
|
||||||
str(int(round(self.elevation - 1)))]
|
|
||||||
|
|
||||||
y = self.center_y - height / 2 - dp(.5 * 1.18 ** self.elevation)
|
|
||||||
self._hard_shadow_pos = (x, y)
|
|
||||||
self._hard_shadow_a = .4 * .9 ** self.elevation
|
|
||||||
self._hard_shadow_texture = self._shadow.textures[
|
|
||||||
str(int(round(self.elevation)))]
|
|
||||||
|
|
||||||
else:
|
|
||||||
self._soft_shadow_a = 0
|
|
||||||
self._hard_shadow_a = 0
|
|
||||||
|
|
||||||
|
|
||||||
class RoundElevationBehavior(object):
|
|
||||||
_elevation = NumericProperty(1)
|
|
||||||
|
|
||||||
def _get_elevation(self):
|
|
||||||
return self._elevation
|
|
||||||
|
|
||||||
def _set_elevation(self, elevation):
|
|
||||||
try:
|
|
||||||
self._elevation = elevation
|
|
||||||
except:
|
|
||||||
self._elevation = 1
|
|
||||||
|
|
||||||
elevation = AliasProperty(_get_elevation, _set_elevation,
|
|
||||||
bind=('_elevation',))
|
|
||||||
|
|
||||||
_soft_shadow_texture = ObjectProperty()
|
|
||||||
_soft_shadow_size = ListProperty([0, 0])
|
|
||||||
_soft_shadow_pos = ListProperty([0, 0])
|
|
||||||
_soft_shadow_a = NumericProperty(0)
|
|
||||||
_hard_shadow_texture = ObjectProperty()
|
|
||||||
_hard_shadow_size = ListProperty([0, 0])
|
|
||||||
_hard_shadow_pos = ListProperty([0, 0])
|
|
||||||
_hard_shadow_a = NumericProperty(0)
|
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
|
||||||
super(RoundElevationBehavior, self).__init__(**kwargs)
|
|
||||||
self._shadow = App.get_running_app().theme_cls.round_shadow
|
|
||||||
self.bind(elevation=self._update_shadow,
|
|
||||||
pos=self._update_shadow,
|
|
||||||
size=self._update_shadow)
|
|
||||||
|
|
||||||
def _update_shadow(self, *args):
|
|
||||||
if self.elevation > 0:
|
|
||||||
width = self.width * 2
|
|
||||||
height = self.height * 2
|
|
||||||
|
|
||||||
x = self.center_x - width / 2
|
|
||||||
self._soft_shadow_size = (width, height)
|
|
||||||
|
|
||||||
self._hard_shadow_size = (width, height)
|
|
||||||
|
|
||||||
y = self.center_y - height / 2 - dp(.1 * 1.5 ** self.elevation)
|
|
||||||
self._soft_shadow_pos = (x, y)
|
|
||||||
self._soft_shadow_a = 0.1 * 1.1 ** self.elevation
|
|
||||||
self._soft_shadow_texture = self._shadow.textures[
|
|
||||||
str(int(round(self.elevation)))]
|
|
||||||
|
|
||||||
y = self.center_y - height / 2 - dp(.5 * 1.18 ** self.elevation)
|
|
||||||
self._hard_shadow_pos = (x, y)
|
|
||||||
self._hard_shadow_a = .4 * .9 ** self.elevation
|
|
||||||
self._hard_shadow_texture = self._shadow.textures[
|
|
||||||
str(int(round(self.elevation - 1)))]
|
|
||||||
|
|
||||||
else:
|
|
||||||
self._soft_shadow_a = 0
|
|
||||||
self._hard_shadow_a = 0
|
|
|
@ -1,168 +0,0 @@
|
||||||
# coding=utf-8
|
|
||||||
from kivy.lang import Builder
|
|
||||||
from kivy.properties import StringProperty, BooleanProperty, ObjectProperty, \
|
|
||||||
NumericProperty, ListProperty, OptionProperty
|
|
||||||
from kivy.uix.behaviors import ButtonBehavior
|
|
||||||
from kivy.uix.boxlayout import BoxLayout
|
|
||||||
from kivy.uix.floatlayout import FloatLayout
|
|
||||||
from kivymd.ripplebehavior import RectangularRippleBehavior
|
|
||||||
from kivymd.theming import ThemableBehavior
|
|
||||||
|
|
||||||
Builder.load_string("""
|
|
||||||
<SmartTile>
|
|
||||||
_img_widget: img
|
|
||||||
_img_overlay: img_overlay
|
|
||||||
_box_overlay: box
|
|
||||||
AsyncImage:
|
|
||||||
id: img
|
|
||||||
allow_stretch: root.allow_stretch
|
|
||||||
anim_delay: root.anim_delay
|
|
||||||
anim_loop: root.anim_loop
|
|
||||||
color: root.img_color
|
|
||||||
keep_ratio: root.keep_ratio
|
|
||||||
mipmap: root.mipmap
|
|
||||||
source: root.source
|
|
||||||
size_hint_y: 1 if root.overlap else None
|
|
||||||
x: root.x
|
|
||||||
y: root.y if root.overlap or root.box_position == 'header' else box.top
|
|
||||||
BoxLayout:
|
|
||||||
id: img_overlay
|
|
||||||
size_hint: img.size_hint
|
|
||||||
size: img.size
|
|
||||||
pos: img.pos
|
|
||||||
BoxLayout:
|
|
||||||
canvas:
|
|
||||||
Color:
|
|
||||||
rgba: root.box_color
|
|
||||||
Rectangle:
|
|
||||||
pos: self.pos
|
|
||||||
size: self.size
|
|
||||||
id: box
|
|
||||||
size_hint_y: None
|
|
||||||
height: dp(68) if root.lines == 2 else dp(48)
|
|
||||||
x: root.x
|
|
||||||
y: root.y if root.box_position == 'footer' else root.y + root.height - self.height
|
|
||||||
|
|
||||||
<SmartTileWithLabel>
|
|
||||||
_img_widget: img
|
|
||||||
_img_overlay: img_overlay
|
|
||||||
_box_overlay: box
|
|
||||||
_box_label: boxlabel
|
|
||||||
AsyncImage:
|
|
||||||
id: img
|
|
||||||
allow_stretch: root.allow_stretch
|
|
||||||
anim_delay: root.anim_delay
|
|
||||||
anim_loop: root.anim_loop
|
|
||||||
color: root.img_color
|
|
||||||
keep_ratio: root.keep_ratio
|
|
||||||
mipmap: root.mipmap
|
|
||||||
source: root.source
|
|
||||||
size_hint_y: 1 if root.overlap else None
|
|
||||||
x: root.x
|
|
||||||
y: root.y if root.overlap or root.box_position == 'header' else box.top
|
|
||||||
BoxLayout:
|
|
||||||
id: img_overlay
|
|
||||||
size_hint: img.size_hint
|
|
||||||
size: img.size
|
|
||||||
pos: img.pos
|
|
||||||
BoxLayout:
|
|
||||||
canvas:
|
|
||||||
Color:
|
|
||||||
rgba: root.box_color
|
|
||||||
Rectangle:
|
|
||||||
pos: self.pos
|
|
||||||
size: self.size
|
|
||||||
id: box
|
|
||||||
size_hint_y: None
|
|
||||||
height: dp(68) if root.lines == 2 else dp(48)
|
|
||||||
x: root.x
|
|
||||||
y: root.y if root.box_position == 'footer' else root.y + root.height - self.height
|
|
||||||
MDLabel:
|
|
||||||
id: boxlabel
|
|
||||||
font_style: "Caption"
|
|
||||||
halign: "center"
|
|
||||||
text: root.text
|
|
||||||
""")
|
|
||||||
|
|
||||||
|
|
||||||
class Tile(ThemableBehavior, RectangularRippleBehavior, ButtonBehavior,
|
|
||||||
BoxLayout):
|
|
||||||
"""A simple tile. It does nothing special, just inherits the right behaviors
|
|
||||||
to work as a building block.
|
|
||||||
"""
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class SmartTile(ThemableBehavior, RectangularRippleBehavior, ButtonBehavior,
|
|
||||||
FloatLayout):
|
|
||||||
"""A tile for more complex needs.
|
|
||||||
|
|
||||||
Includes an image, a container to place overlays and a box that can act
|
|
||||||
as a header or a footer, as described in the Material Design specs.
|
|
||||||
"""
|
|
||||||
|
|
||||||
box_color = ListProperty([0, 0, 0, 0.5])
|
|
||||||
"""Sets the color and opacity for the information box."""
|
|
||||||
|
|
||||||
box_position = OptionProperty('footer', options=['footer', 'header'])
|
|
||||||
"""Determines wether the information box acts as a header or footer to the
|
|
||||||
image.
|
|
||||||
"""
|
|
||||||
|
|
||||||
lines = OptionProperty(1, options=[1, 2])
|
|
||||||
"""Number of lines in the header/footer.
|
|
||||||
|
|
||||||
As per Material Design specs, only 1 and 2 are valid values.
|
|
||||||
"""
|
|
||||||
|
|
||||||
overlap = BooleanProperty(True)
|
|
||||||
"""Determines if the header/footer overlaps on top of the image or not"""
|
|
||||||
|
|
||||||
# Img properties
|
|
||||||
allow_stretch = BooleanProperty(True)
|
|
||||||
anim_delay = NumericProperty(0.25)
|
|
||||||
anim_loop = NumericProperty(0)
|
|
||||||
img_color = ListProperty([1, 1, 1, 1])
|
|
||||||
keep_ratio = BooleanProperty(False)
|
|
||||||
mipmap = BooleanProperty(False)
|
|
||||||
source = StringProperty()
|
|
||||||
|
|
||||||
_img_widget = ObjectProperty()
|
|
||||||
_img_overlay = ObjectProperty()
|
|
||||||
_box_overlay = ObjectProperty()
|
|
||||||
_box_label = ObjectProperty()
|
|
||||||
|
|
||||||
def reload(self):
|
|
||||||
self._img_widget.reload()
|
|
||||||
|
|
||||||
def add_widget(self, widget, index=0):
|
|
||||||
if issubclass(widget.__class__, IOverlay):
|
|
||||||
self._img_overlay.add_widget(widget, index)
|
|
||||||
elif issubclass(widget.__class__, IBoxOverlay):
|
|
||||||
self._box_overlay.add_widget(widget, index)
|
|
||||||
else:
|
|
||||||
super(SmartTile, self).add_widget(widget, index)
|
|
||||||
|
|
||||||
|
|
||||||
class SmartTileWithLabel(SmartTile):
|
|
||||||
_box_label = ObjectProperty()
|
|
||||||
|
|
||||||
# MDLabel properties
|
|
||||||
font_style = StringProperty("Caption")
|
|
||||||
theme_text_color = StringProperty("")
|
|
||||||
text = StringProperty("")
|
|
||||||
"""Determines the text for the box footer/header"""
|
|
||||||
|
|
||||||
|
|
||||||
class IBoxOverlay():
|
|
||||||
"""An interface to specify widgets that belong to to the image overlay
|
|
||||||
in the :class:`SmartTile` widget when added as a child.
|
|
||||||
"""
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class IOverlay():
|
|
||||||
"""An interface to specify widgets that belong to to the image overlay
|
|
||||||
in the :class:`SmartTile` widget when added as a child.
|
|
||||||
"""
|
|
||||||
pass
|
|
|
@ -1,1569 +0,0 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
|
||||||
# Thanks to Sergey Kupletsky (github.com/zavoloklom) for its Material Design
|
|
||||||
# Iconic Font, which provides KivyMD's icons.
|
|
||||||
|
|
||||||
# GALLERY HERE:
|
|
||||||
# https://zavoloklom.github.io/material-design-iconic-font/icons.html
|
|
||||||
|
|
||||||
# LAST UPDATED: version 2.2.0 of Material Design Iconic Font
|
|
||||||
|
|
||||||
md_icons = {
|
|
||||||
'3d-rotation': u'',
|
|
||||||
|
|
||||||
'airplane-off': u'',
|
|
||||||
|
|
||||||
'address': u'',
|
|
||||||
|
|
||||||
'airplane': u'',
|
|
||||||
|
|
||||||
'album': u'',
|
|
||||||
|
|
||||||
'archive': u'',
|
|
||||||
|
|
||||||
'assignment-account': u'',
|
|
||||||
|
|
||||||
'assignment-alert': u'',
|
|
||||||
|
|
||||||
'assignment-check': u'',
|
|
||||||
|
|
||||||
'assignment-o': u'',
|
|
||||||
|
|
||||||
'assignment-return': u'',
|
|
||||||
|
|
||||||
'assignment-returned': u'',
|
|
||||||
|
|
||||||
'assignment': u'',
|
|
||||||
|
|
||||||
'attachment-alt': u'',
|
|
||||||
|
|
||||||
'attachment': u'',
|
|
||||||
|
|
||||||
'audio': u'',
|
|
||||||
|
|
||||||
'badge-check': u'',
|
|
||||||
|
|
||||||
'balance-wallet': u'',
|
|
||||||
|
|
||||||
'balance': u'',
|
|
||||||
|
|
||||||
'battery-alert': u'',
|
|
||||||
|
|
||||||
'battery-flash': u'',
|
|
||||||
|
|
||||||
'battery-unknown': u'',
|
|
||||||
|
|
||||||
'battery': u'',
|
|
||||||
|
|
||||||
'bike': u'',
|
|
||||||
|
|
||||||
'block-alt': u'',
|
|
||||||
|
|
||||||
'block': u'',
|
|
||||||
|
|
||||||
'boat': u'',
|
|
||||||
|
|
||||||
'book-image': u'',
|
|
||||||
|
|
||||||
'book': u'',
|
|
||||||
|
|
||||||
'bookmark-outline': u'',
|
|
||||||
|
|
||||||
'bookmark': u'',
|
|
||||||
|
|
||||||
'brush': u'',
|
|
||||||
|
|
||||||
'bug': u'',
|
|
||||||
|
|
||||||
'bus': u'',
|
|
||||||
|
|
||||||
'cake': u'',
|
|
||||||
|
|
||||||
'car-taxi': u'',
|
|
||||||
|
|
||||||
'car-wash': u'',
|
|
||||||
|
|
||||||
'car': u'',
|
|
||||||
|
|
||||||
'card-giftcard': u'',
|
|
||||||
|
|
||||||
'card-membership': u'',
|
|
||||||
|
|
||||||
'card-travel': u'',
|
|
||||||
|
|
||||||
'card': u'',
|
|
||||||
|
|
||||||
'case-check': u'',
|
|
||||||
|
|
||||||
'case-download': u'',
|
|
||||||
|
|
||||||
'case-play': u'',
|
|
||||||
|
|
||||||
'case': u'',
|
|
||||||
|
|
||||||
'cast-connected': u'',
|
|
||||||
|
|
||||||
'cast': u'',
|
|
||||||
|
|
||||||
'chart-donut': u'',
|
|
||||||
|
|
||||||
'chart': u'',
|
|
||||||
|
|
||||||
'city-alt': u'',
|
|
||||||
|
|
||||||
'city': u'',
|
|
||||||
|
|
||||||
'close-circle-o': u'',
|
|
||||||
|
|
||||||
'close-circle': u'',
|
|
||||||
|
|
||||||
'close': u'',
|
|
||||||
|
|
||||||
'cocktail': u'',
|
|
||||||
|
|
||||||
'code-setting': u'',
|
|
||||||
|
|
||||||
'code-smartphone': u'',
|
|
||||||
|
|
||||||
'code': u'',
|
|
||||||
|
|
||||||
'coffee': u'',
|
|
||||||
|
|
||||||
'collection-bookmark': u'',
|
|
||||||
|
|
||||||
'collection-case-play': u'',
|
|
||||||
|
|
||||||
'collection-folder-image': u'',
|
|
||||||
|
|
||||||
'collection-image-o': u'',
|
|
||||||
|
|
||||||
'collection-image': u'',
|
|
||||||
|
|
||||||
'collection-item-1': u'',
|
|
||||||
|
|
||||||
'collection-item-2': u'',
|
|
||||||
|
|
||||||
'collection-item-3': u'',
|
|
||||||
|
|
||||||
'collection-item-4': u'',
|
|
||||||
|
|
||||||
'collection-item-5': u'',
|
|
||||||
|
|
||||||
'collection-item-6': u'',
|
|
||||||
|
|
||||||
'collection-item-7': u'',
|
|
||||||
|
|
||||||
'collection-item-8': u'',
|
|
||||||
|
|
||||||
'collection-item-9-plus': u'',
|
|
||||||
|
|
||||||
'collection-item-9': u'',
|
|
||||||
|
|
||||||
'collection-item': u'',
|
|
||||||
|
|
||||||
'collection-music': u'',
|
|
||||||
|
|
||||||
'collection-pdf': u'',
|
|
||||||
|
|
||||||
'collection-plus': u'',
|
|
||||||
|
|
||||||
'collection-speaker': u'',
|
|
||||||
|
|
||||||
'collection-text': u'',
|
|
||||||
|
|
||||||
'collection-video': u'',
|
|
||||||
|
|
||||||
'compass': u'',
|
|
||||||
|
|
||||||
'cutlery': u'',
|
|
||||||
|
|
||||||
'delete': u'',
|
|
||||||
|
|
||||||
'dialpad': u'',
|
|
||||||
|
|
||||||
'dns': u'',
|
|
||||||
|
|
||||||
'drink': u'',
|
|
||||||
|
|
||||||
'edit': u'',
|
|
||||||
|
|
||||||
'email-open': u'',
|
|
||||||
|
|
||||||
'email': u'',
|
|
||||||
|
|
||||||
'eye-off': u'',
|
|
||||||
|
|
||||||
'eye': u'',
|
|
||||||
|
|
||||||
'eyedropper': u'',
|
|
||||||
|
|
||||||
'favorite-outline': u'',
|
|
||||||
|
|
||||||
'favorite': u'',
|
|
||||||
|
|
||||||
'filter-list': u'',
|
|
||||||
|
|
||||||
'fire': u'',
|
|
||||||
|
|
||||||
'flag': u'',
|
|
||||||
|
|
||||||
'flare': u'',
|
|
||||||
|
|
||||||
'flash-auto': u'',
|
|
||||||
|
|
||||||
'flash-off': u'',
|
|
||||||
|
|
||||||
'flash': u'',
|
|
||||||
|
|
||||||
'flip': u'',
|
|
||||||
|
|
||||||
'flower-alt': u'',
|
|
||||||
|
|
||||||
'flower': u'',
|
|
||||||
|
|
||||||
'font': u'',
|
|
||||||
|
|
||||||
'fullscreen-alt': u'',
|
|
||||||
|
|
||||||
'fullscreen-exit': u'',
|
|
||||||
|
|
||||||
'fullscreen': u'',
|
|
||||||
|
|
||||||
'functions': u'',
|
|
||||||
|
|
||||||
'gas-station': u'',
|
|
||||||
|
|
||||||
'gesture': u'',
|
|
||||||
|
|
||||||
'globe-alt': u'',
|
|
||||||
|
|
||||||
'globe-lock': u'',
|
|
||||||
|
|
||||||
'globe': u'',
|
|
||||||
|
|
||||||
'graduation-cap': u'',
|
|
||||||
|
|
||||||
'group': u'',
|
|
||||||
|
|
||||||
'home': u'',
|
|
||||||
|
|
||||||
'hospital-alt': u'',
|
|
||||||
|
|
||||||
'hospital': u'',
|
|
||||||
|
|
||||||
'hotel': u'',
|
|
||||||
|
|
||||||
'hourglass-alt': u'',
|
|
||||||
|
|
||||||
'hourglass-outline': u'',
|
|
||||||
|
|
||||||
'hourglass': u'',
|
|
||||||
|
|
||||||
'http': u'',
|
|
||||||
|
|
||||||
'image-alt': u'',
|
|
||||||
|
|
||||||
'image-o': u'',
|
|
||||||
|
|
||||||
'image': u'',
|
|
||||||
|
|
||||||
'inbox': u'',
|
|
||||||
|
|
||||||
'invert-colors-off': u'',
|
|
||||||
|
|
||||||
'invert-colors': u'',
|
|
||||||
|
|
||||||
'key': u'',
|
|
||||||
|
|
||||||
'label-alt-outline': u'',
|
|
||||||
|
|
||||||
'label-alt': u'',
|
|
||||||
|
|
||||||
'label-heart': u'',
|
|
||||||
|
|
||||||
'label': u'',
|
|
||||||
|
|
||||||
'labels': u'',
|
|
||||||
|
|
||||||
'lamp': u'',
|
|
||||||
|
|
||||||
'landscape': u'',
|
|
||||||
|
|
||||||
'layers-off': u'',
|
|
||||||
|
|
||||||
'layers': u'',
|
|
||||||
|
|
||||||
'library': u'',
|
|
||||||
|
|
||||||
'link': u'',
|
|
||||||
|
|
||||||
'lock-open': u'',
|
|
||||||
|
|
||||||
'lock-outline': u'',
|
|
||||||
|
|
||||||
'lock': u'',
|
|
||||||
|
|
||||||
'mail-reply-all': u'',
|
|
||||||
|
|
||||||
'mail-reply': u'',
|
|
||||||
|
|
||||||
'mail-send': u'',
|
|
||||||
|
|
||||||
'mall': u'',
|
|
||||||
|
|
||||||
'map': u'',
|
|
||||||
|
|
||||||
'menu': u'',
|
|
||||||
|
|
||||||
'money-box': u'',
|
|
||||||
|
|
||||||
'money-off': u'',
|
|
||||||
|
|
||||||
'money': u'',
|
|
||||||
|
|
||||||
'more-vert': u'',
|
|
||||||
|
|
||||||
'more': u'',
|
|
||||||
|
|
||||||
'movie-alt': u'',
|
|
||||||
|
|
||||||
'movie': u'',
|
|
||||||
|
|
||||||
'nature-people': u'',
|
|
||||||
|
|
||||||
'nature': u'',
|
|
||||||
|
|
||||||
'navigation': u'',
|
|
||||||
|
|
||||||
'open-in-browser': u'',
|
|
||||||
|
|
||||||
'open-in-new': u'',
|
|
||||||
|
|
||||||
'palette': u'',
|
|
||||||
|
|
||||||
'parking': u'',
|
|
||||||
|
|
||||||
'pin-account': u'',
|
|
||||||
|
|
||||||
'pin-assistant': u'',
|
|
||||||
|
|
||||||
'pin-drop': u'',
|
|
||||||
|
|
||||||
'pin-help': u'',
|
|
||||||
|
|
||||||
'pin-off': u'',
|
|
||||||
|
|
||||||
'pin': u'',
|
|
||||||
|
|
||||||
'pizza': u'',
|
|
||||||
|
|
||||||
'plaster': u'',
|
|
||||||
|
|
||||||
'power-setting': u'',
|
|
||||||
|
|
||||||
'power': u'',
|
|
||||||
|
|
||||||
'print': u'',
|
|
||||||
|
|
||||||
'puzzle-piece': u'',
|
|
||||||
|
|
||||||
'quote': u'',
|
|
||||||
|
|
||||||
'railway': u'',
|
|
||||||
|
|
||||||
'receipt': u'',
|
|
||||||
|
|
||||||
'refresh-alt': u'',
|
|
||||||
|
|
||||||
'refresh-sync-alert': u'',
|
|
||||||
|
|
||||||
'refresh-sync-off': u'',
|
|
||||||
|
|
||||||
'refresh-sync': u'',
|
|
||||||
|
|
||||||
'refresh': u'',
|
|
||||||
|
|
||||||
'roller': u'',
|
|
||||||
|
|
||||||
'ruler': u'',
|
|
||||||
|
|
||||||
'scissors': u'',
|
|
||||||
|
|
||||||
'screen-rotation-lock': u'',
|
|
||||||
|
|
||||||
'screen-rotation': u'',
|
|
||||||
|
|
||||||
'search-for': u'',
|
|
||||||
|
|
||||||
'search-in-file': u'',
|
|
||||||
|
|
||||||
'search-in-page': u'',
|
|
||||||
|
|
||||||
'search-replace': u'',
|
|
||||||
|
|
||||||
'search': u'',
|
|
||||||
|
|
||||||
'seat': u'',
|
|
||||||
|
|
||||||
'settings-square': u'',
|
|
||||||
|
|
||||||
'settings': u'',
|
|
||||||
|
|
||||||
'shape': u'',
|
|
||||||
|
|
||||||
'shield-check': u'',
|
|
||||||
|
|
||||||
'shield-security': u'',
|
|
||||||
|
|
||||||
'shopping-basket': u'',
|
|
||||||
|
|
||||||
'shopping-cart-plus': u'',
|
|
||||||
|
|
||||||
'shopping-cart': u'',
|
|
||||||
|
|
||||||
'sign-in': u'',
|
|
||||||
|
|
||||||
'sort-amount-asc': u'',
|
|
||||||
|
|
||||||
'sort-amount-desc': u'',
|
|
||||||
|
|
||||||
'sort-asc': u'',
|
|
||||||
|
|
||||||
'sort-desc': u'',
|
|
||||||
|
|
||||||
'spellcheck': u'',
|
|
||||||
|
|
||||||
'spinner': u'',
|
|
||||||
|
|
||||||
'storage': u'',
|
|
||||||
|
|
||||||
'store-24': u'',
|
|
||||||
|
|
||||||
'store': u'',
|
|
||||||
|
|
||||||
'subway': u'',
|
|
||||||
|
|
||||||
'sun': u'',
|
|
||||||
|
|
||||||
'tab-unselected': u'',
|
|
||||||
|
|
||||||
'tab': u'',
|
|
||||||
|
|
||||||
'tag-close': u'',
|
|
||||||
|
|
||||||
'tag-more': u'',
|
|
||||||
|
|
||||||
'tag': u'',
|
|
||||||
|
|
||||||
'thumb-down': u'',
|
|
||||||
|
|
||||||
'thumb-up-down': u'',
|
|
||||||
|
|
||||||
'thumb-up': u'',
|
|
||||||
|
|
||||||
'ticket-star': u'',
|
|
||||||
|
|
||||||
'toll': u'',
|
|
||||||
|
|
||||||
'toys': u'',
|
|
||||||
|
|
||||||
'traffic': u'',
|
|
||||||
|
|
||||||
'translate': u'',
|
|
||||||
|
|
||||||
'triangle-down': u'',
|
|
||||||
|
|
||||||
'triangle-up': u'',
|
|
||||||
|
|
||||||
'truck': u'',
|
|
||||||
|
|
||||||
'turning-sign': u'',
|
|
||||||
|
|
||||||
' ungroup': u'',
|
|
||||||
|
|
||||||
'wallpaper': u'',
|
|
||||||
|
|
||||||
'washing-machine': u'',
|
|
||||||
|
|
||||||
'window-maximize': u'',
|
|
||||||
|
|
||||||
'window-minimize': u'',
|
|
||||||
|
|
||||||
'window-restore': u'',
|
|
||||||
|
|
||||||
'wrench': u'',
|
|
||||||
|
|
||||||
'zoom-in': u'',
|
|
||||||
|
|
||||||
'zoom-out': u'',
|
|
||||||
|
|
||||||
'alert-circle-o': u'',
|
|
||||||
|
|
||||||
'alert-circle': u'',
|
|
||||||
|
|
||||||
'alert-octagon': u'',
|
|
||||||
|
|
||||||
'alert-polygon': u'',
|
|
||||||
|
|
||||||
'alert-triangle': u'',
|
|
||||||
|
|
||||||
'help-outline': u'',
|
|
||||||
|
|
||||||
'help': u'',
|
|
||||||
|
|
||||||
'info-outline': u'',
|
|
||||||
|
|
||||||
'info': u'',
|
|
||||||
|
|
||||||
'notifications-active': u'',
|
|
||||||
|
|
||||||
'notifications-add': u'',
|
|
||||||
|
|
||||||
'notifications-none': u'',
|
|
||||||
|
|
||||||
'notifications-off': u'',
|
|
||||||
|
|
||||||
'notifications-paused': u'',
|
|
||||||
|
|
||||||
'notifications': u'',
|
|
||||||
|
|
||||||
'account-add': u'',
|
|
||||||
|
|
||||||
'account-box-mail': u'',
|
|
||||||
|
|
||||||
'account-box-o': u'',
|
|
||||||
|
|
||||||
'account-box-phone': u'',
|
|
||||||
|
|
||||||
'account-box': u'',
|
|
||||||
|
|
||||||
'account-calendar': u'',
|
|
||||||
|
|
||||||
'account-circle': u'',
|
|
||||||
|
|
||||||
'account-o': u'',
|
|
||||||
|
|
||||||
'account': u'',
|
|
||||||
|
|
||||||
'accounts-add': u'',
|
|
||||||
|
|
||||||
'accounts-alt': u'',
|
|
||||||
|
|
||||||
'accounts-list-alt': u'',
|
|
||||||
|
|
||||||
'accounts-list': u'',
|
|
||||||
|
|
||||||
'accounts-outline': u'',
|
|
||||||
|
|
||||||
'accounts': u'',
|
|
||||||
|
|
||||||
'face': u'',
|
|
||||||
|
|
||||||
'female': u'',
|
|
||||||
|
|
||||||
'male-alt': u'',
|
|
||||||
|
|
||||||
'male-female': u'',
|
|
||||||
|
|
||||||
'male': u'',
|
|
||||||
|
|
||||||
'mood-bad': u'',
|
|
||||||
|
|
||||||
'mood': u'',
|
|
||||||
|
|
||||||
'run': u'',
|
|
||||||
|
|
||||||
'walk': u'',
|
|
||||||
|
|
||||||
'cloud-box': u'',
|
|
||||||
|
|
||||||
'cloud-circle': u'',
|
|
||||||
|
|
||||||
'cloud-done': u'',
|
|
||||||
|
|
||||||
'cloud-download': u'',
|
|
||||||
|
|
||||||
'cloud-off': u'',
|
|
||||||
|
|
||||||
'cloud-outline-alt': u'',
|
|
||||||
|
|
||||||
'cloud-outline': u'',
|
|
||||||
|
|
||||||
'cloud-upload': u'',
|
|
||||||
|
|
||||||
'cloud': u'',
|
|
||||||
|
|
||||||
'download': u'',
|
|
||||||
|
|
||||||
'file-plus': u'',
|
|
||||||
|
|
||||||
'file-text': u'',
|
|
||||||
|
|
||||||
'file': u'',
|
|
||||||
|
|
||||||
'folder-outline': u'',
|
|
||||||
|
|
||||||
'folder-person': u'',
|
|
||||||
|
|
||||||
'folder-star-alt': u'',
|
|
||||||
|
|
||||||
'folder-star': u'',
|
|
||||||
|
|
||||||
'folder': u'',
|
|
||||||
|
|
||||||
'gif': u'',
|
|
||||||
|
|
||||||
'upload': u'',
|
|
||||||
|
|
||||||
'border-all': u'',
|
|
||||||
|
|
||||||
'border-bottom': u'',
|
|
||||||
|
|
||||||
'border-clear': u'',
|
|
||||||
|
|
||||||
'border-color': u'',
|
|
||||||
|
|
||||||
'border-horizontal': u'',
|
|
||||||
|
|
||||||
'border-inner': u'',
|
|
||||||
|
|
||||||
'border-left': u'',
|
|
||||||
|
|
||||||
'border-outer': u'',
|
|
||||||
|
|
||||||
'border-right': u'',
|
|
||||||
|
|
||||||
'border-style': u'',
|
|
||||||
|
|
||||||
'border-top': u'',
|
|
||||||
|
|
||||||
'border-vertical': u'',
|
|
||||||
|
|
||||||
'copy': u'',
|
|
||||||
|
|
||||||
'crop': u'',
|
|
||||||
|
|
||||||
'format-align-center': u'',
|
|
||||||
|
|
||||||
'format-align-justify': u'',
|
|
||||||
|
|
||||||
'format-align-left': u'',
|
|
||||||
|
|
||||||
'format-align-right': u'',
|
|
||||||
|
|
||||||
'format-bold': u'',
|
|
||||||
|
|
||||||
'format-clear-all': u'',
|
|
||||||
|
|
||||||
'format-clear': u'',
|
|
||||||
|
|
||||||
'format-color-fill': u'',
|
|
||||||
|
|
||||||
'format-color-reset': u'',
|
|
||||||
|
|
||||||
'format-color-text': u'',
|
|
||||||
|
|
||||||
'format-indent-decrease': u'',
|
|
||||||
|
|
||||||
'format-indent-increase': u'',
|
|
||||||
|
|
||||||
'format-italic': u'',
|
|
||||||
|
|
||||||
'format-line-spacing': u'',
|
|
||||||
|
|
||||||
'format-list-bulleted': u'',
|
|
||||||
|
|
||||||
'format-list-numbered': u'',
|
|
||||||
|
|
||||||
'format-ltr': u'',
|
|
||||||
|
|
||||||
'format-rtl': u'',
|
|
||||||
|
|
||||||
'format-size': u'',
|
|
||||||
|
|
||||||
'format-strikethrough-s': u'',
|
|
||||||
|
|
||||||
'format-strikethrough': u'',
|
|
||||||
|
|
||||||
'format-subject': u'',
|
|
||||||
|
|
||||||
'format-underlined': u'',
|
|
||||||
|
|
||||||
'format-valign-bottom': u'',
|
|
||||||
|
|
||||||
'format-valign-center': u'',
|
|
||||||
|
|
||||||
'format-valign-top': u'',
|
|
||||||
|
|
||||||
'redo': u'',
|
|
||||||
|
|
||||||
'select-all': u'',
|
|
||||||
|
|
||||||
'space-bar': u'',
|
|
||||||
|
|
||||||
'text-format': u'',
|
|
||||||
|
|
||||||
'transform': u'',
|
|
||||||
|
|
||||||
'undo': u'',
|
|
||||||
|
|
||||||
'wrap-text': u'',
|
|
||||||
|
|
||||||
'comment-alert': u'',
|
|
||||||
|
|
||||||
'comment-alt-text': u'',
|
|
||||||
|
|
||||||
'comment-alt': u'',
|
|
||||||
|
|
||||||
'comment-edit': u'',
|
|
||||||
|
|
||||||
'comment-image': u'',
|
|
||||||
|
|
||||||
'comment-list': u'',
|
|
||||||
|
|
||||||
'comment-more': u'',
|
|
||||||
|
|
||||||
'comment-outline': u'',
|
|
||||||
|
|
||||||
'comment-text-alt': u'',
|
|
||||||
|
|
||||||
'comment-text': u'',
|
|
||||||
|
|
||||||
'comment-video': u'',
|
|
||||||
|
|
||||||
'comment': u'',
|
|
||||||
|
|
||||||
'comments': u'',
|
|
||||||
|
|
||||||
'rm': u'F',
|
|
||||||
|
|
||||||
'check-all': u'',
|
|
||||||
|
|
||||||
'check-circle-u': u'',
|
|
||||||
|
|
||||||
'check-circle': u'',
|
|
||||||
|
|
||||||
'check-square': u'',
|
|
||||||
|
|
||||||
'check': u'',
|
|
||||||
|
|
||||||
'circle-o': u'',
|
|
||||||
|
|
||||||
'circle': u'',
|
|
||||||
|
|
||||||
'dot-circle-alt': u'',
|
|
||||||
|
|
||||||
'dot-circle': u'',
|
|
||||||
|
|
||||||
'minus-circle-outline': u'',
|
|
||||||
|
|
||||||
'minus-circle': u'',
|
|
||||||
|
|
||||||
'minus-square': u'',
|
|
||||||
|
|
||||||
'minus': u'',
|
|
||||||
|
|
||||||
'plus-circle-o-duplicate': u'',
|
|
||||||
|
|
||||||
'plus-circle-o': u'',
|
|
||||||
|
|
||||||
'plus-circle': u'',
|
|
||||||
|
|
||||||
'plus-square': u'',
|
|
||||||
|
|
||||||
'plus': u'',
|
|
||||||
|
|
||||||
'square-o': u'',
|
|
||||||
|
|
||||||
'star-circle': u'',
|
|
||||||
|
|
||||||
'star-half': u'',
|
|
||||||
|
|
||||||
'star-outline': u'',
|
|
||||||
|
|
||||||
'star': u'',
|
|
||||||
|
|
||||||
'bluetooth-connected': u'',
|
|
||||||
|
|
||||||
'bluetooth-off': u'',
|
|
||||||
|
|
||||||
'bluetooth-search': u'',
|
|
||||||
|
|
||||||
'bluetooth-setting': u'',
|
|
||||||
|
|
||||||
'bluetooth': u'',
|
|
||||||
|
|
||||||
'camera-add': u'',
|
|
||||||
|
|
||||||
'camera-alt': u'',
|
|
||||||
|
|
||||||
'camera-bw': u'',
|
|
||||||
|
|
||||||
'camera-front': u'',
|
|
||||||
|
|
||||||
'camera-mic': u'',
|
|
||||||
|
|
||||||
'camera-party-mode': u'',
|
|
||||||
|
|
||||||
'camera-rear': u'',
|
|
||||||
|
|
||||||
'camera-roll': u'',
|
|
||||||
|
|
||||||
'camera-switch': u'',
|
|
||||||
|
|
||||||
'camera': u'',
|
|
||||||
|
|
||||||
'card-alert': u'',
|
|
||||||
|
|
||||||
'card-off': u'',
|
|
||||||
|
|
||||||
'card-sd': u'',
|
|
||||||
|
|
||||||
'card-sim': u'',
|
|
||||||
|
|
||||||
'desktop-mac': u'',
|
|
||||||
|
|
||||||
'desktop-windows': u'',
|
|
||||||
|
|
||||||
'device-hub': u'',
|
|
||||||
|
|
||||||
'devices-off': u'',
|
|
||||||
|
|
||||||
'devices': u'',
|
|
||||||
|
|
||||||
'dock': u'',
|
|
||||||
|
|
||||||
'floppy': u'',
|
|
||||||
|
|
||||||
'gamepad': u'',
|
|
||||||
|
|
||||||
'gps-dot': u'',
|
|
||||||
|
|
||||||
'gps-off': u'',
|
|
||||||
|
|
||||||
'gps': u'',
|
|
||||||
|
|
||||||
'headset-mic': u'',
|
|
||||||
|
|
||||||
'headset': u'',
|
|
||||||
|
|
||||||
'input-antenna': u'',
|
|
||||||
|
|
||||||
'input-composite': u'',
|
|
||||||
|
|
||||||
'input-hdmi': u'',
|
|
||||||
|
|
||||||
'input-power': u'',
|
|
||||||
|
|
||||||
'input-svideo': u'',
|
|
||||||
|
|
||||||
'keyboard-hide': u'',
|
|
||||||
|
|
||||||
'keyboard': u'',
|
|
||||||
|
|
||||||
'laptop-chromebook': u'',
|
|
||||||
|
|
||||||
'laptop-mac': u'',
|
|
||||||
|
|
||||||
'laptop': u'',
|
|
||||||
|
|
||||||
'mic-off': u'',
|
|
||||||
|
|
||||||
'mic-outline': u'',
|
|
||||||
|
|
||||||
'mic-setting': u'',
|
|
||||||
|
|
||||||
'mic': u'',
|
|
||||||
|
|
||||||
'mouse': u'',
|
|
||||||
|
|
||||||
'network-alert': u'',
|
|
||||||
|
|
||||||
'network-locked': u'',
|
|
||||||
|
|
||||||
'network-off': u'',
|
|
||||||
|
|
||||||
'network-outline': u'',
|
|
||||||
|
|
||||||
'network-setting': u'',
|
|
||||||
|
|
||||||
'network': u'',
|
|
||||||
|
|
||||||
'phone-bluetooth': u'',
|
|
||||||
|
|
||||||
'phone-end': u'',
|
|
||||||
|
|
||||||
'phone-forwarded': u'',
|
|
||||||
|
|
||||||
'phone-in-talk': u'',
|
|
||||||
|
|
||||||
'phone-locked': u'',
|
|
||||||
|
|
||||||
'phone-missed': u'',
|
|
||||||
|
|
||||||
'phone-msg': u'',
|
|
||||||
|
|
||||||
'phone-paused': u'',
|
|
||||||
|
|
||||||
'phone-ring': u'',
|
|
||||||
|
|
||||||
'phone-setting': u'',
|
|
||||||
|
|
||||||
'phone-sip': u'',
|
|
||||||
|
|
||||||
'phone': u'',
|
|
||||||
|
|
||||||
'portable-wifi-changes': u'',
|
|
||||||
|
|
||||||
'portable-wifi-off': u'',
|
|
||||||
|
|
||||||
'portable-wifi': u'',
|
|
||||||
|
|
||||||
'radio': u'',
|
|
||||||
|
|
||||||
'reader': u'',
|
|
||||||
|
|
||||||
'remote-control-alt': u'',
|
|
||||||
|
|
||||||
'remote-control': u'',
|
|
||||||
|
|
||||||
'router': u'',
|
|
||||||
|
|
||||||
'scanner': u'',
|
|
||||||
|
|
||||||
'smartphone-android': u'',
|
|
||||||
|
|
||||||
'smartphone-download': u'',
|
|
||||||
|
|
||||||
'smartphone-erase': u'',
|
|
||||||
|
|
||||||
'smartphone-info': u'',
|
|
||||||
|
|
||||||
'smartphone-iphone': u'',
|
|
||||||
|
|
||||||
'smartphone-landscape-lock': u'',
|
|
||||||
|
|
||||||
'smartphone-landscape': u'',
|
|
||||||
|
|
||||||
'smartphone-lock': u'',
|
|
||||||
|
|
||||||
'smartphone-portrait-lock': u'',
|
|
||||||
|
|
||||||
'smartphone-ring': u'',
|
|
||||||
|
|
||||||
'smartphone-setting': u'',
|
|
||||||
|
|
||||||
'smartphone-setup': u'',
|
|
||||||
|
|
||||||
'smartphone': u'',
|
|
||||||
|
|
||||||
'speaker': u'',
|
|
||||||
|
|
||||||
'tablet-android': u'',
|
|
||||||
|
|
||||||
'tablet-mac': u'',
|
|
||||||
|
|
||||||
'tablet': u'',
|
|
||||||
|
|
||||||
'tv-alt-play': u'',
|
|
||||||
|
|
||||||
'tv-list': u'',
|
|
||||||
|
|
||||||
'tv-play': u'',
|
|
||||||
|
|
||||||
'tv': u'',
|
|
||||||
|
|
||||||
'usb': u'',
|
|
||||||
|
|
||||||
'videocam-off': u'',
|
|
||||||
|
|
||||||
'videocam-switch': u'',
|
|
||||||
|
|
||||||
'videocam': u'',
|
|
||||||
|
|
||||||
'watch': u'',
|
|
||||||
|
|
||||||
'wifi-alt-2': u'',
|
|
||||||
|
|
||||||
'wifi-alt': u'',
|
|
||||||
|
|
||||||
'wifi-info': u'',
|
|
||||||
|
|
||||||
'wifi-lock': u'',
|
|
||||||
|
|
||||||
'wifi-off': u'',
|
|
||||||
|
|
||||||
'wifi-outline': u'',
|
|
||||||
|
|
||||||
'wifi': u'',
|
|
||||||
|
|
||||||
'arrow-left-bottom': u'',
|
|
||||||
|
|
||||||
'arrow-left': u'',
|
|
||||||
|
|
||||||
'arrow-merge': u'',
|
|
||||||
|
|
||||||
'arrow-missed': u'',
|
|
||||||
|
|
||||||
'arrow-right-top': u'',
|
|
||||||
|
|
||||||
'arrow-right': u'',
|
|
||||||
|
|
||||||
'arrow-split': u'',
|
|
||||||
|
|
||||||
'arrows': u'',
|
|
||||||
|
|
||||||
'caret-down-circle': u'',
|
|
||||||
|
|
||||||
'caret-down': u'',
|
|
||||||
|
|
||||||
'caret-left-circle': u'',
|
|
||||||
|
|
||||||
'caret-left': u'',
|
|
||||||
|
|
||||||
'caret-right-circle': u'',
|
|
||||||
|
|
||||||
'caret-right': u'',
|
|
||||||
|
|
||||||
'caret-up-circle': u'',
|
|
||||||
|
|
||||||
'caret-up': u'',
|
|
||||||
|
|
||||||
'chevron-down': u'',
|
|
||||||
|
|
||||||
'chevron-left': u'',
|
|
||||||
|
|
||||||
'chevron-right': u'',
|
|
||||||
|
|
||||||
'chevron-up': u'',
|
|
||||||
|
|
||||||
'forward': u'',
|
|
||||||
|
|
||||||
'long-arrow-down': u'',
|
|
||||||
|
|
||||||
'long-arrow-left': u'',
|
|
||||||
|
|
||||||
'long-arrow-return': u'',
|
|
||||||
|
|
||||||
'long-arrow-right': u'',
|
|
||||||
|
|
||||||
'long-arrow-tab': u'',
|
|
||||||
|
|
||||||
'long-arrow-up': u'',
|
|
||||||
|
|
||||||
'rotate-ccw': u'',
|
|
||||||
|
|
||||||
'rotate-cw': u'',
|
|
||||||
|
|
||||||
'rotate-left': u'',
|
|
||||||
|
|
||||||
'rotate-right': u'',
|
|
||||||
|
|
||||||
'square-down': u'',
|
|
||||||
|
|
||||||
'square-right': u'',
|
|
||||||
|
|
||||||
'swap-alt': u'',
|
|
||||||
|
|
||||||
'swap-vertical-circle': u'',
|
|
||||||
|
|
||||||
'swap-vertical': u'',
|
|
||||||
|
|
||||||
'swap': u'',
|
|
||||||
|
|
||||||
'trending-down': u'',
|
|
||||||
|
|
||||||
'trending-flat': u'',
|
|
||||||
|
|
||||||
'trending-up': u'',
|
|
||||||
|
|
||||||
'unfold-less': u'',
|
|
||||||
|
|
||||||
'unfold-more': u'',
|
|
||||||
|
|
||||||
'apps': u'',
|
|
||||||
|
|
||||||
'grid-off': u'',
|
|
||||||
|
|
||||||
'grid': u'',
|
|
||||||
|
|
||||||
'view-agenda': u'',
|
|
||||||
|
|
||||||
'view-array': u'',
|
|
||||||
|
|
||||||
'view-carousel': u'',
|
|
||||||
|
|
||||||
'view-column': u'',
|
|
||||||
|
|
||||||
'view-comfy': u'',
|
|
||||||
|
|
||||||
'view-compact': u'',
|
|
||||||
|
|
||||||
'view-dashboard': u'',
|
|
||||||
|
|
||||||
'view-day': u'',
|
|
||||||
|
|
||||||
'view-headline': u'',
|
|
||||||
|
|
||||||
'view-list-alt': u'',
|
|
||||||
|
|
||||||
'view-list': u'',
|
|
||||||
|
|
||||||
'view-module': u'',
|
|
||||||
|
|
||||||
'view-quilt': u'',
|
|
||||||
|
|
||||||
'view-stream': u'',
|
|
||||||
|
|
||||||
'view-subtitles': u'',
|
|
||||||
|
|
||||||
'view-toc': u'',
|
|
||||||
|
|
||||||
'view-web': u'',
|
|
||||||
|
|
||||||
'view-week': u'',
|
|
||||||
|
|
||||||
'widgets': u'',
|
|
||||||
|
|
||||||
'alarm-check': u'',
|
|
||||||
|
|
||||||
'alarm-off': u'',
|
|
||||||
|
|
||||||
'alarm-plus': u'',
|
|
||||||
|
|
||||||
'alarm-snooze': u'',
|
|
||||||
|
|
||||||
'alarm': u'',
|
|
||||||
|
|
||||||
'calendar-alt': u'',
|
|
||||||
|
|
||||||
'calendar-check': u'',
|
|
||||||
|
|
||||||
'calendar-close': u'',
|
|
||||||
|
|
||||||
'calendar-note': u'',
|
|
||||||
|
|
||||||
'calendar': u'',
|
|
||||||
|
|
||||||
'time-countdown': u'',
|
|
||||||
|
|
||||||
'time-interval': u'',
|
|
||||||
|
|
||||||
'time-restore-setting': u'',
|
|
||||||
|
|
||||||
'time-restore': u'',
|
|
||||||
|
|
||||||
'time': u'',
|
|
||||||
|
|
||||||
'timer-off': u'',
|
|
||||||
|
|
||||||
'timer': u'',
|
|
||||||
|
|
||||||
'android-alt': u'',
|
|
||||||
|
|
||||||
'android': u'',
|
|
||||||
|
|
||||||
'apple': u'',
|
|
||||||
|
|
||||||
'behance': u'',
|
|
||||||
|
|
||||||
'codepen': u'',
|
|
||||||
|
|
||||||
'dribbble': u'',
|
|
||||||
|
|
||||||
'dropbox': u'',
|
|
||||||
|
|
||||||
'evernote': u'',
|
|
||||||
|
|
||||||
'facebook-box': u'',
|
|
||||||
|
|
||||||
'facebook': u'',
|
|
||||||
|
|
||||||
'github-box': u'',
|
|
||||||
|
|
||||||
'github': u'',
|
|
||||||
|
|
||||||
'google-drive': u'',
|
|
||||||
|
|
||||||
'google-earth': u'',
|
|
||||||
|
|
||||||
'google-glass': u'',
|
|
||||||
|
|
||||||
'google-maps': u'',
|
|
||||||
|
|
||||||
'google-pages': u'',
|
|
||||||
|
|
||||||
'google-play': u'',
|
|
||||||
|
|
||||||
'google-plus-box': u'',
|
|
||||||
|
|
||||||
'google-plus': u'',
|
|
||||||
|
|
||||||
'google': u'',
|
|
||||||
|
|
||||||
'instagram': u'',
|
|
||||||
|
|
||||||
'language-css3': u'',
|
|
||||||
|
|
||||||
'language-html5': u'',
|
|
||||||
|
|
||||||
'language-javascript': u'',
|
|
||||||
|
|
||||||
'language-python-alt': u'',
|
|
||||||
|
|
||||||
'language-python': u'',
|
|
||||||
|
|
||||||
'lastfm': u'',
|
|
||||||
|
|
||||||
'linkedin-box': u'',
|
|
||||||
|
|
||||||
'paypal': u'',
|
|
||||||
|
|
||||||
'pinterest-box': u'',
|
|
||||||
|
|
||||||
'pocket': u'',
|
|
||||||
|
|
||||||
'polymer': u'',
|
|
||||||
|
|
||||||
'rss': u'',
|
|
||||||
|
|
||||||
'share': u'',
|
|
||||||
|
|
||||||
'stackoverflow': u'',
|
|
||||||
|
|
||||||
'steam-square': u'',
|
|
||||||
|
|
||||||
'steam': u'',
|
|
||||||
|
|
||||||
'twitter-box': u'',
|
|
||||||
|
|
||||||
'twitter': u'',
|
|
||||||
|
|
||||||
'vk': u'',
|
|
||||||
|
|
||||||
'wikipedia': u'',
|
|
||||||
|
|
||||||
'windows': u'',
|
|
||||||
|
|
||||||
'500px': u'',
|
|
||||||
|
|
||||||
'8tracks': u'',
|
|
||||||
|
|
||||||
'amazon': u'',
|
|
||||||
|
|
||||||
'blogger': u'',
|
|
||||||
|
|
||||||
'delicious': u'',
|
|
||||||
|
|
||||||
'disqus': u'',
|
|
||||||
|
|
||||||
'flattr': u'',
|
|
||||||
|
|
||||||
'flickr': u'',
|
|
||||||
|
|
||||||
'github-alt': u'',
|
|
||||||
|
|
||||||
'google-old': u'',
|
|
||||||
|
|
||||||
'linkedin': u'',
|
|
||||||
|
|
||||||
'odnoklassniki': u'',
|
|
||||||
|
|
||||||
'outlook': u'',
|
|
||||||
|
|
||||||
'paypal-alt': u'',
|
|
||||||
|
|
||||||
'pinterest': u'',
|
|
||||||
|
|
||||||
'playstation': u'',
|
|
||||||
|
|
||||||
'reddit': u'',
|
|
||||||
|
|
||||||
'skype': u'',
|
|
||||||
|
|
||||||
'slideshare': u'',
|
|
||||||
|
|
||||||
'soundcloud': u'',
|
|
||||||
|
|
||||||
'tumblr': u'',
|
|
||||||
|
|
||||||
'twitch': u'',
|
|
||||||
|
|
||||||
'vimeo': u'',
|
|
||||||
|
|
||||||
'whatsapp': u'',
|
|
||||||
|
|
||||||
'xbox': u'',
|
|
||||||
|
|
||||||
'yahoo': u'',
|
|
||||||
|
|
||||||
'youtube-play': u'',
|
|
||||||
|
|
||||||
'youtube': u'',
|
|
||||||
|
|
||||||
'aspect-ratio-alt': u'',
|
|
||||||
|
|
||||||
'aspect-ratio': u'',
|
|
||||||
|
|
||||||
'blur-circular': u'',
|
|
||||||
|
|
||||||
'blur-linear': u'',
|
|
||||||
|
|
||||||
'blur-off': u'',
|
|
||||||
|
|
||||||
'blur': u'',
|
|
||||||
|
|
||||||
'brightness-2': u'',
|
|
||||||
|
|
||||||
'brightness-3': u'',
|
|
||||||
|
|
||||||
'brightness-4': u'',
|
|
||||||
|
|
||||||
'brightness-5': u'',
|
|
||||||
|
|
||||||
'brightness-6': u'',
|
|
||||||
|
|
||||||
'brightness-7': u'',
|
|
||||||
|
|
||||||
'brightness-auto': u'',
|
|
||||||
|
|
||||||
'brightness-setting': u'',
|
|
||||||
|
|
||||||
'broken-image': u'',
|
|
||||||
|
|
||||||
'center-focus-strong': u'',
|
|
||||||
|
|
||||||
'center-focus-weak': u'',
|
|
||||||
|
|
||||||
'compare': u'',
|
|
||||||
|
|
||||||
'crop-16-9': u'',
|
|
||||||
|
|
||||||
'crop-3-2': u'',
|
|
||||||
|
|
||||||
'crop-5-4': u'',
|
|
||||||
|
|
||||||
'crop-7-5': u'',
|
|
||||||
|
|
||||||
'crop-din': u'',
|
|
||||||
|
|
||||||
'crop-free': u'',
|
|
||||||
|
|
||||||
'crop-landscape': u'',
|
|
||||||
|
|
||||||
'crop-portrait': u'',
|
|
||||||
|
|
||||||
'crop-square': u'',
|
|
||||||
|
|
||||||
'exposure-alt': u'',
|
|
||||||
|
|
||||||
'exposure': u'',
|
|
||||||
|
|
||||||
'filter-b-and-w': u'',
|
|
||||||
|
|
||||||
'filter-center-focus': u'',
|
|
||||||
|
|
||||||
'filter-frames': u'',
|
|
||||||
|
|
||||||
'filter-tilt-shift': u'',
|
|
||||||
|
|
||||||
'gradient': u'',
|
|
||||||
|
|
||||||
'grain': u'',
|
|
||||||
|
|
||||||
'graphic-eq': u'',
|
|
||||||
|
|
||||||
'hdr-off': u'',
|
|
||||||
|
|
||||||
'hdr-strong': u'',
|
|
||||||
|
|
||||||
'hdr-weak': u'',
|
|
||||||
|
|
||||||
'hdr': u'',
|
|
||||||
|
|
||||||
'iridescent': u'',
|
|
||||||
|
|
||||||
'leak-off': u'',
|
|
||||||
|
|
||||||
'leak': u'',
|
|
||||||
|
|
||||||
'looks': u'',
|
|
||||||
|
|
||||||
'loupe': u'',
|
|
||||||
|
|
||||||
'panorama-horizontal': u'',
|
|
||||||
|
|
||||||
'panorama-vertical': u'',
|
|
||||||
|
|
||||||
'panorama-wide-angle': u'',
|
|
||||||
|
|
||||||
'photo-size-select-large': u'',
|
|
||||||
|
|
||||||
'photo-size-select-small': u'',
|
|
||||||
|
|
||||||
'picture-in-picture': u'',
|
|
||||||
|
|
||||||
'slideshow': u'',
|
|
||||||
|
|
||||||
'texture': u'',
|
|
||||||
|
|
||||||
'tonality': u'',
|
|
||||||
|
|
||||||
'vignette': u'',
|
|
||||||
|
|
||||||
'wb-auto': u'',
|
|
||||||
|
|
||||||
'eject-alt': u'',
|
|
||||||
|
|
||||||
'eject': u'',
|
|
||||||
|
|
||||||
'equalizer': u'',
|
|
||||||
|
|
||||||
'fast-forward': u'',
|
|
||||||
|
|
||||||
'fast-rewind': u'',
|
|
||||||
|
|
||||||
'forward-10': u'',
|
|
||||||
|
|
||||||
'forward-30': u'',
|
|
||||||
|
|
||||||
'forward-5': u'',
|
|
||||||
|
|
||||||
'hearing': u'',
|
|
||||||
|
|
||||||
'pause-circle-outline': u'',
|
|
||||||
|
|
||||||
'pause-circle': u'',
|
|
||||||
|
|
||||||
'pause': u'',
|
|
||||||
|
|
||||||
'play-circle-outline': u'',
|
|
||||||
|
|
||||||
'play-circle': u'',
|
|
||||||
|
|
||||||
'play': u'',
|
|
||||||
|
|
||||||
'playlist-audio': u'',
|
|
||||||
|
|
||||||
'playlist-plus': u'',
|
|
||||||
|
|
||||||
'repeat-one': u'',
|
|
||||||
|
|
||||||
'repeat': u'',
|
|
||||||
|
|
||||||
'replay-10': u'',
|
|
||||||
|
|
||||||
'replay-30': u'',
|
|
||||||
|
|
||||||
'replay-5': u'',
|
|
||||||
|
|
||||||
'replay': u'',
|
|
||||||
|
|
||||||
'shuffle': u'',
|
|
||||||
|
|
||||||
'skip-next': u'',
|
|
||||||
|
|
||||||
'skip-previous': u'',
|
|
||||||
|
|
||||||
'stop': u'',
|
|
||||||
|
|
||||||
'surround-sound': u'',
|
|
||||||
|
|
||||||
'tune': u'',
|
|
||||||
|
|
||||||
'volume-down': u'',
|
|
||||||
|
|
||||||
'volume-mute': u'',
|
|
||||||
|
|
||||||
'volume-off': u'',
|
|
||||||
|
|
||||||
'volume-up': u'',
|
|
||||||
|
|
||||||
'n-1-square': u'',
|
|
||||||
|
|
||||||
'n-2-square': u'',
|
|
||||||
|
|
||||||
'n-3-square': u'',
|
|
||||||
|
|
||||||
'n-4-square': u'',
|
|
||||||
|
|
||||||
'n-5-square': u'',
|
|
||||||
|
|
||||||
'n-6-square': u'',
|
|
||||||
|
|
||||||
'neg-1': u'',
|
|
||||||
|
|
||||||
'neg-2': u'',
|
|
||||||
|
|
||||||
'plus-1': u'',
|
|
||||||
|
|
||||||
'plus-2': u'',
|
|
||||||
|
|
||||||
'sec-10': u'',
|
|
||||||
|
|
||||||
'sec-3': u'',
|
|
||||||
|
|
||||||
'zero': u'',
|
|
||||||
|
|
||||||
'airline-seat-flat-angled': u'',
|
|
||||||
|
|
||||||
'airline-seat-flat': u'',
|
|
||||||
|
|
||||||
'airline-seat-individual-suite': u'',
|
|
||||||
|
|
||||||
'airline-seat-legroom-extra': u'',
|
|
||||||
|
|
||||||
'airline-seat-legroom-normal': u'',
|
|
||||||
|
|
||||||
'airline-seat-legroom-reduced': u'',
|
|
||||||
|
|
||||||
'airline-seat-recline-extra': u'',
|
|
||||||
|
|
||||||
'airline-seat-recline-normal': u'',
|
|
||||||
|
|
||||||
'airplay': u'',
|
|
||||||
|
|
||||||
'closed-caption': u'',
|
|
||||||
|
|
||||||
'confirmation-number': u'',
|
|
||||||
|
|
||||||
'developer-board': u'',
|
|
||||||
|
|
||||||
'disc-full': u'',
|
|
||||||
|
|
||||||
'explicit': u'',
|
|
||||||
|
|
||||||
'flight-land': u'',
|
|
||||||
|
|
||||||
'flight-takeoff': u'',
|
|
||||||
|
|
||||||
'flip-to-back': u'',
|
|
||||||
|
|
||||||
'flip-to-front': u'',
|
|
||||||
|
|
||||||
'group-work': u'',
|
|
||||||
|
|
||||||
'hd': u'',
|
|
||||||
|
|
||||||
'hq': u'',
|
|
||||||
|
|
||||||
'markunread-mailbox': u'',
|
|
||||||
|
|
||||||
'memory': u'',
|
|
||||||
|
|
||||||
'nfc': u'',
|
|
||||||
|
|
||||||
'play-for-work': u'',
|
|
||||||
|
|
||||||
'power-input': u'',
|
|
||||||
|
|
||||||
'present-to-all': u'',
|
|
||||||
|
|
||||||
'satellite': u'',
|
|
||||||
|
|
||||||
'tap-and-play': u'',
|
|
||||||
|
|
||||||
'vibration': u'',
|
|
||||||
|
|
||||||
'voicemail': u'',
|
|
||||||
}
|
|
Before Width: | Height: | Size: 30 KiB |
Before Width: | Height: | Size: 41 KiB |
Before Width: | Height: | Size: 29 KiB |
Before Width: | Height: | Size: 30 KiB |
Before Width: | Height: | Size: 19 KiB |
|
@ -1 +0,0 @@
|
||||||
{"quad_shadow-1.png": {"20": [2, 136, 128, 128], "21": [132, 136, 128, 128], "22": [262, 136, 128, 128], "23": [2, 6, 128, 128], "19": [132, 266, 128, 128], "18": [2, 266, 128, 128], "1": [262, 266, 128, 128], "3": [262, 6, 128, 128], "2": [132, 6, 128, 128]}, "quad_shadow-0.png": {"11": [262, 266, 128, 128], "10": [132, 266, 128, 128], "13": [132, 136, 128, 128], "12": [2, 136, 128, 128], "15": [2, 6, 128, 128], "14": [262, 136, 128, 128], "17": [262, 6, 128, 128], "16": [132, 6, 128, 128], "0": [2, 266, 128, 128]}, "quad_shadow-2.png": {"5": [132, 266, 128, 128], "4": [2, 266, 128, 128], "7": [2, 136, 128, 128], "6": [262, 266, 128, 128], "9": [262, 136, 128, 128], "8": [132, 136, 128, 128]}}
|
|
Before Width: | Height: | Size: 46 KiB |
Before Width: | Height: | Size: 43 KiB |
|
@ -1 +0,0 @@
|
||||||
{"rec_shadow-1.png": {"20": [2, 266, 256, 128], "21": [260, 266, 256, 128], "22": [518, 266, 256, 128], "23": [776, 266, 256, 128], "3": [260, 136, 256, 128], "2": [2, 136, 256, 128], "5": [776, 136, 256, 128], "4": [518, 136, 256, 128], "7": [260, 6, 256, 128], "6": [2, 6, 256, 128], "9": [776, 6, 256, 128], "8": [518, 6, 256, 128]}, "rec_shadow-0.png": {"11": [518, 266, 256, 128], "10": [260, 266, 256, 128], "13": [2, 136, 256, 128], "12": [776, 266, 256, 128], "15": [518, 136, 256, 128], "14": [260, 136, 256, 128], "17": [2, 6, 256, 128], "16": [776, 136, 256, 128], "19": [518, 6, 256, 128], "18": [260, 6, 256, 128], "1": [776, 6, 256, 128], "0": [2, 266, 256, 128]}}
|
|
Before Width: | Height: | Size: 30 KiB |
Before Width: | Height: | Size: 32 KiB |
Before Width: | Height: | Size: 28 KiB |
|
@ -1 +0,0 @@
|
||||||
{"rec_st_shadow-0.png": {"11": [262, 138, 128, 256], "10": [132, 138, 128, 256], "13": [522, 138, 128, 256], "12": [392, 138, 128, 256], "15": [782, 138, 128, 256], "14": [652, 138, 128, 256], "16": [912, 138, 128, 256], "0": [2, 138, 128, 256]}, "rec_st_shadow-1.png": {"20": [522, 138, 128, 256], "21": [652, 138, 128, 256], "17": [2, 138, 128, 256], "23": [912, 138, 128, 256], "19": [262, 138, 128, 256], "18": [132, 138, 128, 256], "22": [782, 138, 128, 256], "1": [392, 138, 128, 256]}, "rec_st_shadow-2.png": {"3": [132, 138, 128, 256], "2": [2, 138, 128, 256], "5": [392, 138, 128, 256], "4": [262, 138, 128, 256], "7": [652, 138, 128, 256], "6": [522, 138, 128, 256], "9": [912, 138, 128, 256], "8": [782, 138, 128, 256]}}
|
|
Before Width: | Height: | Size: 39 KiB |
Before Width: | Height: | Size: 40 KiB |
Before Width: | Height: | Size: 26 KiB |
|
@ -1 +0,0 @@
|
||||||
{"round_shadow-1.png": {"20": [2, 136, 128, 128], "21": [132, 136, 128, 128], "22": [262, 136, 128, 128], "23": [2, 6, 128, 128], "19": [132, 266, 128, 128], "18": [2, 266, 128, 128], "1": [262, 266, 128, 128], "3": [262, 6, 128, 128], "2": [132, 6, 128, 128]}, "round_shadow-0.png": {"11": [262, 266, 128, 128], "10": [132, 266, 128, 128], "13": [132, 136, 128, 128], "12": [2, 136, 128, 128], "15": [2, 6, 128, 128], "14": [262, 136, 128, 128], "17": [262, 6, 128, 128], "16": [132, 6, 128, 128], "0": [2, 266, 128, 128]}, "round_shadow-2.png": {"5": [132, 266, 128, 128], "4": [2, 266, 128, 128], "7": [2, 136, 128, 128], "6": [262, 266, 128, 128], "9": [262, 136, 128, 128], "8": [132, 136, 128, 128]}}
|
|
|
@ -1,94 +0,0 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
from kivy.lang import Builder
|
|
||||||
from kivy.metrics import sp
|
|
||||||
from kivy.properties import OptionProperty, DictProperty, ListProperty
|
|
||||||
from kivy.uix.label import Label
|
|
||||||
from kivymd.material_resources import DEVICE_TYPE
|
|
||||||
from kivymd.theming import ThemableBehavior
|
|
||||||
|
|
||||||
Builder.load_string('''
|
|
||||||
<MDLabel>
|
|
||||||
disabled_color: self.theme_cls.disabled_hint_text_color
|
|
||||||
text_size: (self.width, None)
|
|
||||||
''')
|
|
||||||
|
|
||||||
|
|
||||||
class MDLabel(ThemableBehavior, Label):
|
|
||||||
font_style = OptionProperty(
|
|
||||||
'Body1', options=['Body1', 'Body2', 'Caption', 'Subhead', 'Title',
|
|
||||||
'Headline', 'Display1', 'Display2', 'Display3',
|
|
||||||
'Display4', 'Button', 'Icon'])
|
|
||||||
|
|
||||||
# Font, Bold, Mobile size, Desktop size (None if same as Mobile)
|
|
||||||
_font_styles = DictProperty({'Body1': ['Roboto', False, 14, 13],
|
|
||||||
'Body2': ['Roboto', True, 14, 13],
|
|
||||||
'Caption': ['Roboto', False, 12, None],
|
|
||||||
'Subhead': ['Roboto', False, 16, 15],
|
|
||||||
'Title': ['Roboto', True, 20, None],
|
|
||||||
'Headline': ['Roboto', False, 24, None],
|
|
||||||
'Display1': ['Roboto', False, 34, None],
|
|
||||||
'Display2': ['Roboto', False, 45, None],
|
|
||||||
'Display3': ['Roboto', False, 56, None],
|
|
||||||
'Display4': ['RobotoLight', False, 112, None],
|
|
||||||
'Button': ['Roboto', True, 14, None],
|
|
||||||
'Icon': ['Icons', False, 24, None]})
|
|
||||||
|
|
||||||
theme_text_color = OptionProperty(None, allownone=True,
|
|
||||||
options=['Primary', 'Secondary', 'Hint',
|
|
||||||
'Error', 'Custom'])
|
|
||||||
|
|
||||||
text_color = ListProperty(None, allownone=True)
|
|
||||||
|
|
||||||
_currently_bound_property = {}
|
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
|
||||||
super(MDLabel, self).__init__(**kwargs)
|
|
||||||
self.on_theme_text_color(None, self.theme_text_color)
|
|
||||||
self.on_font_style(None, self.font_style)
|
|
||||||
self.on_opposite_colors(None, self.opposite_colors)
|
|
||||||
|
|
||||||
def on_font_style(self, instance, style):
|
|
||||||
info = self._font_styles[style]
|
|
||||||
self.font_name = info[0]
|
|
||||||
self.bold = info[1]
|
|
||||||
if DEVICE_TYPE == 'desktop' and info[3] is not None:
|
|
||||||
self.font_size = sp(info[3])
|
|
||||||
else:
|
|
||||||
self.font_size = sp(info[2])
|
|
||||||
|
|
||||||
def on_theme_text_color(self, instance, value):
|
|
||||||
t = self.theme_cls
|
|
||||||
op = self.opposite_colors
|
|
||||||
setter = self.setter('color')
|
|
||||||
t.unbind(**self._currently_bound_property)
|
|
||||||
c = {}
|
|
||||||
if value == 'Primary':
|
|
||||||
c = {'text_color' if not op else 'opposite_text_color': setter}
|
|
||||||
t.bind(**c)
|
|
||||||
self.color = t.text_color if not op else t.opposite_text_color
|
|
||||||
elif value == 'Secondary':
|
|
||||||
c = {'secondary_text_color' if not op else
|
|
||||||
'opposite_secondary_text_color': setter}
|
|
||||||
t.bind(**c)
|
|
||||||
self.color = t.secondary_text_color if not op else \
|
|
||||||
t.opposite_secondary_text_color
|
|
||||||
elif value == 'Hint':
|
|
||||||
c = {'disabled_hint_text_color' if not op else
|
|
||||||
'opposite_disabled_hint_text_color': setter}
|
|
||||||
t.bind(**c)
|
|
||||||
self.color = t.disabled_hint_text_color if not op else \
|
|
||||||
t.opposite_disabled_hint_text_color
|
|
||||||
elif value == 'Error':
|
|
||||||
c = {'error_color': setter}
|
|
||||||
t.bind(**c)
|
|
||||||
self.color = t.error_color
|
|
||||||
elif value == 'Custom':
|
|
||||||
self.color = self.text_color if self.text_color else (0, 0, 0, 1)
|
|
||||||
self._currently_bound_property = c
|
|
||||||
|
|
||||||
def on_text_color(self, *args):
|
|
||||||
if self.theme_text_color == 'Custom':
|
|
||||||
self.color = self.text_color
|
|
||||||
|
|
||||||
def on_opposite_colors(self, instance, value):
|
|
||||||
self.on_theme_text_color(self, self.theme_text_color)
|
|
|
@ -1,531 +0,0 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
'''
|
|
||||||
Lists
|
|
||||||
=====
|
|
||||||
|
|
||||||
`Material Design spec, Lists page <https://www.google.com/design/spec/components/lists.html>`_
|
|
||||||
|
|
||||||
`Material Design spec, Lists: Controls page <https://www.google.com/design/spec/components/lists-controls.html>`_
|
|
||||||
|
|
||||||
The class :class:`MDList` in combination with a ListItem like
|
|
||||||
:class:`OneLineListItem` will create a list that expands as items are added to
|
|
||||||
it, working nicely with Kivy's :class:`~kivy.uix.scrollview.ScrollView`.
|
|
||||||
|
|
||||||
|
|
||||||
Simple examples
|
|
||||||
---------------
|
|
||||||
|
|
||||||
Kv Lang:
|
|
||||||
|
|
||||||
.. code-block:: python
|
|
||||||
|
|
||||||
ScrollView:
|
|
||||||
do_scroll_x: False # Important for MD compliance
|
|
||||||
MDList:
|
|
||||||
OneLineListItem:
|
|
||||||
text: "Single-line item"
|
|
||||||
TwoLineListItem:
|
|
||||||
text: "Two-line item"
|
|
||||||
secondary_text: "Secondary text here"
|
|
||||||
ThreeLineListItem:
|
|
||||||
text: "Three-line item"
|
|
||||||
secondary_text: "This is a multi-line label where you can fit more text than usual"
|
|
||||||
|
|
||||||
|
|
||||||
Python:
|
|
||||||
|
|
||||||
.. code-block:: python
|
|
||||||
|
|
||||||
# Sets up ScrollView with MDList, as normally used in Android:
|
|
||||||
sv = ScrollView()
|
|
||||||
ml = MDList()
|
|
||||||
sv.add_widget(ml)
|
|
||||||
|
|
||||||
contacts = ["Paula", "John", "Kate", "Vlad"]
|
|
||||||
for c in contacts:
|
|
||||||
ml.add_widget(
|
|
||||||
OneLineListItem(
|
|
||||||
text=c
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
Advanced usage
|
|
||||||
--------------
|
|
||||||
|
|
||||||
Due to the variety in sizes and controls in the MD spec, this module suffers
|
|
||||||
from a certain level of complexity to keep the widgets compliant, flexible
|
|
||||||
and performant.
|
|
||||||
|
|
||||||
For this KivyMD provides ListItems that try to cover the most common usecases,
|
|
||||||
when those are insufficient, there's a base class called :class:`ListItem`
|
|
||||||
which you can use to create your own ListItems. This documentation will only
|
|
||||||
cover the provided ones, for custom implementations please refer to this
|
|
||||||
module's source code.
|
|
||||||
|
|
||||||
Text only ListItems
|
|
||||||
-------------------
|
|
||||||
|
|
||||||
- :class:`~OneLineListItem`
|
|
||||||
- :class:`~TwoLineListItem`
|
|
||||||
- :class:`~ThreeLineListItem`
|
|
||||||
|
|
||||||
These are the simplest ones. The :attr:`~ListItem.text` attribute changes the
|
|
||||||
text in the most prominent line, while :attr:`~ListItem.secondary_text`
|
|
||||||
changes the second and third line.
|
|
||||||
|
|
||||||
If there are only two lines, :attr:`~ListItem.secondary_text` will shorten
|
|
||||||
the text to fit in case it is too long; if a third line is available, it will
|
|
||||||
instead wrap the text to make use of it.
|
|
||||||
|
|
||||||
ListItems with widget containers
|
|
||||||
--------------------------------
|
|
||||||
|
|
||||||
- :class:`~OneLineAvatarListItem`
|
|
||||||
- :class:`~TwoLineAvatarListItem`
|
|
||||||
- :class:`~ThreeLineAvatarListItem`
|
|
||||||
- :class:`~OneLineIconListItem`
|
|
||||||
- :class:`~TwoLineIconListItem`
|
|
||||||
- :class:`~ThreeLineIconListItem`
|
|
||||||
- :class:`~OneLineAvatarIconListItem`
|
|
||||||
- :class:`~TwoLineAvatarIconListItem`
|
|
||||||
- :class:`~ThreeLineAvatarIconListItem`
|
|
||||||
|
|
||||||
These widgets will take other widgets that inherit from :class:`~ILeftBody`,
|
|
||||||
:class:`ILeftBodyTouch`, :class:`~IRightBody` or :class:`~IRightBodyTouch` and
|
|
||||||
put them in their corresponding container.
|
|
||||||
|
|
||||||
As the name implies, :class:`~ILeftBody` and :class:`~IRightBody` will signal
|
|
||||||
that the widget goes into the left or right container, respectively.
|
|
||||||
|
|
||||||
:class:`~ILeftBodyTouch` and :class:`~IRightBodyTouch` do the same thing,
|
|
||||||
except these widgets will also receive touch events that occur within their
|
|
||||||
surfaces.
|
|
||||||
|
|
||||||
Python example:
|
|
||||||
|
|
||||||
.. code-block:: python
|
|
||||||
|
|
||||||
class ContactPhoto(ILeftBody, AsyncImage):
|
|
||||||
pass
|
|
||||||
|
|
||||||
class MessageButton(IRightBodyTouch, MDIconButton):
|
|
||||||
phone_number = StringProperty()
|
|
||||||
|
|
||||||
def on_release(self):
|
|
||||||
# sample code:
|
|
||||||
Dialer.send_sms(phone_number, "Hey! What's up?")
|
|
||||||
pass
|
|
||||||
|
|
||||||
# Sets up ScrollView with MDList, as normally used in Android:
|
|
||||||
sv = ScrollView()
|
|
||||||
ml = MDList()
|
|
||||||
sv.add_widget(ml)
|
|
||||||
|
|
||||||
contacts = [
|
|
||||||
["Annie", "555-24235", "http://myphotos.com/annie.png"],
|
|
||||||
["Bob", "555-15423", "http://myphotos.com/bob.png"],
|
|
||||||
["Claire", "555-66098", "http://myphotos.com/claire.png"]
|
|
||||||
]
|
|
||||||
|
|
||||||
for c in contacts:
|
|
||||||
item = TwoLineAvatarIconListItem(
|
|
||||||
text=c[0],
|
|
||||||
secondary_text=c[1]
|
|
||||||
)
|
|
||||||
item.add_widget(ContactPhoto(source=c[2]))
|
|
||||||
item.add_widget(MessageButton(phone_number=c[1])
|
|
||||||
ml.add_widget(item)
|
|
||||||
|
|
||||||
API
|
|
||||||
---
|
|
||||||
'''
|
|
||||||
|
|
||||||
from kivy.lang import Builder
|
|
||||||
from kivy.metrics import dp
|
|
||||||
from kivy.properties import ObjectProperty, StringProperty, NumericProperty, \
|
|
||||||
ListProperty, OptionProperty
|
|
||||||
from kivy.uix.behaviors import ButtonBehavior
|
|
||||||
from kivy.uix.floatlayout import FloatLayout
|
|
||||||
from kivy.uix.gridlayout import GridLayout
|
|
||||||
import kivymd.material_resources as m_res
|
|
||||||
from kivymd.ripplebehavior import RectangularRippleBehavior
|
|
||||||
from kivymd.theming import ThemableBehavior
|
|
||||||
|
|
||||||
Builder.load_string('''
|
|
||||||
#:import m_res kivymd.material_resources
|
|
||||||
<MDList>
|
|
||||||
cols: 1
|
|
||||||
size_hint_y: None
|
|
||||||
height: self._min_list_height
|
|
||||||
padding: 0, self._list_vertical_padding
|
|
||||||
|
|
||||||
<BaseListItem>
|
|
||||||
size_hint_y: None
|
|
||||||
canvas:
|
|
||||||
Color:
|
|
||||||
rgba: self.theme_cls.divider_color
|
|
||||||
Line:
|
|
||||||
points: root.x,root.y, root.x+self.width,root.y
|
|
||||||
BoxLayout:
|
|
||||||
id: _text_container
|
|
||||||
orientation: 'vertical'
|
|
||||||
pos: root.pos
|
|
||||||
padding: root._txt_left_pad, root._txt_top_pad, root._txt_right_pad, root._txt_bot_pad
|
|
||||||
MDLabel:
|
|
||||||
id: _lbl_primary
|
|
||||||
text: root.text
|
|
||||||
font_style: root.font_style
|
|
||||||
theme_text_color: root.theme_text_color
|
|
||||||
text_color: root.text_color
|
|
||||||
size_hint_y: None
|
|
||||||
height: self.texture_size[1]
|
|
||||||
MDLabel:
|
|
||||||
id: _lbl_secondary
|
|
||||||
text: '' if root._num_lines == 1 else root.secondary_text
|
|
||||||
font_style: root.secondary_font_style
|
|
||||||
theme_text_color: root.secondary_theme_text_color
|
|
||||||
text_color: root.secondary_text_color
|
|
||||||
size_hint_y: None
|
|
||||||
height: 0 if root._num_lines == 1 else self.texture_size[1]
|
|
||||||
shorten: True if root._num_lines == 2 else False
|
|
||||||
|
|
||||||
<OneLineAvatarListItem>
|
|
||||||
BoxLayout:
|
|
||||||
id: _left_container
|
|
||||||
size_hint: None, None
|
|
||||||
x: root.x + dp(16)
|
|
||||||
y: root.y + root.height/2 - self.height/2
|
|
||||||
size: dp(40), dp(40)
|
|
||||||
|
|
||||||
<ThreeLineAvatarListItem>
|
|
||||||
BoxLayout:
|
|
||||||
id: _left_container
|
|
||||||
size_hint: None, None
|
|
||||||
x: root.x + dp(16)
|
|
||||||
y: root.y + root.height - root._txt_top_pad - self.height - dp(5)
|
|
||||||
size: dp(40), dp(40)
|
|
||||||
|
|
||||||
<OneLineIconListItem>
|
|
||||||
BoxLayout:
|
|
||||||
id: _left_container
|
|
||||||
size_hint: None, None
|
|
||||||
x: root.x + dp(16)
|
|
||||||
y: root.y + root.height/2 - self.height/2
|
|
||||||
size: dp(48), dp(48)
|
|
||||||
|
|
||||||
<ThreeLineIconListItem>
|
|
||||||
BoxLayout:
|
|
||||||
id: _left_container
|
|
||||||
size_hint: None, None
|
|
||||||
x: root.x + dp(16)
|
|
||||||
y: root.y + root.height - root._txt_top_pad - self.height - dp(5)
|
|
||||||
size: dp(48), dp(48)
|
|
||||||
|
|
||||||
<OneLineRightIconListItem>
|
|
||||||
BoxLayout:
|
|
||||||
id: _right_container
|
|
||||||
size_hint: None, None
|
|
||||||
x: root.x + root.width - m_res.HORIZ_MARGINS - self.width
|
|
||||||
y: root.y + root.height/2 - self.height/2
|
|
||||||
size: dp(48), dp(48)
|
|
||||||
|
|
||||||
<ThreeLineRightIconListItem>
|
|
||||||
BoxLayout:
|
|
||||||
id: _right_container
|
|
||||||
size_hint: None, None
|
|
||||||
x: root.x + root.width - m_res.HORIZ_MARGINS - self.width
|
|
||||||
y: root.y + root.height/2 - self.height/2
|
|
||||||
size: dp(48), dp(48)
|
|
||||||
|
|
||||||
<OneLineAvatarIconListItem>
|
|
||||||
BoxLayout:
|
|
||||||
id: _right_container
|
|
||||||
size_hint: None, None
|
|
||||||
x: root.x + root.width - m_res.HORIZ_MARGINS - self.width
|
|
||||||
y: root.y + root.height/2 - self.height/2
|
|
||||||
size: dp(48), dp(48)
|
|
||||||
|
|
||||||
<TwoLineAvatarIconListItem>
|
|
||||||
BoxLayout:
|
|
||||||
id: _right_container
|
|
||||||
size_hint: None, None
|
|
||||||
x: root.x + root.width - m_res.HORIZ_MARGINS - self.width
|
|
||||||
y: root.y + root.height/2 - self.height/2
|
|
||||||
size: dp(48), dp(48)
|
|
||||||
|
|
||||||
<ThreeLineAvatarIconListItem>
|
|
||||||
BoxLayout:
|
|
||||||
id: _right_container
|
|
||||||
size_hint: None, None
|
|
||||||
x: root.x + root.width - m_res.HORIZ_MARGINS - self.width
|
|
||||||
y: root.y + root.height - root._txt_top_pad - self.height - dp(5)
|
|
||||||
size: dp(48), dp(48)
|
|
||||||
''')
|
|
||||||
|
|
||||||
|
|
||||||
class MDList(GridLayout):
|
|
||||||
'''ListItem container. Best used in conjunction with a
|
|
||||||
:class:`kivy.uix.ScrollView`.
|
|
||||||
|
|
||||||
When adding (or removing) a widget, it will resize itself to fit its
|
|
||||||
children, plus top and bottom paddings as described by the MD spec.
|
|
||||||
'''
|
|
||||||
selected = ObjectProperty()
|
|
||||||
_min_list_height = dp(16)
|
|
||||||
_list_vertical_padding = dp(8)
|
|
||||||
|
|
||||||
icon = StringProperty()
|
|
||||||
|
|
||||||
def add_widget(self, widget, index=0):
|
|
||||||
super(MDList, self).add_widget(widget, index)
|
|
||||||
self.height += widget.height
|
|
||||||
|
|
||||||
def remove_widget(self, widget):
|
|
||||||
super(MDList, self).remove_widget(widget)
|
|
||||||
self.height -= widget.height
|
|
||||||
|
|
||||||
|
|
||||||
class BaseListItem(ThemableBehavior, RectangularRippleBehavior,
|
|
||||||
ButtonBehavior, FloatLayout):
|
|
||||||
'''Base class to all ListItems. Not supposed to be instantiated on its own.
|
|
||||||
'''
|
|
||||||
|
|
||||||
text = StringProperty()
|
|
||||||
'''Text shown in the first line.
|
|
||||||
|
|
||||||
:attr:`text` is a :class:`~kivy.properties.StringProperty` and defaults
|
|
||||||
to "".
|
|
||||||
'''
|
|
||||||
|
|
||||||
text_color = ListProperty(None)
|
|
||||||
''' Text color used if theme_text_color is set to 'Custom' '''
|
|
||||||
|
|
||||||
font_style = OptionProperty(
|
|
||||||
'Subhead', options=['Body1', 'Body2', 'Caption', 'Subhead', 'Title',
|
|
||||||
'Headline', 'Display1', 'Display2', 'Display3',
|
|
||||||
'Display4', 'Button', 'Icon'])
|
|
||||||
|
|
||||||
theme_text_color = StringProperty('Primary',allownone=True)
|
|
||||||
''' Theme text color for primary text '''
|
|
||||||
|
|
||||||
secondary_text = StringProperty()
|
|
||||||
'''Text shown in the second and potentially third line.
|
|
||||||
|
|
||||||
The text will wrap into the third line if the ListItem's type is set to
|
|
||||||
\'one-line\'. It can be forced into the third line by adding a \\n
|
|
||||||
escape sequence.
|
|
||||||
|
|
||||||
:attr:`secondary_text` is a :class:`~kivy.properties.StringProperty` and
|
|
||||||
defaults to "".
|
|
||||||
'''
|
|
||||||
|
|
||||||
secondary_text_color = ListProperty(None)
|
|
||||||
''' Text color used for secondary text if secondary_theme_text_color
|
|
||||||
is set to 'Custom' '''
|
|
||||||
|
|
||||||
secondary_theme_text_color = StringProperty('Secondary',allownone=True)
|
|
||||||
''' Theme text color for secondary primary text '''
|
|
||||||
|
|
||||||
secondary_font_style = OptionProperty(
|
|
||||||
'Body1', options=['Body1', 'Body2', 'Caption', 'Subhead', 'Title',
|
|
||||||
'Headline', 'Display1', 'Display2', 'Display3',
|
|
||||||
'Display4', 'Button', 'Icon'])
|
|
||||||
|
|
||||||
_txt_left_pad = NumericProperty(dp(16))
|
|
||||||
_txt_top_pad = NumericProperty()
|
|
||||||
_txt_bot_pad = NumericProperty()
|
|
||||||
_txt_right_pad = NumericProperty(m_res.HORIZ_MARGINS)
|
|
||||||
_num_lines = 2
|
|
||||||
|
|
||||||
|
|
||||||
class ILeftBody:
|
|
||||||
'''Pseudo-interface for widgets that go in the left container for
|
|
||||||
ListItems that support it.
|
|
||||||
|
|
||||||
Implements nothing and requires no implementation, for annotation only.
|
|
||||||
'''
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class ILeftBodyTouch:
|
|
||||||
'''Same as :class:`~ILeftBody`, but allows the widget to receive touch
|
|
||||||
events instead of triggering the ListItem's ripple effect
|
|
||||||
'''
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class IRightBody:
|
|
||||||
'''Pseudo-interface for widgets that go in the right container for
|
|
||||||
ListItems that support it.
|
|
||||||
|
|
||||||
Implements nothing and requires no implementation, for annotation only.
|
|
||||||
'''
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class IRightBodyTouch:
|
|
||||||
'''Same as :class:`~IRightBody`, but allows the widget to receive touch
|
|
||||||
events instead of triggering the ListItem's ripple effect
|
|
||||||
'''
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class ContainerSupport:
|
|
||||||
'''Overrides add_widget in a ListItem to include support for I*Body
|
|
||||||
widgets when the appropiate containers are present.
|
|
||||||
'''
|
|
||||||
_touchable_widgets = ListProperty()
|
|
||||||
|
|
||||||
def add_widget(self, widget, index=0):
|
|
||||||
if issubclass(widget.__class__, ILeftBody):
|
|
||||||
self.ids['_left_container'].add_widget(widget)
|
|
||||||
elif issubclass(widget.__class__, ILeftBodyTouch):
|
|
||||||
self.ids['_left_container'].add_widget(widget)
|
|
||||||
self._touchable_widgets.append(widget)
|
|
||||||
elif issubclass(widget.__class__, IRightBody):
|
|
||||||
self.ids['_right_container'].add_widget(widget)
|
|
||||||
elif issubclass(widget.__class__, IRightBodyTouch):
|
|
||||||
self.ids['_right_container'].add_widget(widget)
|
|
||||||
self._touchable_widgets.append(widget)
|
|
||||||
else:
|
|
||||||
return super(BaseListItem, self).add_widget(widget,index)
|
|
||||||
|
|
||||||
def remove_widget(self, widget):
|
|
||||||
super(BaseListItem, self).remove_widget(widget)
|
|
||||||
if widget in self._touchable_widgets:
|
|
||||||
self._touchable_widgets.remove(widget)
|
|
||||||
|
|
||||||
def on_touch_down(self, touch):
|
|
||||||
if self.propagate_touch_to_touchable_widgets(touch, 'down'):
|
|
||||||
return
|
|
||||||
super(BaseListItem, self).on_touch_down(touch)
|
|
||||||
|
|
||||||
def on_touch_move(self, touch, *args):
|
|
||||||
if self.propagate_touch_to_touchable_widgets(touch, 'move', *args):
|
|
||||||
return
|
|
||||||
super(BaseListItem, self).on_touch_move(touch, *args)
|
|
||||||
|
|
||||||
def on_touch_up(self, touch):
|
|
||||||
if self.propagate_touch_to_touchable_widgets(touch, 'up'):
|
|
||||||
return
|
|
||||||
super(BaseListItem, self).on_touch_up(touch)
|
|
||||||
|
|
||||||
def propagate_touch_to_touchable_widgets(self, touch, touch_event, *args):
|
|
||||||
triggered = False
|
|
||||||
for i in self._touchable_widgets:
|
|
||||||
if i.collide_point(touch.x, touch.y):
|
|
||||||
triggered = True
|
|
||||||
if touch_event == 'down':
|
|
||||||
i.on_touch_down(touch)
|
|
||||||
elif touch_event == 'move':
|
|
||||||
i.on_touch_move(touch, *args)
|
|
||||||
elif touch_event == 'up':
|
|
||||||
i.on_touch_up(touch)
|
|
||||||
return triggered
|
|
||||||
|
|
||||||
|
|
||||||
class OneLineListItem(BaseListItem):
|
|
||||||
_txt_top_pad = NumericProperty(dp(16))
|
|
||||||
_txt_bot_pad = NumericProperty(dp(15)) # dp(20) - dp(5)
|
|
||||||
_num_lines = 1
|
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
|
||||||
super(OneLineListItem, self).__init__(**kwargs)
|
|
||||||
self.height = dp(48)
|
|
||||||
|
|
||||||
|
|
||||||
class TwoLineListItem(BaseListItem):
|
|
||||||
_txt_top_pad = NumericProperty(dp(20))
|
|
||||||
_txt_bot_pad = NumericProperty(dp(15)) # dp(20) - dp(5)
|
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
|
||||||
super(TwoLineListItem, self).__init__(**kwargs)
|
|
||||||
self.height = dp(72)
|
|
||||||
|
|
||||||
|
|
||||||
class ThreeLineListItem(BaseListItem):
|
|
||||||
_txt_top_pad = NumericProperty(dp(16))
|
|
||||||
_txt_bot_pad = NumericProperty(dp(15)) # dp(20) - dp(5)
|
|
||||||
_num_lines = 3
|
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
|
||||||
super(ThreeLineListItem, self).__init__(**kwargs)
|
|
||||||
self.height = dp(88)
|
|
||||||
|
|
||||||
|
|
||||||
class OneLineAvatarListItem(ContainerSupport, BaseListItem):
|
|
||||||
_txt_left_pad = NumericProperty(dp(72))
|
|
||||||
_txt_top_pad = NumericProperty(dp(20))
|
|
||||||
_txt_bot_pad = NumericProperty(dp(19)) # dp(24) - dp(5)
|
|
||||||
_num_lines = 1
|
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
|
||||||
super(OneLineAvatarListItem, self).__init__(**kwargs)
|
|
||||||
self.height = dp(56)
|
|
||||||
|
|
||||||
|
|
||||||
class TwoLineAvatarListItem(OneLineAvatarListItem):
|
|
||||||
_txt_top_pad = NumericProperty(dp(20))
|
|
||||||
_txt_bot_pad = NumericProperty(dp(15)) # dp(20) - dp(5)
|
|
||||||
_num_lines = 2
|
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
|
||||||
super(BaseListItem, self).__init__(**kwargs)
|
|
||||||
self.height = dp(72)
|
|
||||||
|
|
||||||
|
|
||||||
class ThreeLineAvatarListItem(ContainerSupport, ThreeLineListItem):
|
|
||||||
_txt_left_pad = NumericProperty(dp(72))
|
|
||||||
|
|
||||||
|
|
||||||
class OneLineIconListItem(ContainerSupport, OneLineListItem):
|
|
||||||
_txt_left_pad = NumericProperty(dp(72))
|
|
||||||
|
|
||||||
|
|
||||||
class TwoLineIconListItem(OneLineIconListItem):
|
|
||||||
_txt_top_pad = NumericProperty(dp(20))
|
|
||||||
_txt_bot_pad = NumericProperty(dp(15)) # dp(20) - dp(5)
|
|
||||||
_num_lines = 2
|
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
|
||||||
super(BaseListItem, self).__init__(**kwargs)
|
|
||||||
self.height = dp(72)
|
|
||||||
|
|
||||||
|
|
||||||
class ThreeLineIconListItem(ContainerSupport, ThreeLineListItem):
|
|
||||||
_txt_left_pad = NumericProperty(dp(72))
|
|
||||||
|
|
||||||
|
|
||||||
class OneLineRightIconListItem(ContainerSupport, OneLineListItem):
|
|
||||||
# dp(40) = dp(16) + dp(24):
|
|
||||||
_txt_right_pad = NumericProperty(dp(40) + m_res.HORIZ_MARGINS)
|
|
||||||
|
|
||||||
|
|
||||||
class TwoLineRightIconListItem(OneLineRightIconListItem):
|
|
||||||
_txt_top_pad = NumericProperty(dp(20))
|
|
||||||
_txt_bot_pad = NumericProperty(dp(15)) # dp(20) - dp(5)
|
|
||||||
_num_lines = 2
|
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
|
||||||
super(BaseListItem, self).__init__(**kwargs)
|
|
||||||
self.height = dp(72)
|
|
||||||
|
|
||||||
|
|
||||||
class ThreeLineRightIconListitem(ContainerSupport, ThreeLineListItem):
|
|
||||||
# dp(40) = dp(16) + dp(24):
|
|
||||||
_txt_right_pad = NumericProperty(dp(40) + m_res.HORIZ_MARGINS)
|
|
||||||
|
|
||||||
|
|
||||||
class OneLineAvatarIconListItem(OneLineAvatarListItem):
|
|
||||||
# dp(40) = dp(16) + dp(24):
|
|
||||||
_txt_right_pad = NumericProperty(dp(40) + m_res.HORIZ_MARGINS)
|
|
||||||
|
|
||||||
|
|
||||||
class TwoLineAvatarIconListItem(TwoLineAvatarListItem):
|
|
||||||
# dp(40) = dp(16) + dp(24):
|
|
||||||
_txt_right_pad = NumericProperty(dp(40) + m_res.HORIZ_MARGINS)
|
|
||||||
|
|
||||||
|
|
||||||
class ThreeLineAvatarIconListItem(ThreeLineAvatarListItem):
|
|
||||||
# dp(40) = dp(16) + dp(24):
|
|
||||||
_txt_right_pad = NumericProperty(dp(40) + m_res.HORIZ_MARGINS)
|
|
|
@ -1,50 +0,0 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
from kivy import platform
|
|
||||||
from kivy.core.window import Window
|
|
||||||
from kivy.metrics import dp
|
|
||||||
from kivymd import fonts_path
|
|
||||||
|
|
||||||
# Feel free to override this const if you're designing for a device such as
|
|
||||||
# a GNU/Linux tablet.
|
|
||||||
if platform != "android" and platform != "ios":
|
|
||||||
DEVICE_TYPE = "desktop"
|
|
||||||
elif Window.width >= dp(600) and Window.height >= dp(600):
|
|
||||||
DEVICE_TYPE = "tablet"
|
|
||||||
else:
|
|
||||||
DEVICE_TYPE = "mobile"
|
|
||||||
|
|
||||||
if DEVICE_TYPE == "mobile":
|
|
||||||
MAX_NAV_DRAWER_WIDTH = dp(300)
|
|
||||||
HORIZ_MARGINS = dp(16)
|
|
||||||
STANDARD_INCREMENT = dp(56)
|
|
||||||
PORTRAIT_TOOLBAR_HEIGHT = STANDARD_INCREMENT
|
|
||||||
LANDSCAPE_TOOLBAR_HEIGHT = STANDARD_INCREMENT - dp(8)
|
|
||||||
else:
|
|
||||||
MAX_NAV_DRAWER_WIDTH = dp(400)
|
|
||||||
HORIZ_MARGINS = dp(24)
|
|
||||||
STANDARD_INCREMENT = dp(64)
|
|
||||||
PORTRAIT_TOOLBAR_HEIGHT = STANDARD_INCREMENT
|
|
||||||
LANDSCAPE_TOOLBAR_HEIGHT = STANDARD_INCREMENT
|
|
||||||
|
|
||||||
TOUCH_TARGET_HEIGHT = dp(48)
|
|
||||||
|
|
||||||
FONTS = [
|
|
||||||
{
|
|
||||||
"name": "Roboto",
|
|
||||||
"fn_regular": fonts_path + 'Roboto-Regular.ttf',
|
|
||||||
"fn_bold": fonts_path + 'Roboto-Medium.ttf',
|
|
||||||
"fn_italic": fonts_path + 'Roboto-Italic.ttf',
|
|
||||||
"fn_bolditalic": fonts_path + 'Roboto-MediumItalic.ttf'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "RobotoLight",
|
|
||||||
"fn_regular": fonts_path + 'Roboto-Thin.ttf',
|
|
||||||
"fn_bold": fonts_path + 'Roboto-Light.ttf',
|
|
||||||
"fn_italic": fonts_path + 'Roboto-ThinItalic.ttf',
|
|
||||||
"fn_bolditalic": fonts_path + 'Roboto-LightItalic.ttf'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Icons",
|
|
||||||
"fn_regular": fonts_path + 'Material-Design-Iconic-Font.ttf'
|
|
||||||
}
|
|
||||||
]
|
|
|
@ -1,192 +0,0 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
from kivy.animation import Animation
|
|
||||||
from kivy.clock import Clock
|
|
||||||
from kivy.core.window import Window
|
|
||||||
from kivy.lang import Builder
|
|
||||||
from kivy.garden.recycleview import RecycleView
|
|
||||||
from kivy.metrics import dp
|
|
||||||
from kivy.properties import NumericProperty, ListProperty, OptionProperty, \
|
|
||||||
StringProperty
|
|
||||||
from kivy.uix.behaviors import ButtonBehavior
|
|
||||||
from kivy.uix.boxlayout import BoxLayout
|
|
||||||
import kivymd.material_resources as m_res
|
|
||||||
from kivymd.theming import ThemableBehavior
|
|
||||||
|
|
||||||
Builder.load_string('''
|
|
||||||
#:import STD_INC kivymd.material_resources.STANDARD_INCREMENT
|
|
||||||
<MDMenuItem>
|
|
||||||
size_hint_y: None
|
|
||||||
height: dp(48)
|
|
||||||
padding: dp(16), 0
|
|
||||||
on_release: root.parent.parent.parent.parent.dismiss() # Horrible, but hey it works
|
|
||||||
MDLabel:
|
|
||||||
text: root.text
|
|
||||||
theme_text_color: 'Primary'
|
|
||||||
|
|
||||||
<MDMenu>
|
|
||||||
size_hint: None, None
|
|
||||||
width: root.width_mult * STD_INC
|
|
||||||
key_viewclass: 'viewclass'
|
|
||||||
key_size: 'height'
|
|
||||||
|
|
||||||
<MDDropdownMenu>
|
|
||||||
FloatLayout:
|
|
||||||
id: fl
|
|
||||||
MDMenu:
|
|
||||||
id: md_menu
|
|
||||||
data: root.items
|
|
||||||
width_mult: root.width_mult
|
|
||||||
size_hint: None, None
|
|
||||||
size: 0,0
|
|
||||||
canvas.before:
|
|
||||||
Color:
|
|
||||||
rgba: root.theme_cls.bg_light
|
|
||||||
Rectangle:
|
|
||||||
size: self.size
|
|
||||||
pos: self.pos
|
|
||||||
''')
|
|
||||||
|
|
||||||
|
|
||||||
class MDMenuItem(ButtonBehavior, BoxLayout):
|
|
||||||
text = StringProperty()
|
|
||||||
|
|
||||||
|
|
||||||
class MDMenu(RecycleView):
|
|
||||||
width_mult = NumericProperty(1)
|
|
||||||
|
|
||||||
|
|
||||||
class MDDropdownMenu(ThemableBehavior, BoxLayout):
|
|
||||||
items = ListProperty()
|
|
||||||
'''See :attr:`~kivy.garden.recycleview.RecycleView.data`
|
|
||||||
'''
|
|
||||||
|
|
||||||
width_mult = NumericProperty(1)
|
|
||||||
'''This number multiplied by the standard increment (56dp on mobile,
|
|
||||||
64dp on desktop, determines the width of the menu items.
|
|
||||||
|
|
||||||
If the resulting number were to be too big for the application Window,
|
|
||||||
the multiplier will be adjusted for the biggest possible one.
|
|
||||||
'''
|
|
||||||
|
|
||||||
max_height = NumericProperty()
|
|
||||||
'''The menu will grow no bigger than this number.
|
|
||||||
|
|
||||||
Set to 0 for no limit. Defaults to 0.
|
|
||||||
'''
|
|
||||||
|
|
||||||
border_margin = NumericProperty(dp(4))
|
|
||||||
'''Margin between Window border and menu
|
|
||||||
'''
|
|
||||||
|
|
||||||
ver_growth = OptionProperty(None, allownone=True,
|
|
||||||
options=['up', 'down'])
|
|
||||||
'''Where the menu will grow vertically to when opening
|
|
||||||
|
|
||||||
Set to None to let the widget pick for you. Defaults to None.
|
|
||||||
'''
|
|
||||||
|
|
||||||
hor_growth = OptionProperty(None, allownone=True,
|
|
||||||
options=['left', 'right'])
|
|
||||||
'''Where the menu will grow horizontally to when opening
|
|
||||||
|
|
||||||
Set to None to let the widget pick for you. Defaults to None.
|
|
||||||
'''
|
|
||||||
|
|
||||||
def open(self, *largs):
|
|
||||||
Window.add_widget(self)
|
|
||||||
Clock.schedule_once(lambda x: self.display_menu(largs[0]), -1)
|
|
||||||
|
|
||||||
def display_menu(self, caller):
|
|
||||||
# We need to pick a starting point, see how big we need to be,
|
|
||||||
# and where to grow to.
|
|
||||||
|
|
||||||
c = caller.to_window(caller.center_x,
|
|
||||||
caller.center_y) # Starting coords
|
|
||||||
|
|
||||||
# ---ESTABLISH INITIAL TARGET SIZE ESTIMATE---
|
|
||||||
target_width = self.width_mult * m_res.STANDARD_INCREMENT
|
|
||||||
# If we're wider than the Window...
|
|
||||||
if target_width > Window.width:
|
|
||||||
# ...reduce our multiplier to max allowed.
|
|
||||||
target_width = int(
|
|
||||||
Window.width / m_res.STANDARD_INCREMENT) * m_res.STANDARD_INCREMENT
|
|
||||||
|
|
||||||
target_height = sum([dp(48) for i in self.items])
|
|
||||||
# If we're over max_height...
|
|
||||||
if 0 < self.max_height < target_height:
|
|
||||||
target_height = self.max_height
|
|
||||||
|
|
||||||
# ---ESTABLISH VERTICAL GROWTH DIRECTION---
|
|
||||||
if self.ver_growth is not None:
|
|
||||||
ver_growth = self.ver_growth
|
|
||||||
else:
|
|
||||||
# If there's enough space below us:
|
|
||||||
if target_height <= c[1] - self.border_margin:
|
|
||||||
ver_growth = 'down'
|
|
||||||
# if there's enough space above us:
|
|
||||||
elif target_height < Window.height - c[1] - self.border_margin:
|
|
||||||
ver_growth = 'up'
|
|
||||||
# otherwise, let's pick the one with more space and adjust ourselves
|
|
||||||
else:
|
|
||||||
# if there's more space below us:
|
|
||||||
if c[1] >= Window.height - c[1]:
|
|
||||||
ver_growth = 'down'
|
|
||||||
target_height = c[1] - self.border_margin
|
|
||||||
# if there's more space above us:
|
|
||||||
else:
|
|
||||||
ver_growth = 'up'
|
|
||||||
target_height = Window.height - c[1] - self.border_margin
|
|
||||||
|
|
||||||
if self.hor_growth is not None:
|
|
||||||
hor_growth = self.hor_growth
|
|
||||||
else:
|
|
||||||
# If there's enough space to the right:
|
|
||||||
if target_width <= Window.width - c[0] - self.border_margin:
|
|
||||||
hor_growth = 'right'
|
|
||||||
# if there's enough space to the left:
|
|
||||||
elif target_width < c[0] - self.border_margin:
|
|
||||||
hor_growth = 'left'
|
|
||||||
# otherwise, let's pick the one with more space and adjust ourselves
|
|
||||||
else:
|
|
||||||
# if there's more space to the right:
|
|
||||||
if Window.width - c[0] >= c[0]:
|
|
||||||
hor_growth = 'right'
|
|
||||||
target_width = Window.width - c[0] - self.border_margin
|
|
||||||
# if there's more space to the left:
|
|
||||||
else:
|
|
||||||
hor_growth = 'left'
|
|
||||||
target_width = c[0] - self.border_margin
|
|
||||||
|
|
||||||
if ver_growth == 'down':
|
|
||||||
tar_y = c[1] - target_height
|
|
||||||
else: # should always be 'up'
|
|
||||||
tar_y = c[1]
|
|
||||||
|
|
||||||
if hor_growth == 'right':
|
|
||||||
tar_x = c[0]
|
|
||||||
else: # should always be 'left'
|
|
||||||
tar_x = c[0] - target_width
|
|
||||||
anim = Animation(x=tar_x, y=tar_y,
|
|
||||||
width=target_width, height=target_height,
|
|
||||||
duration=.3, transition='out_quint')
|
|
||||||
menu = self.ids['md_menu']
|
|
||||||
menu.pos = c
|
|
||||||
anim.start(menu)
|
|
||||||
|
|
||||||
def on_touch_down(self, touch):
|
|
||||||
if not self.ids['md_menu'].collide_point(*touch.pos):
|
|
||||||
self.dismiss()
|
|
||||||
return True
|
|
||||||
super(MDDropdownMenu, self).on_touch_down(touch)
|
|
||||||
return True
|
|
||||||
|
|
||||||
def on_touch_move(self, touch):
|
|
||||||
super(MDDropdownMenu, self).on_touch_move(touch)
|
|
||||||
return True
|
|
||||||
|
|
||||||
def on_touch_up(self, touch):
|
|
||||||
super(MDDropdownMenu, self).on_touch_up(touch)
|
|
||||||
return True
|
|
||||||
|
|
||||||
def dismiss(self):
|
|
||||||
Window.remove_widget(self)
|
|
|
@ -1,76 +0,0 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
from kivy.animation import Animation
|
|
||||||
from kivy.lang import Builder
|
|
||||||
from kivy.properties import StringProperty, ObjectProperty
|
|
||||||
from kivymd.elevationbehavior import ElevationBehavior
|
|
||||||
from kivymd.icon_definitions import md_icons
|
|
||||||
from kivymd.label import MDLabel
|
|
||||||
from kivymd.list import OneLineIconListItem, ILeftBody, BaseListItem
|
|
||||||
from kivymd.slidingpanel import SlidingPanel
|
|
||||||
from kivymd.theming import ThemableBehavior
|
|
||||||
|
|
||||||
Builder.load_string('''
|
|
||||||
<NavDrawerToolbar@Toolbar>
|
|
||||||
canvas:
|
|
||||||
Color:
|
|
||||||
rgba: root.theme_cls.divider_color
|
|
||||||
Line:
|
|
||||||
points: self.x, self.y, self.x+self.width,self.y
|
|
||||||
|
|
||||||
<NavigationDrawer>
|
|
||||||
_list: list
|
|
||||||
elevation: 0
|
|
||||||
canvas:
|
|
||||||
Color:
|
|
||||||
rgba: root.theme_cls.bg_light
|
|
||||||
Rectangle:
|
|
||||||
size: root.size
|
|
||||||
pos: root.pos
|
|
||||||
NavDrawerToolbar:
|
|
||||||
title: root.title
|
|
||||||
opposite_colors: False
|
|
||||||
title_theme_color: 'Secondary'
|
|
||||||
background_color: root.theme_cls.bg_light
|
|
||||||
elevation: 0
|
|
||||||
ScrollView:
|
|
||||||
do_scroll_x: False
|
|
||||||
MDList:
|
|
||||||
id: ml
|
|
||||||
id: list
|
|
||||||
|
|
||||||
<NavigationDrawerIconButton>
|
|
||||||
NDIconLabel:
|
|
||||||
id: _icon
|
|
||||||
font_style: 'Icon'
|
|
||||||
theme_text_color: 'Secondary'
|
|
||||||
''')
|
|
||||||
|
|
||||||
|
|
||||||
class NavigationDrawer(SlidingPanel, ThemableBehavior, ElevationBehavior):
|
|
||||||
title = StringProperty()
|
|
||||||
|
|
||||||
_list = ObjectProperty()
|
|
||||||
|
|
||||||
def add_widget(self, widget, index=0):
|
|
||||||
if issubclass(widget.__class__, BaseListItem):
|
|
||||||
self._list.add_widget(widget, index)
|
|
||||||
widget.bind(on_release=lambda x: self.toggle())
|
|
||||||
else:
|
|
||||||
super(NavigationDrawer, self).add_widget(widget, index)
|
|
||||||
|
|
||||||
def _get_main_animation(self, duration, t, x, is_closing):
|
|
||||||
a = super(NavigationDrawer, self)._get_main_animation(duration, t, x,
|
|
||||||
is_closing)
|
|
||||||
a &= Animation(elevation=0 if is_closing else 5, t=t, duration=duration)
|
|
||||||
return a
|
|
||||||
|
|
||||||
|
|
||||||
class NDIconLabel(ILeftBody, MDLabel):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class NavigationDrawerIconButton(OneLineIconListItem):
|
|
||||||
icon = StringProperty()
|
|
||||||
|
|
||||||
def on_icon(self, instance, value):
|
|
||||||
self.ids['_icon'].text = u"{}".format(md_icons[value])
|
|
|
@ -1,79 +0,0 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
|
||||||
from kivy.lang import Builder
|
|
||||||
from kivy.properties import ListProperty, OptionProperty, BooleanProperty
|
|
||||||
from kivy.utils import get_color_from_hex
|
|
||||||
from kivymd.color_definitions import colors
|
|
||||||
from kivymd.theming import ThemableBehavior
|
|
||||||
from kivy.uix.progressbar import ProgressBar
|
|
||||||
|
|
||||||
|
|
||||||
Builder.load_string('''
|
|
||||||
<MDProgressBar>:
|
|
||||||
canvas:
|
|
||||||
Clear
|
|
||||||
Color:
|
|
||||||
rgba: self.theme_cls.divider_color
|
|
||||||
Rectangle:
|
|
||||||
size: (self.width , dp(4)) if self.orientation == 'horizontal' else (dp(4),self.height)
|
|
||||||
pos: (self.x, self.center_y - dp(4)) if self.orientation == 'horizontal' \
|
|
||||||
else (self.center_x - dp(4),self.y)
|
|
||||||
|
|
||||||
|
|
||||||
Color:
|
|
||||||
rgba: self.theme_cls.primary_color
|
|
||||||
Rectangle:
|
|
||||||
size: (self.width*self.value_normalized, sp(4)) if self.orientation == 'horizontal' else (sp(4), \
|
|
||||||
self.height*self.value_normalized)
|
|
||||||
pos: (self.width*(1-self.value_normalized)+self.x if self.reversed else self.x, self.center_y - dp(4)) \
|
|
||||||
if self.orientation == 'horizontal' else \
|
|
||||||
(self.center_x - dp(4),self.height*(1-self.value_normalized)+self.y if self.reversed else self.y)
|
|
||||||
|
|
||||||
''')
|
|
||||||
|
|
||||||
|
|
||||||
class MDProgressBar(ThemableBehavior, ProgressBar):
|
|
||||||
reversed = BooleanProperty(False)
|
|
||||||
''' Reverse the direction the progressbar moves. '''
|
|
||||||
|
|
||||||
orientation = OptionProperty('horizontal', options=['horizontal', 'vertical'])
|
|
||||||
''' Orientation of progressbar'''
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
from kivy.app import App
|
|
||||||
from kivymd.theming import ThemeManager
|
|
||||||
|
|
||||||
class ProgressBarApp(App):
|
|
||||||
theme_cls = ThemeManager()
|
|
||||||
|
|
||||||
def build(self):
|
|
||||||
return Builder.load_string("""#:import MDSlider kivymd.slider.MDSlider
|
|
||||||
BoxLayout:
|
|
||||||
orientation:'vertical'
|
|
||||||
padding: '8dp'
|
|
||||||
MDSlider:
|
|
||||||
id:slider
|
|
||||||
min:0
|
|
||||||
max:100
|
|
||||||
value: 40
|
|
||||||
|
|
||||||
MDProgressBar:
|
|
||||||
value: slider.value
|
|
||||||
MDProgressBar:
|
|
||||||
reversed: True
|
|
||||||
value: slider.value
|
|
||||||
BoxLayout:
|
|
||||||
MDProgressBar:
|
|
||||||
orientation:"vertical"
|
|
||||||
reversed: True
|
|
||||||
value: slider.value
|
|
||||||
|
|
||||||
MDProgressBar:
|
|
||||||
orientation:"vertical"
|
|
||||||
value: slider.value
|
|
||||||
|
|
||||||
""")
|
|
||||||
|
|
||||||
|
|
||||||
ProgressBarApp().run()
|
|
|
@ -1,169 +0,0 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
from kivy.properties import ListProperty, NumericProperty, StringProperty, \
|
|
||||||
BooleanProperty
|
|
||||||
from kivy.animation import Animation
|
|
||||||
from kivy.graphics import Color, Ellipse, StencilPush, StencilPop, \
|
|
||||||
StencilUse, StencilUnUse, Rectangle
|
|
||||||
|
|
||||||
|
|
||||||
class CommonRipple(object):
|
|
||||||
ripple_rad = NumericProperty()
|
|
||||||
ripple_rad_default = NumericProperty(1)
|
|
||||||
ripple_post = ListProperty()
|
|
||||||
ripple_color = ListProperty()
|
|
||||||
ripple_alpha = NumericProperty(.5)
|
|
||||||
ripple_scale = NumericProperty(None)
|
|
||||||
ripple_duration_in_fast = NumericProperty(.3)
|
|
||||||
# FIXME: These speeds should be calculated based on widget size in dp
|
|
||||||
ripple_duration_in_slow = NumericProperty(2)
|
|
||||||
ripple_duration_out = NumericProperty(.3)
|
|
||||||
ripple_func_in = StringProperty('out_quad')
|
|
||||||
ripple_func_out = StringProperty('out_quad')
|
|
||||||
|
|
||||||
doing_ripple = BooleanProperty(False)
|
|
||||||
finishing_ripple = BooleanProperty(False)
|
|
||||||
fading_out = BooleanProperty(False)
|
|
||||||
|
|
||||||
def on_touch_down(self, touch):
|
|
||||||
if touch.is_mouse_scrolling:
|
|
||||||
return False
|
|
||||||
if not self.collide_point(touch.x, touch.y):
|
|
||||||
return False
|
|
||||||
|
|
||||||
if not self.disabled:
|
|
||||||
if self.doing_ripple:
|
|
||||||
Animation.cancel_all(self, 'ripple_rad', 'ripple_color',
|
|
||||||
'rect_color')
|
|
||||||
self.anim_complete()
|
|
||||||
self.ripple_rad = self.ripple_rad_default
|
|
||||||
self.ripple_pos = (touch.x, touch.y)
|
|
||||||
|
|
||||||
if self.ripple_color != []:
|
|
||||||
pass
|
|
||||||
elif hasattr(self, 'theme_cls'):
|
|
||||||
self.ripple_color = self.theme_cls.ripple_color
|
|
||||||
else:
|
|
||||||
# If no theme, set Grey 300
|
|
||||||
self.ripple_color = [0.8784313725490196, 0.8784313725490196,
|
|
||||||
0.8784313725490196, self.ripple_alpha]
|
|
||||||
self.ripple_color[3] = self.ripple_alpha
|
|
||||||
|
|
||||||
self.lay_canvas_instructions()
|
|
||||||
self.finish_rad = max(self.width, self.height) * self.ripple_scale
|
|
||||||
self.start_ripple()
|
|
||||||
return super(CommonRipple, self).on_touch_down(touch)
|
|
||||||
|
|
||||||
def lay_canvas_instructions(self):
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
def on_touch_move(self, touch, *args):
|
|
||||||
if not self.collide_point(touch.x, touch.y):
|
|
||||||
if not self.finishing_ripple and self.doing_ripple:
|
|
||||||
self.finish_ripple()
|
|
||||||
return super(CommonRipple, self).on_touch_move(touch, *args)
|
|
||||||
|
|
||||||
def on_touch_up(self, touch):
|
|
||||||
if self.collide_point(touch.x, touch.y) and self.doing_ripple:
|
|
||||||
self.finish_ripple()
|
|
||||||
return super(CommonRipple, self).on_touch_up(touch)
|
|
||||||
|
|
||||||
def start_ripple(self):
|
|
||||||
if not self.doing_ripple:
|
|
||||||
anim = Animation(
|
|
||||||
ripple_rad=self.finish_rad,
|
|
||||||
t='linear',
|
|
||||||
duration=self.ripple_duration_in_slow)
|
|
||||||
anim.bind(on_complete=self.fade_out)
|
|
||||||
self.doing_ripple = True
|
|
||||||
anim.start(self)
|
|
||||||
|
|
||||||
def _set_ellipse(self, instance, value):
|
|
||||||
self.ellipse.size = (self.ripple_rad, self.ripple_rad)
|
|
||||||
|
|
||||||
# Adjust ellipse pos here
|
|
||||||
|
|
||||||
def _set_color(self, instance, value):
|
|
||||||
self.col_instruction.a = value[3]
|
|
||||||
|
|
||||||
def finish_ripple(self):
|
|
||||||
if self.doing_ripple and not self.finishing_ripple:
|
|
||||||
Animation.cancel_all(self, 'ripple_rad')
|
|
||||||
anim = Animation(ripple_rad=self.finish_rad,
|
|
||||||
t=self.ripple_func_in,
|
|
||||||
duration=self.ripple_duration_in_fast)
|
|
||||||
anim.bind(on_complete=self.fade_out)
|
|
||||||
self.finishing_ripple = True
|
|
||||||
anim.start(self)
|
|
||||||
|
|
||||||
def fade_out(self, *args):
|
|
||||||
rc = self.ripple_color
|
|
||||||
if not self.fading_out:
|
|
||||||
Animation.cancel_all(self, 'ripple_color')
|
|
||||||
anim = Animation(ripple_color=[rc[0], rc[1], rc[2], 0.],
|
|
||||||
t=self.ripple_func_out,
|
|
||||||
duration=self.ripple_duration_out)
|
|
||||||
anim.bind(on_complete=self.anim_complete)
|
|
||||||
self.fading_out = True
|
|
||||||
anim.start(self)
|
|
||||||
|
|
||||||
def anim_complete(self, *args):
|
|
||||||
self.doing_ripple = False
|
|
||||||
self.finishing_ripple = False
|
|
||||||
self.fading_out = False
|
|
||||||
self.canvas.after.clear()
|
|
||||||
|
|
||||||
|
|
||||||
class RectangularRippleBehavior(CommonRipple):
|
|
||||||
ripple_scale = NumericProperty(2.75)
|
|
||||||
|
|
||||||
def lay_canvas_instructions(self):
|
|
||||||
with self.canvas.after:
|
|
||||||
StencilPush()
|
|
||||||
Rectangle(pos=self.pos, size=self.size)
|
|
||||||
StencilUse()
|
|
||||||
self.col_instruction = Color(rgba=self.ripple_color)
|
|
||||||
self.ellipse = \
|
|
||||||
Ellipse(size=(self.ripple_rad, self.ripple_rad),
|
|
||||||
pos=(self.ripple_pos[0] - self.ripple_rad / 2.,
|
|
||||||
self.ripple_pos[1] - self.ripple_rad / 2.))
|
|
||||||
StencilUnUse()
|
|
||||||
Rectangle(pos=self.pos, size=self.size)
|
|
||||||
StencilPop()
|
|
||||||
self.bind(ripple_color=self._set_color,
|
|
||||||
ripple_rad=self._set_ellipse)
|
|
||||||
|
|
||||||
def _set_ellipse(self, instance, value):
|
|
||||||
super(RectangularRippleBehavior, self)._set_ellipse(instance, value)
|
|
||||||
self.ellipse.pos = (self.ripple_pos[0] - self.ripple_rad / 2.,
|
|
||||||
self.ripple_pos[1] - self.ripple_rad / 2.)
|
|
||||||
|
|
||||||
|
|
||||||
class CircularRippleBehavior(CommonRipple):
|
|
||||||
ripple_scale = NumericProperty(1)
|
|
||||||
|
|
||||||
def lay_canvas_instructions(self):
|
|
||||||
with self.canvas.after:
|
|
||||||
StencilPush()
|
|
||||||
self.stencil = Ellipse(size=(self.width * self.ripple_scale,
|
|
||||||
self.height * self.ripple_scale),
|
|
||||||
pos=(self.center_x - (
|
|
||||||
self.width * self.ripple_scale) / 2,
|
|
||||||
self.center_y - (
|
|
||||||
self.height * self.ripple_scale) / 2))
|
|
||||||
StencilUse()
|
|
||||||
self.col_instruction = Color(rgba=self.ripple_color)
|
|
||||||
self.ellipse = Ellipse(size=(self.ripple_rad, self.ripple_rad),
|
|
||||||
pos=(self.center_x - self.ripple_rad / 2.,
|
|
||||||
self.center_y - self.ripple_rad / 2.))
|
|
||||||
StencilUnUse()
|
|
||||||
Ellipse(pos=self.pos, size=self.size)
|
|
||||||
StencilPop()
|
|
||||||
self.bind(ripple_color=self._set_color,
|
|
||||||
ripple_rad=self._set_ellipse)
|
|
||||||
|
|
||||||
def _set_ellipse(self, instance, value):
|
|
||||||
super(CircularRippleBehavior, self)._set_ellipse(instance, value)
|
|
||||||
if self.ellipse.size[0] > self.width * .6 and not self.fading_out:
|
|
||||||
self.fade_out()
|
|
||||||
self.ellipse.pos = (self.center_x - self.ripple_rad / 2.,
|
|
||||||
self.center_y - self.ripple_rad / 2.)
|
|
|
@ -1,240 +0,0 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
|
||||||
from kivy.lang import Builder
|
|
||||||
from kivy.properties import StringProperty, ListProperty, NumericProperty
|
|
||||||
from kivy.uix.behaviors import ToggleButtonBehavior
|
|
||||||
from kivy.uix.label import Label
|
|
||||||
from kivy.uix.floatlayout import FloatLayout
|
|
||||||
from kivy.properties import AliasProperty, BooleanProperty
|
|
||||||
from kivy.metrics import dp, sp
|
|
||||||
from kivy.animation import Animation
|
|
||||||
from kivy.utils import get_color_from_hex
|
|
||||||
from kivymd.color_definitions import colors
|
|
||||||
from kivymd.icon_definitions import md_icons
|
|
||||||
from kivymd.theming import ThemableBehavior
|
|
||||||
from kivymd.elevationbehavior import RoundElevationBehavior
|
|
||||||
from kivymd.ripplebehavior import CircularRippleBehavior
|
|
||||||
from kivy.uix.behaviors import ButtonBehavior
|
|
||||||
from kivy.uix.widget import Widget
|
|
||||||
|
|
||||||
Builder.load_string('''
|
|
||||||
<MDCheckbox>:
|
|
||||||
canvas:
|
|
||||||
Clear
|
|
||||||
Color:
|
|
||||||
rgba: self.color
|
|
||||||
Rectangle:
|
|
||||||
texture: self.texture
|
|
||||||
size: self.texture_size
|
|
||||||
pos: int(self.center_x - self.texture_size[0] / 2.), int(self.center_y - self.texture_size[1] / 2.)
|
|
||||||
|
|
||||||
text: self._radio_icon if self.group else self._checkbox_icon
|
|
||||||
font_name: 'Icons'
|
|
||||||
font_size: sp(24)
|
|
||||||
color: self.theme_cls.primary_color if self.active else self.theme_cls.secondary_text_color
|
|
||||||
halign: 'center'
|
|
||||||
valign: 'middle'
|
|
||||||
|
|
||||||
<Thumb>:
|
|
||||||
color: 1, 1, 1, 1
|
|
||||||
canvas:
|
|
||||||
Color:
|
|
||||||
rgba: self.color
|
|
||||||
Ellipse:
|
|
||||||
size: self.size
|
|
||||||
pos: self.pos
|
|
||||||
|
|
||||||
<MDSwitch>:
|
|
||||||
canvas.before:
|
|
||||||
Color:
|
|
||||||
rgba: self._track_color_disabled if self.disabled else \
|
|
||||||
(self._track_color_active if self.active else self._track_color_normal)
|
|
||||||
Ellipse:
|
|
||||||
size: dp(16), dp(16)
|
|
||||||
pos: self.x, self.center_y - dp(8)
|
|
||||||
angle_start: 180
|
|
||||||
angle_end: 360
|
|
||||||
Rectangle:
|
|
||||||
size: self.width - dp(16), dp(16)
|
|
||||||
pos: self.x + dp(8), self.center_y - dp(8)
|
|
||||||
Ellipse:
|
|
||||||
size: dp(16), dp(16)
|
|
||||||
pos: self.right - dp(16), self.center_y - dp(8)
|
|
||||||
angle_start: 0
|
|
||||||
angle_end: 180
|
|
||||||
on_release: thumb.trigger_action()
|
|
||||||
|
|
||||||
Thumb:
|
|
||||||
id: thumb
|
|
||||||
size_hint: None, None
|
|
||||||
size: dp(24), dp(24)
|
|
||||||
pos: root._thumb_pos
|
|
||||||
color: root.thumb_color_disabled if root.disabled else \
|
|
||||||
(root.thumb_color_down if root.active else root.thumb_color)
|
|
||||||
elevation: 4 if root.active else 2
|
|
||||||
on_release: setattr(root, 'active', not root.active)
|
|
||||||
''')
|
|
||||||
|
|
||||||
|
|
||||||
class MDCheckbox(ThemableBehavior, CircularRippleBehavior,
|
|
||||||
ToggleButtonBehavior, Label):
|
|
||||||
active = BooleanProperty(False)
|
|
||||||
|
|
||||||
_checkbox_icon = StringProperty(
|
|
||||||
u"{}".format(md_icons['square-o']))
|
|
||||||
_radio_icon = StringProperty(u"{}".format(md_icons['circle-o']))
|
|
||||||
_icon_active = StringProperty(u"{}".format(md_icons['check-square']))
|
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
|
||||||
super(MDCheckbox, self).__init__(**kwargs)
|
|
||||||
self.register_event_type('on_active')
|
|
||||||
self.check_anim_out = Animation(font_size=0, duration=.1, t='out_quad')
|
|
||||||
self.check_anim_in = Animation(font_size=sp(24), duration=.1,
|
|
||||||
t='out_quad')
|
|
||||||
self.check_anim_out.bind(
|
|
||||||
on_complete=lambda *x: self.check_anim_in.start(self))
|
|
||||||
|
|
||||||
def on_state(self, *args):
|
|
||||||
if self.state == 'down':
|
|
||||||
self.check_anim_in.cancel(self)
|
|
||||||
self.check_anim_out.start(self)
|
|
||||||
self._radio_icon = u"{}".format(md_icons['dot-circle'])
|
|
||||||
self._checkbox_icon = u"{}".format(md_icons['check-square'])
|
|
||||||
self.active = True
|
|
||||||
else:
|
|
||||||
self.check_anim_in.cancel(self)
|
|
||||||
self.check_anim_out.start(self)
|
|
||||||
self._radio_icon = u"{}".format(md_icons['circle-o'])
|
|
||||||
self._checkbox_icon = u"{}".format(
|
|
||||||
md_icons['square-o'])
|
|
||||||
self.active = False
|
|
||||||
|
|
||||||
def on_active(self, instance, value):
|
|
||||||
self.state = 'down' if value else 'normal'
|
|
||||||
|
|
||||||
|
|
||||||
class Thumb(RoundElevationBehavior, CircularRippleBehavior, ButtonBehavior,
|
|
||||||
Widget):
|
|
||||||
ripple_scale = NumericProperty(2)
|
|
||||||
|
|
||||||
def _set_ellipse(self, instance, value):
|
|
||||||
self.ellipse.size = (self.ripple_rad, self.ripple_rad)
|
|
||||||
if self.ellipse.size[0] > self.width * 1.5 and not self.fading_out:
|
|
||||||
self.fade_out()
|
|
||||||
self.ellipse.pos = (self.center_x - self.ripple_rad / 2.,
|
|
||||||
self.center_y - self.ripple_rad / 2.)
|
|
||||||
self.stencil.pos = (
|
|
||||||
self.center_x - (self.width * self.ripple_scale) / 2,
|
|
||||||
self.center_y - (self.height * self.ripple_scale) / 2)
|
|
||||||
|
|
||||||
|
|
||||||
class MDSwitch(ThemableBehavior, ButtonBehavior, FloatLayout):
|
|
||||||
active = BooleanProperty(False)
|
|
||||||
|
|
||||||
_thumb_color = ListProperty(get_color_from_hex(colors['Grey']['50']))
|
|
||||||
|
|
||||||
def _get_thumb_color(self):
|
|
||||||
return self._thumb_color
|
|
||||||
|
|
||||||
def _set_thumb_color(self, color, alpha=None):
|
|
||||||
if len(color) == 2:
|
|
||||||
self._thumb_color = get_color_from_hex(colors[color[0]][color[1]])
|
|
||||||
if alpha:
|
|
||||||
self._thumb_color[3] = alpha
|
|
||||||
elif len(color) == 4:
|
|
||||||
self._thumb_color = color
|
|
||||||
|
|
||||||
thumb_color = AliasProperty(_get_thumb_color, _set_thumb_color,
|
|
||||||
bind=['_thumb_color'])
|
|
||||||
|
|
||||||
_thumb_color_down = ListProperty([1, 1, 1, 1])
|
|
||||||
|
|
||||||
def _get_thumb_color_down(self):
|
|
||||||
return self._thumb_color_down
|
|
||||||
|
|
||||||
def _set_thumb_color_down(self, color, alpha=None):
|
|
||||||
if len(color) == 2:
|
|
||||||
self._thumb_color_down = get_color_from_hex(
|
|
||||||
colors[color[0]][color[1]])
|
|
||||||
if alpha:
|
|
||||||
self._thumb_color_down[3] = alpha
|
|
||||||
else:
|
|
||||||
self._thumb_color_down[3] = 1
|
|
||||||
elif len(color) == 4:
|
|
||||||
self._thumb_color_down = color
|
|
||||||
|
|
||||||
thumb_color_down = AliasProperty(_get_thumb_color_down,
|
|
||||||
_set_thumb_color_down,
|
|
||||||
bind=['_thumb_color_down'])
|
|
||||||
|
|
||||||
_thumb_color_disabled = ListProperty(
|
|
||||||
get_color_from_hex(colors['Grey']['400']))
|
|
||||||
|
|
||||||
def _get_thumb_color_disabled(self):
|
|
||||||
return self._thumb_color_disabled
|
|
||||||
|
|
||||||
def _set_thumb_color_disabled(self, color, alpha=None):
|
|
||||||
if len(color) == 2:
|
|
||||||
self._thumb_color_disabled = get_color_from_hex(
|
|
||||||
colors[color[0]][color[1]])
|
|
||||||
if alpha:
|
|
||||||
self._thumb_color_disabled[3] = alpha
|
|
||||||
elif len(color) == 4:
|
|
||||||
self._thumb_color_disabled = color
|
|
||||||
|
|
||||||
thumb_color_down = AliasProperty(_get_thumb_color_disabled,
|
|
||||||
_set_thumb_color_disabled,
|
|
||||||
bind=['_thumb_color_disabled'])
|
|
||||||
|
|
||||||
_track_color_active = ListProperty()
|
|
||||||
_track_color_normal = ListProperty()
|
|
||||||
_track_color_disabled = ListProperty()
|
|
||||||
_thumb_pos = ListProperty([0, 0])
|
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
|
||||||
super(MDSwitch, self).__init__(**kwargs)
|
|
||||||
self.theme_cls.bind(theme_style=self._set_colors,
|
|
||||||
primary_color=self._set_colors,
|
|
||||||
primary_palette=self._set_colors)
|
|
||||||
self._set_colors()
|
|
||||||
|
|
||||||
def _set_colors(self, *args):
|
|
||||||
self._track_color_normal = self.theme_cls.disabled_hint_text_color
|
|
||||||
if self.theme_cls.theme_style == 'Dark':
|
|
||||||
self._track_color_active = self.theme_cls.primary_color
|
|
||||||
self._track_color_active[3] = .5
|
|
||||||
self._track_color_disabled = get_color_from_hex('FFFFFF')
|
|
||||||
self._track_color_disabled[3] = .1
|
|
||||||
self.thumb_color = get_color_from_hex(colors['Grey']['400'])
|
|
||||||
self.thumb_color_down = get_color_from_hex(
|
|
||||||
colors[self.theme_cls.primary_palette]['200'])
|
|
||||||
self.thumb_color_disabled = get_color_from_hex(
|
|
||||||
colors['Grey']['800'])
|
|
||||||
else:
|
|
||||||
self._track_color_active = get_color_from_hex(
|
|
||||||
colors[self.theme_cls.primary_palette]['200'])
|
|
||||||
self._track_color_active[3] = .5
|
|
||||||
self._track_color_disabled = self.theme_cls.disabled_hint_text_color
|
|
||||||
self.thumb_color_down = self.theme_cls.primary_color
|
|
||||||
|
|
||||||
def on_pos(self, *args):
|
|
||||||
if self.active:
|
|
||||||
self._thumb_pos = (self.right - dp(12), self.center_y - dp(12))
|
|
||||||
else:
|
|
||||||
self._thumb_pos = (self.x - dp(12), self.center_y - dp(12))
|
|
||||||
self.bind(active=self._update_thumb)
|
|
||||||
|
|
||||||
def _update_thumb(self, *args):
|
|
||||||
if self.active:
|
|
||||||
Animation.cancel_all(self, '_thumb_pos')
|
|
||||||
anim = Animation(
|
|
||||||
_thumb_pos=(self.right - dp(12), self.center_y - dp(12)),
|
|
||||||
duration=.2,
|
|
||||||
t='out_quad')
|
|
||||||
else:
|
|
||||||
Animation.cancel_all(self, '_thumb_pos')
|
|
||||||
anim = Animation(
|
|
||||||
_thumb_pos=(self.x - dp(12), self.center_y - dp(12)),
|
|
||||||
duration=.2,
|
|
||||||
t='out_quad')
|
|
||||||
anim.start(self)
|
|
|
@ -1,247 +0,0 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
|
||||||
from kivy.lang import Builder
|
|
||||||
from kivy.properties import StringProperty, ListProperty, NumericProperty,AliasProperty, BooleanProperty
|
|
||||||
from kivy.utils import get_color_from_hex
|
|
||||||
from kivy.metrics import dp, sp
|
|
||||||
from kivymd.color_definitions import colors
|
|
||||||
from kivymd.theming import ThemableBehavior
|
|
||||||
from kivy.uix.slider import Slider
|
|
||||||
|
|
||||||
|
|
||||||
Builder.load_string('''
|
|
||||||
#:import Thumb kivymd.selectioncontrols.Thumb
|
|
||||||
|
|
||||||
<MDSlider>:
|
|
||||||
id: slider
|
|
||||||
canvas:
|
|
||||||
Clear
|
|
||||||
Color:
|
|
||||||
rgba: self._track_color_disabled if self.disabled else (self._track_color_active if self.active \
|
|
||||||
else self._track_color_normal)
|
|
||||||
Rectangle:
|
|
||||||
size: (self.width - self.padding*2 - self._offset[0], dp(4)) if self.orientation == 'horizontal' \
|
|
||||||
else (dp(4),self.height - self.padding*2 - self._offset[1])
|
|
||||||
pos: (self.x + self.padding + self._offset[0], self.center_y - dp(4)) \
|
|
||||||
if self.orientation == 'horizontal' else (self.center_x - dp(4),self.y + self.padding + self._offset[1])
|
|
||||||
|
|
||||||
# If 0 draw circle
|
|
||||||
Color:
|
|
||||||
rgba: [0,0,0,0] if not self._is_off else (self._track_color_disabled if self.disabled \
|
|
||||||
else (self._track_color_active if self.active else self._track_color_normal))
|
|
||||||
Line:
|
|
||||||
width: 2
|
|
||||||
circle: (self.x+self.padding+dp(3),self.center_y-dp(2),8 if self.active else 6 ) \
|
|
||||||
if self.orientation == 'horizontal' else (self.center_x-dp(2),self.y+self.padding+dp(3),8 \
|
|
||||||
if self.active else 6)
|
|
||||||
|
|
||||||
Color:
|
|
||||||
rgba: [0,0,0,0] if self._is_off \
|
|
||||||
else (self.thumb_color_down if not self.disabled else self._track_color_disabled)
|
|
||||||
Rectangle:
|
|
||||||
size: ((self.width-self.padding*2)*self.value_normalized, sp(4)) \
|
|
||||||
if slider.orientation == 'horizontal' else (sp(4), (self.height-self.padding*2)*self.value_normalized)
|
|
||||||
pos: (self.x + self.padding, self.center_y - dp(4)) if self.orientation == 'horizontal' \
|
|
||||||
else (self.center_x - dp(4),self.y + self.padding)
|
|
||||||
Thumb:
|
|
||||||
id: thumb
|
|
||||||
size_hint: None, None
|
|
||||||
size: (dp(12), dp(12)) if root.disabled else ((dp(24), dp(24)) if root.active else (dp(16),dp(16)))
|
|
||||||
pos: (slider.value_pos[0] - dp(8), slider.center_y - thumb.height/2 - dp(2)) \
|
|
||||||
if slider.orientation == 'horizontal' \
|
|
||||||
else (slider.center_x - thumb.width/2 - dp(2), slider.value_pos[1]-dp(8))
|
|
||||||
color: [0,0,0,0] if slider._is_off else (root._track_color_disabled if root.disabled \
|
|
||||||
else root.thumb_color_down)
|
|
||||||
elevation: 0 if slider._is_off else (4 if root.active else 2)
|
|
||||||
|
|
||||||
''')
|
|
||||||
|
|
||||||
|
|
||||||
class MDSlider(ThemableBehavior, Slider):
|
|
||||||
# If the slider is clicked
|
|
||||||
active = BooleanProperty(False)
|
|
||||||
|
|
||||||
# Show the "off" ring when set to minimum value
|
|
||||||
show_off = BooleanProperty(True)
|
|
||||||
|
|
||||||
# Internal state of ring
|
|
||||||
_is_off = BooleanProperty(False)
|
|
||||||
|
|
||||||
# Internal adjustment to reposition sliders for ring
|
|
||||||
_offset = ListProperty((0, 0))
|
|
||||||
|
|
||||||
_thumb_color = ListProperty(get_color_from_hex(colors['Grey']['50']))
|
|
||||||
|
|
||||||
def _get_thumb_color(self):
|
|
||||||
return self._thumb_color
|
|
||||||
|
|
||||||
def _set_thumb_color(self, color, alpha=None):
|
|
||||||
if len(color) == 2:
|
|
||||||
self._thumb_color = get_color_from_hex(colors[color[0]][color[1]])
|
|
||||||
if alpha:
|
|
||||||
self._thumb_color[3] = alpha
|
|
||||||
elif len(color) == 4:
|
|
||||||
self._thumb_color = color
|
|
||||||
|
|
||||||
thumb_color = AliasProperty(_get_thumb_color, _set_thumb_color,
|
|
||||||
bind=['_thumb_color'])
|
|
||||||
|
|
||||||
_thumb_color_down = ListProperty([1, 1, 1, 1])
|
|
||||||
|
|
||||||
def _get_thumb_color_down(self):
|
|
||||||
return self._thumb_color_down
|
|
||||||
|
|
||||||
def _set_thumb_color_down(self, color, alpha=None):
|
|
||||||
if len(color) == 2:
|
|
||||||
self._thumb_color_down = get_color_from_hex(
|
|
||||||
colors[color[0]][color[1]])
|
|
||||||
if alpha:
|
|
||||||
self._thumb_color_down[3] = alpha
|
|
||||||
else:
|
|
||||||
self._thumb_color_down[3] = 1
|
|
||||||
elif len(color) == 4:
|
|
||||||
self._thumb_color_down = color
|
|
||||||
|
|
||||||
thumb_color_down = AliasProperty(_get_thumb_color_down,
|
|
||||||
_set_thumb_color_down,
|
|
||||||
bind=['_thumb_color_down'])
|
|
||||||
|
|
||||||
_thumb_color_disabled = ListProperty(
|
|
||||||
get_color_from_hex(colors['Grey']['400']))
|
|
||||||
|
|
||||||
def _get_thumb_color_disabled(self):
|
|
||||||
return self._thumb_color_disabled
|
|
||||||
|
|
||||||
def _set_thumb_color_disabled(self, color, alpha=None):
|
|
||||||
if len(color) == 2:
|
|
||||||
self._thumb_color_disabled = get_color_from_hex(
|
|
||||||
colors[color[0]][color[1]])
|
|
||||||
if alpha:
|
|
||||||
self._thumb_color_disabled[3] = alpha
|
|
||||||
elif len(color) == 4:
|
|
||||||
self._thumb_color_disabled = color
|
|
||||||
|
|
||||||
thumb_color_down = AliasProperty(_get_thumb_color_disabled,
|
|
||||||
_set_thumb_color_disabled,
|
|
||||||
bind=['_thumb_color_disabled'])
|
|
||||||
|
|
||||||
_track_color_active = ListProperty()
|
|
||||||
_track_color_normal = ListProperty()
|
|
||||||
_track_color_disabled = ListProperty()
|
|
||||||
_thumb_pos = ListProperty([0, 0])
|
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
|
||||||
super(MDSlider, self).__init__(**kwargs)
|
|
||||||
self.theme_cls.bind(theme_style=self._set_colors,
|
|
||||||
primary_color=self._set_colors,
|
|
||||||
primary_palette=self._set_colors)
|
|
||||||
self._set_colors()
|
|
||||||
|
|
||||||
def _set_colors(self, *args):
|
|
||||||
if self.theme_cls.theme_style == 'Dark':
|
|
||||||
self._track_color_normal = get_color_from_hex('FFFFFF')
|
|
||||||
self._track_color_normal[3] = .3
|
|
||||||
self._track_color_active = self._track_color_normal
|
|
||||||
self._track_color_disabled = self._track_color_normal
|
|
||||||
self.thumb_color = get_color_from_hex(colors['Grey']['400'])
|
|
||||||
self.thumb_color_down = get_color_from_hex(
|
|
||||||
colors[self.theme_cls.primary_palette]['200'])
|
|
||||||
self.thumb_color_disabled = get_color_from_hex(
|
|
||||||
colors['Grey']['800'])
|
|
||||||
else:
|
|
||||||
self._track_color_normal = get_color_from_hex('000000')
|
|
||||||
self._track_color_normal[3] = 0.26
|
|
||||||
self._track_color_active = get_color_from_hex('000000')
|
|
||||||
self._track_color_active[3] = 0.38
|
|
||||||
self._track_color_disabled = get_color_from_hex('000000')
|
|
||||||
self._track_color_disabled[3] = 0.26
|
|
||||||
self.thumb_color_down = self.theme_cls.primary_color
|
|
||||||
|
|
||||||
def on_value_normalized(self, *args):
|
|
||||||
""" When the value == min set it to "off" state and make slider a ring """
|
|
||||||
self._update_is_off()
|
|
||||||
|
|
||||||
def on_show_off(self, *args):
|
|
||||||
self._update_is_off()
|
|
||||||
|
|
||||||
def _update_is_off(self):
|
|
||||||
self._is_off = self.show_off and (self.value_normalized == 0)
|
|
||||||
|
|
||||||
def on__is_off(self, *args):
|
|
||||||
self._update_offset()
|
|
||||||
|
|
||||||
def on_active(self, *args):
|
|
||||||
self._update_offset()
|
|
||||||
|
|
||||||
def _update_offset(self):
|
|
||||||
""" Offset is used to shift the sliders so the background color
|
|
||||||
shows through the off circle.
|
|
||||||
"""
|
|
||||||
d = 2 if self.active else 0
|
|
||||||
self._offset = (dp(11+d), dp(11+d)) if self._is_off else (0, 0)
|
|
||||||
|
|
||||||
def on_touch_down(self, touch):
|
|
||||||
if super(MDSlider, self).on_touch_down(touch):
|
|
||||||
self.active = True
|
|
||||||
|
|
||||||
def on_touch_up(self,touch):
|
|
||||||
if super(MDSlider, self).on_touch_up(touch):
|
|
||||||
self.active = False
|
|
||||||
# thumb = self.ids['thumb']
|
|
||||||
# if thumb.collide_point(*touch.pos):
|
|
||||||
# thumb.on_touch_down(touch)
|
|
||||||
# thumb.on_touch_up(touch)
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
from kivy.app import App
|
|
||||||
from kivymd.theming import ThemeManager
|
|
||||||
|
|
||||||
class SliderApp(App):
|
|
||||||
theme_cls = ThemeManager()
|
|
||||||
|
|
||||||
def build(self):
|
|
||||||
return Builder.load_string("""
|
|
||||||
BoxLayout:
|
|
||||||
orientation:'vertical'
|
|
||||||
BoxLayout:
|
|
||||||
size_hint_y:None
|
|
||||||
height: '48dp'
|
|
||||||
Label:
|
|
||||||
text:"Toggle disabled"
|
|
||||||
color: [0,0,0,1]
|
|
||||||
CheckBox:
|
|
||||||
on_press: slider.disabled = not slider.disabled
|
|
||||||
BoxLayout:
|
|
||||||
size_hint_y:None
|
|
||||||
height: '48dp'
|
|
||||||
Label:
|
|
||||||
text:"Toggle active"
|
|
||||||
color: [0,0,0,1]
|
|
||||||
CheckBox:
|
|
||||||
on_press: slider.active = not slider.active
|
|
||||||
BoxLayout:
|
|
||||||
size_hint_y:None
|
|
||||||
height: '48dp'
|
|
||||||
Label:
|
|
||||||
text:"Toggle show off"
|
|
||||||
color: [0,0,0,1]
|
|
||||||
CheckBox:
|
|
||||||
on_press: slider.show_off = not slider.show_off
|
|
||||||
|
|
||||||
MDSlider:
|
|
||||||
id:slider
|
|
||||||
min:0
|
|
||||||
max:100
|
|
||||||
value: 40
|
|
||||||
|
|
||||||
MDSlider:
|
|
||||||
id:slider2
|
|
||||||
orientation:"vertical"
|
|
||||||
min:0
|
|
||||||
max:100
|
|
||||||
value: 40
|
|
||||||
|
|
||||||
""")
|
|
||||||
|
|
||||||
|
|
||||||
SliderApp().run()
|
|
|
@ -1,92 +0,0 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
from kivy.animation import Animation
|
|
||||||
from kivy.clock import Clock
|
|
||||||
from kivy.core.window import Window
|
|
||||||
from kivy.lang import Builder
|
|
||||||
from kivy.metrics import dp
|
|
||||||
from kivy.properties import OptionProperty, NumericProperty, StringProperty, \
|
|
||||||
BooleanProperty, ListProperty
|
|
||||||
from kivy.uix.boxlayout import BoxLayout
|
|
||||||
from kivy.uix.relativelayout import RelativeLayout
|
|
||||||
|
|
||||||
Builder.load_string("""
|
|
||||||
#: import Window kivy.core.window.Window
|
|
||||||
<SlidingPanel>
|
|
||||||
orientation: 'vertical'
|
|
||||||
size_hint_x: None
|
|
||||||
width: dp(320)
|
|
||||||
x: -1 * self.width if self.side == 'left' else Window.width
|
|
||||||
|
|
||||||
<PanelShadow>
|
|
||||||
canvas:
|
|
||||||
Color:
|
|
||||||
rgba: root.color
|
|
||||||
Rectangle:
|
|
||||||
size: root.size
|
|
||||||
""")
|
|
||||||
|
|
||||||
|
|
||||||
class PanelShadow(BoxLayout):
|
|
||||||
color = ListProperty([0, 0, 0, 0])
|
|
||||||
|
|
||||||
|
|
||||||
class SlidingPanel(BoxLayout):
|
|
||||||
anim_length_close = NumericProperty(0.3)
|
|
||||||
anim_length_open = NumericProperty(0.3)
|
|
||||||
animation_t_open = StringProperty('out_sine')
|
|
||||||
animation_t_close = StringProperty('out_sine')
|
|
||||||
side = OptionProperty('left', options=['left', 'right'])
|
|
||||||
|
|
||||||
_open = False
|
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
|
||||||
super(SlidingPanel, self).__init__(**kwargs)
|
|
||||||
self.shadow = PanelShadow()
|
|
||||||
Clock.schedule_once(lambda x: Window.add_widget(self.shadow,89), 0)
|
|
||||||
Clock.schedule_once(lambda x: Window.add_widget(self,90), 0)
|
|
||||||
|
|
||||||
def toggle(self):
|
|
||||||
Animation.stop_all(self, 'x')
|
|
||||||
Animation.stop_all(self.shadow, 'color')
|
|
||||||
if self._open:
|
|
||||||
if self.side == 'left':
|
|
||||||
target_x = -1 * self.width
|
|
||||||
else:
|
|
||||||
target_x = Window.width
|
|
||||||
|
|
||||||
sh_anim = Animation(duration=self.anim_length_open,
|
|
||||||
t=self.animation_t_open,
|
|
||||||
color=[0, 0, 0, 0])
|
|
||||||
sh_anim.start(self.shadow)
|
|
||||||
self._get_main_animation(duration=self.anim_length_close,
|
|
||||||
t=self.animation_t_close,
|
|
||||||
x=target_x,
|
|
||||||
is_closing=True).start(self)
|
|
||||||
self._open = False
|
|
||||||
else:
|
|
||||||
if self.side == 'left':
|
|
||||||
target_x = 0
|
|
||||||
else:
|
|
||||||
target_x = Window.width - self.width
|
|
||||||
Animation(duration=self.anim_length_open, t=self.animation_t_open,
|
|
||||||
color=[0, 0, 0, 0.5]).start(self.shadow)
|
|
||||||
self._get_main_animation(duration=self.anim_length_open,
|
|
||||||
t=self.animation_t_open,
|
|
||||||
x=target_x,
|
|
||||||
is_closing=False).start(self)
|
|
||||||
self._open = True
|
|
||||||
|
|
||||||
def _get_main_animation(self, duration, t, x, is_closing):
|
|
||||||
return Animation(duration=duration, t=t, x=x)
|
|
||||||
|
|
||||||
def on_touch_down(self, touch):
|
|
||||||
# Prevents touch events from propagating to anything below the widget.
|
|
||||||
super(SlidingPanel, self).on_touch_down(touch)
|
|
||||||
if self.collide_point(*touch.pos) or self._open:
|
|
||||||
return True
|
|
||||||
|
|
||||||
def on_touch_up(self, touch):
|
|
||||||
if not self.collide_point(touch.x, touch.y) and self._open:
|
|
||||||
self.toggle()
|
|
||||||
return True
|
|
||||||
super(SlidingPanel, self).on_touch_up(touch)
|
|
|
@ -1,115 +0,0 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
from collections import deque
|
|
||||||
from kivy.animation import Animation
|
|
||||||
from kivy.clock import Clock
|
|
||||||
from kivy.core.window import Window
|
|
||||||
from kivy.lang import Builder
|
|
||||||
from kivy.metrics import dp
|
|
||||||
from kivy.properties import ObjectProperty, StringProperty, NumericProperty
|
|
||||||
from kivy.uix.relativelayout import RelativeLayout
|
|
||||||
from kivymd.material_resources import DEVICE_TYPE
|
|
||||||
|
|
||||||
Builder.load_string('''
|
|
||||||
#:import Window kivy.core.window.Window
|
|
||||||
#:import get_color_from_hex kivy.utils.get_color_from_hex
|
|
||||||
#:import MDFlatButton kivymd.button.MDFlatButton
|
|
||||||
#:import MDLabel kivymd.label.MDLabel
|
|
||||||
#:import DEVICE_TYPE kivymd.material_resources.DEVICE_TYPE
|
|
||||||
<_SnackbarWidget>
|
|
||||||
canvas:
|
|
||||||
Color:
|
|
||||||
rgb: get_color_from_hex('323232')
|
|
||||||
Rectangle:
|
|
||||||
size: self.size
|
|
||||||
size_hint_y: None
|
|
||||||
size_hint_x: 1 if DEVICE_TYPE == 'mobile' else None
|
|
||||||
height: dp(48) if _label.texture_size[1] < dp(30) else dp(80)
|
|
||||||
width: dp(24) + _label.width + _spacer.width + root.padding_right if root.button_text == '' else dp(24) + \
|
|
||||||
_label.width + _spacer.width + _button.width + root.padding_right
|
|
||||||
top: 0
|
|
||||||
x: 0 if DEVICE_TYPE == 'mobile' else Window.width/2 - self.width/2
|
|
||||||
BoxLayout:
|
|
||||||
width: Window.width - root.padding_right - _spacer.width - dp(24) if DEVICE_TYPE == 'mobile' and \
|
|
||||||
root.button_text == '' else Window.width - root.padding_right - _button.width - _spacer.width - dp(24) \
|
|
||||||
if DEVICE_TYPE == 'mobile' else _label.texture_size[0] if (dp(568) - root.padding_right - _button.width - \
|
|
||||||
_spacer.width - _label.texture_size[0] - dp(24)) >= 0 else (dp(568) - root.padding_right - _button.width - \
|
|
||||||
_spacer.width - dp(24))
|
|
||||||
size_hint_x: None
|
|
||||||
x: dp(24)
|
|
||||||
MDLabel:
|
|
||||||
id: _label
|
|
||||||
text: root.text
|
|
||||||
size: self.texture_size
|
|
||||||
BoxLayout:
|
|
||||||
id: _spacer
|
|
||||||
size_hint_x: None
|
|
||||||
x: _label.right
|
|
||||||
width: 0
|
|
||||||
MDFlatButton:
|
|
||||||
id: _button
|
|
||||||
text: root.button_text
|
|
||||||
size_hint_x: None
|
|
||||||
x: _spacer.right if root.button_text != '' else root.right
|
|
||||||
center_y: root.height/2
|
|
||||||
on_release: root.button_callback()
|
|
||||||
''')
|
|
||||||
|
|
||||||
|
|
||||||
class _SnackbarWidget(RelativeLayout):
|
|
||||||
text = StringProperty()
|
|
||||||
button_text = StringProperty()
|
|
||||||
button_callback = ObjectProperty()
|
|
||||||
duration = NumericProperty()
|
|
||||||
padding_right = NumericProperty(dp(24))
|
|
||||||
|
|
||||||
def __init__(self, text, duration, button_text='', button_callback=None,
|
|
||||||
**kwargs):
|
|
||||||
super(_SnackbarWidget, self).__init__(**kwargs)
|
|
||||||
self.text = text
|
|
||||||
self.button_text = button_text
|
|
||||||
self.button_callback = button_callback
|
|
||||||
self.duration = duration
|
|
||||||
self.ids['_label'].text_size = (None, None)
|
|
||||||
|
|
||||||
def begin(self):
|
|
||||||
if self.button_text == '':
|
|
||||||
self.remove_widget(self.ids['_button'])
|
|
||||||
else:
|
|
||||||
self.ids['_spacer'].width = dp(16) if \
|
|
||||||
DEVICE_TYPE == "mobile" else dp(40)
|
|
||||||
self.padding_right = dp(16)
|
|
||||||
Window.add_widget(self)
|
|
||||||
anim = Animation(y=0, duration=.3, t='out_quad')
|
|
||||||
anim.start(self)
|
|
||||||
Clock.schedule_once(lambda dt: self.die(), self.duration)
|
|
||||||
|
|
||||||
def die(self):
|
|
||||||
anim = Animation(top=0, duration=.3, t='out_quad')
|
|
||||||
anim.bind(on_complete=lambda *args: _play_next(self))
|
|
||||||
anim.bind(on_complete=lambda *args: Window.remove_widget(self))
|
|
||||||
anim.start(self)
|
|
||||||
|
|
||||||
|
|
||||||
queue = deque()
|
|
||||||
playing = False
|
|
||||||
|
|
||||||
|
|
||||||
def make(text, button_text=None, button_callback=None, duration=3):
|
|
||||||
if button_text is not None and button_callback is not None:
|
|
||||||
queue.append(_SnackbarWidget(text=text,
|
|
||||||
button_text=button_text,
|
|
||||||
button_callback=button_callback,
|
|
||||||
duration=duration))
|
|
||||||
else:
|
|
||||||
queue.append(_SnackbarWidget(text=text,
|
|
||||||
duration=duration))
|
|
||||||
_play_next()
|
|
||||||
|
|
||||||
|
|
||||||
def _play_next(dying_widget=None):
|
|
||||||
global playing
|
|
||||||
if (dying_widget or not playing) and len(queue) > 0:
|
|
||||||
playing = True
|
|
||||||
queue.popleft().begin()
|
|
||||||
elif len(queue) == 0:
|
|
||||||
playing = False
|
|
|
@ -1,149 +0,0 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
|
||||||
from kivy.lang import Builder
|
|
||||||
from kivy.uix.widget import Widget
|
|
||||||
from kivy.properties import NumericProperty, ListProperty, BooleanProperty
|
|
||||||
from kivy.animation import Animation
|
|
||||||
from kivymd.theming import ThemableBehavior
|
|
||||||
from kivy.clock import Clock
|
|
||||||
|
|
||||||
Builder.load_string('''
|
|
||||||
<MDSpinner>:
|
|
||||||
canvas.before:
|
|
||||||
PushMatrix
|
|
||||||
Rotate:
|
|
||||||
angle: self._rotation_angle
|
|
||||||
origin: self.center
|
|
||||||
canvas:
|
|
||||||
Color:
|
|
||||||
rgba: self.color
|
|
||||||
a: self._alpha
|
|
||||||
Line:
|
|
||||||
circle: self.center_x, self.center_y, self.width / 2, \
|
|
||||||
self._angle_start, self._angle_end
|
|
||||||
cap: 'square'
|
|
||||||
width: dp(2)
|
|
||||||
canvas.after:
|
|
||||||
PopMatrix
|
|
||||||
|
|
||||||
''')
|
|
||||||
|
|
||||||
|
|
||||||
class MDSpinner(ThemableBehavior, Widget):
|
|
||||||
""":class:`MDSpinner` is an implementation of the circular progress
|
|
||||||
indicator in Google's Material Design.
|
|
||||||
|
|
||||||
It can be used either as an indeterminate indicator that loops while
|
|
||||||
the user waits for something to happen, or as a determinate indicator.
|
|
||||||
|
|
||||||
Set :attr:`determinate` to **True** to activate determinate mode, and
|
|
||||||
:attr:`determinate_time` to set the duration of the animation.
|
|
||||||
"""
|
|
||||||
|
|
||||||
determinate = BooleanProperty(False)
|
|
||||||
""":attr:`determinate` is a :class:`~kivy.properties.BooleanProperty` and
|
|
||||||
defaults to False
|
|
||||||
"""
|
|
||||||
|
|
||||||
determinate_time = NumericProperty(2)
|
|
||||||
""":attr:`determinate_time` is a :class:`~kivy.properties.NumericProperty`
|
|
||||||
and defaults to 2
|
|
||||||
"""
|
|
||||||
|
|
||||||
active = BooleanProperty(True)
|
|
||||||
"""Use :attr:`active` to start or stop the spinner.
|
|
||||||
|
|
||||||
:attr:`active` is a :class:`~kivy.properties.BooleanProperty` and
|
|
||||||
defaults to True
|
|
||||||
"""
|
|
||||||
|
|
||||||
color = ListProperty([])
|
|
||||||
""":attr:`color` is a :class:`~kivy.properties.ListProperty` and
|
|
||||||
defaults to 'self.theme_cls.primary_color'
|
|
||||||
"""
|
|
||||||
|
|
||||||
_alpha = NumericProperty(0)
|
|
||||||
_rotation_angle = NumericProperty(360)
|
|
||||||
_angle_start = NumericProperty(0)
|
|
||||||
_angle_end = NumericProperty(8)
|
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
|
||||||
super(MDSpinner, self).__init__(**kwargs)
|
|
||||||
Clock.schedule_interval(self._update_color, 5)
|
|
||||||
self.color = self.theme_cls.primary_color
|
|
||||||
self._alpha_anim_in = Animation(_alpha=1, duration=.8, t='out_quad')
|
|
||||||
self._alpha_anim_out = Animation(_alpha=0, duration=.3, t='out_quad')
|
|
||||||
self._alpha_anim_out.bind(on_complete=self._reset)
|
|
||||||
|
|
||||||
if self.determinate:
|
|
||||||
self._start_determinate()
|
|
||||||
else:
|
|
||||||
self._start_loop()
|
|
||||||
|
|
||||||
def _update_color(self, *args):
|
|
||||||
self.color = self.theme_cls.primary_color
|
|
||||||
|
|
||||||
def _start_determinate(self, *args):
|
|
||||||
self._alpha_anim_in.start(self)
|
|
||||||
|
|
||||||
_rot_anim = Animation(_rotation_angle=0,
|
|
||||||
duration=self.determinate_time * .7,
|
|
||||||
t='out_quad')
|
|
||||||
_rot_anim.start(self)
|
|
||||||
|
|
||||||
_angle_start_anim = Animation(_angle_end=360,
|
|
||||||
duration=self.determinate_time,
|
|
||||||
t='in_out_quad')
|
|
||||||
_angle_start_anim.bind(on_complete=lambda *x: \
|
|
||||||
self._alpha_anim_out.start(self))
|
|
||||||
|
|
||||||
_angle_start_anim.start(self)
|
|
||||||
|
|
||||||
def _start_loop(self, *args):
|
|
||||||
if self._alpha == 0:
|
|
||||||
_rot_anim = Animation(_rotation_angle=0,
|
|
||||||
duration=2,
|
|
||||||
t='linear')
|
|
||||||
_rot_anim.start(self)
|
|
||||||
|
|
||||||
self._alpha = 1
|
|
||||||
self._alpha_anim_in.start(self)
|
|
||||||
_angle_start_anim = Animation(_angle_end=self._angle_end + 270,
|
|
||||||
duration=.6,
|
|
||||||
t='in_out_cubic')
|
|
||||||
_angle_start_anim.bind(on_complete=self._anim_back)
|
|
||||||
_angle_start_anim.start(self)
|
|
||||||
|
|
||||||
def _anim_back(self, *args):
|
|
||||||
_angle_back_anim = Animation(_angle_start=self._angle_end - 8,
|
|
||||||
duration=.6,
|
|
||||||
t='in_out_cubic')
|
|
||||||
_angle_back_anim.bind(on_complete=self._start_loop)
|
|
||||||
|
|
||||||
_angle_back_anim.start(self)
|
|
||||||
|
|
||||||
def on__rotation_angle(self, *args):
|
|
||||||
if self._rotation_angle == 0:
|
|
||||||
self._rotation_angle = 360
|
|
||||||
if not self.determinate:
|
|
||||||
_rot_anim = Animation(_rotation_angle=0,
|
|
||||||
duration=2)
|
|
||||||
_rot_anim.start(self)
|
|
||||||
|
|
||||||
def _reset(self, *args):
|
|
||||||
Animation.cancel_all(self, '_angle_start', '_rotation_angle',
|
|
||||||
'_angle_end', '_alpha')
|
|
||||||
self._angle_start = 0
|
|
||||||
self._angle_end = 8
|
|
||||||
self._rotation_angle = 360
|
|
||||||
self._alpha = 0
|
|
||||||
self.active = False
|
|
||||||
|
|
||||||
def on_active(self, *args):
|
|
||||||
if not self.active:
|
|
||||||
self._reset()
|
|
||||||
else:
|
|
||||||
if self.determinate:
|
|
||||||
self._start_determinate()
|
|
||||||
else:
|
|
||||||
self._start_loop()
|
|
|
@ -1,303 +0,0 @@
|
||||||
# Created on Jul 8, 2016
|
|
||||||
#
|
|
||||||
# The default kivy tab implementation seems like a stupid design to me. The
|
|
||||||
# ScreenManager is much better.
|
|
||||||
#
|
|
||||||
# @author: jrm
|
|
||||||
|
|
||||||
from kivy.properties import StringProperty, DictProperty, ListProperty, \
|
|
||||||
ObjectProperty, OptionProperty, BoundedNumericProperty
|
|
||||||
from kivy.uix.screenmanager import Screen
|
|
||||||
from kivy.lang import Builder
|
|
||||||
from kivy.metrics import dp
|
|
||||||
from kivy.uix.boxlayout import BoxLayout
|
|
||||||
from kivymd.theming import ThemableBehavior
|
|
||||||
from kivymd.backgroundcolorbehavior import BackgroundColorBehavior
|
|
||||||
from kivymd.button import MDFlatButton
|
|
||||||
|
|
||||||
Builder.load_string("""
|
|
||||||
<MDTabbedPanel>:
|
|
||||||
id: panel
|
|
||||||
orientation: 'vertical' if panel.tab_orientation in ['top','bottom'] else 'horizontal'
|
|
||||||
ScrollView:
|
|
||||||
id: scroll_view
|
|
||||||
size_hint_y: None
|
|
||||||
height: panel._tab_display_height[panel.tab_display_mode]
|
|
||||||
MDTabBar:
|
|
||||||
id: tab_bar
|
|
||||||
size_hint_y: None
|
|
||||||
height: panel._tab_display_height[panel.tab_display_mode]
|
|
||||||
background_color: panel.tab_color or panel.theme_cls.primary_color
|
|
||||||
canvas:
|
|
||||||
# Draw bottom border
|
|
||||||
Color:
|
|
||||||
rgba: (panel.tab_border_color or panel.tab_color or panel.theme_cls.primary_dark)
|
|
||||||
Rectangle:
|
|
||||||
size: (self.width,dp(2))
|
|
||||||
ScreenManager:
|
|
||||||
id: tab_manager
|
|
||||||
current: root.current
|
|
||||||
screens: root.tabs
|
|
||||||
|
|
||||||
|
|
||||||
<MDTabHeader>:
|
|
||||||
canvas:
|
|
||||||
Color:
|
|
||||||
rgba: self.panel.tab_color or self.panel.theme_cls.primary_color
|
|
||||||
Rectangle:
|
|
||||||
size: self.size
|
|
||||||
pos: self.pos
|
|
||||||
|
|
||||||
# Draw indicator
|
|
||||||
Color:
|
|
||||||
rgba: (self.panel.tab_indicator_color or self.panel.theme_cls.accent_color) if self.tab \
|
|
||||||
and self.tab.manager and self.tab.manager.current==self.tab.name else (self.panel.tab_border_color \
|
|
||||||
or self.panel.tab_color or self.panel.theme_cls.primary_dark)
|
|
||||||
Rectangle:
|
|
||||||
size: (self.width,dp(2))
|
|
||||||
pos: self.pos
|
|
||||||
|
|
||||||
size_hint: (None,None) #(1, None) if self.panel.tab_width_mode=='fixed' else (None,None)
|
|
||||||
width: (_label.texture_size[0] + dp(16))
|
|
||||||
padding: (dp(12), 0)
|
|
||||||
theme_text_color: 'Custom'
|
|
||||||
text_color: (self.panel.tab_text_color_active or app.theme_cls.bg_light if app.theme_cls.theme_style == "Light" \
|
|
||||||
else app.theme_cls.opposite_bg_light) if self.tab and self.tab.manager \
|
|
||||||
and self.tab.manager.current==self.tab.name else (self.panel.tab_text_color \
|
|
||||||
or self.panel.theme_cls.primary_light)
|
|
||||||
on_press:
|
|
||||||
self.tab.dispatch('on_tab_press')
|
|
||||||
# self.tab.manager.current = self.tab.name
|
|
||||||
on_release: self.tab.dispatch('on_tab_release')
|
|
||||||
on_touch_down: self.tab.dispatch('on_tab_touch_down',*args)
|
|
||||||
on_touch_move: self.tab.dispatch('on_tab_touch_move',*args)
|
|
||||||
on_touch_up: self.tab.dispatch('on_tab_touch_up',*args)
|
|
||||||
|
|
||||||
|
|
||||||
MDLabel:
|
|
||||||
id: _label
|
|
||||||
text: root.tab.text if root.panel.tab_display_mode == 'text' else u"{}".format(md_icons[root.tab.icon])
|
|
||||||
font_style: 'Button' if root.panel.tab_display_mode == 'text' else 'Icon'
|
|
||||||
size_hint_x: None# if root.panel.tab_width_mode=='fixed' else 1
|
|
||||||
text_size: (None, root.height)
|
|
||||||
height: self.texture_size[1]
|
|
||||||
theme_text_color: root.theme_text_color
|
|
||||||
text_color: root.text_color
|
|
||||||
valign: 'middle'
|
|
||||||
halign: 'center'
|
|
||||||
opposite_colors: root.opposite_colors
|
|
||||||
""")
|
|
||||||
|
|
||||||
|
|
||||||
class MDTabBar(ThemableBehavior, BackgroundColorBehavior, BoxLayout):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class MDTabHeader(MDFlatButton):
|
|
||||||
""" Internal widget for headers based on MDFlatButton"""
|
|
||||||
|
|
||||||
width = BoundedNumericProperty(dp(None), min=dp(72), max=dp(264), errorhandler=lambda x: dp(72))
|
|
||||||
tab = ObjectProperty(None)
|
|
||||||
panel = ObjectProperty(None)
|
|
||||||
|
|
||||||
|
|
||||||
class MDTab(Screen):
|
|
||||||
""" A tab is simply a screen with meta information
|
|
||||||
that defines the content that goes in the tab header.
|
|
||||||
"""
|
|
||||||
__events__ = ('on_tab_touch_down', 'on_tab_touch_move', 'on_tab_touch_up', 'on_tab_press', 'on_tab_release')
|
|
||||||
|
|
||||||
# Tab header text
|
|
||||||
text = StringProperty("")
|
|
||||||
|
|
||||||
# Tab header icon
|
|
||||||
icon = StringProperty("circle")
|
|
||||||
|
|
||||||
# Tab dropdown menu items
|
|
||||||
menu_items = ListProperty()
|
|
||||||
|
|
||||||
# Tab dropdown menu (if you want to customize it)
|
|
||||||
menu = ObjectProperty(None)
|
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
|
||||||
super(MDTab, self).__init__(**kwargs)
|
|
||||||
self.index = 0
|
|
||||||
self.parent_widget = None
|
|
||||||
self.register_event_type('on_tab_touch_down')
|
|
||||||
self.register_event_type('on_tab_touch_move')
|
|
||||||
self.register_event_type('on_tab_touch_up')
|
|
||||||
self.register_event_type('on_tab_press')
|
|
||||||
self.register_event_type('on_tab_release')
|
|
||||||
|
|
||||||
def on_leave(self, *args):
|
|
||||||
self.parent_widget.ids.tab_manager.transition.direction = self.parent_widget.prev_dir
|
|
||||||
|
|
||||||
def on_tab_touch_down(self, *args):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def on_tab_touch_move(self, *args):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def on_tab_touch_up(self, *args):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def on_tab_press(self, *args):
|
|
||||||
par = self.parent_widget
|
|
||||||
if par.previous_tab is not self:
|
|
||||||
par.prev_dir = str(par.ids.tab_manager.transition.direction)
|
|
||||||
if par.previous_tab.index > self.index:
|
|
||||||
par.ids.tab_manager.transition.direction = "right"
|
|
||||||
elif par.previous_tab.index < self.index:
|
|
||||||
par.ids.tab_manager.transition.direction = "left"
|
|
||||||
par.ids.tab_manager.current = self.name
|
|
||||||
par.previous_tab = self
|
|
||||||
|
|
||||||
def on_tab_release(self, *args):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
return "<MDTab name='{}', text='{}'>".format(self.name, self.text)
|
|
||||||
|
|
||||||
|
|
||||||
class MDTabbedPanel(ThemableBehavior, BackgroundColorBehavior, BoxLayout):
|
|
||||||
""" A tab panel that is implemented by delegating all tabs
|
|
||||||
to a ScreenManager.
|
|
||||||
"""
|
|
||||||
# If tabs should fill space
|
|
||||||
tab_width_mode = OptionProperty('stacked', options=['stacked', 'fixed'])
|
|
||||||
|
|
||||||
# Where the tabs go
|
|
||||||
tab_orientation = OptionProperty('top', options=['top']) # ,'left','bottom','right'])
|
|
||||||
|
|
||||||
# How tabs are displayed
|
|
||||||
tab_display_mode = OptionProperty('text', options=['text', 'icons']) # ,'both'])
|
|
||||||
_tab_display_height = DictProperty({'text': dp(46), 'icons': dp(46), 'both': dp(72)})
|
|
||||||
|
|
||||||
# Tab background color (leave empty for theme color)
|
|
||||||
tab_color = ListProperty([])
|
|
||||||
|
|
||||||
# Tab text color in normal state (leave empty for theme color)
|
|
||||||
tab_text_color = ListProperty([])
|
|
||||||
|
|
||||||
# Tab text color in active state (leave empty for theme color)
|
|
||||||
tab_text_color_active = ListProperty([])
|
|
||||||
|
|
||||||
# Tab indicator color (leave empty for theme color)
|
|
||||||
tab_indicator_color = ListProperty([])
|
|
||||||
|
|
||||||
# Tab bar bottom border color (leave empty for theme color)
|
|
||||||
tab_border_color = ListProperty([])
|
|
||||||
|
|
||||||
# List of all the tabs so you can dynamically change them
|
|
||||||
tabs = ListProperty([])
|
|
||||||
|
|
||||||
# Current tab name
|
|
||||||
current = StringProperty(None)
|
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
|
||||||
super(MDTabbedPanel, self).__init__(**kwargs)
|
|
||||||
self.previous_tab = None
|
|
||||||
self.prev_dir = None
|
|
||||||
self.index = 0
|
|
||||||
self._refresh_tabs()
|
|
||||||
|
|
||||||
def on_tab_width_mode(self, *args):
|
|
||||||
self._refresh_tabs()
|
|
||||||
|
|
||||||
def on_tab_display_mode(self, *args):
|
|
||||||
self._refresh_tabs()
|
|
||||||
|
|
||||||
def _refresh_tabs(self):
|
|
||||||
""" Refresh all tabs """
|
|
||||||
# if fixed width, use a box layout
|
|
||||||
if not self.ids:
|
|
||||||
return
|
|
||||||
tab_bar = self.ids.tab_bar
|
|
||||||
tab_bar.clear_widgets()
|
|
||||||
tab_manager = self.ids.tab_manager
|
|
||||||
for tab in tab_manager.screens:
|
|
||||||
tab_header = MDTabHeader(tab=tab,
|
|
||||||
panel=self,
|
|
||||||
height=tab_bar.height,
|
|
||||||
)
|
|
||||||
tab_bar.add_widget(tab_header)
|
|
||||||
|
|
||||||
def add_widget(self, widget, **kwargs):
|
|
||||||
""" Add tabs to the screen or the layout.
|
|
||||||
:param widget: The widget to add.
|
|
||||||
"""
|
|
||||||
d = {}
|
|
||||||
if isinstance(widget, MDTab):
|
|
||||||
self.index += 1
|
|
||||||
if self.index == 1:
|
|
||||||
self.previous_tab = widget
|
|
||||||
widget.index = self.index
|
|
||||||
widget.parent_widget = self
|
|
||||||
self.ids.tab_manager.add_widget(widget)
|
|
||||||
self._refresh_tabs()
|
|
||||||
else:
|
|
||||||
super(MDTabbedPanel, self).add_widget(widget)
|
|
||||||
|
|
||||||
def remove_widget(self, widget):
|
|
||||||
""" Remove tabs from the screen or the layout.
|
|
||||||
:param widget: The widget to remove.
|
|
||||||
"""
|
|
||||||
self.index -= 1
|
|
||||||
if isinstance(widget, MDTab):
|
|
||||||
self.ids.tab_manager.remove_widget(widget)
|
|
||||||
self._refresh_tabs()
|
|
||||||
else:
|
|
||||||
super(MDTabbedPanel, self).remove_widget(widget)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
from kivy.app import App
|
|
||||||
from kivymd.theming import ThemeManager
|
|
||||||
|
|
||||||
class TabsApp(App):
|
|
||||||
theme_cls = ThemeManager()
|
|
||||||
|
|
||||||
def build(self):
|
|
||||||
from kivy.core.window import Window
|
|
||||||
Window.size = (540, 720)
|
|
||||||
# self.theme_cls.theme_style = 'Dark'
|
|
||||||
|
|
||||||
return Builder.load_string("""
|
|
||||||
#:import Toolbar kivymd.toolbar.Toolbar
|
|
||||||
BoxLayout:
|
|
||||||
orientation:'vertical'
|
|
||||||
Toolbar:
|
|
||||||
id: toolbar
|
|
||||||
title: 'Page title'
|
|
||||||
background_color: app.theme_cls.primary_color
|
|
||||||
left_action_items: [['menu', lambda x: '']]
|
|
||||||
right_action_items: [['search', lambda x: ''],['more-vert',lambda x:'']]
|
|
||||||
MDTabbedPanel:
|
|
||||||
id: tab_mgr
|
|
||||||
tab_display_mode:'icons'
|
|
||||||
|
|
||||||
MDTab:
|
|
||||||
name: 'music'
|
|
||||||
text: "Music" # Why are these not set!!!
|
|
||||||
icon: "playlist-audio"
|
|
||||||
MDLabel:
|
|
||||||
font_style: 'Body1'
|
|
||||||
theme_text_color: 'Primary'
|
|
||||||
text: "Here is my music list :)"
|
|
||||||
halign: 'center'
|
|
||||||
MDTab:
|
|
||||||
name: 'movies'
|
|
||||||
text: 'Movies'
|
|
||||||
icon: "movie"
|
|
||||||
|
|
||||||
MDLabel:
|
|
||||||
font_style: 'Body1'
|
|
||||||
theme_text_color: 'Primary'
|
|
||||||
text: "Show movies here :)"
|
|
||||||
halign: 'center'
|
|
||||||
|
|
||||||
|
|
||||||
""")
|
|
||||||
|
|
||||||
|
|
||||||
TabsApp().run()
|
|
|
@ -1,215 +0,0 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
|
||||||
from kivy.lang import Builder
|
|
||||||
from kivy.uix.textinput import TextInput
|
|
||||||
from kivy.properties import ObjectProperty, NumericProperty, StringProperty, \
|
|
||||||
ListProperty, BooleanProperty
|
|
||||||
from kivy.metrics import sp, dp
|
|
||||||
from kivy.animation import Animation
|
|
||||||
from kivymd.label import MDLabel
|
|
||||||
from kivymd.theming import ThemableBehavior
|
|
||||||
from kivy.clock import Clock
|
|
||||||
|
|
||||||
Builder.load_string('''
|
|
||||||
<SingleLineTextField>:
|
|
||||||
canvas.before:
|
|
||||||
Clear
|
|
||||||
Color:
|
|
||||||
rgba: self.line_color_normal
|
|
||||||
Line:
|
|
||||||
id: "the_line"
|
|
||||||
points: self.x, self.y + dp(8), self.x + self.width, self.y + dp(8)
|
|
||||||
width: 1
|
|
||||||
dash_length: dp(3)
|
|
||||||
dash_offset: 2 if self.disabled else 0
|
|
||||||
Color:
|
|
||||||
rgba: self._current_line_color
|
|
||||||
Rectangle:
|
|
||||||
size: self._line_width, dp(2)
|
|
||||||
pos: self.center_x - (self._line_width / 2), self.y + dp(8)
|
|
||||||
Color:
|
|
||||||
rgba: self._current_error_color
|
|
||||||
Rectangle:
|
|
||||||
texture: self._msg_lbl.texture
|
|
||||||
size: self._msg_lbl.texture_size
|
|
||||||
pos: self.x, self.y - dp(8)
|
|
||||||
Color:
|
|
||||||
rgba: (self._current_line_color if self.focus and not self.cursor_blink \
|
|
||||||
else (0, 0, 0, 0))
|
|
||||||
Rectangle:
|
|
||||||
pos: [int(x) for x in self.cursor_pos]
|
|
||||||
size: 1, -self.line_height
|
|
||||||
Color:
|
|
||||||
#rgba: self._hint_txt_color if not self.text and not self.focus\
|
|
||||||
#else (self.line_color_focus if not self.text or self.focus\
|
|
||||||
#else self.line_color_normal)
|
|
||||||
rgba: self._current_hint_text_color
|
|
||||||
Rectangle:
|
|
||||||
texture: self._hint_lbl.texture
|
|
||||||
size: self._hint_lbl.texture_size
|
|
||||||
pos: self.x, self.y + self._hint_y
|
|
||||||
Color:
|
|
||||||
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)
|
|
||||||
|
|
||||||
font_name: 'Roboto'
|
|
||||||
foreground_color: app.theme_cls.text_color
|
|
||||||
font_size: sp(16)
|
|
||||||
bold: False
|
|
||||||
padding: 0, dp(16), 0, dp(10)
|
|
||||||
multiline: False
|
|
||||||
size_hint_y: None
|
|
||||||
height: dp(48)
|
|
||||||
''')
|
|
||||||
|
|
||||||
|
|
||||||
class SingleLineTextField(ThemableBehavior, TextInput):
|
|
||||||
line_color_normal = ListProperty()
|
|
||||||
line_color_focus = ListProperty()
|
|
||||||
error_color = ListProperty()
|
|
||||||
error = BooleanProperty(False)
|
|
||||||
message = StringProperty("")
|
|
||||||
message_mode = StringProperty("none")
|
|
||||||
mode = message_mode
|
|
||||||
|
|
||||||
_hint_txt_color = ListProperty()
|
|
||||||
_hint_lbl = ObjectProperty()
|
|
||||||
_hint_lbl_font_size = NumericProperty(sp(16))
|
|
||||||
_hint_y = NumericProperty(dp(10))
|
|
||||||
_error_label = ObjectProperty()
|
|
||||||
_line_width = NumericProperty(0)
|
|
||||||
_hint_txt = StringProperty('')
|
|
||||||
_current_line_color = line_color_focus
|
|
||||||
_current_error_color = ListProperty([0.0, 0.0, 0.0, 0.0])
|
|
||||||
_current_hint_text_color = _hint_txt_color
|
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
|
||||||
Clock.schedule_interval(self._update_color, 5)
|
|
||||||
self._msg_lbl = MDLabel(font_style='Caption',
|
|
||||||
theme_text_color='Error',
|
|
||||||
halign='left',
|
|
||||||
valign='middle',
|
|
||||||
text=self.message)
|
|
||||||
|
|
||||||
self._hint_lbl = MDLabel(font_style='Subhead',
|
|
||||||
halign='left',
|
|
||||||
valign='middle')
|
|
||||||
super(SingleLineTextField, self).__init__(**kwargs)
|
|
||||||
self.line_color_normal = self.theme_cls.divider_color
|
|
||||||
self.line_color_focus = list(self.theme_cls.primary_color)
|
|
||||||
self.base_line_color_focus = list(self.theme_cls.primary_color)
|
|
||||||
self.error_color = self.theme_cls.error_color
|
|
||||||
|
|
||||||
self._hint_txt_color = self.theme_cls.disabled_hint_text_color
|
|
||||||
self.hint_text_color = (1, 1, 1, 0)
|
|
||||||
self.cursor_color = self.theme_cls.primary_color
|
|
||||||
self.bind(message=self._set_msg,
|
|
||||||
hint_text=self._set_hint,
|
|
||||||
_hint_lbl_font_size=self._hint_lbl.setter('font_size'),
|
|
||||||
message_mode=self._set_mode)
|
|
||||||
self.hint_anim_in = Animation(_hint_y=dp(34),
|
|
||||||
_hint_lbl_font_size=sp(12), duration=.2,
|
|
||||||
t='out_quad')
|
|
||||||
|
|
||||||
self.hint_anim_out = Animation(_hint_y=dp(10),
|
|
||||||
_hint_lbl_font_size=sp(16), duration=.2,
|
|
||||||
t='out_quad')
|
|
||||||
|
|
||||||
def _update_color(self, *args):
|
|
||||||
self.line_color_normal = self.theme_cls.divider_color
|
|
||||||
self.base_line_color_focus = list(self.theme_cls.primary_color)
|
|
||||||
if not self.focus and not self.error:
|
|
||||||
self.line_color_focus = self.theme_cls.primary_color
|
|
||||||
Animation(duration=.2, _current_hint_text_color=self.theme_cls.disabled_hint_text_color).start(self)
|
|
||||||
if self.mode == "persistent":
|
|
||||||
Animation(duration=.1, _current_error_color=self.theme_cls.disabled_hint_text_color).start(self)
|
|
||||||
if self.focus and not self.error:
|
|
||||||
self.cursor_color = self.theme_cls.primary_color
|
|
||||||
|
|
||||||
def on_hint_text_color(self, instance, color):
|
|
||||||
self._hint_txt_color = self.theme_cls.disabled_hint_text_color
|
|
||||||
self.hint_text_color = (1, 1, 1, 0)
|
|
||||||
|
|
||||||
def on_width(self, instance, width):
|
|
||||||
if self.focus and instance is not None or self.error and instance is not None:
|
|
||||||
self._line_width = width
|
|
||||||
self.anim = Animation(_line_width=width, duration=.2, t='out_quad')
|
|
||||||
self._msg_lbl.width = self.width
|
|
||||||
self._hint_lbl.width = self.width
|
|
||||||
|
|
||||||
def on_pos(self, *args):
|
|
||||||
self.hint_anim_in = Animation(_hint_y=dp(34),
|
|
||||||
_hint_lbl_font_size=sp(12), duration=.2,
|
|
||||||
t='out_quad')
|
|
||||||
self.hint_anim_out = Animation(_hint_y=dp(10),
|
|
||||||
_hint_lbl_font_size=sp(16), duration=.2,
|
|
||||||
t='out_quad')
|
|
||||||
|
|
||||||
def on_focus(self, *args):
|
|
||||||
if self.focus:
|
|
||||||
Animation.cancel_all(self, '_line_width', '_hint_y',
|
|
||||||
'_hint_lbl_font_size')
|
|
||||||
if len(self.text) == 0:
|
|
||||||
self.hint_anim_in.start(self)
|
|
||||||
if self.error:
|
|
||||||
Animation(duration=.2, _current_hint_text_color=self.error_color).start(self)
|
|
||||||
if self.mode == "on_error":
|
|
||||||
Animation(duration=.2, _current_error_color=self.error_color).start(self)
|
|
||||||
elif self.mode == "persistent":
|
|
||||||
Animation(duration=.2, _current_error_color=self.theme_cls.disabled_hint_text_color).start(self)
|
|
||||||
elif self.mode == "on_focus":
|
|
||||||
Animation(duration=.2, _current_error_color=self.theme_cls.disabled_hint_text_color).start(self)
|
|
||||||
else:
|
|
||||||
pass
|
|
||||||
elif not self.error:
|
|
||||||
self.on_width(None, self.width)
|
|
||||||
self.anim.start(self)
|
|
||||||
Animation(duration=.2, _current_hint_text_color=self.line_color_focus).start(self)
|
|
||||||
if self.mode == "on_error":
|
|
||||||
Animation(duration=.2, _current_error_color=(0, 0, 0, 0)).start(self)
|
|
||||||
if self.mode == "persistent":
|
|
||||||
Animation(duration=.2, _current_error_color=self.theme_cls.disabled_hint_text_color).start(self)
|
|
||||||
elif self.mode == "on_focus":
|
|
||||||
Animation(duration=.2, _current_error_color=self.theme_cls.disabled_hint_text_color).start(self)
|
|
||||||
else:
|
|
||||||
pass
|
|
||||||
else:
|
|
||||||
Animation.cancel_all(self, '_line_width', '_hint_y',
|
|
||||||
'_hint_lbl_font_size')
|
|
||||||
if len(self.text) == 0:
|
|
||||||
self.hint_anim_out.start(self)
|
|
||||||
if not self.error:
|
|
||||||
self.line_color_focus = self.base_line_color_focus
|
|
||||||
Animation(duration=.2, _current_line_color=self.line_color_focus,
|
|
||||||
_current_hint_text_color=self.theme_cls.disabled_hint_text_color).start(self)
|
|
||||||
if self.mode == "on_error":
|
|
||||||
Animation(duration=.2, _current_error_color=(0, 0, 0, 0)).start(self)
|
|
||||||
elif self.mode == "persistent":
|
|
||||||
Animation(duration=.2, _current_error_color=self.theme_cls.disabled_hint_text_color).start(self)
|
|
||||||
elif self.mode == "on_focus":
|
|
||||||
Animation(duration=.2, _current_error_color=(0, 0, 0, 0)).start(self)
|
|
||||||
|
|
||||||
self.on_width(None, 0)
|
|
||||||
self.anim.start(self)
|
|
||||||
elif self.error:
|
|
||||||
Animation(duration=.2, _current_line_color=self.error_color,
|
|
||||||
_current_hint_text_color=self.error_color).start(self)
|
|
||||||
if self.mode == "on_error":
|
|
||||||
Animation(duration=.2, _current_error_color=self.error_color).start(self)
|
|
||||||
elif self.mode == "persistent":
|
|
||||||
Animation(duration=.2, _current_error_color=self.theme_cls.disabled_hint_text_color).start(self)
|
|
||||||
elif self.mode == "on_focus":
|
|
||||||
Animation(duration=.2, _current_error_color=(0, 0, 0, 0)).start(self)
|
|
||||||
|
|
||||||
def _set_hint(self, instance, text):
|
|
||||||
self._hint_lbl.text = text
|
|
||||||
|
|
||||||
def _set_msg(self, instance, text):
|
|
||||||
self._msg_lbl.text = text
|
|
||||||
self.message = text
|
|
||||||
|
|
||||||
def _set_mode(self, instance, text):
|
|
||||||
self.mode = text
|
|
||||||
if self.mode == "persistent":
|
|
||||||
Animation(duration=.1, _current_error_color=self.theme_cls.disabled_hint_text_color).start(self)
|
|
|
@ -1,422 +0,0 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
|
||||||
from kivy.lang import Builder
|
|
||||||
from kivy.uix.modalview import ModalView
|
|
||||||
from kivy.uix.floatlayout import FloatLayout
|
|
||||||
from kivy.uix.boxlayout import BoxLayout
|
|
||||||
from kivymd.button import MDFlatButton, MDIconButton
|
|
||||||
from kivymd.theming import ThemableBehavior
|
|
||||||
from kivymd.elevationbehavior import ElevationBehavior
|
|
||||||
from kivy.properties import ObjectProperty, ListProperty
|
|
||||||
from kivymd.label import MDLabel
|
|
||||||
from kivy.metrics import dp
|
|
||||||
from kivy.utils import get_color_from_hex
|
|
||||||
from kivymd.color_definitions import colors
|
|
||||||
|
|
||||||
Builder.load_string("""
|
|
||||||
#:import SingleLineTextField kivymd.textfields.SingleLineTextField
|
|
||||||
#:import MDTabbedPanel kivymd.tabs.MDTabbedPanel
|
|
||||||
#:import MDTab kivymd.tabs.MDTab
|
|
||||||
<MDThemePicker>:
|
|
||||||
size_hint: (None, None)
|
|
||||||
size: dp(260), dp(120)+dp(290)
|
|
||||||
pos_hint: {'center_x': .5, 'center_y': .5}
|
|
||||||
canvas:
|
|
||||||
Color:
|
|
||||||
rgb: app.theme_cls.primary_color
|
|
||||||
Rectangle:
|
|
||||||
size: dp(260), dp(120)
|
|
||||||
pos: root.pos[0], root.pos[1] + root.height-dp(120)
|
|
||||||
Color:
|
|
||||||
rgb: app.theme_cls.bg_normal
|
|
||||||
Rectangle:
|
|
||||||
size: dp(260), dp(290)
|
|
||||||
pos: root.pos[0], root.pos[1] + root.height-(dp(120)+dp(290))
|
|
||||||
|
|
||||||
MDFlatButton:
|
|
||||||
pos: root.pos[0]+root.size[0]-dp(72), root.pos[1] + dp(10)
|
|
||||||
text: "Close"
|
|
||||||
on_release: root.dismiss()
|
|
||||||
MDLabel:
|
|
||||||
font_style: "Headline"
|
|
||||||
text: "Change theme"
|
|
||||||
size_hint: (None, None)
|
|
||||||
size: dp(160), dp(50)
|
|
||||||
pos_hint: {'center_x': 0.5, 'center_y': 0.9}
|
|
||||||
MDTabbedPanel:
|
|
||||||
size_hint: (None, None)
|
|
||||||
size: dp(260), root.height-dp(135)
|
|
||||||
pos_hint: {'center_x': 0.5, 'center_y': 0.475}
|
|
||||||
id: tab_panel
|
|
||||||
tab_display_mode:'text'
|
|
||||||
|
|
||||||
MDTab:
|
|
||||||
name: 'color'
|
|
||||||
text: "Theme Color"
|
|
||||||
BoxLayout:
|
|
||||||
spacing: dp(4)
|
|
||||||
size_hint: (None, None)
|
|
||||||
size: dp(270), root.height # -dp(120)
|
|
||||||
pos_hint: {'center_x': 0.532, 'center_y': 0.89}
|
|
||||||
orientation: 'vertical'
|
|
||||||
BoxLayout:
|
|
||||||
size_hint: (None, None)
|
|
||||||
pos_hint: {'center_x': 0.5, 'center_y': 0.5}
|
|
||||||
size: dp(230), dp(40)
|
|
||||||
pos: self.pos
|
|
||||||
halign: 'center'
|
|
||||||
orientation: 'horizontal'
|
|
||||||
BoxLayout:
|
|
||||||
MDIconButton:
|
|
||||||
size: dp(40), dp(40)
|
|
||||||
pos: self.pos
|
|
||||||
size_hint: (None, None)
|
|
||||||
canvas:
|
|
||||||
Color:
|
|
||||||
rgba: root.rgb_hex('Red')
|
|
||||||
Ellipse:
|
|
||||||
size: self.size
|
|
||||||
pos: self.pos
|
|
||||||
on_release: app.theme_cls.primary_palette = 'Red'
|
|
||||||
BoxLayout:
|
|
||||||
MDIconButton:
|
|
||||||
size: dp(40), dp(40)
|
|
||||||
pos: self.pos
|
|
||||||
size_hint: (None, None)
|
|
||||||
canvas:
|
|
||||||
Color:
|
|
||||||
rgba: root.rgb_hex('Pink')
|
|
||||||
Ellipse:
|
|
||||||
size: self.size
|
|
||||||
pos: self.pos
|
|
||||||
on_release: app.theme_cls.primary_palette = 'Pink'
|
|
||||||
BoxLayout:
|
|
||||||
MDIconButton:
|
|
||||||
size: dp(40), dp(40)
|
|
||||||
pos: self.pos
|
|
||||||
size_hint: (None, None)
|
|
||||||
canvas:
|
|
||||||
Color:
|
|
||||||
rgba: root.rgb_hex('Purple')
|
|
||||||
Ellipse:
|
|
||||||
size: self.size
|
|
||||||
pos: self.pos
|
|
||||||
on_release: app.theme_cls.primary_palette = 'Purple'
|
|
||||||
BoxLayout:
|
|
||||||
MDIconButton:
|
|
||||||
size: dp(40), dp(40)
|
|
||||||
pos: self.pos
|
|
||||||
size_hint: (None, None)
|
|
||||||
canvas:
|
|
||||||
Color:
|
|
||||||
rgba: root.rgb_hex('DeepPurple')
|
|
||||||
Ellipse:
|
|
||||||
size: self.size
|
|
||||||
pos: self.pos
|
|
||||||
on_release: app.theme_cls.primary_palette = 'DeepPurple'
|
|
||||||
BoxLayout:
|
|
||||||
size_hint: (None, None)
|
|
||||||
pos_hint: {'center_x': .5, 'center_y': 0.5}
|
|
||||||
size: dp(230), dp(40)
|
|
||||||
pos: self.pos
|
|
||||||
halign: 'center'
|
|
||||||
orientation: 'horizontal'
|
|
||||||
BoxLayout:
|
|
||||||
MDIconButton:
|
|
||||||
size: dp(40), dp(40)
|
|
||||||
pos: self.pos
|
|
||||||
size_hint: (None, None)
|
|
||||||
canvas:
|
|
||||||
Color:
|
|
||||||
rgba: root.rgb_hex('Indigo')
|
|
||||||
Ellipse:
|
|
||||||
size: self.size
|
|
||||||
pos: self.pos
|
|
||||||
on_release: app.theme_cls.primary_palette = 'Indigo'
|
|
||||||
BoxLayout:
|
|
||||||
MDIconButton:
|
|
||||||
size: dp(40), dp(40)
|
|
||||||
pos: self.pos
|
|
||||||
size_hint: (None, None)
|
|
||||||
canvas:
|
|
||||||
Color:
|
|
||||||
rgba: root.rgb_hex('Blue')
|
|
||||||
Ellipse:
|
|
||||||
size: self.size
|
|
||||||
pos: self.pos
|
|
||||||
on_release: app.theme_cls.primary_palette = 'Blue'
|
|
||||||
BoxLayout:
|
|
||||||
MDIconButton:
|
|
||||||
size: dp(40), dp(40)
|
|
||||||
pos: self.pos
|
|
||||||
size_hint: (None, None)
|
|
||||||
canvas:
|
|
||||||
Color:
|
|
||||||
rgba: root.rgb_hex('LightBlue')
|
|
||||||
Ellipse:
|
|
||||||
size: self.size
|
|
||||||
pos: self.pos
|
|
||||||
on_release: app.theme_cls.primary_palette = 'LightBlue'
|
|
||||||
BoxLayout:
|
|
||||||
MDIconButton:
|
|
||||||
size: dp(40), dp(40)
|
|
||||||
pos: self.pos
|
|
||||||
size_hint: (None, None)
|
|
||||||
canvas:
|
|
||||||
Color:
|
|
||||||
rgba: root.rgb_hex('Cyan')
|
|
||||||
Ellipse:
|
|
||||||
size: self.size
|
|
||||||
pos: self.pos
|
|
||||||
on_release: app.theme_cls.primary_palette = 'Cyan'
|
|
||||||
BoxLayout:
|
|
||||||
size_hint: (None, None)
|
|
||||||
pos_hint: {'center_x': .5, 'center_y': 0.5}
|
|
||||||
size: dp(230), dp(40)
|
|
||||||
pos: self.pos
|
|
||||||
halign: 'center'
|
|
||||||
orientation: 'horizontal'
|
|
||||||
padding: 0, 0, 0, dp(1)
|
|
||||||
BoxLayout:
|
|
||||||
MDIconButton:
|
|
||||||
size: dp(40), dp(40)
|
|
||||||
pos: self.pos
|
|
||||||
size_hint: (None, None)
|
|
||||||
canvas:
|
|
||||||
Color:
|
|
||||||
rgba: root.rgb_hex('Teal')
|
|
||||||
Ellipse:
|
|
||||||
size: self.size
|
|
||||||
pos: self.pos
|
|
||||||
on_release: app.theme_cls.primary_palette = 'Teal'
|
|
||||||
BoxLayout:
|
|
||||||
MDIconButton:
|
|
||||||
size: dp(40), dp(40)
|
|
||||||
pos: self.pos
|
|
||||||
size_hint: (None, None)
|
|
||||||
canvas:
|
|
||||||
Color:
|
|
||||||
rgba: root.rgb_hex('Green')
|
|
||||||
Ellipse:
|
|
||||||
size: self.size
|
|
||||||
pos: self.pos
|
|
||||||
on_release: app.theme_cls.primary_palette = 'Green'
|
|
||||||
BoxLayout:
|
|
||||||
MDIconButton:
|
|
||||||
size: dp(40), dp(40)
|
|
||||||
pos: self.pos
|
|
||||||
size_hint: (None, None)
|
|
||||||
canvas:
|
|
||||||
Color:
|
|
||||||
rgba: root.rgb_hex('LightGreen')
|
|
||||||
Ellipse:
|
|
||||||
size: self.size
|
|
||||||
pos: self.pos
|
|
||||||
on_release: app.theme_cls.primary_palette = 'LightGreen'
|
|
||||||
BoxLayout:
|
|
||||||
MDIconButton:
|
|
||||||
size: dp(40), dp(40)
|
|
||||||
pos: self.pos
|
|
||||||
size_hint: (None, None)
|
|
||||||
canvas:
|
|
||||||
Color:
|
|
||||||
rgba: root.rgb_hex('Lime')
|
|
||||||
Ellipse:
|
|
||||||
size: self.size
|
|
||||||
pos: self.pos
|
|
||||||
on_release: app.theme_cls.primary_palette = 'Lime'
|
|
||||||
BoxLayout:
|
|
||||||
size_hint: (None, None)
|
|
||||||
pos_hint: {'center_x': .5, 'center_y': 0.5}
|
|
||||||
size: dp(230), dp(40)
|
|
||||||
pos: self.pos
|
|
||||||
orientation: 'horizontal'
|
|
||||||
halign: 'center'
|
|
||||||
padding: 0, 0, 0, dp(1)
|
|
||||||
BoxLayout:
|
|
||||||
MDIconButton:
|
|
||||||
size: dp(40), dp(40)
|
|
||||||
pos: self.pos
|
|
||||||
size_hint: (None, None)
|
|
||||||
canvas:
|
|
||||||
Color:
|
|
||||||
rgba: root.rgb_hex('Yellow')
|
|
||||||
Ellipse:
|
|
||||||
size: self.size
|
|
||||||
pos: self.pos
|
|
||||||
on_release: app.theme_cls.primary_palette = 'Yellow'
|
|
||||||
BoxLayout:
|
|
||||||
MDIconButton:
|
|
||||||
size: dp(40), dp(40)
|
|
||||||
pos: self.pos
|
|
||||||
size_hint: (None, None)
|
|
||||||
canvas:
|
|
||||||
Color:
|
|
||||||
rgba: root.rgb_hex('Amber')
|
|
||||||
Ellipse:
|
|
||||||
size: self.size
|
|
||||||
pos: self.pos
|
|
||||||
on_release: app.theme_cls.primary_palette = 'Amber'
|
|
||||||
BoxLayout:
|
|
||||||
MDIconButton:
|
|
||||||
size: dp(40), dp(40)
|
|
||||||
pos: self.pos
|
|
||||||
size_hint: (None, None)
|
|
||||||
canvas:
|
|
||||||
Color:
|
|
||||||
rgba: root.rgb_hex('Orange')
|
|
||||||
Ellipse:
|
|
||||||
size: self.size
|
|
||||||
pos: self.pos
|
|
||||||
on_release: app.theme_cls.primary_palette = 'Orange'
|
|
||||||
BoxLayout:
|
|
||||||
MDIconButton:
|
|
||||||
size: dp(40), dp(40)
|
|
||||||
pos: self.pos
|
|
||||||
size_hint: (None, None)
|
|
||||||
canvas:
|
|
||||||
Color:
|
|
||||||
rgba: root.rgb_hex('DeepOrange')
|
|
||||||
Ellipse:
|
|
||||||
size: self.size
|
|
||||||
pos: self.pos
|
|
||||||
on_release: app.theme_cls.primary_palette = 'DeepOrange'
|
|
||||||
BoxLayout:
|
|
||||||
size_hint: (None, None)
|
|
||||||
pos_hint: {'center_x': .5, 'center_y': 0.5}
|
|
||||||
size: dp(230), dp(40)
|
|
||||||
#pos: self.pos
|
|
||||||
orientation: 'horizontal'
|
|
||||||
padding: 0, 0, 0, dp(1)
|
|
||||||
BoxLayout:
|
|
||||||
MDIconButton:
|
|
||||||
size: dp(40), dp(40)
|
|
||||||
size_hint: (None, None)
|
|
||||||
canvas:
|
|
||||||
Color:
|
|
||||||
rgba: root.rgb_hex('Brown')
|
|
||||||
Ellipse:
|
|
||||||
size: self.size
|
|
||||||
pos: self.pos
|
|
||||||
on_release: app.theme_cls.primary_palette = 'Brown'
|
|
||||||
BoxLayout:
|
|
||||||
MDIconButton:
|
|
||||||
size: dp(40), dp(40)
|
|
||||||
pos: self.pos
|
|
||||||
size_hint: (None, None)
|
|
||||||
canvas:
|
|
||||||
Color:
|
|
||||||
rgba: root.rgb_hex('Grey')
|
|
||||||
Ellipse:
|
|
||||||
size: self.size
|
|
||||||
pos: self.pos
|
|
||||||
on_release: app.theme_cls.primary_palette = 'Grey'
|
|
||||||
BoxLayout:
|
|
||||||
MDIconButton:
|
|
||||||
size: dp(40), dp(40)
|
|
||||||
#pos: self.pos
|
|
||||||
size_hint: (None, None)
|
|
||||||
canvas:
|
|
||||||
Color:
|
|
||||||
rgba: root.rgb_hex('BlueGrey')
|
|
||||||
Ellipse:
|
|
||||||
size: self.size
|
|
||||||
pos: self.pos
|
|
||||||
on_release: app.theme_cls.primary_palette = 'BlueGrey'
|
|
||||||
BoxLayout:
|
|
||||||
MDIconButton:
|
|
||||||
size: dp(40), dp(40)
|
|
||||||
size_hint: (None, None)
|
|
||||||
canvas:
|
|
||||||
Color:
|
|
||||||
rgba: app.theme_cls.bg_normal
|
|
||||||
Ellipse:
|
|
||||||
size: self.size
|
|
||||||
pos: self.pos
|
|
||||||
disabled: True
|
|
||||||
|
|
||||||
MDTab:
|
|
||||||
name: 'style'
|
|
||||||
text: "Theme Style"
|
|
||||||
BoxLayout:
|
|
||||||
size_hint: (None, None)
|
|
||||||
pos_hint: {'center_x': .3, 'center_y': 0.5}
|
|
||||||
size: self.size
|
|
||||||
pos: self.pos
|
|
||||||
halign: 'center'
|
|
||||||
spacing: dp(10)
|
|
||||||
BoxLayout:
|
|
||||||
halign: 'center'
|
|
||||||
size_hint: (None, None)
|
|
||||||
size: dp(100), dp(100)
|
|
||||||
pos: self.pos
|
|
||||||
pos_hint: {'center_x': .3, 'center_y': 0.5}
|
|
||||||
MDIconButton:
|
|
||||||
size: dp(100), dp(100)
|
|
||||||
pos: self.pos
|
|
||||||
size_hint: (None, None)
|
|
||||||
canvas:
|
|
||||||
Color:
|
|
||||||
rgba: 1, 1, 1, 1
|
|
||||||
Ellipse:
|
|
||||||
size: self.size
|
|
||||||
pos: self.pos
|
|
||||||
Color:
|
|
||||||
rgba: 0, 0, 0, 1
|
|
||||||
Line:
|
|
||||||
width: 1.
|
|
||||||
circle: (self.center_x, self.center_y, 50)
|
|
||||||
on_release: app.theme_cls.theme_style = 'Light'
|
|
||||||
BoxLayout:
|
|
||||||
halign: 'center'
|
|
||||||
size_hint: (None, None)
|
|
||||||
size: dp(100), dp(100)
|
|
||||||
MDIconButton:
|
|
||||||
size: dp(100), dp(100)
|
|
||||||
pos: self.pos
|
|
||||||
size_hint: (None, None)
|
|
||||||
canvas:
|
|
||||||
Color:
|
|
||||||
rgba: 0, 0, 0, 1
|
|
||||||
Ellipse:
|
|
||||||
size: self.size
|
|
||||||
pos: self.pos
|
|
||||||
on_release: app.theme_cls.theme_style = 'Dark'
|
|
||||||
""")
|
|
||||||
|
|
||||||
|
|
||||||
class MDThemePicker(ThemableBehavior, FloatLayout, ModalView, ElevationBehavior):
|
|
||||||
# background_color = ListProperty([0, 0, 0, 0])
|
|
||||||
time = ObjectProperty()
|
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
|
||||||
super(MDThemePicker, self).__init__(**kwargs)
|
|
||||||
|
|
||||||
def rgb_hex(self, col):
|
|
||||||
return get_color_from_hex(colors[col][self.theme_cls.accent_hue])
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
from kivy.app import App
|
|
||||||
from kivymd.theming import ThemeManager
|
|
||||||
|
|
||||||
class ThemePickerApp(App):
|
|
||||||
theme_cls = ThemeManager()
|
|
||||||
|
|
||||||
def build(self):
|
|
||||||
main_widget = Builder.load_string("""
|
|
||||||
#:import MDRaisedButton kivymd.button.MDRaisedButton
|
|
||||||
#:import MDThemePicker kivymd.theme_picker.MDThemePicker
|
|
||||||
FloatLayout:
|
|
||||||
MDRaisedButton:
|
|
||||||
size_hint: None, None
|
|
||||||
pos_hint: {'center_x': .5, 'center_y': .5}
|
|
||||||
size: 3 * dp(48), dp(48)
|
|
||||||
center_x: self.parent.center_x
|
|
||||||
text: 'Open theme picker'
|
|
||||||
on_release: MDThemePicker().open()
|
|
||||||
opposite_colors: True
|
|
||||||
""")
|
|
||||||
return main_widget
|
|
||||||
|
|
||||||
ThemePickerApp().run()
|
|
|
@ -1,350 +0,0 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
from kivy.app import App
|
|
||||||
from kivy.core.text import LabelBase
|
|
||||||
from kivy.core.window import Window
|
|
||||||
from kivy.clock import Clock
|
|
||||||
from kivy.metrics import dp
|
|
||||||
from kivy.properties import OptionProperty, AliasProperty, ObjectProperty, \
|
|
||||||
StringProperty, ListProperty, BooleanProperty
|
|
||||||
from kivy.uix.widget import Widget
|
|
||||||
from kivy.utils import get_color_from_hex
|
|
||||||
from kivy.atlas import Atlas
|
|
||||||
from kivymd.color_definitions import colors
|
|
||||||
from kivymd.material_resources import FONTS, DEVICE_TYPE
|
|
||||||
from kivymd import images_path
|
|
||||||
|
|
||||||
for font in FONTS:
|
|
||||||
LabelBase.register(**font)
|
|
||||||
|
|
||||||
|
|
||||||
class ThemeManager(Widget):
|
|
||||||
primary_palette = OptionProperty(
|
|
||||||
'Blue',
|
|
||||||
options=['Pink', 'Blue', 'Indigo', 'BlueGrey', 'Brown',
|
|
||||||
'LightBlue',
|
|
||||||
'Purple', 'Grey', 'Yellow', 'LightGreen', 'DeepOrange',
|
|
||||||
'Green', 'Red', 'Teal', 'Orange', 'Cyan', 'Amber',
|
|
||||||
'DeepPurple', 'Lime'])
|
|
||||||
|
|
||||||
primary_hue = OptionProperty(
|
|
||||||
'500',
|
|
||||||
options=['50', '100', '200', '300', '400', '500', '600', '700',
|
|
||||||
'800',
|
|
||||||
'900', 'A100', 'A200', 'A400', 'A700'])
|
|
||||||
|
|
||||||
primary_light_hue = OptionProperty(
|
|
||||||
'200',
|
|
||||||
options=['50', '100', '200', '300', '400', '500', '600', '700',
|
|
||||||
'800',
|
|
||||||
'900', 'A100', 'A200', 'A400', 'A700'])
|
|
||||||
|
|
||||||
primary_dark_hue = OptionProperty(
|
|
||||||
'700',
|
|
||||||
options=['50', '100', '200', '300', '400', '500', '600', '700',
|
|
||||||
'800',
|
|
||||||
'900', 'A100', 'A200', 'A400', 'A700'])
|
|
||||||
|
|
||||||
def _get_primary_color(self):
|
|
||||||
return get_color_from_hex(
|
|
||||||
colors[self.primary_palette][self.primary_hue])
|
|
||||||
|
|
||||||
primary_color = AliasProperty(_get_primary_color,
|
|
||||||
bind=('primary_palette', 'primary_hue'))
|
|
||||||
|
|
||||||
def _get_primary_light(self):
|
|
||||||
return get_color_from_hex(
|
|
||||||
colors[self.primary_palette][self.primary_light_hue])
|
|
||||||
|
|
||||||
primary_light = AliasProperty(
|
|
||||||
_get_primary_light, bind=('primary_palette', 'primary_light_hue'))
|
|
||||||
|
|
||||||
def _get_primary_dark(self):
|
|
||||||
return get_color_from_hex(
|
|
||||||
colors[self.primary_palette][self.primary_dark_hue])
|
|
||||||
|
|
||||||
primary_dark = AliasProperty(_get_primary_dark,
|
|
||||||
bind=('primary_palette', 'primary_dark_hue'))
|
|
||||||
|
|
||||||
accent_palette = OptionProperty(
|
|
||||||
'Amber',
|
|
||||||
options=['Pink', 'Blue', 'Indigo', 'BlueGrey', 'Brown',
|
|
||||||
'LightBlue',
|
|
||||||
'Purple', 'Grey', 'Yellow', 'LightGreen', 'DeepOrange',
|
|
||||||
'Green', 'Red', 'Teal', 'Orange', 'Cyan', 'Amber',
|
|
||||||
'DeepPurple', 'Lime'])
|
|
||||||
|
|
||||||
accent_hue = OptionProperty(
|
|
||||||
'500',
|
|
||||||
options=['50', '100', '200', '300', '400', '500', '600', '700',
|
|
||||||
'800',
|
|
||||||
'900', 'A100', 'A200', 'A400', 'A700'])
|
|
||||||
|
|
||||||
accent_light_hue = OptionProperty(
|
|
||||||
'200',
|
|
||||||
options=['50', '100', '200', '300', '400', '500', '600', '700',
|
|
||||||
'800',
|
|
||||||
'900', 'A100', 'A200', 'A400', 'A700'])
|
|
||||||
|
|
||||||
accent_dark_hue = OptionProperty(
|
|
||||||
'700',
|
|
||||||
options=['50', '100', '200', '300', '400', '500', '600', '700',
|
|
||||||
'800',
|
|
||||||
'900', 'A100', 'A200', 'A400', 'A700'])
|
|
||||||
|
|
||||||
def _get_accent_color(self):
|
|
||||||
return get_color_from_hex(
|
|
||||||
colors[self.accent_palette][self.accent_hue])
|
|
||||||
|
|
||||||
accent_color = AliasProperty(_get_accent_color,
|
|
||||||
bind=['accent_palette', 'accent_hue'])
|
|
||||||
|
|
||||||
def _get_accent_light(self):
|
|
||||||
return get_color_from_hex(
|
|
||||||
colors[self.accent_palette][self.accent_light_hue])
|
|
||||||
|
|
||||||
accent_light = AliasProperty(_get_accent_light,
|
|
||||||
bind=['accent_palette', 'accent_light_hue'])
|
|
||||||
|
|
||||||
def _get_accent_dark(self):
|
|
||||||
return get_color_from_hex(
|
|
||||||
colors[self.accent_palette][self.accent_dark_hue])
|
|
||||||
|
|
||||||
accent_dark = AliasProperty(_get_accent_dark,
|
|
||||||
bind=['accent_palette', 'accent_dark_hue'])
|
|
||||||
|
|
||||||
theme_style = OptionProperty('Light', options=['Light', 'Dark'])
|
|
||||||
|
|
||||||
def _get_theme_style(self, opposite):
|
|
||||||
if opposite:
|
|
||||||
return 'Light' if self.theme_style == 'Dark' else 'Dark'
|
|
||||||
else:
|
|
||||||
return self.theme_style
|
|
||||||
|
|
||||||
def _get_bg_darkest(self, opposite=False):
|
|
||||||
theme_style = self._get_theme_style(opposite)
|
|
||||||
if theme_style == 'Light':
|
|
||||||
return get_color_from_hex(colors['Light']['StatusBar'])
|
|
||||||
elif theme_style == 'Dark':
|
|
||||||
return get_color_from_hex(colors['Dark']['StatusBar'])
|
|
||||||
|
|
||||||
bg_darkest = AliasProperty(_get_bg_darkest, bind=['theme_style'])
|
|
||||||
|
|
||||||
def _get_op_bg_darkest(self):
|
|
||||||
return self._get_bg_darkest(True)
|
|
||||||
|
|
||||||
opposite_bg_darkest = AliasProperty(_get_op_bg_darkest,
|
|
||||||
bind=['theme_style'])
|
|
||||||
|
|
||||||
def _get_bg_dark(self, opposite=False):
|
|
||||||
theme_style = self._get_theme_style(opposite)
|
|
||||||
if theme_style == 'Light':
|
|
||||||
return get_color_from_hex(colors['Light']['AppBar'])
|
|
||||||
elif theme_style == 'Dark':
|
|
||||||
return get_color_from_hex(colors['Dark']['AppBar'])
|
|
||||||
|
|
||||||
bg_dark = AliasProperty(_get_bg_dark, bind=['theme_style'])
|
|
||||||
|
|
||||||
def _get_op_bg_dark(self):
|
|
||||||
return self._get_bg_dark(True)
|
|
||||||
|
|
||||||
opposite_bg_dark = AliasProperty(_get_op_bg_dark, bind=['theme_style'])
|
|
||||||
|
|
||||||
def _get_bg_normal(self, opposite=False):
|
|
||||||
theme_style = self._get_theme_style(opposite)
|
|
||||||
if theme_style == 'Light':
|
|
||||||
return get_color_from_hex(colors['Light']['Background'])
|
|
||||||
elif theme_style == 'Dark':
|
|
||||||
return get_color_from_hex(colors['Dark']['Background'])
|
|
||||||
|
|
||||||
bg_normal = AliasProperty(_get_bg_normal, bind=['theme_style'])
|
|
||||||
|
|
||||||
def _get_op_bg_normal(self):
|
|
||||||
return self._get_bg_normal(True)
|
|
||||||
|
|
||||||
opposite_bg_normal = AliasProperty(_get_op_bg_normal, bind=['theme_style'])
|
|
||||||
|
|
||||||
def _get_bg_light(self, opposite=False):
|
|
||||||
theme_style = self._get_theme_style(opposite)
|
|
||||||
if theme_style == 'Light':
|
|
||||||
return get_color_from_hex(colors['Light']['CardsDialogs'])
|
|
||||||
elif theme_style == 'Dark':
|
|
||||||
return get_color_from_hex(colors['Dark']['CardsDialogs'])
|
|
||||||
|
|
||||||
bg_light = AliasProperty(_get_bg_light, bind=['theme_style'])
|
|
||||||
|
|
||||||
def _get_op_bg_light(self):
|
|
||||||
return self._get_bg_light(True)
|
|
||||||
|
|
||||||
opposite_bg_light = AliasProperty(_get_op_bg_light, bind=['theme_style'])
|
|
||||||
|
|
||||||
def _get_divider_color(self, opposite=False):
|
|
||||||
theme_style = self._get_theme_style(opposite)
|
|
||||||
if theme_style == 'Light':
|
|
||||||
color = get_color_from_hex('000000')
|
|
||||||
elif theme_style == 'Dark':
|
|
||||||
color = get_color_from_hex('FFFFFF')
|
|
||||||
color[3] = .12
|
|
||||||
return color
|
|
||||||
|
|
||||||
divider_color = AliasProperty(_get_divider_color, bind=['theme_style'])
|
|
||||||
|
|
||||||
def _get_op_divider_color(self):
|
|
||||||
return self._get_divider_color(True)
|
|
||||||
|
|
||||||
opposite_divider_color = AliasProperty(_get_op_divider_color,
|
|
||||||
bind=['theme_style'])
|
|
||||||
|
|
||||||
def _get_text_color(self, opposite=False):
|
|
||||||
theme_style = self._get_theme_style(opposite)
|
|
||||||
if theme_style == 'Light':
|
|
||||||
color = get_color_from_hex('000000')
|
|
||||||
color[3] = .87
|
|
||||||
elif theme_style == 'Dark':
|
|
||||||
color = get_color_from_hex('FFFFFF')
|
|
||||||
return color
|
|
||||||
|
|
||||||
text_color = AliasProperty(_get_text_color, bind=['theme_style'])
|
|
||||||
|
|
||||||
def _get_op_text_color(self):
|
|
||||||
return self._get_text_color(True)
|
|
||||||
|
|
||||||
opposite_text_color = AliasProperty(_get_op_text_color,
|
|
||||||
bind=['theme_style'])
|
|
||||||
|
|
||||||
def _get_secondary_text_color(self, opposite=False):
|
|
||||||
theme_style = self._get_theme_style(opposite)
|
|
||||||
if theme_style == 'Light':
|
|
||||||
color = get_color_from_hex('000000')
|
|
||||||
color[3] = .54
|
|
||||||
elif theme_style == 'Dark':
|
|
||||||
color = get_color_from_hex('FFFFFF')
|
|
||||||
color[3] = .70
|
|
||||||
return color
|
|
||||||
|
|
||||||
secondary_text_color = AliasProperty(_get_secondary_text_color,
|
|
||||||
bind=['theme_style'])
|
|
||||||
|
|
||||||
def _get_op_secondary_text_color(self):
|
|
||||||
return self._get_secondary_text_color(True)
|
|
||||||
|
|
||||||
opposite_secondary_text_color = AliasProperty(_get_op_secondary_text_color,
|
|
||||||
bind=['theme_style'])
|
|
||||||
|
|
||||||
def _get_icon_color(self, opposite=False):
|
|
||||||
theme_style = self._get_theme_style(opposite)
|
|
||||||
if theme_style == 'Light':
|
|
||||||
color = get_color_from_hex('000000')
|
|
||||||
color[3] = .54
|
|
||||||
elif theme_style == 'Dark':
|
|
||||||
color = get_color_from_hex('FFFFFF')
|
|
||||||
return color
|
|
||||||
|
|
||||||
icon_color = AliasProperty(_get_icon_color,
|
|
||||||
bind=['theme_style'])
|
|
||||||
|
|
||||||
def _get_op_icon_color(self):
|
|
||||||
return self._get_icon_color(True)
|
|
||||||
|
|
||||||
opposite_icon_color = AliasProperty(_get_op_icon_color,
|
|
||||||
bind=['theme_style'])
|
|
||||||
|
|
||||||
def _get_disabled_hint_text_color(self, opposite=False):
|
|
||||||
theme_style = self._get_theme_style(opposite)
|
|
||||||
if theme_style == 'Light':
|
|
||||||
color = get_color_from_hex('000000')
|
|
||||||
color[3] = .26
|
|
||||||
elif theme_style == 'Dark':
|
|
||||||
color = get_color_from_hex('FFFFFF')
|
|
||||||
color[3] = .30
|
|
||||||
return color
|
|
||||||
|
|
||||||
disabled_hint_text_color = AliasProperty(_get_disabled_hint_text_color,
|
|
||||||
bind=['theme_style'])
|
|
||||||
|
|
||||||
def _get_op_disabled_hint_text_color(self):
|
|
||||||
return self._get_disabled_hint_text_color(True)
|
|
||||||
|
|
||||||
opposite_disabled_hint_text_color = AliasProperty(
|
|
||||||
_get_op_disabled_hint_text_color, bind=['theme_style'])
|
|
||||||
|
|
||||||
# Hardcoded because muh standard
|
|
||||||
def _get_error_color(self):
|
|
||||||
return get_color_from_hex(colors['Red']['A700'])
|
|
||||||
|
|
||||||
error_color = AliasProperty(_get_error_color)
|
|
||||||
|
|
||||||
def _get_ripple_color(self):
|
|
||||||
return self._ripple_color
|
|
||||||
|
|
||||||
def _set_ripple_color(self, value):
|
|
||||||
self._ripple_color = value
|
|
||||||
|
|
||||||
_ripple_color = ListProperty(get_color_from_hex(colors['Grey']['400']))
|
|
||||||
ripple_color = AliasProperty(_get_ripple_color,
|
|
||||||
_set_ripple_color,
|
|
||||||
bind=['_ripple_color'])
|
|
||||||
|
|
||||||
def _determine_device_orientation(self, _, window_size):
|
|
||||||
if window_size[0] > window_size[1]:
|
|
||||||
self.device_orientation = 'landscape'
|
|
||||||
elif window_size[1] >= window_size[0]:
|
|
||||||
self.device_orientation = 'portrait'
|
|
||||||
|
|
||||||
device_orientation = StringProperty('')
|
|
||||||
|
|
||||||
def _get_standard_increment(self):
|
|
||||||
if DEVICE_TYPE == 'mobile':
|
|
||||||
if self.device_orientation == 'landscape':
|
|
||||||
return dp(48)
|
|
||||||
else:
|
|
||||||
return dp(56)
|
|
||||||
else:
|
|
||||||
return dp(64)
|
|
||||||
|
|
||||||
standard_increment = AliasProperty(_get_standard_increment,
|
|
||||||
bind=['device_orientation'])
|
|
||||||
|
|
||||||
def _get_horizontal_margins(self):
|
|
||||||
if DEVICE_TYPE == 'mobile':
|
|
||||||
return dp(16)
|
|
||||||
else:
|
|
||||||
return dp(24)
|
|
||||||
|
|
||||||
horizontal_margins = AliasProperty(_get_horizontal_margins)
|
|
||||||
|
|
||||||
def on_theme_style(self, instance, value):
|
|
||||||
if hasattr(App.get_running_app(), 'theme_cls') and \
|
|
||||||
App.get_running_app().theme_cls == self:
|
|
||||||
self.set_clearcolor_by_theme_style(value)
|
|
||||||
|
|
||||||
def set_clearcolor_by_theme_style(self, theme_style):
|
|
||||||
if theme_style == 'Light':
|
|
||||||
Window.clearcolor = get_color_from_hex(
|
|
||||||
colors['Light']['Background'])
|
|
||||||
elif theme_style == 'Dark':
|
|
||||||
Window.clearcolor = get_color_from_hex(
|
|
||||||
colors['Dark']['Background'])
|
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
|
||||||
super(ThemeManager, self).__init__(**kwargs)
|
|
||||||
self.rec_shadow = Atlas('{}rec_shadow.atlas'.format(images_path))
|
|
||||||
self.rec_st_shadow = Atlas('{}rec_st_shadow.atlas'.format(images_path))
|
|
||||||
self.quad_shadow = Atlas('{}quad_shadow.atlas'.format(images_path))
|
|
||||||
self.round_shadow = Atlas('{}round_shadow.atlas'.format(images_path))
|
|
||||||
Clock.schedule_once(lambda x: self.on_theme_style(0, self.theme_style))
|
|
||||||
self._determine_device_orientation(None, Window.size)
|
|
||||||
Window.bind(size=self._determine_device_orientation)
|
|
||||||
|
|
||||||
|
|
||||||
class ThemableBehavior(object):
|
|
||||||
theme_cls = ObjectProperty(None)
|
|
||||||
opposite_colors = BooleanProperty(False)
|
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
|
||||||
if self.theme_cls is not None:
|
|
||||||
pass
|
|
||||||
elif hasattr(App.get_running_app(), 'theme_cls'):
|
|
||||||
self.theme_cls = App.get_running_app().theme_cls
|
|
||||||
else:
|
|
||||||
self.theme_cls = ThemeManager()
|
|
||||||
super(ThemableBehavior, self).__init__(**kwargs)
|
|
|
@ -1,84 +0,0 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
|
||||||
from kivy.lang import Builder
|
|
||||||
from kivy.uix.modalview import ModalView
|
|
||||||
from kivy.uix.floatlayout import FloatLayout
|
|
||||||
from kivymd.theming import ThemableBehavior
|
|
||||||
from kivymd.elevationbehavior import ElevationBehavior
|
|
||||||
from kivy.properties import ObjectProperty, ListProperty
|
|
||||||
|
|
||||||
Builder.load_string("""
|
|
||||||
#:import MDFlatButton kivymd.button.MDFlatButton
|
|
||||||
#:import CircularTimePicker kivymd.vendor.circularTimePicker.CircularTimePicker
|
|
||||||
#:import dp kivy.metrics.dp
|
|
||||||
<MDTimePicker>:
|
|
||||||
size_hint: (None, None)
|
|
||||||
size: [dp(270), dp(335)+dp(95)]
|
|
||||||
#if root.theme_cls.device_orientation == 'portrait' else [dp(520), dp(325)]
|
|
||||||
pos_hint: {'center_x': .5, 'center_y': .5}
|
|
||||||
canvas:
|
|
||||||
Color:
|
|
||||||
rgba: self.theme_cls.bg_light
|
|
||||||
Rectangle:
|
|
||||||
size: [dp(270), dp(335)]
|
|
||||||
#if root.theme_cls.device_orientation == 'portrait' else [dp(250), root.height]
|
|
||||||
pos: [root.pos[0], root.pos[1] + root.height - dp(335) - dp(95)]
|
|
||||||
#if root.theme_cls.device_orientation == 'portrait' else [root.pos[0]+dp(270), root.pos[1]]
|
|
||||||
Color:
|
|
||||||
rgba: self.theme_cls.primary_color
|
|
||||||
Rectangle:
|
|
||||||
size: [dp(270), dp(95)]
|
|
||||||
#if root.theme_cls.device_orientation == 'portrait' else [dp(270), root.height]
|
|
||||||
pos: [root.pos[0], root.pos[1] + root.height - dp(95)]
|
|
||||||
#if root.theme_cls.device_orientation == 'portrait' else [root.pos[0], root.pos[1]]
|
|
||||||
Color:
|
|
||||||
rgba: self.theme_cls.bg_dark
|
|
||||||
Ellipse:
|
|
||||||
size: [dp(220), dp(220)]
|
|
||||||
#if root.theme_cls.device_orientation == 'portrait' else [dp(195), dp(195)]
|
|
||||||
pos: root.pos[0]+dp(270)/2-dp(220)/2, root.pos[1] + root.height - (dp(335)/2+dp(95)) - dp(220)/2 + dp(35)
|
|
||||||
#Color:
|
|
||||||
#rgba: (1, 0, 0, 1)
|
|
||||||
#Line:
|
|
||||||
#width: 4
|
|
||||||
#points: dp(270)/2, root.height, dp(270)/2, 0
|
|
||||||
CircularTimePicker:
|
|
||||||
id: time_picker
|
|
||||||
pos: (dp(270)/2)-(self.width/2), root.height-self.height
|
|
||||||
size_hint: [.8, .8]
|
|
||||||
#if root.theme_cls.device_orientation == 'portrait' else [0.35, 0.9]
|
|
||||||
pos_hint: {'center_x': 0.5, 'center_y': 0.585}
|
|
||||||
#if root.theme_cls.device_orientation == 'portrait' else {'center_x': 0.75, 'center_y': 0.7}
|
|
||||||
MDFlatButton:
|
|
||||||
pos: root.pos[0]+root.size[0]-dp(72)*2, root.pos[1] + dp(10)
|
|
||||||
text: "Cancel"
|
|
||||||
on_release: root.close_cancel()
|
|
||||||
MDFlatButton:
|
|
||||||
pos: root.pos[0]+root.size[0]-dp(72), root.pos[1] + dp(10)
|
|
||||||
text: "OK"
|
|
||||||
on_release: root.close_ok()
|
|
||||||
""")
|
|
||||||
|
|
||||||
|
|
||||||
class MDTimePicker(ThemableBehavior, FloatLayout, ModalView, ElevationBehavior):
|
|
||||||
# background_color = ListProperty((0, 0, 0, 0))
|
|
||||||
time = ObjectProperty()
|
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
|
||||||
super(MDTimePicker, self).__init__(**kwargs)
|
|
||||||
self.current_time = self.ids.time_picker.time
|
|
||||||
|
|
||||||
def set_time(self, time):
|
|
||||||
try:
|
|
||||||
self.ids.time_picker.set_time(time)
|
|
||||||
except AttributeError:
|
|
||||||
raise TypeError("MDTimePicker._set_time must receive a datetime object, not a \"" +
|
|
||||||
type(time).__name__ + "\"")
|
|
||||||
|
|
||||||
def close_cancel(self):
|
|
||||||
self.dismiss()
|
|
||||||
|
|
||||||
def close_ok(self):
|
|
||||||
self.current_time = self.ids.time_picker.time
|
|
||||||
self.time = self.current_time
|
|
||||||
self.dismiss()
|
|
|
@ -1,98 +0,0 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
from kivy.clock import Clock
|
|
||||||
from kivy.lang import Builder
|
|
||||||
from kivy.metrics import dp
|
|
||||||
from kivy.properties import ListProperty, StringProperty, OptionProperty
|
|
||||||
from kivy.uix.boxlayout import BoxLayout
|
|
||||||
from kivymd.backgroundcolorbehavior import BackgroundColorBehavior
|
|
||||||
from kivymd.button import MDIconButton
|
|
||||||
from kivymd.theming import ThemableBehavior
|
|
||||||
from kivymd.elevationbehavior import ElevationBehavior
|
|
||||||
|
|
||||||
Builder.load_string('''
|
|
||||||
#:import m_res kivymd.material_resources
|
|
||||||
<Toolbar>
|
|
||||||
size_hint_y: None
|
|
||||||
height: root.theme_cls.standard_increment
|
|
||||||
background_color: root.background_color
|
|
||||||
padding: [root.theme_cls.horizontal_margins - dp(12), 0]
|
|
||||||
opposite_colors: True
|
|
||||||
elevation: 6
|
|
||||||
BoxLayout:
|
|
||||||
id: left_actions
|
|
||||||
orientation: 'horizontal'
|
|
||||||
size_hint_x: None
|
|
||||||
padding: [0, (self.height - dp(48))/2]
|
|
||||||
BoxLayout:
|
|
||||||
padding: dp(12), 0
|
|
||||||
MDLabel:
|
|
||||||
font_style: 'Title'
|
|
||||||
opposite_colors: root.opposite_colors
|
|
||||||
theme_text_color: root.title_theme_color
|
|
||||||
text_color: root.title_color
|
|
||||||
text: root.title
|
|
||||||
shorten: True
|
|
||||||
shorten_from: 'right'
|
|
||||||
BoxLayout:
|
|
||||||
id: right_actions
|
|
||||||
orientation: 'horizontal'
|
|
||||||
size_hint_x: None
|
|
||||||
padding: [0, (self.height - dp(48))/2]
|
|
||||||
''')
|
|
||||||
|
|
||||||
|
|
||||||
class Toolbar(ThemableBehavior, ElevationBehavior, BackgroundColorBehavior,
|
|
||||||
BoxLayout):
|
|
||||||
left_action_items = ListProperty()
|
|
||||||
"""The icons on the left of the Toolbar.
|
|
||||||
|
|
||||||
To add one, append a list like the following:
|
|
||||||
|
|
||||||
['icon_name', callback]
|
|
||||||
|
|
||||||
where 'icon_name' is a string that corresponds to an icon definition and
|
|
||||||
callback is the function called on a touch release event.
|
|
||||||
"""
|
|
||||||
|
|
||||||
right_action_items = ListProperty()
|
|
||||||
"""The icons on the left of the Toolbar.
|
|
||||||
|
|
||||||
Works the same way as :attr:`left_action_items`
|
|
||||||
"""
|
|
||||||
|
|
||||||
title = StringProperty()
|
|
||||||
"""The text displayed on the Toolbar."""
|
|
||||||
|
|
||||||
title_theme_color = OptionProperty(None, allownone=True,
|
|
||||||
options=['Primary', 'Secondary', 'Hint',
|
|
||||||
'Error', 'Custom'])
|
|
||||||
|
|
||||||
title_color = ListProperty(None, allownone=True)
|
|
||||||
|
|
||||||
background_color = ListProperty([0, 0, 0, 1])
|
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
|
||||||
super(Toolbar, self).__init__(**kwargs)
|
|
||||||
Clock.schedule_once(
|
|
||||||
lambda x: self.on_left_action_items(0, self.left_action_items))
|
|
||||||
Clock.schedule_once(
|
|
||||||
lambda x: self.on_right_action_items(0,
|
|
||||||
self.right_action_items))
|
|
||||||
|
|
||||||
def on_left_action_items(self, instance, value):
|
|
||||||
self.update_action_bar(self.ids['left_actions'], value)
|
|
||||||
|
|
||||||
def on_right_action_items(self, instance, value):
|
|
||||||
self.update_action_bar(self.ids['right_actions'], value)
|
|
||||||
|
|
||||||
def update_action_bar(self, action_bar, action_bar_items):
|
|
||||||
action_bar.clear_widgets()
|
|
||||||
new_width = 0
|
|
||||||
for item in action_bar_items:
|
|
||||||
new_width += dp(48)
|
|
||||||
action_bar.add_widget(MDIconButton(icon=item[0],
|
|
||||||
on_release=item[1],
|
|
||||||
opposite_colors=True,
|
|
||||||
text_color=self.title_color,
|
|
||||||
theme_text_color=self.title_theme_color))
|
|
||||||
action_bar.width = new_width
|
|
1
src/kivymd/vendor/__init__.py
vendored
|
@ -1 +0,0 @@
|
||||||
# coding=utf-8
|
|
22
src/kivymd/vendor/circleLayout/LICENSE
vendored
|
@ -1,22 +0,0 @@
|
||||||
The MIT License (MIT)
|
|
||||||
|
|
||||||
Copyright (c) 2015 Davide Depau
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
||||||
|
|
21
src/kivymd/vendor/circleLayout/README.md
vendored
|
@ -1,21 +0,0 @@
|
||||||
CircularLayout
|
|
||||||
==============
|
|
||||||
|
|
||||||
CircularLayout is a special layout that places widgets around a circle.
|
|
||||||
|
|
||||||
See the widget's documentation and the example for more information.
|
|
||||||
|
|
||||||
![Screenshot](screenshot.png)
|
|
||||||
|
|
||||||
size_hint
|
|
||||||
---------
|
|
||||||
|
|
||||||
size_hint_x is used as an angle-quota hint (widget with higher
|
|
||||||
size_hint_x will be farther from each other, and viceversa), while
|
|
||||||
size_hint_y is used as a widget size hint (widgets with a higher size
|
|
||||||
hint will be bigger).size_hint_x cannot be None.
|
|
||||||
|
|
||||||
Widgets are all squares, unless you set size_hint_y to None (in that
|
|
||||||
case you'll be able to specify your own size), and their size is the
|
|
||||||
difference between the outer and the inner circle's radii. To make the
|
|
||||||
widgets bigger you can just decrease inner_radius_hint.
|
|
196
src/kivymd/vendor/circleLayout/__init__.py
vendored
|
@ -1,196 +0,0 @@
|
||||||
#!/usr/bin/env python
|
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
|
||||||
"""
|
|
||||||
CircularLayout
|
|
||||||
==============
|
|
||||||
|
|
||||||
CircularLayout is a special layout that places widgets around a circle.
|
|
||||||
|
|
||||||
size_hint
|
|
||||||
---------
|
|
||||||
|
|
||||||
size_hint_x is used as an angle-quota hint (widget with higher
|
|
||||||
size_hint_x will be farther from each other, and vice versa), while
|
|
||||||
size_hint_y is used as a widget size hint (widgets with a higher size
|
|
||||||
hint will be bigger).size_hint_x cannot be None.
|
|
||||||
|
|
||||||
Widgets are all squares, unless you set size_hint_y to None (in that
|
|
||||||
case you'll be able to specify your own size), and their size is the
|
|
||||||
difference between the outer and the inner circle's radii. To make the
|
|
||||||
widgets bigger you can just decrease inner_radius_hint.
|
|
||||||
"""
|
|
||||||
|
|
||||||
from kivy.uix.layout import Layout
|
|
||||||
from kivy.properties import NumericProperty, ReferenceListProperty, OptionProperty, \
|
|
||||||
BoundedNumericProperty, VariableListProperty, AliasProperty
|
|
||||||
from math import sin, cos, pi, radians
|
|
||||||
|
|
||||||
__all__ = ('CircularLayout')
|
|
||||||
|
|
||||||
try:
|
|
||||||
xrange(1, 2)
|
|
||||||
except NameError:
|
|
||||||
def xrange(first, second, third=None):
|
|
||||||
if third:
|
|
||||||
return range(first, second, third)
|
|
||||||
else:
|
|
||||||
return range(first, second)
|
|
||||||
|
|
||||||
|
|
||||||
class CircularLayout(Layout):
|
|
||||||
'''
|
|
||||||
Circular layout class. See module documentation for more information.
|
|
||||||
'''
|
|
||||||
|
|
||||||
padding = VariableListProperty([0, 0, 0, 0])
|
|
||||||
'''Padding between the layout box and it's children: [padding_left,
|
|
||||||
padding_top, padding_right, padding_bottom].
|
|
||||||
|
|
||||||
padding also accepts a two argument form [padding_horizontal,
|
|
||||||
padding_vertical] and a one argument form [padding].
|
|
||||||
|
|
||||||
.. version changed:: 1.7.0
|
|
||||||
Replaced NumericProperty with VariableListProperty.
|
|
||||||
|
|
||||||
:attr:`padding` is a :class:`~kivy.properties.VariableListProperty` and
|
|
||||||
defaults to [0, 0, 0, 0].
|
|
||||||
'''
|
|
||||||
|
|
||||||
start_angle = NumericProperty(0)
|
|
||||||
'''Angle (in degrees) at which the first widget will be placed.
|
|
||||||
Start counting angles from the X axis, going counterclockwise.
|
|
||||||
|
|
||||||
:attr:`start_angle` is a :class:`~kivy.properties.NumericProperty` and
|
|
||||||
defaults to 0 (start from the right).
|
|
||||||
'''
|
|
||||||
|
|
||||||
circle_quota = BoundedNumericProperty(360, min=0, max=360)
|
|
||||||
'''Size (in degrees) of the part of the circumference that will actually
|
|
||||||
be used to place widgets.
|
|
||||||
|
|
||||||
:attr:`circle_quota` is a :class:`~kivy.properties.BoundedNumericProperty`
|
|
||||||
and defaults to 360 (all the circumference).
|
|
||||||
'''
|
|
||||||
|
|
||||||
direction = OptionProperty("ccw", options=("cw", "ccw"))
|
|
||||||
'''Direction of widgets in the circle.
|
|
||||||
|
|
||||||
:attr:`direction` is an :class:`~kivy.properties.OptionProperty` and
|
|
||||||
defaults to 'ccw'. Can be 'ccw' (counterclockwise) or 'cw' (clockwise).
|
|
||||||
'''
|
|
||||||
|
|
||||||
outer_radius_hint = NumericProperty(1)
|
|
||||||
'''Sets the size of the outer circle. A number greater than 1 will make the
|
|
||||||
widgets larger than the actual widget, a number smaller than 1 will leave
|
|
||||||
a gap.
|
|
||||||
|
|
||||||
:attr:`outer_radius_hint` is a :class:`~kivy.properties.NumericProperty` and
|
|
||||||
defaults to 1.
|
|
||||||
'''
|
|
||||||
|
|
||||||
inner_radius_hint = NumericProperty(.6)
|
|
||||||
'''Sets the size of the inner circle. A number greater than
|
|
||||||
:attr:`outer_radius_hint` will cause glitches. The closest it is to
|
|
||||||
:attr:`outer_radius_hint`, the smallest will be the widget in the layout.
|
|
||||||
|
|
||||||
:attr:`outer_radius_hint` is a :class:`~kivy.properties.NumericProperty` and
|
|
||||||
defaults to 1.
|
|
||||||
'''
|
|
||||||
|
|
||||||
radius_hint = ReferenceListProperty(inner_radius_hint, outer_radius_hint)
|
|
||||||
'''Combined :attr:`outer_radius_hint` and :attr:`inner_radius_hint` in a list
|
|
||||||
for convenience. See their documentation for more details.
|
|
||||||
|
|
||||||
:attr:`radius_hint` is a :class:`~kivy.properties.ReferenceListProperty`.
|
|
||||||
'''
|
|
||||||
|
|
||||||
def _get_delta_radii(self):
|
|
||||||
radius = min(self.width-self.padding[0]-self.padding[2], self.height-self.padding[1]-self.padding[3]) / 2.
|
|
||||||
outer_r = radius * self.outer_radius_hint
|
|
||||||
inner_r = radius * self.inner_radius_hint
|
|
||||||
return outer_r - inner_r
|
|
||||||
delta_radii = AliasProperty(_get_delta_radii, None, bind=("radius_hint", "padding", "size"))
|
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
|
||||||
super(CircularLayout, self).__init__(**kwargs)
|
|
||||||
|
|
||||||
self.bind(
|
|
||||||
start_angle=self._trigger_layout,
|
|
||||||
parent=self._trigger_layout,
|
|
||||||
# padding=self._trigger_layout,
|
|
||||||
children=self._trigger_layout,
|
|
||||||
size=self._trigger_layout,
|
|
||||||
radius_hint=self._trigger_layout,
|
|
||||||
pos=self._trigger_layout)
|
|
||||||
|
|
||||||
def do_layout(self, *largs):
|
|
||||||
# optimize layout by preventing looking at the same attribute in a loop
|
|
||||||
len_children = len(self.children)
|
|
||||||
if len_children == 0:
|
|
||||||
return
|
|
||||||
selfcx = self.center_x
|
|
||||||
selfcy = self.center_y
|
|
||||||
direction = self.direction
|
|
||||||
cquota = radians(self.circle_quota)
|
|
||||||
start_angle_r = radians(self.start_angle)
|
|
||||||
padding_left = self.padding[0]
|
|
||||||
padding_top = self.padding[1]
|
|
||||||
padding_right = self.padding[2]
|
|
||||||
padding_bottom = self.padding[3]
|
|
||||||
padding_x = padding_left + padding_right
|
|
||||||
padding_y = padding_top + padding_bottom
|
|
||||||
|
|
||||||
radius = min(self.width-padding_x, self.height-padding_y) / 2.
|
|
||||||
outer_r = radius * self.outer_radius_hint
|
|
||||||
inner_r = radius * self.inner_radius_hint
|
|
||||||
middle_r = radius * sum(self.radius_hint) / 2.
|
|
||||||
delta_r = outer_r - inner_r
|
|
||||||
|
|
||||||
stretch_weight_angle = 0.
|
|
||||||
for w in self.children:
|
|
||||||
sha = w.size_hint_x
|
|
||||||
if sha is None:
|
|
||||||
raise ValueError("size_hint_x cannot be None in a CircularLayout")
|
|
||||||
else:
|
|
||||||
stretch_weight_angle += sha
|
|
||||||
|
|
||||||
sign = +1.
|
|
||||||
angle_offset = start_angle_r
|
|
||||||
if direction == 'cw':
|
|
||||||
angle_offset = 2 * pi - start_angle_r
|
|
||||||
sign = -1.
|
|
||||||
|
|
||||||
for c in reversed(self.children):
|
|
||||||
sha = c.size_hint_x
|
|
||||||
shs = c.size_hint_y
|
|
||||||
|
|
||||||
angle_quota = cquota / stretch_weight_angle * sha
|
|
||||||
angle = angle_offset + (sign * angle_quota / 2)
|
|
||||||
angle_offset += sign * angle_quota
|
|
||||||
|
|
||||||
# kived: looking it up, yes. x = cos(angle) * radius + centerx; y = sin(angle) * radius + centery
|
|
||||||
ccx = cos(angle) * middle_r + selfcx + padding_left - padding_right
|
|
||||||
ccy = sin(angle) * middle_r + selfcy + padding_bottom - padding_top
|
|
||||||
|
|
||||||
c.center_x = ccx
|
|
||||||
c.center_y = ccy
|
|
||||||
if shs:
|
|
||||||
s = delta_r * shs
|
|
||||||
c.width = s
|
|
||||||
c.height = s
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
from kivy.app import App
|
|
||||||
from kivy.uix.button import Button
|
|
||||||
|
|
||||||
class CircLayoutApp(App):
|
|
||||||
def build(self):
|
|
||||||
cly = CircularLayout(direction="cw", start_angle=-75, inner_radius_hint=.7, padding="20dp")
|
|
||||||
|
|
||||||
for i in xrange(1, 13):
|
|
||||||
cly.add_widget(Button(text=str(i), font_size="30dp"))
|
|
||||||
|
|
||||||
return cly
|
|
||||||
|
|
||||||
CircLayoutApp().run()
|
|
22
src/kivymd/vendor/circularTimePicker/LICENSE
vendored
|
@ -1,22 +0,0 @@
|
||||||
The MIT License (MIT)
|
|
||||||
|
|
||||||
Copyright (c) 2015 Davide Depau
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
||||||
|
|
43
src/kivymd/vendor/circularTimePicker/README.md
vendored
|
@ -1,43 +0,0 @@
|
||||||
Circular Date & Time Picker for Kivy
|
|
||||||
====================================
|
|
||||||
|
|
||||||
(currently only time, date coming soon)
|
|
||||||
|
|
||||||
Based on [CircularLayout](https://github.com/kivy-garden/garden.circularlayout).
|
|
||||||
The main aim is to provide a date and time selector similar to the
|
|
||||||
one found in Android KitKat+.
|
|
||||||
|
|
||||||
![Screenshot](screenshot.png)
|
|
||||||
|
|
||||||
Simple usage
|
|
||||||
------------
|
|
||||||
|
|
||||||
Import the widget with
|
|
||||||
|
|
||||||
```python
|
|
||||||
from kivy.garden.circulardatetimepicker import CircularTimePicker
|
|
||||||
```
|
|
||||||
|
|
||||||
then use it! That's it!
|
|
||||||
|
|
||||||
```python
|
|
||||||
c = CircularTimePicker()
|
|
||||||
c.bind(time=self.set_time)
|
|
||||||
root.add_widget(c)
|
|
||||||
```
|
|
||||||
|
|
||||||
in Kv language:
|
|
||||||
|
|
||||||
```
|
|
||||||
<TimeChooserPopup@Popup>:
|
|
||||||
BoxLayout:
|
|
||||||
orientation: "vertical"
|
|
||||||
|
|
||||||
CircularTimePicker
|
|
||||||
|
|
||||||
Button:
|
|
||||||
text: "Dismiss"
|
|
||||||
size_hint_y: None
|
|
||||||
height: "40dp"
|
|
||||||
on_release: root.dismiss()
|
|
||||||
```
|
|
770
src/kivymd/vendor/circularTimePicker/__init__.py
vendored
|
@ -1,770 +0,0 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
|
||||||
"""
|
|
||||||
Circular Date & Time Picker for Kivy
|
|
||||||
====================================
|
|
||||||
|
|
||||||
(currently only time, date coming soon)
|
|
||||||
|
|
||||||
Based on [CircularLayout](https://github.com/kivy-garden/garden.circularlayout).
|
|
||||||
The main aim is to provide a date and time selector similar to the
|
|
||||||
one found in Android KitKat+.
|
|
||||||
|
|
||||||
Simple usage
|
|
||||||
------------
|
|
||||||
|
|
||||||
Import the widget with
|
|
||||||
|
|
||||||
```python
|
|
||||||
from kivy.garden.circulardatetimepicker import CircularTimePicker
|
|
||||||
```
|
|
||||||
|
|
||||||
then use it! That's it!
|
|
||||||
|
|
||||||
```python
|
|
||||||
c = CircularTimePicker()
|
|
||||||
c.bind(time=self.set_time)
|
|
||||||
root.add_widget(c)
|
|
||||||
```
|
|
||||||
|
|
||||||
in Kv language:
|
|
||||||
|
|
||||||
```
|
|
||||||
<TimeChooserPopup@Popup>:
|
|
||||||
BoxLayout:
|
|
||||||
orientation: "vertical"
|
|
||||||
|
|
||||||
CircularTimePicker
|
|
||||||
|
|
||||||
Button:
|
|
||||||
text: "Dismiss"
|
|
||||||
size_hint_y: None
|
|
||||||
height: "40dp"
|
|
||||||
on_release: root.dismiss()
|
|
||||||
```
|
|
||||||
"""
|
|
||||||
|
|
||||||
from kivy.animation import Animation
|
|
||||||
from kivy.clock import Clock
|
|
||||||
from kivymd.vendor.circleLayout import CircularLayout
|
|
||||||
from kivy.graphics import Line, Color, Ellipse
|
|
||||||
from kivy.lang import Builder
|
|
||||||
from kivy.properties import NumericProperty, BoundedNumericProperty, \
|
|
||||||
ObjectProperty, StringProperty, DictProperty, \
|
|
||||||
ListProperty, OptionProperty, BooleanProperty, \
|
|
||||||
ReferenceListProperty, AliasProperty
|
|
||||||
from kivy.uix.boxlayout import BoxLayout
|
|
||||||
from kivy.uix.label import Label
|
|
||||||
from kivy.metrics import dp
|
|
||||||
from kivymd.theming import ThemableBehavior
|
|
||||||
from math import atan, pi, radians, sin, cos
|
|
||||||
import sys
|
|
||||||
import datetime
|
|
||||||
if sys.version_info[0] > 2:
|
|
||||||
def xrange(first=None, second=None, third=None):
|
|
||||||
if third:
|
|
||||||
return range(first, second, third)
|
|
||||||
else:
|
|
||||||
return range(first, second)
|
|
||||||
|
|
||||||
|
|
||||||
def map_number(x, in_min, in_max, out_min, out_max):
|
|
||||||
return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min
|
|
||||||
|
|
||||||
|
|
||||||
def rgb_to_hex(*color):
|
|
||||||
tor = "#"
|
|
||||||
for col in color:
|
|
||||||
tor += "{:>02}".format(hex(int(col * 255))[2:])
|
|
||||||
return tor
|
|
||||||
|
|
||||||
|
|
||||||
Builder.load_string("""
|
|
||||||
|
|
||||||
<Number>:
|
|
||||||
text_size: self.size
|
|
||||||
valign: "middle"
|
|
||||||
halign: "center"
|
|
||||||
font_size: self.height * self.size_factor
|
|
||||||
|
|
||||||
<CircularNumberPicker>:
|
|
||||||
canvas.before:
|
|
||||||
PushMatrix
|
|
||||||
Scale:
|
|
||||||
origin: self.center_x + self.padding[0] - self.padding[2], self.center_y + self.padding[3] - self.padding[1]
|
|
||||||
x: self.scale
|
|
||||||
y: self.scale
|
|
||||||
|
|
||||||
canvas.after:
|
|
||||||
PopMatrix
|
|
||||||
|
|
||||||
<CircularTimePicker>:
|
|
||||||
orientation: "vertical"
|
|
||||||
spacing: "20dp"
|
|
||||||
|
|
||||||
FloatLayout:
|
|
||||||
anchor_x: "center"
|
|
||||||
anchor_y: "center"
|
|
||||||
size_hint_y: 1./3
|
|
||||||
size_hint_x: 1
|
|
||||||
size: root.size
|
|
||||||
pos: root.pos
|
|
||||||
|
|
||||||
GridLayout:
|
|
||||||
cols: 2
|
|
||||||
spacing: "10dp"
|
|
||||||
size_hint_x: None
|
|
||||||
width: self.minimum_width
|
|
||||||
pos_hint: {'center_x': .5, 'center_y': .5}
|
|
||||||
|
|
||||||
Label:
|
|
||||||
id: timelabel
|
|
||||||
text: root.time_text
|
|
||||||
markup: True
|
|
||||||
halign: "right"
|
|
||||||
valign: "middle"
|
|
||||||
# text_size: self.size
|
|
||||||
size_hint_x: None #.6
|
|
||||||
width: self.texture_size[0]
|
|
||||||
font_size: self.height * .75
|
|
||||||
|
|
||||||
Label:
|
|
||||||
id: ampmlabel
|
|
||||||
text: root.ampm_text
|
|
||||||
markup: True
|
|
||||||
halign: "left"
|
|
||||||
valign: "middle"
|
|
||||||
# text_size: self.size
|
|
||||||
size_hint_x: None #.4
|
|
||||||
width: self.texture_size[0]
|
|
||||||
font_size: self.height * .3
|
|
||||||
|
|
||||||
FloatLayout:
|
|
||||||
id: picker_container
|
|
||||||
#size_hint_y: 2./3
|
|
||||||
_bound: {}
|
|
||||||
""")
|
|
||||||
|
|
||||||
|
|
||||||
class Number(Label):
|
|
||||||
"""The class used to show the numbers in the selector.
|
|
||||||
"""
|
|
||||||
|
|
||||||
size_factor = NumericProperty(.5)
|
|
||||||
"""Font size scale.
|
|
||||||
|
|
||||||
:attr:`size_factor` is a :class:`~kivy.properties.NumericProperty` and
|
|
||||||
defaults to 0.5.
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
class CircularNumberPicker(CircularLayout):
|
|
||||||
"""A circular number picker based on CircularLayout. A selector will
|
|
||||||
help you pick a number. You can also set :attr:`multiples_of` to make
|
|
||||||
it show only some numbers and use the space in between for the other
|
|
||||||
numbers.
|
|
||||||
"""
|
|
||||||
|
|
||||||
min = NumericProperty(0)
|
|
||||||
"""The first value of the range.
|
|
||||||
|
|
||||||
:attr:`min` is a :class:`~kivy.properties.NumericProperty` and
|
|
||||||
defaults to 0.
|
|
||||||
"""
|
|
||||||
|
|
||||||
max = NumericProperty(0)
|
|
||||||
"""The last value of the range. Note that it behaves like xrange, so
|
|
||||||
the actual last displayed value will be :attr:`max` - 1.
|
|
||||||
|
|
||||||
:attr:`max` is a :class:`~kivy.properties.NumericProperty` and
|
|
||||||
defaults to 0.
|
|
||||||
"""
|
|
||||||
|
|
||||||
range = ReferenceListProperty(min, max)
|
|
||||||
"""Packs :attr:`min` and :attr:`max` into a list for convenience. See
|
|
||||||
their documentation for further information.
|
|
||||||
|
|
||||||
:attr:`range` is a :class:`~kivy.properties.ReferenceListProperty`.
|
|
||||||
"""
|
|
||||||
|
|
||||||
multiples_of = NumericProperty(1)
|
|
||||||
"""Only show numbers that are multiples of this number. The other numbers
|
|
||||||
will be selectable, but won't have their own label.
|
|
||||||
|
|
||||||
:attr:`multiples_of` is a :class:`~kivy.properties.NumericProperty` and
|
|
||||||
defaults to 1.
|
|
||||||
"""
|
|
||||||
|
|
||||||
# selector_color = ListProperty([.337, .439, .490])
|
|
||||||
selector_color = ListProperty([1, 1, 1])
|
|
||||||
"""Color of the number selector. RGB.
|
|
||||||
|
|
||||||
:attr:`selector_color` is a :class:`~kivy.properties.ListProperty` and
|
|
||||||
defaults to [.337, .439, .490] (material green).
|
|
||||||
"""
|
|
||||||
|
|
||||||
color = ListProperty([0, 0, 0])
|
|
||||||
"""Color of the number labels and of the center dot. RGB.
|
|
||||||
|
|
||||||
:attr:`color` is a :class:`~kivy.properties.ListProperty` and
|
|
||||||
defaults to [1, 1, 1] (white).
|
|
||||||
"""
|
|
||||||
|
|
||||||
selector_alpha = BoundedNumericProperty(.3, min=0, max=1)
|
|
||||||
"""Alpha value for the transparent parts of the selector.
|
|
||||||
|
|
||||||
:attr:`selector_alpha` is a :class:`~kivy.properties.BoundedNumericProperty` and
|
|
||||||
defaults to 0.3 (min=0, max=1).
|
|
||||||
"""
|
|
||||||
|
|
||||||
selected = NumericProperty(None)
|
|
||||||
"""Currently selected number.
|
|
||||||
|
|
||||||
:attr:`selected` is a :class:`~kivy.properties.NumericProperty` and
|
|
||||||
defaults to :attr:`min`.
|
|
||||||
"""
|
|
||||||
|
|
||||||
number_size_factor = NumericProperty(.5)
|
|
||||||
"""Font size scale factor fot the :class:`Number`s.
|
|
||||||
|
|
||||||
:attr:`number_size_factor` is a :class:`~kivy.properties.NumericProperty` and
|
|
||||||
defaults to 0.5.
|
|
||||||
"""
|
|
||||||
|
|
||||||
number_format_string = StringProperty("{}")
|
|
||||||
"""String that will be formatted with the selected number as the first argument.
|
|
||||||
Can be anything supported by :meth:`str.format` (es. "{:02d}").
|
|
||||||
|
|
||||||
:attr:`number_format_string` is a :class:`~kivy.properties.StringProperty` and
|
|
||||||
defaults to "{}".
|
|
||||||
"""
|
|
||||||
|
|
||||||
scale = NumericProperty(1)
|
|
||||||
"""Canvas scale factor. Used in :class:`CircularTimePicker` transitions.
|
|
||||||
|
|
||||||
:attr:`scale` is a :class:`~kivy.properties.NumericProperty` and
|
|
||||||
defaults to 1.
|
|
||||||
"""
|
|
||||||
|
|
||||||
_selection_circle = ObjectProperty(None)
|
|
||||||
_selection_line = ObjectProperty(None)
|
|
||||||
_selection_dot = ObjectProperty(None)
|
|
||||||
_selection_dot_color = ObjectProperty(None)
|
|
||||||
_selection_color = ObjectProperty(None)
|
|
||||||
_center_dot = ObjectProperty(None)
|
|
||||||
_center_color = ObjectProperty(None)
|
|
||||||
|
|
||||||
def _get_items(self):
|
|
||||||
return self.max - self.min
|
|
||||||
|
|
||||||
items = AliasProperty(_get_items, None)
|
|
||||||
|
|
||||||
def _get_shown_items(self):
|
|
||||||
sh = 0
|
|
||||||
for i in xrange(*self.range):
|
|
||||||
if i % self.multiples_of == 0:
|
|
||||||
sh += 1
|
|
||||||
return sh
|
|
||||||
|
|
||||||
shown_items = AliasProperty(_get_shown_items, None)
|
|
||||||
|
|
||||||
def __init__(self, **kw):
|
|
||||||
self._trigger_genitems = Clock.create_trigger(self._genitems, -1)
|
|
||||||
self.bind(min=self._trigger_genitems,
|
|
||||||
max=self._trigger_genitems,
|
|
||||||
multiples_of=self._trigger_genitems)
|
|
||||||
super(CircularNumberPicker, self).__init__(**kw)
|
|
||||||
self.selected = self.min
|
|
||||||
self.bind(selected=self.on_selected,
|
|
||||||
pos=self.on_selected,
|
|
||||||
size=self.on_selected)
|
|
||||||
|
|
||||||
cx = self.center_x + self.padding[0] - self.padding[2]
|
|
||||||
cy = self.center_y + self.padding[3] - self.padding[1]
|
|
||||||
sx, sy = self.pos_for_number(self.selected)
|
|
||||||
epos = [i - (self.delta_radii * self.number_size_factor) for i in (sx, sy)]
|
|
||||||
esize = [self.delta_radii * self.number_size_factor * 2] * 2
|
|
||||||
dsize = [i * .3 for i in esize]
|
|
||||||
dpos = [i + esize[0] / 2. - dsize[0] / 2. for i in epos]
|
|
||||||
csize = [i * .05 for i in esize]
|
|
||||||
cpos = [i - csize[0] / 2. for i in (cx, cy)]
|
|
||||||
dot_alpha = 0 if self.selected % self.multiples_of == 0 else 1
|
|
||||||
color = list(self.selector_color)
|
|
||||||
|
|
||||||
with self.canvas:
|
|
||||||
self._selection_color = Color(*(color + [self.selector_alpha]))
|
|
||||||
self._selection_circle = Ellipse(pos=epos, size=esize)
|
|
||||||
self._selection_line = Line(points=[cx, cy, sx, sy], width=dp(1.25))
|
|
||||||
self._selection_dot_color = Color(*(color + [dot_alpha]))
|
|
||||||
self._selection_dot = Ellipse(pos=dpos, size=dsize)
|
|
||||||
self._center_color = Color(*self.color)
|
|
||||||
self._center_dot = Ellipse(pos=cpos, size=csize)
|
|
||||||
|
|
||||||
self.bind(selector_color=lambda ign, u: setattr(self._selection_color, "rgba", u + [self.selector_alpha]))
|
|
||||||
self.bind(selector_color=lambda ign, u: setattr(self._selection_dot_color, "rgb", u))
|
|
||||||
self.bind(selector_color=lambda ign, u: self.dot_is_none())
|
|
||||||
self.bind(color=lambda ign, u: setattr(self._center_color, "rgb", u))
|
|
||||||
Clock.schedule_once(self._genitems)
|
|
||||||
Clock.schedule_once(self.on_selected) # Just to make sure pos/size are set
|
|
||||||
|
|
||||||
def dot_is_none(self, *args):
|
|
||||||
dot_alpha = 0 if self.selected % self.multiples_of == 0 else 1
|
|
||||||
if self._selection_dot_color:
|
|
||||||
self._selection_dot_color.a = dot_alpha
|
|
||||||
|
|
||||||
def _genitems(self, *a):
|
|
||||||
self.clear_widgets()
|
|
||||||
for i in xrange(*self.range):
|
|
||||||
if i % self.multiples_of != 0:
|
|
||||||
continue
|
|
||||||
n = Number(text=self.number_format_string.format(i), size_factor=self.number_size_factor, color=self.color)
|
|
||||||
self.bind(color=n.setter("color"))
|
|
||||||
self.add_widget(n)
|
|
||||||
|
|
||||||
def on_touch_down(self, touch):
|
|
||||||
if not self.collide_point(*touch.pos):
|
|
||||||
return
|
|
||||||
touch.grab(self)
|
|
||||||
self.selected = self.number_at_pos(*touch.pos)
|
|
||||||
if self.selected == 60:
|
|
||||||
self.selected = 0
|
|
||||||
|
|
||||||
def on_touch_move(self, touch):
|
|
||||||
if touch.grab_current is not self:
|
|
||||||
return super(CircularNumberPicker, self).on_touch_move(touch)
|
|
||||||
self.selected = self.number_at_pos(*touch.pos)
|
|
||||||
if self.selected == 60:
|
|
||||||
self.selected = 0
|
|
||||||
|
|
||||||
def on_touch_up(self, touch):
|
|
||||||
if touch.grab_current is not self:
|
|
||||||
return super(CircularNumberPicker, self).on_touch_up(touch)
|
|
||||||
touch.ungrab(self)
|
|
||||||
|
|
||||||
def on_selected(self, *a):
|
|
||||||
cx = self.center_x + self.padding[0] - self.padding[2]
|
|
||||||
cy = self.center_y + self.padding[3] - self.padding[1]
|
|
||||||
sx, sy = self.pos_for_number(self.selected)
|
|
||||||
epos = [i - (self.delta_radii * self.number_size_factor) for i in (sx, sy)]
|
|
||||||
esize = [self.delta_radii * self.number_size_factor * 2] * 2
|
|
||||||
dsize = [i * .3 for i in esize]
|
|
||||||
dpos = [i + esize[0] / 2. - dsize[0] / 2. for i in epos]
|
|
||||||
csize = [i * .05 for i in esize]
|
|
||||||
cpos = [i - csize[0] / 2. for i in (cx, cy)]
|
|
||||||
dot_alpha = 0 if self.selected % self.multiples_of == 0 else 1
|
|
||||||
|
|
||||||
if self._selection_circle:
|
|
||||||
self._selection_circle.pos = epos
|
|
||||||
self._selection_circle.size = esize
|
|
||||||
if self._selection_line:
|
|
||||||
self._selection_line.points = [cx, cy, sx, sy]
|
|
||||||
if self._selection_dot:
|
|
||||||
self._selection_dot.pos = dpos
|
|
||||||
self._selection_dot.size = dsize
|
|
||||||
if self._selection_dot_color:
|
|
||||||
self._selection_dot_color.a = dot_alpha
|
|
||||||
if self._center_dot:
|
|
||||||
self._center_dot.pos = cpos
|
|
||||||
self._center_dot.size = csize
|
|
||||||
|
|
||||||
def pos_for_number(self, n):
|
|
||||||
"""Returns the center x, y coordinates for a given number.
|
|
||||||
"""
|
|
||||||
|
|
||||||
if self.items == 0:
|
|
||||||
return 0, 0
|
|
||||||
radius = min(self.width - self.padding[0] - self.padding[2],
|
|
||||||
self.height - self.padding[1] - self.padding[3]) / 2.
|
|
||||||
middle_r = radius * sum(self.radius_hint) / 2.
|
|
||||||
cx = self.center_x + self.padding[0] - self.padding[2]
|
|
||||||
cy = self.center_y + self.padding[3] - self.padding[1]
|
|
||||||
sign = +1.
|
|
||||||
angle_offset = radians(self.start_angle)
|
|
||||||
if self.direction == 'cw':
|
|
||||||
angle_offset = 2 * pi - angle_offset
|
|
||||||
sign = -1.
|
|
||||||
quota = 2 * pi / self.items
|
|
||||||
mult_quota = 2 * pi / self.shown_items
|
|
||||||
angle = angle_offset + n * sign * quota
|
|
||||||
|
|
||||||
if self.items == self.shown_items:
|
|
||||||
angle += quota / 2
|
|
||||||
else:
|
|
||||||
angle -= mult_quota / 2
|
|
||||||
|
|
||||||
# kived: looking it up, yes. x = cos(angle) * radius + centerx; y = sin(angle) * radius + centery
|
|
||||||
x = cos(angle) * middle_r + cx
|
|
||||||
y = sin(angle) * middle_r + cy
|
|
||||||
|
|
||||||
return x, y
|
|
||||||
|
|
||||||
def number_at_pos(self, x, y):
|
|
||||||
"""Returns the number at a given x, y position. The number is found
|
|
||||||
using the widget's center as a starting point for angle calculations.
|
|
||||||
|
|
||||||
Not thoroughly tested, may yield wrong results.
|
|
||||||
"""
|
|
||||||
if self.items == 0:
|
|
||||||
return self.min
|
|
||||||
cx = self.center_x + self.padding[0] - self.padding[2]
|
|
||||||
cy = self.center_y + self.padding[3] - self.padding[1]
|
|
||||||
lx = x - cx
|
|
||||||
ly = y - cy
|
|
||||||
quota = 2 * pi / self.items
|
|
||||||
mult_quota = 2 * pi / self.shown_items
|
|
||||||
if lx == 0 and ly > 0:
|
|
||||||
angle = pi / 2
|
|
||||||
elif lx == 0 and ly < 0:
|
|
||||||
angle = 3 * pi / 2
|
|
||||||
else:
|
|
||||||
angle = atan(ly / lx)
|
|
||||||
if lx < 0 < ly:
|
|
||||||
angle += pi
|
|
||||||
if lx > 0 > ly:
|
|
||||||
angle += 2 * pi
|
|
||||||
if lx < 0 and ly < 0:
|
|
||||||
angle += pi
|
|
||||||
angle += radians(self.start_angle)
|
|
||||||
if self.direction == "cw":
|
|
||||||
angle = 2 * pi - angle
|
|
||||||
if mult_quota != quota:
|
|
||||||
angle -= mult_quota / 2
|
|
||||||
if angle < 0:
|
|
||||||
angle += 2 * pi
|
|
||||||
elif angle > 2 * pi:
|
|
||||||
angle -= 2 * pi
|
|
||||||
|
|
||||||
return int(angle / quota) + self.min
|
|
||||||
|
|
||||||
|
|
||||||
class CircularMinutePicker(CircularNumberPicker):
|
|
||||||
""":class:`CircularNumberPicker` implementation for minutes.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, **kw):
|
|
||||||
super(CircularMinutePicker, self).__init__(**kw)
|
|
||||||
self.min = 0
|
|
||||||
self.max = 60
|
|
||||||
self.multiples_of = 5
|
|
||||||
self.number_format_string = "{:02d}"
|
|
||||||
self.direction = "cw"
|
|
||||||
self.bind(shown_items=self._update_start_angle)
|
|
||||||
Clock.schedule_once(self._update_start_angle)
|
|
||||||
Clock.schedule_once(self.on_selected)
|
|
||||||
|
|
||||||
def _update_start_angle(self, *a):
|
|
||||||
self.start_angle = -(360. / self.shown_items / 2) - 90
|
|
||||||
|
|
||||||
|
|
||||||
class CircularHourPicker(CircularNumberPicker):
|
|
||||||
""":class:`CircularNumberPicker` implementation for hours.
|
|
||||||
"""
|
|
||||||
|
|
||||||
# military = BooleanProperty(False)
|
|
||||||
|
|
||||||
def __init__(self, **kw):
|
|
||||||
super(CircularHourPicker, self).__init__(**kw)
|
|
||||||
self.min = 1
|
|
||||||
self.max = 13
|
|
||||||
# 25 if self.military else 13
|
|
||||||
# self.inner_radius_hint = .8 if self.military else .6
|
|
||||||
self.multiples_of = 1
|
|
||||||
self.number_format_string = "{}"
|
|
||||||
self.direction = "cw"
|
|
||||||
self.bind(shown_items=self._update_start_angle)
|
|
||||||
# self.bind(military=lambda v: setattr(self, "max", 25 if v else 13))
|
|
||||||
# self.bind(military=lambda v: setattr(self, "inner_radius_hint", .8 if self.military else .6))
|
|
||||||
# Clock.schedule_once(self._genitems)
|
|
||||||
Clock.schedule_once(self._update_start_angle)
|
|
||||||
Clock.schedule_once(self.on_selected)
|
|
||||||
|
|
||||||
def _update_start_angle(self, *a):
|
|
||||||
self.start_angle = (360. / self.shown_items / 2) - 90
|
|
||||||
|
|
||||||
|
|
||||||
class CircularTimePicker(BoxLayout, ThemableBehavior):
|
|
||||||
"""Widget that makes use of :class:`CircularHourPicker` and
|
|
||||||
:class:`CircularMinutePicker` to create a user-friendly, animated
|
|
||||||
time picker like the one seen on Android.
|
|
||||||
|
|
||||||
See module documentation for more details.
|
|
||||||
"""
|
|
||||||
|
|
||||||
primary_dark = ListProperty([1, 1, 1])
|
|
||||||
|
|
||||||
hours = NumericProperty(0)
|
|
||||||
"""The hours, in military format (0-23).
|
|
||||||
|
|
||||||
:attr:`hours` is a :class:`~kivy.properties.NumericProperty` and
|
|
||||||
defaults to 0 (12am).
|
|
||||||
"""
|
|
||||||
|
|
||||||
minutes = NumericProperty(0)
|
|
||||||
"""The minutes.
|
|
||||||
|
|
||||||
:attr:`minutes` is a :class:`~kivy.properties.NumericProperty` and
|
|
||||||
defaults to 0.
|
|
||||||
"""
|
|
||||||
|
|
||||||
time_list = ReferenceListProperty(hours, minutes)
|
|
||||||
"""Packs :attr:`hours` and :attr:`minutes` in a list for convenience.
|
|
||||||
|
|
||||||
:attr:`time_list` is a :class:`~kivy.properties.ReferenceListProperty`.
|
|
||||||
"""
|
|
||||||
|
|
||||||
# military = BooleanProperty(False)
|
|
||||||
time_format = StringProperty(
|
|
||||||
"[color={hours_color}][ref=hours]{hours}[/ref][/color][color={primary_dark}][ref=colon]:[/ref][/color]\
|
|
||||||
[color={minutes_color}][ref=minutes]{minutes:02d}[/ref][/color]")
|
|
||||||
"""String that will be formatted with the time and shown in the time label.
|
|
||||||
Can be anything supported by :meth:`str.format`. Make sure you don't
|
|
||||||
remove the refs. See the default for the arguments passed to format.
|
|
||||||
:attr:`time_format` is a :class:`~kivy.properties.StringProperty` and
|
|
||||||
defaults to "[color={hours_color}][ref=hours]{hours}[/ref][/color]:[color={minutes_color}][ref=minutes]\
|
|
||||||
{minutes:02d}[/ref][/color]".
|
|
||||||
"""
|
|
||||||
|
|
||||||
ampm_format = StringProperty(
|
|
||||||
"[color={am_color}][ref=am]AM[/ref][/color]\n[color={pm_color}][ref=pm]PM[/ref][/color]")
|
|
||||||
"""String that will be formatted and shown in the AM/PM label.
|
|
||||||
Can be anything supported by :meth:`str.format`. Make sure you don't
|
|
||||||
remove the refs. See the default for the arguments passed to format.
|
|
||||||
|
|
||||||
:attr:`ampm_format` is a :class:`~kivy.properties.StringProperty` and
|
|
||||||
defaults to "[color={am_color}][ref=am]AM[/ref][/color]\n[color={pm_color}][ref=pm]PM[/ref][/color]".
|
|
||||||
"""
|
|
||||||
|
|
||||||
picker = OptionProperty("hours", options=("minutes", "hours"))
|
|
||||||
"""Currently shown time picker. Can be one of "minutes", "hours".
|
|
||||||
|
|
||||||
:attr:`picker` is a :class:`~kivy.properties.OptionProperty` and
|
|
||||||
defaults to "hours".
|
|
||||||
"""
|
|
||||||
|
|
||||||
# selector_color = ListProperty([.337, .439, .490])
|
|
||||||
selector_color = ListProperty([0, 0, 0])
|
|
||||||
"""Color of the number selector and of the highlighted text. RGB.
|
|
||||||
|
|
||||||
:attr:`selector_color` is a :class:`~kivy.properties.ListProperty` and
|
|
||||||
defaults to [.337, .439, .490] (material green).
|
|
||||||
"""
|
|
||||||
|
|
||||||
color = ListProperty([1, 1, 1])
|
|
||||||
"""Color of the number labels and of the center dot. RGB.
|
|
||||||
|
|
||||||
:attr:`color` is a :class:`~kivy.properties.ListProperty` and
|
|
||||||
defaults to [1, 1, 1] (white).
|
|
||||||
"""
|
|
||||||
|
|
||||||
selector_alpha = BoundedNumericProperty(.3, min=0, max=1)
|
|
||||||
"""Alpha value for the transparent parts of the selector.
|
|
||||||
|
|
||||||
:attr:`selector_alpha` is a :class:`~kivy.properties.BoundedNumericProperty` and
|
|
||||||
defaults to 0.3 (min=0, max=1).
|
|
||||||
"""
|
|
||||||
|
|
||||||
_am = BooleanProperty(True)
|
|
||||||
_h_picker = ObjectProperty(None)
|
|
||||||
_m_picker = ObjectProperty(None)
|
|
||||||
_bound = DictProperty({})
|
|
||||||
|
|
||||||
def _get_time(self):
|
|
||||||
try:
|
|
||||||
return datetime.time(*self.time_list)
|
|
||||||
except ValueError:
|
|
||||||
self.time_list = [self.hours, 0]
|
|
||||||
return datetime.time(*self.time_list)
|
|
||||||
|
|
||||||
def set_time(self, dt):
|
|
||||||
if dt.hour >= 12:
|
|
||||||
dt.strftime("%I:%M")
|
|
||||||
self._am = False
|
|
||||||
self.time_list = [dt.hour, dt.minute]
|
|
||||||
|
|
||||||
time = AliasProperty(_get_time, set_time, bind=("time_list",))
|
|
||||||
"""Selected time as a datetime.time object.
|
|
||||||
|
|
||||||
:attr:`time` is an :class:`~kivy.properties.AliasProperty`.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def _get_picker(self):
|
|
||||||
if self.picker == "hours":
|
|
||||||
return self._h_picker
|
|
||||||
return self._m_picker
|
|
||||||
|
|
||||||
_picker = AliasProperty(_get_picker, None)
|
|
||||||
|
|
||||||
def _get_time_text(self):
|
|
||||||
hc = rgb_to_hex(0, 0, 0) if self.picker == "hours" else rgb_to_hex(*self.primary_dark)
|
|
||||||
mc = rgb_to_hex(0, 0, 0) if self.picker == "minutes" else rgb_to_hex(*self.primary_dark)
|
|
||||||
h = self.hours == 0 and 12 or self.hours <= 12 and self.hours or self.hours - 12
|
|
||||||
m = self.minutes
|
|
||||||
primary_dark = rgb_to_hex(*self.primary_dark)
|
|
||||||
return self.time_format.format(hours_color=hc,
|
|
||||||
minutes_color=mc,
|
|
||||||
hours=h,
|
|
||||||
minutes=m,
|
|
||||||
primary_dark=primary_dark)
|
|
||||||
time_text = AliasProperty(_get_time_text, None, bind=("hours", "minutes", "time_format", "picker"))
|
|
||||||
|
|
||||||
def _get_ampm_text(self, *args):
|
|
||||||
amc = rgb_to_hex(0, 0, 0) if self._am else rgb_to_hex(*self.primary_dark)
|
|
||||||
pmc = rgb_to_hex(0, 0, 0) if not self._am else rgb_to_hex(*self.primary_dark)
|
|
||||||
return self.ampm_format.format(am_color=amc,
|
|
||||||
pm_color=pmc)
|
|
||||||
|
|
||||||
ampm_text = AliasProperty(_get_ampm_text, None, bind=("hours", "ampm_format", "_am"))
|
|
||||||
|
|
||||||
def __init__(self, **kw):
|
|
||||||
super(CircularTimePicker, self).__init__(**kw)
|
|
||||||
self.selector_color = self.theme_cls.primary_color[0], self.theme_cls.primary_color[1], \
|
|
||||||
self.theme_cls.primary_color[2]
|
|
||||||
self.color = self.theme_cls.text_color
|
|
||||||
self.primary_dark = self.theme_cls.primary_dark[0] / 2, self.theme_cls.primary_dark[1] / 2, \
|
|
||||||
self.theme_cls.primary_dark[2] / 2
|
|
||||||
self.on_ampm()
|
|
||||||
if self.hours >= 12:
|
|
||||||
self._am = False
|
|
||||||
self.bind(time_list=self.on_time_list,
|
|
||||||
picker=self._switch_picker,
|
|
||||||
_am=self.on_ampm,
|
|
||||||
primary_dark=self._get_ampm_text)
|
|
||||||
self._h_picker = CircularHourPicker()
|
|
||||||
self.h_picker_touch = False
|
|
||||||
self._m_picker = CircularMinutePicker()
|
|
||||||
self.animating = False
|
|
||||||
Clock.schedule_once(self.on_selected)
|
|
||||||
Clock.schedule_once(self.on_time_list)
|
|
||||||
Clock.schedule_once(self._init_later)
|
|
||||||
Clock.schedule_once(lambda *a: self._switch_picker(noanim=True))
|
|
||||||
|
|
||||||
def _init_later(self, *args):
|
|
||||||
self.ids.timelabel.bind(on_ref_press=self.on_ref_press)
|
|
||||||
self.ids.ampmlabel.bind(on_ref_press=self.on_ref_press)
|
|
||||||
|
|
||||||
def on_ref_press(self, ign, ref):
|
|
||||||
if not self.animating:
|
|
||||||
if ref == "hours":
|
|
||||||
self.picker = "hours"
|
|
||||||
elif ref == "minutes":
|
|
||||||
self.picker = "minutes"
|
|
||||||
if ref == "am":
|
|
||||||
self._am = True
|
|
||||||
elif ref == "pm":
|
|
||||||
self._am = False
|
|
||||||
|
|
||||||
def on_selected(self, *a):
|
|
||||||
if not self._picker:
|
|
||||||
return
|
|
||||||
if self.picker == "hours":
|
|
||||||
hours = self._picker.selected if self._am else self._picker.selected + 12
|
|
||||||
if hours == 24 and not self._am:
|
|
||||||
hours = 12
|
|
||||||
elif hours == 12 and self._am:
|
|
||||||
hours = 0
|
|
||||||
self.hours = hours
|
|
||||||
elif self.picker == "minutes":
|
|
||||||
self.minutes = self._picker.selected
|
|
||||||
|
|
||||||
def on_time_list(self, *a):
|
|
||||||
if not self._picker:
|
|
||||||
return
|
|
||||||
self._h_picker.selected = self.hours == 0 and 12 or self._am and self.hours or self.hours - 12
|
|
||||||
self._m_picker.selected = self.minutes
|
|
||||||
self.on_selected()
|
|
||||||
|
|
||||||
def on_ampm(self, *a):
|
|
||||||
if self._am:
|
|
||||||
self.hours = self.hours if self.hours < 12 else self.hours - 12
|
|
||||||
else:
|
|
||||||
self.hours = self.hours if self.hours >= 12 else self.hours + 12
|
|
||||||
|
|
||||||
def is_animating(self, *args):
|
|
||||||
self.animating = True
|
|
||||||
|
|
||||||
def is_not_animating(self, *args):
|
|
||||||
self.animating = False
|
|
||||||
|
|
||||||
def on_touch_down(self, touch):
|
|
||||||
if not self._h_picker.collide_point(*touch.pos):
|
|
||||||
self.h_picker_touch = False
|
|
||||||
else:
|
|
||||||
self.h_picker_touch = True
|
|
||||||
super(CircularTimePicker, self).on_touch_down(touch)
|
|
||||||
|
|
||||||
def on_touch_up(self, touch):
|
|
||||||
try:
|
|
||||||
if not self.h_picker_touch:
|
|
||||||
return
|
|
||||||
if not self.animating:
|
|
||||||
if touch.grab_current is not self:
|
|
||||||
if self.picker == "hours":
|
|
||||||
self.picker = "minutes"
|
|
||||||
except AttributeError:
|
|
||||||
pass
|
|
||||||
super(CircularTimePicker, self).on_touch_up(touch)
|
|
||||||
|
|
||||||
def _switch_picker(self, *a, **kw):
|
|
||||||
noanim = "noanim" in kw
|
|
||||||
if noanim:
|
|
||||||
noanim = kw["noanim"]
|
|
||||||
|
|
||||||
try:
|
|
||||||
container = self.ids.picker_container
|
|
||||||
except (AttributeError, NameError):
|
|
||||||
Clock.schedule_once(lambda *a: self._switch_picker(noanim=noanim))
|
|
||||||
|
|
||||||
if self.picker == "hours":
|
|
||||||
picker = self._h_picker
|
|
||||||
prevpicker = self._m_picker
|
|
||||||
elif self.picker == "minutes":
|
|
||||||
picker = self._m_picker
|
|
||||||
prevpicker = self._h_picker
|
|
||||||
|
|
||||||
if len(self._bound) > 0:
|
|
||||||
prevpicker.unbind(selected=self.on_selected)
|
|
||||||
self.unbind(**self._bound)
|
|
||||||
picker.bind(selected=self.on_selected)
|
|
||||||
self._bound = {"selector_color": picker.setter("selector_color"),
|
|
||||||
"color": picker.setter("color"),
|
|
||||||
"selector_alpha": picker.setter("selector_alpha")}
|
|
||||||
self.bind(**self._bound)
|
|
||||||
|
|
||||||
if len(container._bound) > 0:
|
|
||||||
container.unbind(**container._bound)
|
|
||||||
container._bound = {"size": picker.setter("size"),
|
|
||||||
"pos": picker.setter("pos")}
|
|
||||||
container.bind(**container._bound)
|
|
||||||
|
|
||||||
picker.pos = container.pos
|
|
||||||
picker.size = container.size
|
|
||||||
picker.selector_color = self.selector_color
|
|
||||||
picker.color = self.color
|
|
||||||
picker.selector_alpha = self.selector_alpha
|
|
||||||
if noanim:
|
|
||||||
if prevpicker in container.children:
|
|
||||||
container.remove_widget(prevpicker)
|
|
||||||
if picker.parent:
|
|
||||||
picker.parent.remove_widget(picker)
|
|
||||||
container.add_widget(picker)
|
|
||||||
else:
|
|
||||||
self.is_animating()
|
|
||||||
if prevpicker in container.children:
|
|
||||||
anim = Animation(scale=1.5, d=.5, t="in_back") & Animation(opacity=0, d=.5, t="in_cubic")
|
|
||||||
anim.start(prevpicker)
|
|
||||||
Clock.schedule_once(lambda *y: container.remove_widget(prevpicker), .5) # .31)
|
|
||||||
picker.scale = 1.5
|
|
||||||
picker.opacity = 0
|
|
||||||
if picker.parent:
|
|
||||||
picker.parent.remove_widget(picker)
|
|
||||||
container.add_widget(picker)
|
|
||||||
anim = Animation(scale=1, d=.5, t="out_back") & Animation(opacity=1, d=.5, t="out_cubic")
|
|
||||||
anim.bind(on_complete=self.is_not_animating)
|
|
||||||
Clock.schedule_once(lambda *y: anim.start(picker), .3)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
from kivy.base import runTouchApp
|
|
||||||
|
|
||||||
c = CircularTimePicker()
|
|
||||||
runTouchApp(c)
|
|
|
@ -1,6 +1,11 @@
|
||||||
"""This module is for thread start."""
|
"""This module is for thread start."""
|
||||||
import state
|
import state
|
||||||
|
import sys
|
||||||
from bitmessagemain import main
|
from bitmessagemain import main
|
||||||
|
from termcolor import colored
|
||||||
|
print(colored('kivy is not supported at the moment for this version..', 'red'))
|
||||||
|
sys.exit()
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
state.kivy = True
|
state.kivy = True
|
||||||
|
|
|
@ -1,82 +0,0 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
from kivy.animation import Animation
|
|
||||||
from kivy.lang import Builder
|
|
||||||
from kivy.properties import StringProperty, ObjectProperty
|
|
||||||
from kivymd.elevationbehavior import ElevationBehavior
|
|
||||||
from kivymd.icon_definitions import md_icons
|
|
||||||
from kivymd.label import MDLabel
|
|
||||||
from kivymd.list import OneLineIconListItem, ILeftBody, BaseListItem
|
|
||||||
from kivymd.slidingpanel import SlidingPanel
|
|
||||||
from kivymd.theming import ThemableBehavior
|
|
||||||
|
|
||||||
Builder.load_string('''
|
|
||||||
<NavDrawerToolbar@Label>
|
|
||||||
canvas:
|
|
||||||
Color:
|
|
||||||
rgba: root.parent.parent.theme_cls.divider_color
|
|
||||||
Line:
|
|
||||||
points: self.x, self.y, self.x+self.width,self.y
|
|
||||||
|
|
||||||
<NavigationDrawer>
|
|
||||||
widget_list: widget_list
|
|
||||||
elevation: 0
|
|
||||||
canvas:
|
|
||||||
Color:
|
|
||||||
rgba: root.theme_cls.bg_light
|
|
||||||
Rectangle:
|
|
||||||
size: root.size
|
|
||||||
pos: root.pos
|
|
||||||
BoxLayout:
|
|
||||||
size_hint: (1, .4)
|
|
||||||
NavDrawerToolbar:
|
|
||||||
padding: 10, 10
|
|
||||||
canvas.after:
|
|
||||||
Color:
|
|
||||||
rgba: (1, 1, 1, 1)
|
|
||||||
RoundedRectangle:
|
|
||||||
size: (self.size[1]-dp(14), self.size[1]-dp(14))
|
|
||||||
pos: (self.pos[0]+(self.size[0]-self.size[1])/2, self.pos[1]+dp(7))
|
|
||||||
source: root.image_source
|
|
||||||
radius: [self.size[1]-(self.size[1]/2)]
|
|
||||||
|
|
||||||
ScrollView:
|
|
||||||
do_scroll_x: False
|
|
||||||
MDList:
|
|
||||||
id: ml
|
|
||||||
id: widget_list
|
|
||||||
|
|
||||||
<NavigationDrawerIconButton>
|
|
||||||
NDIconLabel:
|
|
||||||
id: _icon
|
|
||||||
font_style: 'Icon'
|
|
||||||
theme_text_color: 'Secondary'
|
|
||||||
''')
|
|
||||||
|
|
||||||
|
|
||||||
class NavigationDrawer(SlidingPanel, ThemableBehavior, ElevationBehavior):
|
|
||||||
image_source = StringProperty()
|
|
||||||
widget_list = ObjectProperty()
|
|
||||||
|
|
||||||
def add_widget(self, widget, index=0):
|
|
||||||
if issubclass(widget.__class__, BaseListItem):
|
|
||||||
self.widget_list.add_widget(widget, index)
|
|
||||||
widget.bind(on_release=lambda x: self.toggle())
|
|
||||||
else:
|
|
||||||
super(NavigationDrawer, self).add_widget(widget, index)
|
|
||||||
|
|
||||||
def _get_main_animation(self, duration, t, x, is_closing):
|
|
||||||
a = super(NavigationDrawer, self)._get_main_animation(duration, t, x,
|
|
||||||
is_closing)
|
|
||||||
a &= Animation(elevation=0 if is_closing else 5, t=t, duration=duration)
|
|
||||||
return a
|
|
||||||
|
|
||||||
|
|
||||||
class NDIconLabel(ILeftBody, MDLabel):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class NavigationDrawerIconButton(OneLineIconListItem):
|
|
||||||
icon = StringProperty()
|
|
||||||
|
|
||||||
def on_icon(self, instance, value):
|
|
||||||
self.ids['_icon'].text = u"{}".format(md_icons[value])
|
|