Added Navigation Drawer UI with create feature
This commit is contained in:
parent
3b4d73d291
commit
bb8d4c5a1f
43
android_instruction.rst
Normal file
43
android_instruction.rst
Normal file
|
@ -0,0 +1,43 @@
|
|||
PyBitmessage(Android)
|
||||
|
||||
This sample aims to be as close to a real world example of a mobile. It has a more refined design and also provides a practical example of how a mobile app would interact and communicate with its adresses.
|
||||
|
||||
Steps for trying out this sample:
|
||||
|
||||
Compile and install the mobile app onto your mobile device or emulator.
|
||||
|
||||
|
||||
Getting Started
|
||||
|
||||
This sample uses the kivy as Kivy is an open source, cross-platform Python framework for the development of applications that make use of innovative, multi-touch user interfaces. The aim is to allow for quick and easy interaction design and rapid prototyping whilst making your code reusable and deployable.
|
||||
|
||||
Kivy is written in Python and Cython, supports various input devices and has an extensive widget library. With the same codebase, you can target Windows, OS X, Linux, Android and iOS. All Kivy widgets are built with multitouch support.
|
||||
|
||||
Kivy in support take Buildozer which is a tool that automates the entire build process. It downloads and sets up all the prequisites for python-for-android, including the android SDK and NDK, then builds an apk that can be automatically pushed to the device.
|
||||
|
||||
Buildozer currently works only in Linux, and is an alpha release, but it already works well and can significantly simplify the apk build.
|
||||
|
||||
To build this project, use the "Buildozer android release deploy run" command or use.
|
||||
Buildozer ue=sed for creating application packages easily.The goal is to have one "buildozer.spec" file in your app directory, describing your application requirements and settings such as title, icon, included modules etc. Buildozer will use that spec to create a package for Android, iOS, Windows, OSX and/or Linux.
|
||||
|
||||
Installing Requirements
|
||||
|
||||
You can create a package for android using the python-for-android project as with using the Buildozer tool to automate the entire process. You can also see Packaging your application for the Kivy Launcher to run kivy programs without compiling them.
|
||||
|
||||
You can get buildozer at https://github.com/kivy/buildozer or you can directly install using pip install buildozer
|
||||
|
||||
This will install buildozer in your system. Afterwards, navigate to your project directory and run:
|
||||
|
||||
buildozer init
|
||||
|
||||
This creates a buildozer.spec file controlling your build configuration. You should edit it appropriately with your app name etc. You can set variables to control most or all of the parameters passed to python-for-android.
|
||||
|
||||
Install buildozer’s dependencies.
|
||||
|
||||
Finally, plug in your android device and run:
|
||||
|
||||
buildozer android debug deploy run >> To build, push and automatically run the apk on your device. Here we used debug as tested in debug mode for now.
|
||||
|
||||
Packaging your application for the Kivy Launcher
|
||||
|
||||
|
|
@ -1,167 +1,173 @@
|
|||
#:import FadeTransition kivy.uix.screenmanager.FadeTransition
|
||||
|
||||
<Display>:
|
||||
BoxLayout:
|
||||
orientation: "vertical"
|
||||
BoxLayout:
|
||||
size_hint: 1, None
|
||||
<Navigator>:
|
||||
id: nav_drawer
|
||||
NavigationDrawerIconButton:
|
||||
Button:
|
||||
id: btn
|
||||
background_color:app.theme_cls.primary_dark
|
||||
text: 'BM-2cSvewtJw8o8eqcFzmLeXHza4BVsRsirKk'
|
||||
on_release: dropdown.open(self)
|
||||
pos_hint: {'x': 0, 'y': 0.3}
|
||||
size_hint_y: None
|
||||
height: '48dp'
|
||||
|
||||
DropDown:
|
||||
|
||||
id: dropdown
|
||||
on_parent: self.dismiss()
|
||||
on_select: btn.text = '{}'.format(args[1])
|
||||
Button:
|
||||
text: 'messages'
|
||||
on_release: sm.current = 'screen_one'
|
||||
background_color: (0,0,0,0)
|
||||
color: 0,0,0,1
|
||||
Button:
|
||||
text: 'send'
|
||||
on_release: sm.current = 'screen_two'
|
||||
background_color: (0,0,0,0)
|
||||
color: 0,0,0,1
|
||||
Button:
|
||||
text: 'subscription'
|
||||
on_release: sm.current = 'screen_three'
|
||||
background_color: (0,0,0,0)
|
||||
color: 0,0,0,1
|
||||
Button:
|
||||
text: 'four'
|
||||
on_release: sm.current = 'screen_four'
|
||||
background_color: (0,0,0,0)
|
||||
color: 0,0,0,1
|
||||
background_color:app.theme_cls.primary_dark
|
||||
text: 'BM-2cSvewtJw8o8eqcFzmLeXHza4BVsRsirKk'
|
||||
size_hint_y: None
|
||||
height: '48dp'
|
||||
on_release: dropdown.select('BM-2cSvewtJw8o8eqcFzmLeXHza4BVsRsirKk')
|
||||
on_release: app.root.ids.scr_mngr.current = 'dialog'
|
||||
|
||||
Button:
|
||||
text: 'chans'
|
||||
on_release: sm.current = 'screen_five'
|
||||
background_color: (0,0,0,0)
|
||||
color: 0,0,0,1
|
||||
Button:
|
||||
text: 'blacklist'
|
||||
on_release: sm.current = 'screen_six'
|
||||
background_color: (0,0,0,0)
|
||||
color: 0,0,0,1
|
||||
background_color:app.theme_cls.primary_dark
|
||||
text: 'BM-2cSvewtJw8o8eqcFzmLeXHza4BVsRsirKk'
|
||||
size_hint_y: None
|
||||
height: '48dp'
|
||||
on_release: dropdown.select('BM-2cSvewtJw8o8eqcFzmLeXHza4BVsRsirKk')
|
||||
on_release: app.root.ids.scr_mngr.current = 'button'
|
||||
|
||||
ScreenManager:
|
||||
id: sm
|
||||
transition: FadeTransition()
|
||||
Screen_One:
|
||||
Screen_Two:
|
||||
Screen_three:
|
||||
Screen_four:
|
||||
Screen_five:
|
||||
Screen_six:
|
||||
EmailScreen:
|
||||
|
||||
<Screen_One>:
|
||||
name: 'screen_one'
|
||||
BoxLayout:
|
||||
padding: 10,10,10,300
|
||||
orientation: "vertical"
|
||||
NavigationDrawerIconButton:
|
||||
icon: 'email'
|
||||
text: "inbox"
|
||||
on_release: app.root.ids.scr_mngr.current = 'inbox'
|
||||
NavigationDrawerIconButton:
|
||||
icon: 'email'
|
||||
text: "sent"
|
||||
on_release: app.root.ids.scr_mngr.current = 'sent'
|
||||
NavigationDrawerIconButton:
|
||||
icon: 'email'
|
||||
text: "trash"
|
||||
on_release: app.root.ids.scr_mngr.current = 'trash'
|
||||
NavigationDrawerIconButton:
|
||||
icon: 'email'
|
||||
text: "dialog"
|
||||
on_release: app.root.ids.scr_mngr.current = 'dialog'
|
||||
NavigationDrawerIconButton:
|
||||
icon: 'email'
|
||||
text: "test"
|
||||
on_release: app.root.ids.scr_mngr.current = 'test'
|
||||
|
||||
|
||||
|
||||
BoxLayout:
|
||||
orientation: 'vertical'
|
||||
Toolbar:
|
||||
id: toolbar
|
||||
title: 'PyBitmessage'
|
||||
background_color: app.theme_cls.primary_dark
|
||||
left_action_items: [['menu', lambda x: app.nav_drawer.toggle()]]
|
||||
Button:
|
||||
text: "create"
|
||||
size_hint_y: 0.3
|
||||
size_hint_x: 0.1
|
||||
text:"EXIT"
|
||||
color: 0,0,0,1
|
||||
background_color: (0,0,0,0)
|
||||
Button:
|
||||
text: "inbox"
|
||||
background_color: (0,0,0,0)
|
||||
size_hint_y: 0.3
|
||||
size_hint_y: 0.1
|
||||
size_hint_x: 0.1
|
||||
color: 0,0,0,1
|
||||
Button:
|
||||
text: "new"
|
||||
background_color: (0,0,0,0)
|
||||
size_hint_y: 0.3
|
||||
size_hint_x: 0.1
|
||||
color: 0,0,0,1
|
||||
on_press:
|
||||
root.manager.current = 'email'
|
||||
Button:
|
||||
text: "sents"
|
||||
background_color: (0,0,0,0)
|
||||
size_hint_y: 0.3
|
||||
size_hint_x: 0.1
|
||||
color: 0,0,0,1
|
||||
pos_hint: {'x': 0.8, 'y':0.4}
|
||||
on_press: app.say_exit()
|
||||
|
||||
<Screen_Two>:
|
||||
name: 'screen_two'
|
||||
|
||||
ScreenManager:
|
||||
id: scr_mngr
|
||||
Inbox:
|
||||
id:sc1
|
||||
Sent:
|
||||
id:sc2
|
||||
Trash:
|
||||
id:sc3
|
||||
Dialog:
|
||||
id:sc4
|
||||
Test:
|
||||
id:sc5
|
||||
Create:
|
||||
id:sc6
|
||||
|
||||
Button:
|
||||
height:100
|
||||
size_hint_y: 0.1
|
||||
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
|
||||
x: self.parent.x + self.parent.width - 50
|
||||
size: 70, 70
|
||||
<Inbox>:
|
||||
name: 'inbox'
|
||||
|
||||
<Sent>:
|
||||
name: 'sent'
|
||||
|
||||
<Trash>:
|
||||
name: 'trash'
|
||||
Label:
|
||||
text: "Two"
|
||||
|
||||
<Screen_three>:
|
||||
name: 'screen_three'
|
||||
text:"I am in card"
|
||||
color: 0,0,0,1
|
||||
<Dialog>:
|
||||
name: 'dialog'
|
||||
Label:
|
||||
text: "Three"
|
||||
|
||||
<Screen_four>:
|
||||
name: 'screen_four'
|
||||
text:"I have a good dialox box"
|
||||
color: 0,0,0,1
|
||||
<Test>:
|
||||
name: 'test'
|
||||
Label:
|
||||
text: "four"
|
||||
text:"a rapid data env"
|
||||
color: 0,0,0,1
|
||||
|
||||
<Screen_five>:
|
||||
name: 'screen_five'
|
||||
Label:
|
||||
text: "Two"
|
||||
|
||||
<Screen_six>:
|
||||
name: 'screen_six'
|
||||
Label:
|
||||
text: "Two"
|
||||
|
||||
<EmailScreen>:
|
||||
name: 'email'
|
||||
BoxLayout:
|
||||
padding: 100,10,100,100
|
||||
id: login_layout
|
||||
orientation: "vertical"
|
||||
GridLayout:
|
||||
rows: 2
|
||||
orientation: 'vertical'
|
||||
spacing: 10
|
||||
<Create>:
|
||||
name: 'create'
|
||||
GridLayout:
|
||||
rows: 5
|
||||
cols: 1
|
||||
padding: 100,100,100,100
|
||||
spacing: 50
|
||||
BoxLayout:
|
||||
size_hint_y: None
|
||||
height: '32dp'
|
||||
Label:
|
||||
text: 'From: '
|
||||
background_color: (0,0,0,0)
|
||||
text: 'FROM'
|
||||
color: 0,0,0,1
|
||||
font_size: 18
|
||||
pos_hint: {"x":.15,"y":1}
|
||||
text_size: root.width-20, 20
|
||||
|
||||
TextInput:
|
||||
multiline:False
|
||||
size_hint: 1,.4
|
||||
hint_text: 'email'
|
||||
|
||||
GridLayout:
|
||||
rows: 2
|
||||
orientation: 'vertical'
|
||||
BoxLayout:
|
||||
size_hint_y: None
|
||||
height: '32dp'
|
||||
Label:
|
||||
text: 'To: '
|
||||
background_color: (0,0,0,0)
|
||||
text: 'TO'
|
||||
color: 0,0,0,1
|
||||
font_size: 18
|
||||
pos_hint: {"x":.15,"y":.5}
|
||||
text_size: root.width-20, 20
|
||||
|
||||
TextInput:
|
||||
multiline:False
|
||||
size_hint: 1,.4
|
||||
pos_hint: {"x":.2,"y":.5}
|
||||
hint_text: 'email'
|
||||
|
||||
GridLayout:
|
||||
rows: 2
|
||||
orientation: 'vertical'
|
||||
BoxLayout:
|
||||
size_hint_y: None
|
||||
height: '32dp'
|
||||
Label:
|
||||
text: 'Message'
|
||||
background_color: (0,0,0,0)
|
||||
text: 'SUBJECT'
|
||||
color: 0,0,0,1
|
||||
pos_hint: {"x":.15,"y":.5}
|
||||
font_size: 18
|
||||
text_size: root.width-20, 20
|
||||
TextInput:
|
||||
hint_text: 'subject'
|
||||
|
||||
BoxLayout:
|
||||
size_hint_y: None
|
||||
height: '32dp'
|
||||
Label:
|
||||
text: 'BODY'
|
||||
color: 0,0,0,1
|
||||
TextInput:
|
||||
id: msg
|
||||
multiline:True
|
||||
size_hint: 1,2
|
||||
|
||||
Button:
|
||||
text: "SEND"
|
||||
size_hint_y: 0.1
|
||||
size_hint_x: 0.2
|
||||
pos_hint: {"x":.15,"y":1}
|
||||
Button:
|
||||
text: 'SEND'
|
||||
size_hint_y: 0.1
|
||||
size_hint_x: 0.3
|
||||
height: '32dp'
|
||||
pos_hint: {'x': .5, 'y': 0.1}
|
|
@ -1,97 +1,79 @@
|
|||
import time
|
||||
|
||||
import os
|
||||
from kivy.app import App
|
||||
from kivy.core.window import Window
|
||||
from kivy.lang import Builder
|
||||
from kivy.uix.screenmanager import Screen
|
||||
from kivy.properties import ObjectProperty, StringProperty
|
||||
from kivymd.theming import ThemeManager
|
||||
from kivy.uix.widget import Widget
|
||||
from navigationdrawer import NavigationDrawer
|
||||
from kivymd.toolbar import Toolbar
|
||||
from kivy.uix.dropdown import DropDown
|
||||
from kivy.uix.button import Button
|
||||
from kivy.uix.gridlayout import GridLayout
|
||||
from kivy.uix.label import Label
|
||||
from kivy.uix.textinput import TextInput
|
||||
from kivy.clock import Clock
|
||||
from kivy.uix.boxlayout import BoxLayout
|
||||
# from kivy.uix.gridlayout import GridLayout
|
||||
|
||||
from debug import logger
|
||||
|
||||
from addresses import addBMIfNotPresent, decodeAddress
|
||||
|
||||
from bmconfigparser import BMConfigParser
|
||||
|
||||
from helper_ackPayload import genAckPayload
|
||||
|
||||
from helper_sql import sqlExecute
|
||||
|
||||
import queues
|
||||
|
||||
from kivy.core.window import Window
|
||||
from os import environ
|
||||
import shutdown
|
||||
statusIconColor = 'red'
|
||||
|
||||
class NavigateApp(App):
|
||||
theme_cls = ThemeManager()
|
||||
nav_drawer = ObjectProperty()
|
||||
|
||||
class LoginScreen(BoxLayout):
|
||||
"""This will use for sending message to recipents from mobile client."""
|
||||
def build(self):
|
||||
main_widget = Builder.load_file(os.path.join(os.path.dirname(__file__), 'main.kv'))
|
||||
self.nav_drawer = Navigator()
|
||||
return main_widget
|
||||
|
||||
def send(self):
|
||||
"""It is used for sending message with subject and body."""
|
||||
queues.apiAddressGeneratorReturnQueue.queue.clear()
|
||||
streamNumberForAddress = 1
|
||||
label = "CisDevelper"
|
||||
eighteenByteRipe = False
|
||||
nonceTrialsPerByte = 1000
|
||||
payloadLengthExtraBytes = 1000
|
||||
queues.addressGeneratorQueue.put((
|
||||
'createRandomAddress', 4,
|
||||
streamNumberForAddress, label, 1,
|
||||
"", eighteenByteRipe, nonceTrialsPerByte,
|
||||
payloadLengthExtraBytes
|
||||
))
|
||||
fromAddress = queues.apiAddressGeneratorReturnQueue.get()
|
||||
toAddress = "BM-2cWyUfBdY2FbgyuCb7abFZ49JYxSzUhNFe"
|
||||
message = self.ids.user_input.text
|
||||
subject = 'Test'
|
||||
encoding = 3
|
||||
sendMessageToPeople = True
|
||||
if sendMessageToPeople:
|
||||
if toAddress != '':
|
||||
status, addressVersionNumber, streamNumber, ripe = decodeAddress(
|
||||
toAddress)
|
||||
if status == 'success':
|
||||
toAddress = addBMIfNotPresent(toAddress)
|
||||
|
||||
if addressVersionNumber > 4 or addressVersionNumber <= 1:
|
||||
logger.info("addressVersionNumber > 4 or addressVersionNumber <= 1")
|
||||
if streamNumber > 1 or streamNumber == 0:
|
||||
logger.info("streamNumber > 1 or streamNumber == 0")
|
||||
if statusIconColor == 'red':
|
||||
logger.info("shared.statusIconColor == 'red'")
|
||||
stealthLevel = BMConfigParser().safeGetInt(
|
||||
'bitmessagesettings', 'ackstealthlevel')
|
||||
ackdata = genAckPayload(streamNumber, stealthLevel)
|
||||
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'))
|
||||
queues.workerQueue.put(('sendmessage', toAddress))
|
||||
return None
|
||||
|
||||
def sayexit(self):
|
||||
"""Method will exit the application screen."""
|
||||
def say_exit(self):
|
||||
print ("**************************EXITING FROM APPLICATION*****************************")
|
||||
App.get_running_app().stop()
|
||||
shutdown.doCleanShutdown()
|
||||
Window.close()
|
||||
|
||||
class Navigator(NavigationDrawer):
|
||||
image_source = StringProperty('images/me.jpg')
|
||||
title = StringProperty('Navigation')
|
||||
|
||||
class MainApp(App):
|
||||
"""The App class is the base for creating Kivy applications
|
||||
Think of it as your main entry point into the Kivy run loop."""
|
||||
|
||||
def build(self):
|
||||
"""To initialize an app with a widget tree, we need to override the build().
|
||||
method in our app class and return the widget tree which we have constructed..
|
||||
"""
|
||||
return LoginScreen()
|
||||
class Inbox(Screen):
|
||||
def __init__ (self,**kwargs):
|
||||
super (Inbox, self).__init__(**kwargs)
|
||||
val_y = .1
|
||||
val_z = 0
|
||||
my_box1 = BoxLayout(orientation='vertical')
|
||||
for i in range(1, 5):
|
||||
my_box1.add_widget(Label(text="I am in inbox", size_hint = (.3,.1), pos_hint = {'x': val_z,'top': val_y},color = (0,0,0,1), background_color = (0,0,0,0)))
|
||||
val_y+=.1
|
||||
self.add_widget(my_box1)
|
||||
|
||||
class Sent(Screen):
|
||||
def __init__ (self,**kwargs):
|
||||
super (Sent, self).__init__(**kwargs)
|
||||
val_y = .1
|
||||
val_z = 0
|
||||
my_box1 = BoxLayout(orientation='vertical')
|
||||
for i in range(1, 5):
|
||||
my_box1.add_widget(Label(text="I am in sent", size_hint = (.3,.1), pos_hint = {'x': val_z,'top': val_y},color = (0,0,0,1), background_color = (0,0,0,0)))
|
||||
val_y+=.1
|
||||
self.add_widget(my_box1)
|
||||
|
||||
class Trash(Screen):
|
||||
pass
|
||||
|
||||
|
||||
class Dialog(Screen):
|
||||
pass
|
||||
|
||||
|
||||
class Test(Screen):
|
||||
pass
|
||||
|
||||
|
||||
class Create(Screen):
|
||||
pass
|
||||
|
||||
if __name__ == '__main__':
|
||||
NavigateApp().run()
|
|
@ -398,8 +398,8 @@ class Main:
|
|||
bitmessagecurses.runwrapper()
|
||||
elif state.kivy:
|
||||
BMConfigParser().remove_option('bitmessagesettings', 'dontconnect')
|
||||
from bitmessagekivy.mpybit import MainApp
|
||||
MainApp().run()
|
||||
from bitmessagekivy.mpybit import NavigateApp
|
||||
NavigateApp().run()
|
||||
else:
|
||||
import bitmessageqt
|
||||
bitmessageqt.run()
|
||||
|
|
BIN
src/images/kivymd_logo.png
Normal file
BIN
src/images/kivymd_logo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 41 KiB |
BIN
src/images/me.jpg
Normal file
BIN
src/images/me.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 31 KiB |
BIN
src/images/plus.png
Normal file
BIN
src/images/plus.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 18 KiB |
21
src/kivymd/LICENSE
Normal file
21
src/kivymd/LICENSE
Normal file
|
@ -0,0 +1,21 @@
|
|||
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.
|
6
src/kivymd/__init__.py
Normal file
6
src/kivymd/__init__.py
Normal file
|
@ -0,0 +1,6 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
import os
|
||||
|
||||
path = os.path.dirname(__file__)
|
||||
fonts_path = os.path.join(path, "fonts/")
|
||||
images_path = os.path.join(path, 'images/')
|
254
src/kivymd/accordion.py
Normal file
254
src/kivymd/accordion.py
Normal file
|
@ -0,0 +1,254 @@
|
|||
# -*- 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()
|
23
src/kivymd/backgroundcolorbehavior.py
Normal file
23
src/kivymd/backgroundcolorbehavior.py
Normal file
|
@ -0,0 +1,23 @@
|
|||
# -*- 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)
|
211
src/kivymd/bottomsheet.py
Normal file
211
src/kivymd/bottomsheet.py
Normal file
|
@ -0,0 +1,211 @@
|
|||
# -*- 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)
|
453
src/kivymd/button.py
Normal file
453
src/kivymd/button.py
Normal file
|
@ -0,0 +1,453 @@
|
|||
# -*- 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')
|
||||
|
||||