PyBitmessage/src/kivymd/tabs.py

304 lines
10 KiB
Python

# 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()