mirror of https://github.com/kivy/kivy.git
Merge branch 'master' of ssh://github.com/kivy/kivy into ios-support
This commit is contained in:
commit
596cbab408
|
@ -23,6 +23,7 @@ import kivy
|
|||
|
||||
# force loading of kivy modules
|
||||
import kivy.app
|
||||
import kivy.metrics
|
||||
import kivy.atlas
|
||||
import kivy.core.audio
|
||||
import kivy.core.camera
|
||||
|
@ -43,6 +44,7 @@ import kivy.modules.monitor
|
|||
import kivy.modules.touchring
|
||||
import kivy.modules.inspector
|
||||
import kivy.modules.recorder
|
||||
import kivy.modules.screen
|
||||
import kivy.network.urlrequest
|
||||
import kivy.support
|
||||
import kivy.input.recorder
|
||||
|
|
|
@ -32,11 +32,6 @@ KIVY_NO_FILELOG
|
|||
KIVY_NO_CONSOLELOG
|
||||
If set, logs will be not print on the console
|
||||
|
||||
KIVY_DPI
|
||||
If set, the value will be used instead of the value returned by the window.
|
||||
|
||||
.. versionadded:: 1.4.0
|
||||
|
||||
Path control
|
||||
------------
|
||||
|
||||
|
@ -101,3 +96,22 @@ KIVY_CLIPBOARD
|
|||
|
||||
Values: pygame, dummy
|
||||
|
||||
Metrics
|
||||
-------
|
||||
|
||||
KIVY_DPI
|
||||
If set, the value will be used for :data:`Metrics.dpi`.
|
||||
|
||||
.. versionadded:: 1.4.0
|
||||
|
||||
KIVY_METRICS_DENSITY
|
||||
If set, the value will be used for :data:`Metrics.density`.
|
||||
|
||||
.. versionadded:: 1.5.0
|
||||
|
||||
KIVY_METRICS_FONTSCALE
|
||||
|
||||
If set, the value will be used for :data:`Metrics.fontscale`.
|
||||
|
||||
.. versionadded:: 1.5.0
|
||||
|
||||
|
|
|
@ -81,6 +81,8 @@ You can customize the build in many ways:
|
|||
#. Go to the settings panel > build, search for "strip" options, and
|
||||
triple-check that they are all set to NO. Stripping is not working with
|
||||
Python dynamic modules, and will strip needed symbols.
|
||||
#. Indicate a launch image in portrait/landscape for ipad with and without
|
||||
retina display.
|
||||
|
||||
.. _Known issues:
|
||||
|
||||
|
@ -90,12 +92,6 @@ Known issues
|
|||
Currently, the project have few issues as (we'll fixes them during the
|
||||
development):
|
||||
|
||||
- Loading time: Apple provide a way to reduce the feeling of a slow application
|
||||
loading by showing an image when the application is initialize. But, due to
|
||||
the SDL approach, IOS remove the launch image before we have started. So if
|
||||
you are using a launch image, the user will see: The launch image -> black
|
||||
screen -> your app. Remove the launch image for now.
|
||||
|
||||
- Application configuration not writing: we are learning how IOS manage its
|
||||
filesystem.
|
||||
|
||||
|
|
|
@ -137,11 +137,11 @@
|
|||
TextInput:
|
||||
id:info_lbl
|
||||
readonly: True
|
||||
font_size: 11
|
||||
font_size: '11sp'
|
||||
background_color: (0, 0, 0, 1)
|
||||
foreground_color: (1, 1, 1, 1)
|
||||
opacity:0
|
||||
size_hint: 1, None
|
||||
text_size: self.size
|
||||
height: '150pt'
|
||||
top: 0
|
||||
top: 0
|
||||
|
|
|
@ -250,16 +250,16 @@ class ShowcaseApp(App):
|
|||
return col
|
||||
|
||||
def show_popup(self):
|
||||
btnclose = Button(text='Close this popup', size_hint_y=None, height=50)
|
||||
btnclose = Button(text='Close this popup', size_hint_y=None, height='50sp')
|
||||
content = BoxLayout(orientation='vertical')
|
||||
content.add_widget(Label(text='Hello world'))
|
||||
content.add_widget(btnclose)
|
||||
popup = Popup(content=content, title='Modal popup example',
|
||||
size_hint=(None, None), size=(300, 300),
|
||||
size_hint=(None, None), size=('300dp', '300dp'),
|
||||
auto_dismiss=False)
|
||||
btnclose.bind(on_release=popup.dismiss)
|
||||
button = Button(text='Open popup', size_hint=(None, None),
|
||||
size=(150, 70))
|
||||
size=('150sp', '70dp'))
|
||||
button.bind(on_release=popup.open)
|
||||
popup.open()
|
||||
col = AnchorLayout()
|
||||
|
|
|
@ -14,17 +14,18 @@
|
|||
orientation: 'vertical'
|
||||
|
||||
BoxLayout:
|
||||
padding: 10
|
||||
spacing: 10
|
||||
padding: '10dp'
|
||||
spacing: '10dp'
|
||||
size_hint: 1, None
|
||||
pos_hint: {'top': 1}
|
||||
height: 44
|
||||
height: '44dp'
|
||||
Image:
|
||||
size_hint: None, None
|
||||
size: 24, 24
|
||||
source: 'data/logo/kivy-icon-24.png'
|
||||
size: '24dp', '24dp'
|
||||
source: 'data/logo/kivy-icon-64.png'
|
||||
mipmap: True
|
||||
Label:
|
||||
height: 24
|
||||
height: '24dp'
|
||||
text_size: self.width, None
|
||||
color: (1, 1, 1, .8)
|
||||
text: 'Kivy %s - Showcase' % kivy.__version__
|
||||
|
@ -55,7 +56,7 @@
|
|||
[Title@Label]
|
||||
pos_hint: {'center_x': .5, 'y': .3}
|
||||
text: ctx.text
|
||||
font_size: 22
|
||||
font_size: '29dp'
|
||||
|
||||
<AnchorLayoutShowcase>
|
||||
Title:
|
||||
|
@ -153,9 +154,9 @@
|
|||
|
||||
[HSeparator@Label]:
|
||||
size_hint_y: None
|
||||
height: 45
|
||||
height: max(dp(45), self.texture_size[1] + dp(10))
|
||||
text: ctx.text if 'text' in ctx else ''
|
||||
text_size: self.size
|
||||
text_size: self.width, None
|
||||
valign: 'middle'
|
||||
halign: 'center'
|
||||
canvas.before:
|
||||
|
@ -257,18 +258,18 @@
|
|||
TextInput:
|
||||
text: 'Monoline textinput'
|
||||
size_hint_y: None
|
||||
height: 30
|
||||
height: sp(30)
|
||||
|
||||
TextInput:
|
||||
text: 'This is a password'
|
||||
size_hint_y: None
|
||||
height: 30
|
||||
height: sp(30)
|
||||
password: True
|
||||
|
||||
TextInput:
|
||||
text: 'Readonly textinput'
|
||||
size_hint_y: None
|
||||
height: 30
|
||||
height: sp(30)
|
||||
readonly: True
|
||||
|
||||
TextInput:
|
||||
|
@ -313,6 +314,8 @@
|
|||
title: 'Panel 1'
|
||||
Label:
|
||||
text: 'This is a label fit to the content view'
|
||||
halign: 'center'
|
||||
text_size: self.width, None
|
||||
|
||||
AccordionItem:
|
||||
title: 'Panel 2'
|
||||
|
@ -323,6 +326,8 @@
|
|||
title: 'Panel 3'
|
||||
Label:
|
||||
text: 'This is a label fit to the content view'
|
||||
halign: 'center'
|
||||
text_size: self.width, None
|
||||
|
||||
|
||||
Accordion:
|
||||
|
@ -333,6 +338,7 @@
|
|||
title: 'Panel 1'
|
||||
Label:
|
||||
text: 'This is a label fit to the content view'
|
||||
text_size: self.width, None
|
||||
|
||||
AccordionItem:
|
||||
title: 'Panel 2'
|
||||
|
@ -343,6 +349,7 @@
|
|||
title: 'Panel 3'
|
||||
Label:
|
||||
text: 'This is a label fit to the content view'
|
||||
text_size: self.width, None
|
||||
|
||||
|
||||
VSeparator
|
||||
|
|
|
@ -10,17 +10,18 @@
|
|||
size: self.size
|
||||
|
||||
BoxLayout:
|
||||
padding: 10
|
||||
spacing: 10
|
||||
padding: '10dp'
|
||||
spacing: '10dp'
|
||||
size_hint: 1, None
|
||||
pos_hint: {'top': 1}
|
||||
height: 44
|
||||
height: '44dp'
|
||||
Image:
|
||||
size_hint: None, None
|
||||
size: 24, 24
|
||||
source: 'data/logo/kivy-icon-24.png'
|
||||
size: '24dp', '24dp'
|
||||
source: 'data/logo/kivy-icon-64.png'
|
||||
mipmap: True
|
||||
Label:
|
||||
height: 24
|
||||
height: '24dp'
|
||||
text_size: self.width, None
|
||||
color: (1, 1, 1, .8)
|
||||
text: 'Kivy %s - Touchtracer' % kivy.__version__
|
||||
|
|
|
@ -371,6 +371,10 @@ if not environ.get('KIVY_DOC_INCLUDE'):
|
|||
Logger.info('Core: Kivy configuration saved.')
|
||||
sys.exit(0)
|
||||
|
||||
# configure all activated modules
|
||||
from kivy.modules import Modules
|
||||
Modules.configure()
|
||||
|
||||
# android hooks: force fullscreen and add android touch input provider
|
||||
if platform() in ('android', 'ios'):
|
||||
from kivy.config import Config
|
||||
|
|
|
@ -381,6 +381,7 @@ class App(EventDispatcher):
|
|||
if clsname.endswith('App'):
|
||||
clsname = clsname[:-3]
|
||||
filename = join(kv_directory, '%s.kv' % clsname.lower())
|
||||
Logger.debug('App: Loading kv <{0}>'.format(filename))
|
||||
if not exists(filename):
|
||||
Logger.debug('App: kv <%s> not found' % filename)
|
||||
return False
|
||||
|
@ -472,6 +473,7 @@ class App(EventDispatcher):
|
|||
filename = self.get_application_config()
|
||||
if filename is None:
|
||||
return config
|
||||
Logger.debug('App: Loading configuration <{0}>'.format(filename))
|
||||
if exists(filename):
|
||||
try:
|
||||
config.read(filename)
|
||||
|
@ -481,6 +483,8 @@ class App(EventDispatcher):
|
|||
self.build_config(config)
|
||||
pass
|
||||
else:
|
||||
Logger.debug('App: First configuration, create <{0}>'.format(
|
||||
filename))
|
||||
config.filename = filename
|
||||
config.write()
|
||||
return config
|
||||
|
|
28
kivy/base.py
28
kivy/base.py
|
@ -15,12 +15,10 @@ __all__ = (
|
|||
'stopTouchApp',
|
||||
)
|
||||
|
||||
from os import environ
|
||||
from kivy.config import Config
|
||||
from kivy.logger import Logger
|
||||
from kivy.clock import Clock
|
||||
from kivy.event import EventDispatcher
|
||||
from kivy.utils import platform, reify
|
||||
|
||||
# private vars
|
||||
EventLoop = None
|
||||
|
@ -106,32 +104,6 @@ class EventLoopBase(EventDispatcher):
|
|||
'''
|
||||
return self.me_list
|
||||
|
||||
@reify
|
||||
def dpi(self):
|
||||
'''Return the DPI of the screen. Depending of the platform, the DPI can
|
||||
be taken from the Window provider (Desktop mainly), or from
|
||||
platform-specific module (like android/ios).
|
||||
|
||||
On desktop, you can overload the value returned by the Window object
|
||||
(96 by default), by setting the environ KIVY_DPI::
|
||||
|
||||
KIVY_DPI=200 python main.py
|
||||
|
||||
.. versionadded:: 1.4.0
|
||||
'''
|
||||
custom_dpi = environ.get('KIVY_DPI')
|
||||
if custom_dpi:
|
||||
return float(custom_dpi)
|
||||
|
||||
plat = platform()
|
||||
if plat == 'android':
|
||||
import android
|
||||
return android.get_dpi()
|
||||
|
||||
# for all other platforms..
|
||||
self.ensure_window()
|
||||
return self.window.dpi
|
||||
|
||||
def ensure_window(self):
|
||||
'''Ensure that we have an window
|
||||
'''
|
||||
|
|
|
@ -138,6 +138,16 @@ Available configuration tokens
|
|||
property in :class:`~kivy.uix.scrollview.Scrollview` widget.
|
||||
Check the widget documentation for more information.
|
||||
|
||||
`scroll_stoptime`: int
|
||||
Default value of :data:`~kivy.uix.scrollview.Scrollview.scroll_stoptime`
|
||||
property in :class:`~kivy.uix.scrollview.Scrollview` widget.
|
||||
Check the widget documentation for more information.
|
||||
|
||||
`scroll_moves`: int
|
||||
Default value of :data:`~kivy.uix.scrollview.Scrollview.scroll_moves`
|
||||
property in :class:`~kivy.uix.scrollview.Scrollview` widget.
|
||||
Check the widget documentation for more information.
|
||||
|
||||
:modules:
|
||||
|
||||
You can activate modules with this syntax::
|
||||
|
@ -174,7 +184,7 @@ from kivy.logger import Logger, logger_config_update
|
|||
from kivy.utils import OrderedDict
|
||||
|
||||
# Version number of current configuration format
|
||||
KIVY_CONFIG_VERSION = 6
|
||||
KIVY_CONFIG_VERSION = 7
|
||||
|
||||
#: Kivy configuration object
|
||||
Config = None
|
||||
|
@ -388,7 +398,7 @@ if not environ.get('KIVY_DOC_INCLUDE'):
|
|||
|
||||
elif version == 3:
|
||||
# add token for scrollview
|
||||
Config.setdefault('widgets', 'scroll_timeout', '250')
|
||||
Config.setdefault('widgets', 'scroll_timeout', '55')
|
||||
Config.setdefault('widgets', 'scroll_distance', '20')
|
||||
Config.setdefault('widgets', 'scroll_friction', '1.')
|
||||
|
||||
|
@ -408,6 +418,13 @@ if not environ.get('KIVY_DOC_INCLUDE'):
|
|||
elif version == 5:
|
||||
Config.setdefault('graphics', 'resizable', '1')
|
||||
|
||||
elif version == 6:
|
||||
# if the timeout is still the default value, change it
|
||||
if Config.getint('widgets', 'scroll_timeout') == 250:
|
||||
Config.set('widgets', 'scroll_timeout', '55')
|
||||
Config.setdefault('widgets', 'scroll_stoptime', '300')
|
||||
Config.setdefault('widgets', 'scroll_moves', '5')
|
||||
|
||||
#elif version == 1:
|
||||
# # add here the command for upgrading from configuration 0 to 1
|
||||
#
|
||||
|
|
|
@ -8,10 +8,16 @@ core provider can select an OpenGL ES or a 'classic' desktop OpenGL library.
|
|||
'''
|
||||
|
||||
from os import environ
|
||||
from sys import platform, exit
|
||||
|
||||
|
||||
MIN_REQUIRED_GL_VERSION = (2, 0)
|
||||
|
||||
def msgbox(message):
|
||||
if platform == 'win32':
|
||||
import win32ui
|
||||
win32ui.MessageBox(message, 'Kivy Fatal Error')
|
||||
exit(1)
|
||||
|
||||
if 'KIVY_DOC' not in environ:
|
||||
|
||||
|
@ -22,16 +28,32 @@ if 'KIVY_DOC' not in environ:
|
|||
|
||||
def init_gl():
|
||||
gl_init_symbols()
|
||||
gl_init_resources()
|
||||
print_gl_version()
|
||||
gl_init_resources()
|
||||
|
||||
def print_gl_version():
|
||||
version = str(glGetString(GL_VERSION))
|
||||
vendor = str(glGetString(GL_VENDOR))
|
||||
renderer = str(glGetString(GL_RENDERER))
|
||||
Logger.info('GL: OpenGL version <%s>' % version)
|
||||
Logger.info('GL: OpenGL vendor <%s>' % str(
|
||||
glGetString(GL_VENDOR)))
|
||||
Logger.info('GL: OpenGL renderer <%s>' % str(
|
||||
glGetString(GL_RENDERER)))
|
||||
Logger.info('GL: OpenGL vendor <%s>' % vendor)
|
||||
Logger.info('GL: OpenGL renderer <%s>' % renderer)
|
||||
|
||||
# Let the user know if his graphics hardware/drivers are too old
|
||||
major, minor = gl_get_version()
|
||||
Logger.info('GL: OpenGL parsed version: %d, %d' % (major, minor))
|
||||
if (major, minor) < MIN_REQUIRED_GL_VERSION:
|
||||
msg = (
|
||||
'GL: Minimum required OpenGL version (2.0) NOT found!\n\n'
|
||||
'OpenGL version detected: {0}.{1}\n\n'
|
||||
'Version: {2}\nVendor: {3}\nRenderer: {4}\n\n'
|
||||
'Try upgrading your graphics drivers and/or your '
|
||||
'graphics hardware in case of problems.\n\n'
|
||||
'The application will leave now.').format(
|
||||
major, minor, version, vendor, renderer)
|
||||
Logger.critical(msg)
|
||||
msgbox(msg)
|
||||
|
||||
Logger.info('GL: Shading version <%s>' % str(
|
||||
glGetString(GL_SHADING_LANGUAGE_VERSION)))
|
||||
Logger.info('GL: Texture max size <%s>' % str(
|
||||
|
@ -39,15 +61,6 @@ if 'KIVY_DOC' not in environ:
|
|||
Logger.info('GL: Texture max units <%s>' % str(
|
||||
glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS)[0]))
|
||||
|
||||
# Let the user know if his graphics hardware/drivers are too old
|
||||
major, minor = gl_get_version()
|
||||
Logger.info('GL: OpenGL parsed version: %d, %d' % (major, minor))
|
||||
if (major, minor) < MIN_REQUIRED_GL_VERSION:
|
||||
msg = 'GL: Minimum required OpenGL version (2.0) NOT found! ' \
|
||||
'Try upgrading your graphics drivers and/or your ' \
|
||||
'graphics hardware in case of problems.'
|
||||
Logger.critical(msg)
|
||||
|
||||
# To be able to use our GL provider, we must have a window
|
||||
# Automaticly import window auto to ensure the default window creation
|
||||
import kivy.core.window
|
||||
|
|
|
@ -38,7 +38,7 @@ If you need to escape the markup from the current text, use
|
|||
__all__ = ('MarkupLabel', )
|
||||
|
||||
from kivy.graphics.texture import Texture
|
||||
from kivy.utils import platform
|
||||
from kivy.properties import dpi2px
|
||||
from kivy.parser import parse_color
|
||||
from kivy.logger import Logger
|
||||
import re
|
||||
|
@ -138,9 +138,14 @@ class MarkupLabel(MarkupLabelBase):
|
|||
spop('italic')
|
||||
self.resolve_font_name()
|
||||
elif item[:6] == '[size=':
|
||||
item = item[6:-1]
|
||||
try:
|
||||
size = int(item[6:-1])
|
||||
if item[-2:] in ('px', 'pt', 'in', 'cm', 'mm', 'dp', 'sp'):
|
||||
size = dpi2px(item[:-2], item[-2:])
|
||||
else:
|
||||
size = int(item)
|
||||
except ValueError:
|
||||
raise
|
||||
size = options['font_size']
|
||||
spush('font_size')
|
||||
options['font_size'] = size
|
||||
|
|
|
@ -109,7 +109,7 @@
|
|||
background_normal: 'atlas://data/images/defaulttheme/tab_btn'
|
||||
background_down: 'atlas://data/images/defaulttheme/tab_btn_pressed'
|
||||
border: (8, 8, 8, 8)
|
||||
font_size: 11
|
||||
font_size: '15sp'
|
||||
|
||||
|
||||
<TextInput>:
|
||||
|
@ -167,7 +167,7 @@
|
|||
|
||||
<TreeViewLabel>:
|
||||
width: self.texture_size[0]
|
||||
height: max(self.texture_size[1], 24)
|
||||
height: max(self.texture_size[1] + dp(10), dp(24))
|
||||
text_size: self.width, None
|
||||
|
||||
|
||||
|
@ -252,7 +252,7 @@
|
|||
|
||||
orientation: 'horizontal'
|
||||
size_hint_y: None
|
||||
height: 24
|
||||
height: '24sp'
|
||||
# Don't allow expansion of the ../ node
|
||||
is_leaf: not ctx.isdir or ctx.name.endswith('..' + ctx.sep) or self.locked
|
||||
on_touch_down: self.collide_point(*args[1].pos) and ctx.controller().entry_touched(self, args[1])
|
||||
|
@ -260,12 +260,13 @@
|
|||
BoxLayout:
|
||||
pos: root.pos
|
||||
Label:
|
||||
text_size: self.size
|
||||
id: filename
|
||||
text_size: self.width, None
|
||||
halign: 'left'
|
||||
shorten: True
|
||||
text: unicode(ctx.name)
|
||||
Label:
|
||||
text_size: self.size
|
||||
text_size: self.width, None
|
||||
size_hint_x: None
|
||||
halign: 'right'
|
||||
text: unicode(ctx.get_nice_size())
|
||||
|
@ -291,8 +292,8 @@
|
|||
width: scrollview.width
|
||||
size_hint_y: None
|
||||
height: self.minimum_height
|
||||
spacing: 10
|
||||
padding: 10
|
||||
spacing: '10dp'
|
||||
padding: '10dp'
|
||||
|
||||
[FileIconEntry@Widget]:
|
||||
locked: False
|
||||
|
@ -302,6 +303,7 @@
|
|||
|
||||
on_touch_down: self.collide_point(*args[1].pos) and ctx.controller().entry_touched(self, args[1])
|
||||
on_touch_up: self.collide_point(*args[1].pos) and ctx.controller().entry_released(self, args[1])
|
||||
size: '100dp', '100dp'
|
||||
|
||||
canvas:
|
||||
Color:
|
||||
|
@ -313,22 +315,22 @@
|
|||
source: 'atlas://data/images/defaulttheme/filechooser_selected'
|
||||
|
||||
Image:
|
||||
size: 48, 48
|
||||
size: '48dp', '48dp'
|
||||
source: 'atlas://data/images/defaulttheme/filechooser_%s' % ('folder' if ctx.isdir else 'file')
|
||||
pos: root.x + 24, root.y + 40
|
||||
pos: root.x + dp(24), root.y + dp(40)
|
||||
Label:
|
||||
text: unicode(ctx.name)
|
||||
text_size: (root.width, self.height)
|
||||
halign: 'center'
|
||||
shorten: True
|
||||
size: 100, 16
|
||||
pos: root.x, root.y + 16
|
||||
size: '100dp', '16dp'
|
||||
pos: root.x, root.y + dp(16)
|
||||
|
||||
Label:
|
||||
text: unicode(ctx.get_nice_size())
|
||||
font_size: 8
|
||||
font_size: '11sp'
|
||||
color: .8, .8, .8, 1
|
||||
size: 100, 16
|
||||
size: '100dp', '16sp'
|
||||
pos: root.pos
|
||||
halign: 'center'
|
||||
|
||||
|
@ -358,7 +360,7 @@
|
|||
size_hint_y: None
|
||||
height: self.texture_size[1]
|
||||
y: pb.center_y - self.height - 8
|
||||
font_size: 10
|
||||
font_size: '13sp'
|
||||
color: (.8, .8, .8, .8)
|
||||
|
||||
AnchorLayout:
|
||||
|
@ -499,7 +501,8 @@
|
|||
size: self.width, 1
|
||||
|
||||
<SettingItem>:
|
||||
size_hint_x: .25
|
||||
size_hint: .25, None
|
||||
height: labellayout.texture_size[1] + dp(10)
|
||||
content: content
|
||||
canvas:
|
||||
Color:
|
||||
|
@ -516,29 +519,17 @@
|
|||
BoxLayout:
|
||||
pos: root.pos
|
||||
|
||||
FloatLayout:
|
||||
Label:
|
||||
size_hint_x: .66
|
||||
id: labellayout
|
||||
orientation: 'vertical'
|
||||
size_hint_x: .5
|
||||
|
||||
Label:
|
||||
pos_hint: {'y': .5, 'x': 0}
|
||||
text: root.title
|
||||
text_size: self.width - 32, None
|
||||
size_hint_y: None
|
||||
height: self.texture_size[1]
|
||||
Label:
|
||||
pos_hint: {'top': .5, 'x': 0}
|
||||
text: root.desc if root.desc else ''
|
||||
text_size: self.width - 32, None
|
||||
size_hint_y: None
|
||||
height: self.texture_size[1]
|
||||
font_size: 10
|
||||
color: (.7, .7, .7, 1) if root.selected_alpha < 0.5 else (.3, .3, .3, 1)
|
||||
markup: True
|
||||
text: '{0}\n[size=13sp][color=999999]{1}[/color][/size]{2}'.format(root.title or '', root.desc or '', self.font_size)
|
||||
font_size: '15sp'
|
||||
text_size: self.width - 32, None
|
||||
|
||||
BoxLayout:
|
||||
id: content
|
||||
size_hint_x: .5
|
||||
size_hint_x: .33
|
||||
|
||||
|
||||
<SettingBoolean>:
|
||||
|
@ -552,20 +543,26 @@
|
|||
Label:
|
||||
text: str(root.value)
|
||||
pos: root.pos
|
||||
font_size: '15sp'
|
||||
|
||||
<SettingPath>:
|
||||
Label:
|
||||
text: str(root.value)
|
||||
pos: root.pos
|
||||
font_size: '15sp'
|
||||
|
||||
<SettingOptions>:
|
||||
Label:
|
||||
text: str(root.value)
|
||||
pos: root.pos
|
||||
font_size: '15sp'
|
||||
|
||||
<SettingTitle>:
|
||||
text_size: self.width - 32, None
|
||||
size_hint_y: None
|
||||
height: max(dp(20), self.texture_size[1] + dp(20))
|
||||
color: (.9, .9, .9, 1)
|
||||
font_size: '15sp'
|
||||
canvas:
|
||||
Color:
|
||||
rgba: .15, .15, .15, .5
|
||||
|
@ -581,7 +578,8 @@
|
|||
<SettingSidebarLabel>:
|
||||
size_hint: 1, None
|
||||
text_size: self.width - 32, None
|
||||
height: max(48, self.texture_size[1] + 8)
|
||||
height: self.texture_size[1] + dp(20)
|
||||
font_size: '15sp'
|
||||
canvas.before:
|
||||
Color:
|
||||
rgba: 47 / 255., 167 / 255., 212 / 255., int(self.selected)
|
||||
|
@ -592,15 +590,16 @@
|
|||
<SettingsPanel>:
|
||||
spacing: 5
|
||||
padding: 5
|
||||
row_default_height: 48
|
||||
row_force_default: True
|
||||
size_hint_y: None
|
||||
height: self.minimum_height
|
||||
|
||||
Label:
|
||||
size_hint_y: None
|
||||
text: root.title
|
||||
text_size: self.width - 32, None
|
||||
height: max(50, self.texture_size[1] + 20)
|
||||
color: (.5, .5, .5, 1)
|
||||
font_size: '15sp'
|
||||
|
||||
canvas.after:
|
||||
Color:
|
||||
|
@ -622,7 +621,7 @@
|
|||
|
||||
FloatLayout:
|
||||
size_hint_x: None
|
||||
width: 200
|
||||
width: '200dp'
|
||||
GridLayout:
|
||||
pos: root.pos
|
||||
cols: 1
|
||||
|
@ -640,10 +639,11 @@
|
|||
Button:
|
||||
text: 'Close'
|
||||
size_hint: None, None
|
||||
width: 180
|
||||
height: 50
|
||||
pos: root.x + 10, root.y + 10
|
||||
width: '180dp'
|
||||
height: max(50, self.texture_size[1] + dp(20))
|
||||
pos: root.x + dp(10), root.y + dp(10)
|
||||
on_release: root.dispatch('on_close')
|
||||
font_size: '15sp'
|
||||
|
||||
ScrollView:
|
||||
do_scroll_x: False
|
||||
|
@ -766,7 +766,7 @@
|
|||
do_translation: False
|
||||
do_rotation: False
|
||||
do_scale: False
|
||||
auto_bring_to_front: False
|
||||
auto_bring_to_front: False
|
||||
|
||||
|
||||
# =============================================================================
|
||||
|
@ -774,16 +774,16 @@
|
|||
# =============================================================================
|
||||
|
||||
<ScreenManager>:
|
||||
canvas.before:
|
||||
StencilPush
|
||||
Rectangle:
|
||||
pos: self.pos
|
||||
size: self.size
|
||||
StencilUse
|
||||
canvas.after:
|
||||
StencilUnUse
|
||||
Rectangle:
|
||||
pos: self.pos
|
||||
size: self.size
|
||||
StencilPop
|
||||
canvas.before:
|
||||
StencilPush
|
||||
Rectangle:
|
||||
pos: self.pos
|
||||
size: self.size
|
||||
StencilUse
|
||||
canvas.after:
|
||||
StencilUnUse
|
||||
Rectangle:
|
||||
pos: self.pos
|
||||
size: self.size
|
||||
StencilPop
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@ cdef class Color(ContextInstruction):
|
|||
|
||||
cdef class BindTexture(ContextInstruction):
|
||||
cdef int _index
|
||||
cdef bytes _source
|
||||
cdef object _source
|
||||
cdef Texture _texture
|
||||
cdef void apply(self)
|
||||
|
||||
|
|
|
@ -41,7 +41,7 @@ cdef object get_default_texture():
|
|||
|
||||
# register Image cache
|
||||
Cache.register('kv.texture', limit=1000, timeout=60)
|
||||
Cache.register('kv.shader', limit=1000, timeout=60)
|
||||
Cache.register('kv.shader', limit=1000, timeout=3600)
|
||||
|
||||
# ensure that our resources are cleaned
|
||||
def gl_init_resources():
|
||||
|
@ -285,9 +285,9 @@ cdef class BindTexture(ContextInstruction):
|
|||
'''
|
||||
def __get__(self):
|
||||
return self._source
|
||||
def __set__(self, bytes filename):
|
||||
def __set__(self, filename):
|
||||
Logger.trace('BindTexture: setting source: <%s>' % filename)
|
||||
self._source = <bytes>resource_find(filename)
|
||||
self._source = resource_find(filename)
|
||||
if self._source:
|
||||
tex = Cache.get('kv.texture', filename)
|
||||
if not tex:
|
||||
|
|
|
@ -75,6 +75,7 @@ void glew_dynamic_binding() {
|
|||
*/
|
||||
if (glGenFramebuffers == NULL) {
|
||||
printf("GL: glGenFramebuffers is NULL, try to detect an extension\n");
|
||||
printf("GL: available extensions: %s\n", gl_extensions);
|
||||
if (strstr(gl_extensions, "ARB_framebuffer_object")) {
|
||||
printf("GL: ARB_framebuffer_object is supported\n");
|
||||
|
||||
|
|
|
@ -69,6 +69,8 @@ cdef class Instruction:
|
|||
self.set_parent(ig)
|
||||
|
||||
cdef void rremove(self, InstructionGroup ig):
|
||||
if self.parent is None:
|
||||
return
|
||||
ig.children.remove(self)
|
||||
self.set_parent(None)
|
||||
|
||||
|
|
|
@ -1570,9 +1570,18 @@ def glViewport(GLint x, GLint y, GLsizei width, GLsizei height):
|
|||
IF USE_GLEW:
|
||||
cdef extern from "gl_redirect.h":
|
||||
int glewInit()
|
||||
int GLEW_OK
|
||||
char *glewGetErrorString(int)
|
||||
void glew_dynamic_binding()
|
||||
def gl_init_symbols():
|
||||
glewInit()
|
||||
cdef int result
|
||||
cdef bytes error
|
||||
result = glewInit()
|
||||
if result != GLEW_OK:
|
||||
error = glewGetErrorString(result)
|
||||
print 'GLEW initialization error:', error
|
||||
else:
|
||||
print 'GLEW initialization succeeded'
|
||||
glew_dynamic_binding()
|
||||
|
||||
ELSE:
|
||||
|
|
|
@ -68,8 +68,8 @@ class AndroidMotionEventProvider(MotionEventProvider):
|
|||
pressed = joy.get_button(0)
|
||||
x = joy.get_axis(0) * 32768. / w
|
||||
y = 1. - (joy.get_axis(1) * 32768. / h)
|
||||
pressure = joy.get_axis(2)
|
||||
radius = joy.get_axis(3)
|
||||
pressure = joy.get_axis(2) / 1000. # python for android do * 1000.
|
||||
radius = joy.get_axis(3) / 1000. # python for android do * 1000.
|
||||
|
||||
# new touche ?
|
||||
if pressed and jid not in touches:
|
||||
|
|
|
@ -54,11 +54,11 @@ else:
|
|||
h = property(lambda self: self.bottom - self.top)
|
||||
win_rect = RECT()
|
||||
|
||||
if hasattr(windll.user32, 'SetWindowLongPtrW'):
|
||||
try:
|
||||
windll.user32.SetWindowLongPtrW.restype = WNDPROC
|
||||
windll.user32.SetWindowLongPtrW.argtypes = [HANDLE, c_int, WNDPROC]
|
||||
SetWindowLong_wrapper = windll.user32.SetWindowLongPtrW
|
||||
else:
|
||||
except AttributeError:
|
||||
windll.user32.SetWindowLongW.restype = WNDPROC
|
||||
windll.user32.SetWindowLongW.argtypes = [HANDLE, c_int, WNDPROC]
|
||||
SetWindowLong_wrapper = windll.user32.SetWindowLongW
|
||||
|
|
|
@ -98,11 +98,11 @@ else:
|
|||
w = property(lambda self: self.right - self.left)
|
||||
h = property(lambda self: self.bottom - self.top)
|
||||
|
||||
if hasattr(windll.user32, 'SetWindowLongPtrW'):
|
||||
try:
|
||||
windll.user32.SetWindowLongPtrW.restype = WNDPROC
|
||||
windll.user32.SetWindowLongPtrW.argtypes = [HANDLE, c_int, WNDPROC]
|
||||
SetWindowLong_wrapper = windll.user32.SetWindowLongPtrW
|
||||
else:
|
||||
except AttributeError:
|
||||
windll.user32.SetWindowLongW.restype = WNDPROC
|
||||
windll.user32.SetWindowLongW.argtypes = [HANDLE, c_int, WNDPROC]
|
||||
SetWindowLong_wrapper = windll.user32.SetWindowLongW
|
||||
|
|
|
@ -458,6 +458,7 @@ from kivy.utils import OrderedDict, QueryDict
|
|||
from kivy.cache import Cache
|
||||
from kivy import kivy_data_dir, require
|
||||
from kivy.lib.debug import make_traceback
|
||||
import kivy.metrics as metrics
|
||||
|
||||
|
||||
trace = Logger.trace
|
||||
|
@ -517,7 +518,14 @@ class ProxyApp(object):
|
|||
object.__getattribute__(self, '_ensure_app')()
|
||||
return repr(object.__getattribute__(self, '_obj'))
|
||||
|
||||
|
||||
global_idmap['app'] = ProxyApp()
|
||||
global_idmap['pt'] = metrics.pt
|
||||
global_idmap['inch'] = metrics.inch
|
||||
global_idmap['cm'] = metrics.cm
|
||||
global_idmap['mm'] = metrics.mm
|
||||
global_idmap['dp'] = metrics.dp
|
||||
global_idmap['sp'] = metrics.sp
|
||||
|
||||
|
||||
class ParserException(Exception):
|
||||
|
|
|
@ -0,0 +1,214 @@
|
|||
'''
|
||||
Metrics
|
||||
=======
|
||||
|
||||
.. versionadded:: 1.5.0
|
||||
|
||||
A screen is defined by its actual physical size, density, resolution. These
|
||||
factors are essential for creating UI with correct size everywhere.
|
||||
|
||||
In Kivy, all our graphics pipeline is working with pixels. But using pixels as a
|
||||
measurement unit is wrong, because the size will changes according to the
|
||||
screen.
|
||||
|
||||
Dimensions
|
||||
----------
|
||||
|
||||
As you design your UI for different screen sizes, you'll need new measurement
|
||||
unit to work with.
|
||||
|
||||
:Units:
|
||||
`pt`
|
||||
Points - 1/72 of an inch based on the physical size of the screen.
|
||||
Prefer to use sp instead of pt.
|
||||
`mm`
|
||||
Millimeters - Based on the physical size of the screen
|
||||
`cm`
|
||||
Centimeters - Based on the physical size of the screen
|
||||
`in`
|
||||
Inches - Based on the physical size of the screen
|
||||
`dp`
|
||||
Density-independent Pixels - An abstract unit that is based on the
|
||||
physical density of the screen. With a :data:`Metrics.density` of 1, 1dp
|
||||
is equal to 1px. When running on a higher density screen, the number of
|
||||
pixels used to draw 1dp is scaled up by the factor of appropriate
|
||||
screen's dpi, and the inverse for lower dpi.
|
||||
The ratio dp-to-pixes will change with the screen density, but not
|
||||
necessarily in direct proportions. Using dp unit is a simple solution to
|
||||
making the view dimensions in your layout resize properly for different
|
||||
screen densities. In others words, it provides consistency for the
|
||||
real-world size of your UI across different devices.
|
||||
`sp`
|
||||
Scale-independent Pixels - This is like the dp unit, but it is also
|
||||
scaled by the user's font size preference. It is recommended to use this
|
||||
unit when specifying font sizes, so they will be adjusted for both the
|
||||
screen density and the user's preference.
|
||||
|
||||
Examples
|
||||
--------
|
||||
|
||||
An example of creating a label with a sp font_size, and set a manual height with
|
||||
a 10dp margin::
|
||||
|
||||
#:kivy 1.5.0
|
||||
<MyWidget>:
|
||||
Label:
|
||||
text: 'Hello world'
|
||||
font_size: '15sp'
|
||||
size_hint_y: None
|
||||
height: self.texture_size[1] + dp(10)
|
||||
|
||||
Manual control of metrics
|
||||
-------------------------
|
||||
|
||||
The metrics cannot be changed in runtime. Once a value has been converted to
|
||||
pixels, you don't have the original value anymore. It's not like you'll change
|
||||
the DPI or density in runtime.
|
||||
|
||||
We provide new environment variables to control the metrics:
|
||||
|
||||
- `KIVY_METRICS_DENSITY`: if set, this value will be used for
|
||||
:data:`Metrics.density` instead of the system one. On android, the value
|
||||
varies between 0.75, 1, 1.5. 2.
|
||||
|
||||
- `KIVY_METRICS_FONTSCALE`: if set, this value will be used for
|
||||
:data:`Metrics.fontscale` instead of the system one. On android, the value
|
||||
varies between 0.8-1.2.
|
||||
|
||||
- `KIVY_DPI`: if set, this value will be used for :data:`Metrics.dpi`. Please
|
||||
note that settings the DPI will not impact dp/sp notation, because thoses are
|
||||
based on the density.
|
||||
|
||||
For example, if you want to simulate an high-density screen (like HTC One X)::
|
||||
|
||||
KIVY_DPI=320 KIVY_METRICS_DENSITY=2 python main.py --size 1280x720
|
||||
|
||||
Or a medium-density (like Motorola Droid 2)::
|
||||
|
||||
KIVY_DPI=240 KIVY_METRICS_DENSITY=1.5 python main.py --size 854x480
|
||||
|
||||
You can also simulate an alternative user preference for fontscale, like::
|
||||
|
||||
KIVY_METRICS_FONTSCALE=1.2 python main.py
|
||||
|
||||
'''
|
||||
|
||||
|
||||
__all__ = ('metrics', 'Metrics', 'pt', 'inch', 'cm', 'mm', 'dp', 'sp')
|
||||
|
||||
|
||||
from os import environ
|
||||
from kivy.utils import reify, platform
|
||||
from kivy.properties import dpi2px
|
||||
|
||||
|
||||
def pt(value):
|
||||
'''Convert from points to pixels
|
||||
'''
|
||||
return dpi2px(value, 'pt')
|
||||
|
||||
|
||||
def inch(value):
|
||||
'''Convert from inches to pixels
|
||||
'''
|
||||
return dpi2px(value, 'in')
|
||||
|
||||
|
||||
def cm(value):
|
||||
'''Convert from centimeters to pixels
|
||||
'''
|
||||
return dpi2px(value, 'cm')
|
||||
|
||||
|
||||
def mm(value):
|
||||
'''Convert from millimeters to pixels
|
||||
'''
|
||||
return dpi2px(value, 'mm')
|
||||
|
||||
|
||||
def dp(value):
|
||||
'''Convert from density-independent pixels to pixels
|
||||
'''
|
||||
return dpi2px(value, 'dp')
|
||||
|
||||
|
||||
def sp(value):
|
||||
'''Convert from scale-independent pixels to pixels
|
||||
'''
|
||||
return dpi2px(value, 'sp')
|
||||
|
||||
|
||||
class Metrics(object):
|
||||
'''Class that contain the default attribute for metrics. Don't use the class
|
||||
directly, but use the `metrics` instance.
|
||||
'''
|
||||
|
||||
@reify
|
||||
def dpi(self):
|
||||
'''Return the DPI of the screen. Depending of the platform, the DPI can
|
||||
be taken from the Window provider (Desktop mainly), or from
|
||||
platform-specific module (like android/ios).
|
||||
'''
|
||||
custom_dpi = environ.get('KIVY_DPI')
|
||||
if custom_dpi:
|
||||
return float(custom_dpi)
|
||||
|
||||
if platform() == 'android':
|
||||
import android
|
||||
return android.get_dpi()
|
||||
|
||||
# for all other platforms..
|
||||
from kivy.base import EventLoop
|
||||
EventLoop.ensure_window()
|
||||
return EventLoop.window.dpi
|
||||
|
||||
@reify
|
||||
def dpi_rounded(self):
|
||||
'''Return the dpi of the screen, rounded to the nearest of 120, 160,
|
||||
240, 320.
|
||||
'''
|
||||
dpi = self.dpi
|
||||
if dpi < 140:
|
||||
return 120
|
||||
elif dpi < 200:
|
||||
return 160
|
||||
elif dpi < 280:
|
||||
return 240
|
||||
return 320
|
||||
|
||||
@reify
|
||||
def density(self):
|
||||
'''Return the density of the screen. This value is 1 by default
|
||||
on desktop, and varies on android depending the screen.
|
||||
'''
|
||||
custom_density = environ.get('KIVY_METRICS_DENSITY')
|
||||
if custom_density:
|
||||
return float(custom_density)
|
||||
|
||||
if platform() == 'android':
|
||||
import jnius
|
||||
Hardware = jnius.autoclass('org.renpy.android.Hardware')
|
||||
return Hardware.metrics.scaledDensity
|
||||
|
||||
return 1.0
|
||||
|
||||
@reify
|
||||
def fontscale(self):
|
||||
'''Return the fontscale user preference. This value is 1 by default, and
|
||||
can varies between 0.8-1.2.
|
||||
'''
|
||||
custom_fontscale = environ.get('KIVY_METRICS_FONTSCALE')
|
||||
if custom_fontscale:
|
||||
return float(custom_fontscale)
|
||||
|
||||
if platform() == 'android':
|
||||
import jnius
|
||||
PythonActivity = jnius.autoclass('org.renpy.android.PythonActivity')
|
||||
config = PythonActivity.mActivity.getResources().getConfiguration()
|
||||
return config.fontScale
|
||||
|
||||
return 1.0
|
||||
|
||||
|
||||
#: default instance of :class:`Metrics`, used everywhere in the code
|
||||
metrics = Metrics()
|
|
@ -113,7 +113,9 @@ class ModuleBase:
|
|||
|
||||
def import_module(self, name):
|
||||
try:
|
||||
module = __import__(name=name, fromlist='.')
|
||||
modname = 'kivy.modules.{0}'.format(name)
|
||||
module = __import__(name=modname)
|
||||
module = sys.modules[modname]
|
||||
except ImportError:
|
||||
Logger.exception('Modules: unable to import <%s>' % name)
|
||||
raise
|
||||
|
@ -130,38 +132,17 @@ class ModuleBase:
|
|||
|
||||
def activate_module(self, name, win):
|
||||
'''Activate a module on a window'''
|
||||
if not name in self.mods:
|
||||
if name not in self.mods:
|
||||
Logger.warning('Modules: Module <%s> not found' % name)
|
||||
return
|
||||
|
||||
if not 'module' in self.mods[name]:
|
||||
try:
|
||||
self.import_module(name)
|
||||
except ImportError:
|
||||
return
|
||||
|
||||
module = self.mods[name]['module']
|
||||
if not self.mods[name]['activated']:
|
||||
|
||||
# convert configuration like:
|
||||
# -m mjpegserver:port=8080,fps=8
|
||||
# and pass it in context.config token
|
||||
config = dict()
|
||||
|
||||
args = Config.get('modules', name)
|
||||
if args != '':
|
||||
values = Config.get('modules', name).split(',')
|
||||
for value in values:
|
||||
x = value.split('=', 1)
|
||||
if len(x) == 1:
|
||||
config[x[0]] = True
|
||||
else:
|
||||
config[x[0]] = x[1]
|
||||
|
||||
msg = 'Modules: Start <%s> with config %s' % (name, str(config))
|
||||
context = self.mods[name]['context']
|
||||
msg = 'Modules: Start <{0}> with config {1}'.format(
|
||||
name, context)
|
||||
Logger.debug(msg)
|
||||
self.mods[name]['context'].config = config
|
||||
module.start(win, self.mods[name]['context'])
|
||||
module.start(win, context)
|
||||
|
||||
def deactivate_module(self, name, win):
|
||||
'''Deactivate a module from a window'''
|
||||
|
@ -196,6 +177,45 @@ class ModuleBase:
|
|||
for name in modules_to_activate:
|
||||
self.activate_module(name, win)
|
||||
|
||||
def configure(self):
|
||||
'''(internal) Configure all the modules before using it.
|
||||
'''
|
||||
modules_to_configure = map(lambda x: x[0], Config.items('modules'))
|
||||
for name in modules_to_configure:
|
||||
if name not in self.mods:
|
||||
Logger.warning('Modules: Module <%s> not found' % name)
|
||||
continue
|
||||
self._configure_module(name)
|
||||
|
||||
def _configure_module(self, name):
|
||||
if 'module' not in self.mods[name]:
|
||||
try:
|
||||
self.import_module(name)
|
||||
except ImportError:
|
||||
return
|
||||
|
||||
# convert configuration like:
|
||||
# -m mjpegserver:port=8080,fps=8
|
||||
# and pass it in context.config token
|
||||
config = dict()
|
||||
|
||||
args = Config.get('modules', name)
|
||||
if args != '':
|
||||
values = Config.get('modules', name).split(',')
|
||||
for value in values:
|
||||
x = value.split('=', 1)
|
||||
if len(x) == 1:
|
||||
config[x[0]] = True
|
||||
else:
|
||||
config[x[0]] = x[1]
|
||||
|
||||
self.mods[name]['context'].config = config
|
||||
|
||||
# call configure if module have one
|
||||
if hasattr(self.mods[name]['module'], 'configure'):
|
||||
self.mods[name]['module'].configure(config)
|
||||
|
||||
|
||||
def usage_list(self):
|
||||
print
|
||||
print 'Available modules'
|
||||
|
|
|
@ -0,0 +1,100 @@
|
|||
'''
|
||||
Screen
|
||||
======
|
||||
|
||||
This module change some environement and configuration to match the density /
|
||||
dpi / screensize of a specific devices.
|
||||
|
||||
To see a list of the available screenid, just run::
|
||||
|
||||
python main.py -m screen
|
||||
|
||||
Simulate a medium-density screen as Motolora Droid 2::
|
||||
|
||||
python main.py -m screen,droid2
|
||||
|
||||
Simulate a high-density screen as HTC One X, in portrait::
|
||||
|
||||
python main.py -m screen,onex,portrait
|
||||
|
||||
Simulate the iPad 2 screen::
|
||||
|
||||
python main.py -m screen,ipad
|
||||
'''
|
||||
|
||||
import sys
|
||||
from os import environ
|
||||
from kivy.config import Config
|
||||
from kivy.logger import Logger
|
||||
|
||||
# taken from http://en.wikipedia.org/wiki/List_of_displays_by_pixel_density
|
||||
devices = {
|
||||
# device: (name, width, height, dpi, density)
|
||||
'onex': ('HTC One X', 1280, 720, 312, 2),
|
||||
's3': ('Galaxy SIII', 1280, 720, 306, 2),
|
||||
'droid2': ('Motolora Droid 2', 854, 480, 240, 1.5),
|
||||
'xoom': ('Motolora Xoom', 1280, 800, 149, 1),
|
||||
'ipad': ('iPad (1 and 2)', 1024, 768, 132, 1),
|
||||
'ipad3': ('iPad 3', 2048, 1536, 264, 2),
|
||||
'iphone4': ('iPhone 4', 640, 960, 326, 2),
|
||||
'iphone5': ('iPhone 5', 640, 1136, 326, 2),
|
||||
}
|
||||
|
||||
|
||||
def start(win, ctx):
|
||||
pass
|
||||
|
||||
|
||||
def stop(win, ctx):
|
||||
pass
|
||||
|
||||
|
||||
def apply_device(device, scale, orientation):
|
||||
name, width, height, dpi, density = devices[device]
|
||||
if orientation == 'portrait':
|
||||
width, height = height, width
|
||||
Logger.info('Screen: Apply screen settings for {0}'.format(name))
|
||||
Logger.info('Screen: size={0}x{1} dpi={2} density={3} '
|
||||
'orientation={4}'.format(width, height, dpi, density, orientation))
|
||||
environ['KIVY_METRICS_DENSITY'] = str(density)
|
||||
environ['KIVY_DPI'] = str(dpi)
|
||||
Config.set('graphics', 'width', str(width))
|
||||
Config.set('graphics', 'height', str(height))
|
||||
Config.set('graphics', 'fullscreen', '0')
|
||||
Config.set('graphics', 'show_mousecursor', '1')
|
||||
|
||||
|
||||
def usage(device=None):
|
||||
if device:
|
||||
Logger.error('Screen: The specified device ({0}) is unknow.',
|
||||
device)
|
||||
print '\nModule usage: python main.py -m screen,deviceid[,orientation]\n'
|
||||
print 'Availables devices:\n'
|
||||
print '{0:12} {1:<22} {2:<8} {3:<8} {4:<5} {5:<8}'.format(
|
||||
'Device ID', 'Name', 'Width', 'Height', 'DPI', 'Density')
|
||||
for device, info in devices.iteritems():
|
||||
print '{0:12} {1:<22} {2:<8} {3:<8} {4:<5} {5:<8}'.format(
|
||||
device, *info)
|
||||
print '\n'
|
||||
print 'Simulate a medium-density screen as Motolora Droid 2:\n'
|
||||
print ' python main.py -m screen,droid2\n'
|
||||
print 'Simulate a high-density screen as HTC One X, in portrait:\n'
|
||||
print ' python main.py -m screen,onex,portrait\n'
|
||||
print 'Simulate the iPad 2 screen\n'
|
||||
print ' python main.py -m screen,ipad\n'
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def configure(ctx):
|
||||
scale = ctx.pop('scale', None)
|
||||
orientation = 'landscape'
|
||||
ctx.pop('landscape', None)
|
||||
if ctx.pop('portrait', None):
|
||||
orientation = 'portrait'
|
||||
if not ctx:
|
||||
return usage(None)
|
||||
device = ctx.keys()[0]
|
||||
if device not in devices:
|
||||
return usage('')
|
||||
apply_device(device, scale, orientation)
|
||||
|
|
@ -173,7 +173,33 @@ __all__ = ('Property',
|
|||
|
||||
from weakref import ref
|
||||
|
||||
EventLoop = None
|
||||
cdef float g_dpi = -1
|
||||
cdef float g_density = -1
|
||||
cdef float g_fontscale = -1
|
||||
|
||||
cpdef float dpi2px(value, ext):
|
||||
# 1in = 2.54cm = 25.4mm = 72pt = 12pc
|
||||
global g_dpi, g_density, g_fontscale
|
||||
if g_dpi == -1:
|
||||
from kivy.metrics import metrics
|
||||
g_dpi = metrics.dpi
|
||||
g_density = metrics.density
|
||||
g_fontscale = metrics.fontscale
|
||||
cdef float rv = float(value)
|
||||
if ext == 'in':
|
||||
return rv * g_dpi
|
||||
elif ext == 'px':
|
||||
return rv
|
||||
elif ext == 'dp':
|
||||
return rv * g_density
|
||||
elif ext == 'sp':
|
||||
return rv * g_density * g_fontscale
|
||||
elif ext == 'pt':
|
||||
return rv * g_dpi / 72.
|
||||
elif ext == 'cm':
|
||||
return rv * g_dpi / 2.54
|
||||
elif ext == 'mm':
|
||||
return rv * g_dpi / 25.4
|
||||
|
||||
|
||||
cdef class Property:
|
||||
|
@ -439,23 +465,8 @@ cdef class NumericProperty(Property):
|
|||
return self.parse_list(obj, value[:-2], <str>value[-2:])
|
||||
|
||||
cdef float parse_list(self, EventDispatcher obj, value, str ext):
|
||||
# 1in = 2.54cm = 25.4mm = 72pt = 12pc
|
||||
global EventLoop
|
||||
if EventLoop is None:
|
||||
from kivy.base import EventLoop
|
||||
cdef float rv = float(value)
|
||||
cdef float dpi = EventLoop.dpi
|
||||
obj.__storage[self.name]['format'] = ext
|
||||
if ext == 'in':
|
||||
return rv * dpi
|
||||
elif ext == 'px':
|
||||
return rv
|
||||
elif ext == 'pt':
|
||||
return rv * dpi / 72.
|
||||
elif ext == 'cm':
|
||||
return rv * dpi / 2.54
|
||||
elif ext == 'mm':
|
||||
return rv * dpi / 25.4
|
||||
return dpi2px(value, ext)
|
||||
|
||||
def get_format(self, EventDispatcher obj):
|
||||
'''
|
||||
|
|
|
@ -212,7 +212,7 @@ class AccordionItem(FloatLayout):
|
|||
'''Link to the :attr:`Accordion.orientation` property.
|
||||
'''
|
||||
|
||||
min_space = NumericProperty(44)
|
||||
min_space = NumericProperty('44dp')
|
||||
'''Link to the :attr:`Accordion.min_space` property.
|
||||
'''
|
||||
|
||||
|
@ -321,7 +321,7 @@ class Accordion(Widget):
|
|||
easing function.
|
||||
'''
|
||||
|
||||
min_space = NumericProperty(44)
|
||||
min_space = NumericProperty('44dp')
|
||||
'''Minimum space to use for title of each item. This value is automatically
|
||||
set on each children, each time the layout happens.
|
||||
|
||||
|
|
|
@ -312,11 +312,14 @@ class Carousel(StencilView):
|
|||
if self.collide_point(*touch.pos):
|
||||
return super(Carousel, self).on_touch_move(touch)
|
||||
|
||||
def add_widget(self, *args, **kwargs):
|
||||
def add_widget(self, widget, index=0):
|
||||
slide = RelativeLayout(size=self.size, x=self.x - self.width, y=self.y)
|
||||
slide.add_widget(*args, **kwargs)
|
||||
super(Carousel, self).add_widget(slide)
|
||||
self.slides.append(slide)
|
||||
slide.add_widget(widget)
|
||||
super(Carousel, self).add_widget(slide, index)
|
||||
if index != 0:
|
||||
self.slides.insert(index, slide)
|
||||
else:
|
||||
self.slides.append(slide)
|
||||
|
||||
def remove_widget(self, widget, *args, **kwargs):
|
||||
# XXX be careful, the widget.parent.parent refer to the RelativeLayout
|
||||
|
|
|
@ -66,22 +66,34 @@ class CodeInput(TextInput):
|
|||
def __init__(self, **kwargs):
|
||||
self.formatter = BBCodeFormatter()
|
||||
self.lexer = lexers.PythonLexer()
|
||||
self.text_color = (0, 0, 0, 1)
|
||||
self.text_color = '#000000'
|
||||
self._label_cached = Label()
|
||||
self.use_text_color = True
|
||||
super(CodeInput, self).__init__(**kwargs)
|
||||
self._line_options = kw = self._get_line_options()
|
||||
self._label_cached = Label(**kw)
|
||||
#use text_color as foreground color
|
||||
# use text_color as foreground color
|
||||
text_color = kwargs.get('foreground_color')
|
||||
if text_color:
|
||||
self.text_color = (text_color[0], text_color[1], text_color[2],
|
||||
text_color[3])
|
||||
get_hex_clr = self.get_hex_clr
|
||||
self.text_color = ''.join(('#',
|
||||
get_hex_clr(text_color[0]),
|
||||
get_hex_clr(text_color[1]),
|
||||
get_hex_clr(text_color[2]),
|
||||
get_hex_clr(text_color[3])))
|
||||
# set foreground to white to allow text colors to show
|
||||
# use text_color as the default color in bbcodes
|
||||
self.foreground_color = [1, 1, 1, 1]
|
||||
self.use_text_color = False
|
||||
self.foreground_color = [1, 1, 1, .999]
|
||||
if not kwargs.get('background_color'):
|
||||
self.background_color = [.9, .92, .92, 1]
|
||||
|
||||
def get_hex_clr(self, color):
|
||||
clr = hex(int(color * 255))[2:]
|
||||
if len(str(clr)) < 2:
|
||||
clr = ''.join(('0', str(clr)))
|
||||
return clr
|
||||
|
||||
def _create_line_label(self, text):
|
||||
# Create a label from a text, using line options
|
||||
ntext = text.replace('\n', '').replace('\t', ' ' * self.tab_width)
|
||||
|
@ -104,7 +116,7 @@ class CodeInput(TextInput):
|
|||
try:
|
||||
label.refresh()
|
||||
except ValueError:
|
||||
pass
|
||||
return
|
||||
|
||||
# ok, we found it.
|
||||
texture = label.texture
|
||||
|
@ -116,6 +128,7 @@ class CodeInput(TextInput):
|
|||
kw = super(CodeInput, self)._get_line_options()
|
||||
kw['markup'] = True
|
||||
kw['valign'] = 'top'
|
||||
kw['codeinput'] = True
|
||||
return kw
|
||||
|
||||
def _get_bbcode(self, ntext):
|
||||
|
@ -130,7 +143,7 @@ class CodeInput(TextInput):
|
|||
ntext = highlight(ntext, self.lexer, self.formatter)
|
||||
ntext = ntext.replace(u'⣿;', '&bl;').replace(u'⣾;', '&br;')
|
||||
# replace special chars with &bl; and &br;
|
||||
ntext = ''.join(('[color=rgba', str(self.text_color), ']',
|
||||
ntext = ''.join(('[color=', str(self.text_color), ']',
|
||||
ntext, '[/color]'))
|
||||
ntext = ntext.replace('\n', '')
|
||||
return ntext
|
||||
|
@ -154,6 +167,21 @@ class CodeInput(TextInput):
|
|||
def on_lexer(self, instance, value):
|
||||
self._trigger_refresh_text()
|
||||
|
||||
def on_foreground_color(self, instance, text_color):
|
||||
if not self.use_text_color:
|
||||
self.use_text_color = True
|
||||
return
|
||||
get_hex_clr = self.get_hex_clr
|
||||
self.text_color = ''.join((
|
||||
get_hex_clr(text_color[0]),
|
||||
get_hex_clr(text_color[1]),
|
||||
get_hex_clr(text_color[2]),
|
||||
get_hex_clr(text_color[3])))
|
||||
self.use_text_color = False
|
||||
self.foreground_color = (1, 1, 1, .999)
|
||||
self._trigger_refresh_text()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
from kivy.extras.highlight import KivyLexer
|
||||
from kivy.app import App
|
||||
|
|
|
@ -15,7 +15,7 @@ strings::
|
|||
l = Label(text='Multi\\nLine')
|
||||
|
||||
# size
|
||||
l = Label(text='Hello world', font_size=20)
|
||||
l = Label(text='Hello world', font_size='20sp')
|
||||
|
||||
Markup text
|
||||
-----------
|
||||
|
@ -148,9 +148,6 @@ class Label(Widget):
|
|||
# markup have change, we need to change our rendering method.
|
||||
d = Label._font_properties
|
||||
dkw = dict(zip(d, [getattr(self, x) for x in d]))
|
||||
# XXX font_size core provider compatibility
|
||||
if Label.font_size.get_format(self) == 'px':
|
||||
dkw['font_size'] *= 1.333
|
||||
if markup:
|
||||
self._label = CoreMarkupLabel(**dkw)
|
||||
else:
|
||||
|
@ -166,9 +163,6 @@ class Label(Widget):
|
|||
elif name == 'text_size':
|
||||
self._label.usersize = value
|
||||
elif name == 'font_size':
|
||||
# XXX font_size core provider compatibility
|
||||
if Label.font_size.get_format(self) == 'px':
|
||||
value *= 1.333
|
||||
self._label.options[name] = value
|
||||
else:
|
||||
self._label.options[name] = value
|
||||
|
@ -274,11 +268,11 @@ class Label(Widget):
|
|||
'DroidSans'.
|
||||
'''
|
||||
|
||||
font_size = NumericProperty('12px')
|
||||
font_size = NumericProperty('15sp')
|
||||
'''Font size of the text, in pixels.
|
||||
|
||||
:data:`font_size` is a :class:`~kivy.properties.NumericProperty`, default to
|
||||
12.
|
||||
12dp.
|
||||
'''
|
||||
|
||||
bold = BooleanProperty(False)
|
||||
|
@ -379,7 +373,7 @@ class Label(Widget):
|
|||
|
||||
l = Label(text='Hello world')
|
||||
# l.texture is good
|
||||
l.font_size = 50
|
||||
l.font_size = '50sp'
|
||||
# l.texture is not updated yet
|
||||
l.update_texture()
|
||||
# l.texture is good now.
|
||||
|
|
|
@ -119,13 +119,13 @@ class Popup(ModalView):
|
|||
default to [47 / 255., 167 / 255., 212 / 255., 1.]
|
||||
'''
|
||||
|
||||
separator_height = NumericProperty(2)
|
||||
separator_height = NumericProperty('2dp')
|
||||
'''Height of the separator.
|
||||
|
||||
.. versionadded:: 1.1.0
|
||||
|
||||
:data:`separator_height` is a :class:`~kivy.properties.NumericProperty`,
|
||||
default to 2.
|
||||
default to 2dp.
|
||||
'''
|
||||
|
||||
# Internals properties used for graphical representation.
|
||||
|
|
|
@ -147,9 +147,9 @@ Builder.load_string('''
|
|||
<RstTitle>:
|
||||
markup: True
|
||||
valign: 'top'
|
||||
font_size: 24 - self.section * 2
|
||||
font_size: sp(31 - self.section * 2)
|
||||
size_hint_y: None
|
||||
height: self.texture_size[1] + 20
|
||||
height: self.texture_size[1] + dp(20)
|
||||
text_size: self.width, None
|
||||
bold: True
|
||||
|
||||
|
@ -178,7 +178,7 @@ Builder.load_string('''
|
|||
markup: True
|
||||
valign: 'top'
|
||||
size_hint: None, None
|
||||
size: self.texture_size[0] + 10, self.texture_size[1] + 10
|
||||
size: self.texture_size[0] + dp(10), self.texture_size[1] + dp(10)
|
||||
|
||||
<RstBlockQuote>:
|
||||
cols: 2
|
||||
|
@ -198,7 +198,7 @@ Builder.load_string('''
|
|||
cols: 1
|
||||
content: content
|
||||
size_hint_y: None
|
||||
height: content.texture_size[1] + 20
|
||||
height: content.texture_size[1] + dp(20)
|
||||
canvas:
|
||||
Color:
|
||||
rgb: parse_color('#cccccc')
|
||||
|
@ -277,11 +277,11 @@ Builder.load_string('''
|
|||
|
||||
<RstImage>:
|
||||
size_hint: None, None
|
||||
size: self.texture_size[0], self.texture_size[1] + 10
|
||||
size: self.texture_size[0], self.texture_size[1] + dp(10)
|
||||
|
||||
<RstAsyncImage>:
|
||||
size_hint: None, None
|
||||
size: self.texture_size[0], self.texture_size[1] + 10
|
||||
size: self.texture_size[0], self.texture_size[1] + dp(10)
|
||||
|
||||
<RstDefinitionList>:
|
||||
cols: 1
|
||||
|
@ -350,8 +350,8 @@ Builder.load_string('''
|
|||
markup: True
|
||||
valign: 'top'
|
||||
size_hint_x: None
|
||||
width: self.texture_size[0] + 10
|
||||
text_size: None, self.height - 10
|
||||
width: self.texture_size[0] + dp(10)
|
||||
text_size: None, self.height - dp(10)
|
||||
|
||||
<RstEmptySpace>:
|
||||
size_hint: 0.01, 0.01
|
||||
|
|
|
@ -47,9 +47,10 @@ a root widget for your own screen. Best way is to subclass.
|
|||
|
||||
Here is an example with a 'Menu Screen', and a 'Setting Screen'::
|
||||
|
||||
from kivy.app import App
|
||||
from kivy.lang import Builder
|
||||
from kivy.uix.screenmanager import ScreenManager, Screen
|
||||
|
||||
|
||||
# Create both screen. Please note the root.manager.current: this is how you
|
||||
# can control the ScreenManager from kv. Each screen have by default a
|
||||
# property manager that give you the instance of the ScreenManager used.
|
||||
|
@ -61,7 +62,7 @@ Here is an example with a 'Menu Screen', and a 'Setting Screen'::
|
|||
on_press: root.manager.current = 'settings'
|
||||
Button:
|
||||
text: 'Quit'
|
||||
|
||||
|
||||
<SettingsScreen>:
|
||||
BoxLayout:
|
||||
Button:
|
||||
|
@ -70,18 +71,26 @@ Here is an example with a 'Menu Screen', and a 'Setting Screen'::
|
|||
text: 'Back to menu'
|
||||
on_press: root.manager.current = 'menu'
|
||||
""")
|
||||
|
||||
|
||||
# Declare both screen
|
||||
class MenuScreen(Screen):
|
||||
pass
|
||||
|
||||
|
||||
class SettingsScreen(Screen):
|
||||
pass
|
||||
|
||||
|
||||
# Create the screen manager
|
||||
sm = ScreenManager()
|
||||
sm.add_widget(MenuScreen(name='menu'))
|
||||
sm.add_widget(SettingsScreen(name='settings'))
|
||||
|
||||
class TestApp(App):
|
||||
|
||||
def build(self):
|
||||
return sm
|
||||
|
||||
if __name__ == '__main__':
|
||||
TestApp().run()
|
||||
|
||||
|
||||
Changing transition
|
||||
|
@ -367,7 +376,7 @@ class ShaderTransition(TransitionBase):
|
|||
with self.render_ctx:
|
||||
BindTexture(texture=self.fbo_out.texture, index=1)
|
||||
BindTexture(texture=self.fbo_in.texture, index=2)
|
||||
Rectangle(size=(1, 1))
|
||||
Rectangle(size=(1, -1), pos=(0, 1))
|
||||
self.render_ctx['projection_mat'] = Matrix().\
|
||||
view_clip(0, 1, 0, 1, 0, 1, 0)
|
||||
self.render_ctx['tex_out'] = 1
|
||||
|
|
|
@ -82,23 +82,48 @@ If you want to reduce the default timeout, you can set::
|
|||
|
||||
__all__ = ('ScrollView', )
|
||||
|
||||
from copy import copy
|
||||
from functools import partial
|
||||
from kivy.animation import Animation
|
||||
from kivy.config import Config
|
||||
from kivy.clock import Clock
|
||||
from kivy.uix.stencilview import StencilView
|
||||
from kivy.properties import NumericProperty, BooleanProperty, AliasProperty, \
|
||||
ObjectProperty, ListProperty
|
||||
ObjectProperty, ListProperty
|
||||
|
||||
|
||||
# When we are generating documentation, Config doesn't exist
|
||||
_scroll_timeout = _scroll_distance = _scroll_friction = 0
|
||||
_scroll_moves = _scroll_timeout = _scroll_stoptime = \
|
||||
_scroll_distance = _scroll_friction = 0
|
||||
if Config:
|
||||
_scroll_timeout = Config.getint('widgets', 'scroll_timeout')
|
||||
_scroll_stoptime = Config.getint('widgets', 'scroll_stoptime')
|
||||
_scroll_distance = Config.getint('widgets', 'scroll_distance')
|
||||
_scroll_moves = Config.getint('widgets', 'scroll_moves')
|
||||
_scroll_friction = Config.getfloat('widgets', 'scroll_friction')
|
||||
|
||||
|
||||
class FixedList(list):
|
||||
'''A list. In addition, you can specify the maximum length.
|
||||
This will save memory.
|
||||
'''
|
||||
def __init__(self, maxlength=0, *args, **kwargs):
|
||||
super(FixedList, self).__init__(*args, **kwargs)
|
||||
self.maxlength = maxlength
|
||||
|
||||
def append(self, x):
|
||||
super(FixedList, self).append(x)
|
||||
self._cut()
|
||||
|
||||
def extend(self, L):
|
||||
super(FixedList, self).append(L)
|
||||
self._cut()
|
||||
|
||||
def _cut(self):
|
||||
while len(self) > self.maxlength:
|
||||
self.pop(0)
|
||||
|
||||
|
||||
class ScrollView(StencilView):
|
||||
'''ScrollView class. See module documentation for more information.
|
||||
'''
|
||||
|
@ -206,8 +231,10 @@ class ScrollView(StencilView):
|
|||
return
|
||||
uid = self._get_uid()
|
||||
touch = self._touch
|
||||
mode = touch.ud[uid]['mode']
|
||||
if mode == 'unknown':
|
||||
ud = touch.ud[uid]
|
||||
if ud['mode'] == 'unknown' and \
|
||||
not ud['user_stopped'] and \
|
||||
touch.dx + touch.dy == 0:
|
||||
touch.ungrab(self)
|
||||
self._touch = None
|
||||
# correctly calculate the position of the touch inside the
|
||||
|
@ -231,31 +258,36 @@ class ScrollView(StencilView):
|
|||
super(ScrollView, self).on_touch_up(touch)
|
||||
touch.grab_current = None
|
||||
|
||||
def _do_animation(self, touch):
|
||||
def _do_animation(self, touch, *largs):
|
||||
uid = self._get_uid()
|
||||
ud = touch.ud[uid]
|
||||
dt = touch.time_end - ud['time']
|
||||
if dt > self.scroll_timeout / 1000.:
|
||||
self._tdx = self._tdy = self._ts = 0
|
||||
avgdx = sum([move.dx for move in ud['moves']]) / len(ud['moves'])
|
||||
avgdy = sum([move.dy for move in ud['moves']]) / len(ud['moves'])
|
||||
if ud['same'] > self.scroll_stoptime / 1000.:
|
||||
return
|
||||
dt = ud['dt']
|
||||
if dt == 0:
|
||||
self._tdx = self._tdy = self._ts = 0
|
||||
return
|
||||
dx = touch.dx
|
||||
dy = touch.dy
|
||||
dx = avgdx
|
||||
dy = avgdy
|
||||
self._sx = ud['sx']
|
||||
self._sy = ud['sy']
|
||||
self._tdx = dx = dx / dt
|
||||
self._tdy = dy = dy / dt
|
||||
if abs(dx) < 10 and abs(dy) < 10:
|
||||
return
|
||||
self._ts = self._tsn = touch.time_update
|
||||
|
||||
Clock.unschedule(self._update_animation)
|
||||
Clock.schedule_interval(self._update_animation, 0)
|
||||
|
||||
def _update_animation(self, dt):
|
||||
if self._touch is not None or self._ts == 0:
|
||||
touch = self._touch
|
||||
uid = self._get_uid()
|
||||
ud = touch.ud[uid]
|
||||
# scrolling stopped by user input
|
||||
ud['user_stopped'] = True
|
||||
return False
|
||||
self._tsn += dt
|
||||
global_dt = self._tsn - self._ts
|
||||
|
@ -268,13 +300,10 @@ class ScrollView(StencilView):
|
|||
(self.do_scroll_y and not self.do_scroll_x and test_dy) or\
|
||||
(self.do_scroll_x and self.do_scroll_y and test_dx and test_dy):
|
||||
self._ts = 0
|
||||
# scrolling stopped by friction
|
||||
return False
|
||||
dx *= dt
|
||||
dy *= dt
|
||||
'''
|
||||
print 'move by %.3f %.3f | dt=%.3f, divider=%.3f, tdXY=(%.3f, %.3f)' % (
|
||||
dx, dy, global_dt, divider, self._tdx, self._tdy)
|
||||
'''
|
||||
sx, sy = self.convert_distance_to_scroll(dx, dy)
|
||||
ssx = self.scroll_x
|
||||
ssy = self.scroll_y
|
||||
|
@ -284,8 +313,20 @@ class ScrollView(StencilView):
|
|||
self.scroll_y -= sy
|
||||
self._scroll_y_mouse = self.scroll_y
|
||||
if ssx == self.scroll_x and ssy == self.scroll_y:
|
||||
# scrolling stopped by end of box
|
||||
return False
|
||||
|
||||
def _update_delta(self, dt):
|
||||
touch = self._touch
|
||||
if not touch:
|
||||
return False
|
||||
uid = self._get_uid()
|
||||
ud = touch.ud[uid]
|
||||
if touch.dx + touch.dy != 0:
|
||||
ud['same'] += dt
|
||||
else:
|
||||
ud['same'] = 0
|
||||
|
||||
def on_touch_down(self, touch):
|
||||
if not self.collide_point(*touch.pos):
|
||||
touch.ud[self._get_uid('svavoid')] = True
|
||||
|
@ -300,7 +341,8 @@ class ScrollView(StencilView):
|
|||
if vp.height > self.height:
|
||||
# let's say we want to move over 40 pixels each scroll
|
||||
d = (vp.height - self.height)
|
||||
d = self.scroll_distance / float(d)
|
||||
if d != 0:
|
||||
d = self.scroll_distance / float(d)
|
||||
if touch.button == 'scrollup':
|
||||
syd = self._scroll_y_mouse - d
|
||||
elif touch.button == 'scrolldown':
|
||||
|
@ -319,9 +361,14 @@ class ScrollView(StencilView):
|
|||
'sx': self.scroll_x,
|
||||
'sy': self.scroll_y,
|
||||
'dt': None,
|
||||
'time': touch.time_start}
|
||||
'time': touch.time_start,
|
||||
'user_stopped': False,
|
||||
'same': 0,
|
||||
'moves': FixedList(self.scroll_moves)}
|
||||
|
||||
Clock.schedule_interval(self._update_delta, 0)
|
||||
Clock.schedule_once(self._change_touch_mode,
|
||||
self.scroll_timeout / 1000.)
|
||||
self.scroll_timeout / 1000.)
|
||||
return True
|
||||
|
||||
def on_touch_move(self, touch):
|
||||
|
@ -335,6 +382,7 @@ class ScrollView(StencilView):
|
|||
uid = self._get_uid()
|
||||
ud = touch.ud[uid]
|
||||
mode = ud['mode']
|
||||
ud['moves'].append(copy(touch))
|
||||
|
||||
# seperate the distance to both X and Y axis.
|
||||
# if a distance is reach, but on the inverse axis, stop scroll mode !
|
||||
|
@ -364,6 +412,7 @@ class ScrollView(StencilView):
|
|||
self.scroll_y = ud['sy'] + sy
|
||||
ud['dt'] = touch.time_update - ud['time']
|
||||
ud['time'] = touch.time_update
|
||||
ud['user_stopped'] = True
|
||||
|
||||
return True
|
||||
|
||||
|
@ -374,7 +423,7 @@ class ScrollView(StencilView):
|
|||
# never ungrabed, cause their on_touch_up will be never called.
|
||||
# base.py: the me.grab_list[:] => it's a copy, and we are already
|
||||
# iterate on it.
|
||||
|
||||
Clock.unschedule(self._update_delta)
|
||||
if self._get_uid('svavoid') in touch.ud:
|
||||
return
|
||||
|
||||
|
@ -385,10 +434,12 @@ class ScrollView(StencilView):
|
|||
touch.ungrab(self)
|
||||
self._touch = None
|
||||
uid = self._get_uid()
|
||||
mode = touch.ud[uid]['mode']
|
||||
if mode == 'unknown':
|
||||
ud = touch.ud[uid]
|
||||
if ud['mode'] == 'unknown':
|
||||
# we must do the click at least..
|
||||
super(ScrollView, self).on_touch_down(touch)
|
||||
# only send the click if it was not a click to stop autoscrolling
|
||||
if not ud['user_stopped']:
|
||||
super(ScrollView, self).on_touch_down(touch)
|
||||
Clock.schedule_once(partial(self._do_touch_up, touch), .1)
|
||||
elif self.auto_scroll:
|
||||
self._do_animation(touch)
|
||||
|
@ -433,10 +484,39 @@ class ScrollView(StencilView):
|
|||
default to 1, according to the default value in user configuration.
|
||||
'''
|
||||
|
||||
scroll_moves = NumericProperty(_scroll_moves)
|
||||
'''The speed of automatic scrolling is based on previous touch moves. This
|
||||
is to prevent accidental slowing down by the user at the end of the swipe
|
||||
to slow down the automatic scrolling.
|
||||
The moves property specifies the amount of previous scrollmoves that
|
||||
should be taken into consideration when calculating the automatic scrolling
|
||||
speed.
|
||||
|
||||
:data:`scroll_moves` is a :class:`~kivy.properties.NumericProperty`,
|
||||
default to 5.
|
||||
|
||||
.. versionadded:: 1.5.0
|
||||
'''
|
||||
|
||||
scroll_stoptime = NumericProperty(_scroll_stoptime)
|
||||
'''Time after which user input not moving will disable autoscroll for that
|
||||
move. If the user has not moved within the stoptime, autoscroll will not
|
||||
start.
|
||||
This is to prevent autoscroll to trigger while the user has slowed down
|
||||
on purpose to prevent this.
|
||||
|
||||
:data:`scroll_stoptime` is a :class:`~kivy.properties.NumericProperty`,
|
||||
default to 300 (milliseconds)
|
||||
|
||||
.. versionadded:: 1.5.0
|
||||
'''
|
||||
|
||||
scroll_distance = NumericProperty(_scroll_distance)
|
||||
'''Distance to move before scrolling the :class:`ScrollView`, in pixels. As
|
||||
soon as the distance has been traveled, the :class:`ScrollView` will start
|
||||
to scroll, and no touch event will go to children.
|
||||
It is advisable that you base this value on the dpi of your target device's
|
||||
screen.
|
||||
|
||||
:data:`scroll_distance` is a :class:`~kivy.properties.NumericProperty`,
|
||||
default to 20 (pixels), according to the default value in user
|
||||
|
@ -445,12 +525,15 @@ class ScrollView(StencilView):
|
|||
|
||||
scroll_timeout = NumericProperty(_scroll_timeout)
|
||||
'''Timeout allowed to trigger the :data:`scroll_distance`, in milliseconds.
|
||||
If the timeout is reached, the scrolling will be disabled, and the touch
|
||||
event will go to the children.
|
||||
If the user has not moved :data:`scroll_distance` within the timeout,
|
||||
the scrolling will be disabled, and the touch event will go to the children.
|
||||
|
||||
:data:`scroll_timeout` is a :class:`~kivy.properties.NumericProperty`,
|
||||
default to 250 (milliseconds), according to the default value in user
|
||||
default to 55 (milliseconds), according to the default value in user
|
||||
configuration.
|
||||
|
||||
.. versionchanged:: 1.5.0
|
||||
Default value changed from 250 to 55.
|
||||
'''
|
||||
|
||||
scroll_x = NumericProperty(0.)
|
||||
|
@ -568,7 +651,7 @@ class ScrollView(StencilView):
|
|||
[.7, .7, .7, .9].
|
||||
'''
|
||||
|
||||
bar_width = NumericProperty(2)
|
||||
bar_width = NumericProperty('2dp')
|
||||
'''Width of the horizontal / vertical scroll bar. The width is interpreted
|
||||
as a height for the horizontal bar.
|
||||
|
||||
|
@ -601,4 +684,3 @@ class ScrollView(StencilView):
|
|||
if value:
|
||||
value.bind(size=self._set_viewport_size)
|
||||
self._viewport_size = value.size
|
||||
|
||||
|
|
|
@ -108,6 +108,7 @@ __all__ = ('Settings', 'SettingsPanel', 'SettingItem', 'SettingString',
|
|||
|
||||
import json
|
||||
import os
|
||||
from kivy.metrics import dp
|
||||
from kivy.config import ConfigParser
|
||||
from kivy.animation import Animation
|
||||
from kivy.uix.boxlayout import BoxLayout
|
||||
|
@ -339,13 +340,13 @@ class SettingString(SettingItem):
|
|||
|
||||
def _create_popup(self, instance):
|
||||
# create popup layout
|
||||
content = BoxLayout(orientation='vertical', spacing=5)
|
||||
content = BoxLayout(orientation='vertical', spacing='5dp')
|
||||
self.popup = popup = Popup(title=self.title,
|
||||
content=content, size_hint=(None, None), size=(400, 250))
|
||||
content=content, size_hint=(None, None), size=('400dp', '250dp'))
|
||||
|
||||
# create the textinput used for numeric input
|
||||
self.textinput = textinput = TextInput(text=str(self.value),
|
||||
font_size=24, multiline=False, size_hint_y=None, height=50)
|
||||
font_size=24, multiline=False, size_hint_y=None, height='50dp')
|
||||
textinput.bind(on_text_validate=self._validate)
|
||||
self.textinput = textinput
|
||||
|
||||
|
@ -356,7 +357,7 @@ class SettingString(SettingItem):
|
|||
content.add_widget(SettingSpacer())
|
||||
|
||||
# 2 buttons are created for accept or cancel the current value
|
||||
btnlayout = BoxLayout(size_hint_y=None, height=50, spacing=5)
|
||||
btnlayout = BoxLayout(size_hint_y=None, height='50dp', spacing='5dp')
|
||||
btn = Button(text='Ok')
|
||||
btn.bind(on_release=self._validate)
|
||||
btnlayout.add_widget(btn)
|
||||
|
@ -433,7 +434,7 @@ class SettingPath(SettingItem):
|
|||
content.add_widget(SettingSpacer())
|
||||
|
||||
# 2 buttons are created for accept or cancel the current value
|
||||
btnlayout = BoxLayout(size_hint_y=None, height=50, spacing=5)
|
||||
btnlayout = BoxLayout(size_hint_y=None, height='50dp', spacing='5dp')
|
||||
btn = Button(text='Ok')
|
||||
btn.bind(on_release=self._validate)
|
||||
btnlayout.add_widget(btn)
|
||||
|
@ -500,10 +501,10 @@ class SettingOptions(SettingItem):
|
|||
|
||||
def _create_popup(self, instance):
|
||||
# create the popup
|
||||
content = BoxLayout(orientation='vertical', spacing=5)
|
||||
content = BoxLayout(orientation='vertical', spacing='5dp')
|
||||
self.popup = popup = Popup(content=content,
|
||||
title=self.title, size_hint=(None, None), size=(400, 400))
|
||||
popup.height = len(self.options) * 55 + 150
|
||||
title=self.title, size_hint=(None, None), size=('400dp', '400dp'))
|
||||
popup.height = len(self.options) * dp(55) + dp(150)
|
||||
|
||||
# add all the options
|
||||
content.add_widget(Widget(size_hint_y=None, height=1))
|
||||
|
@ -516,7 +517,7 @@ class SettingOptions(SettingItem):
|
|||
|
||||
# finally, add a cancel button to return on the previous panel
|
||||
content.add_widget(SettingSpacer())
|
||||
btn = Button(text='Cancel', size_hint_y=None, height=50)
|
||||
btn = Button(text='Cancel', size_hint_y=None, height=dp(50))
|
||||
btn.bind(on_release=popup.dismiss)
|
||||
content.add_widget(btn)
|
||||
|
||||
|
@ -654,12 +655,12 @@ class Settings(BoxLayout):
|
|||
'''
|
||||
self._types[tp] = cls
|
||||
|
||||
def add_json_panel(self, title, config, filename=None, data=None):
|
||||
'''Create and add a new :class:`SettingsPanel` using the configuration
|
||||
`config`, with the JSON definition `filename`.
|
||||
def create_json_panel(self, title, config, filename=None, data=None):
|
||||
'''Create new :class:`SettingsPanel`.
|
||||
|
||||
Check the :ref:`settings_json` section in the documentation for more
|
||||
information about JSON format, and the usage of this function.
|
||||
.. versionadded:: 1.5.0
|
||||
|
||||
Check the documentation of :meth:`add_json_panel` for more information.
|
||||
'''
|
||||
if filename is None and data is None:
|
||||
raise Exception('You must specify either the filename or data')
|
||||
|
@ -671,7 +672,6 @@ class Settings(BoxLayout):
|
|||
if type(data) != list:
|
||||
raise ValueError('The first element must be a list')
|
||||
panel = SettingsPanel(title=title, settings=self, config=config)
|
||||
self.add_widget(panel)
|
||||
|
||||
for setting in data:
|
||||
# determine the type and the class to use
|
||||
|
@ -696,6 +696,16 @@ class Settings(BoxLayout):
|
|||
|
||||
return panel
|
||||
|
||||
def add_json_panel(self, title, config, filename=None, data=None):
|
||||
'''Create and add a new :class:`SettingsPanel` using the configuration
|
||||
`config`, with the JSON definition `filename`.
|
||||
|
||||
Check the :ref:`settings_json` section in the documentation for more
|
||||
information about JSON format, and the usage of this function.
|
||||
'''
|
||||
panel = self.create_json_panel(title, config, filename, data)
|
||||
self.add_widget(panel)
|
||||
|
||||
def add_kivy_panel(self):
|
||||
'''Add a panel for configuring Kivy. This panel acts directly on the
|
||||
kivy configuration. Feel free to include or exclude it in your
|
||||
|
|
|
@ -234,14 +234,14 @@ class TabbedPanel(GridLayout):
|
|||
default to 'bottom_mid'.
|
||||
'''
|
||||
|
||||
tab_height = NumericProperty(40)
|
||||
tab_height = NumericProperty('40dp')
|
||||
'''Specifies the height of the tab header.
|
||||
|
||||
:data:`tab_height` is a :class:`~kivy.properties.NumericProperty`,
|
||||
default to 40.
|
||||
'''
|
||||
|
||||
tab_width = NumericProperty(100, allownone=True)
|
||||
tab_width = NumericProperty('100dp', allownone=True)
|
||||
'''Specifies the width of the tab header.
|
||||
|
||||
:data:`tab_width` is a :class:`~kivy.properties.NumericProperty`,
|
||||
|
|
|
@ -1171,15 +1171,9 @@ class TextInput(Widget):
|
|||
|
||||
def _get_line_options(self):
|
||||
# Get or create line options, to be used for Label creation
|
||||
|
||||
# XXX font_size core label compatibility
|
||||
factor = 1.
|
||||
if TextInput.font_size.get_format(self) == 'px':
|
||||
factor = 1.333
|
||||
|
||||
if self._line_options is None:
|
||||
self._line_options = kw = {
|
||||
'font_size': self.font_size * factor,
|
||||
'font_size': self.font_size,
|
||||
'font_name': self.font_name,
|
||||
'anchor_x': 'left',
|
||||
'anchor_y': 'top',
|
||||
|
@ -1714,7 +1708,7 @@ class TextInput(Widget):
|
|||
'DroidSans'.
|
||||
'''
|
||||
|
||||
font_size = NumericProperty(10)
|
||||
font_size = NumericProperty('15sp')
|
||||
'''Font size of the text, in pixels.
|
||||
|
||||
:data:`font_size` is a :class:`~kivy.properties.NumericProperty`, default to
|
||||
|
|
|
@ -509,7 +509,7 @@ class TreeView(Widget):
|
|||
of (:data:`minimum_width`, :data:`minimum_height`) properties.
|
||||
'''
|
||||
|
||||
indent_level = NumericProperty(16)
|
||||
indent_level = NumericProperty('16dp')
|
||||
'''Width used for identation of each level, except the first level.
|
||||
|
||||
Computation of spacing for eaching level of tree is::
|
||||
|
@ -520,7 +520,7 @@ class TreeView(Widget):
|
|||
defaults to 16.
|
||||
'''
|
||||
|
||||
indent_start = NumericProperty(24)
|
||||
indent_start = NumericProperty('24dp')
|
||||
'''Indentation width of the level 0 / root node. This is mostly the initial
|
||||
size to accommodate a tree icon (collapsed / expanded). See
|
||||
:data:`indent_level` for more information about the computation of level
|
||||
|
|
|
@ -276,7 +276,7 @@ class VKeyboard(Scatter):
|
|||
have_capslock = BooleanProperty(False)
|
||||
have_shift = BooleanProperty(False)
|
||||
active_keys = DictProperty({})
|
||||
font_size = NumericProperty(15)
|
||||
font_size = NumericProperty('20dp')
|
||||
font_name = StringProperty('data/fonts/DejaVuSans.ttf')
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
|
@ -561,6 +561,7 @@ class VKeyboard(Scatter):
|
|||
|
||||
background = resource_find(self.background)
|
||||
texture = Image(background, mipmap=True).texture
|
||||
self.background_key_layer.clear()
|
||||
with self.background_key_layer:
|
||||
Color(*self.background_color)
|
||||
BorderImage(texture=texture, size=self.size,
|
||||
|
@ -572,12 +573,11 @@ class VKeyboard(Scatter):
|
|||
# first draw keys without the font
|
||||
key_normal = resource_find(self.key_background_normal)
|
||||
texture = Image(key_normal, mipmap=True).texture
|
||||
for line_nb in xrange(1, layout_rows + 1):
|
||||
for pos, size in layout_geometry['LINE_%d' % line_nb]:
|
||||
with self.background_key_layer:
|
||||
Color(self.key_background_color)
|
||||
BorderImage(texture=texture, pos=pos, size=size,
|
||||
border=self.key_border)
|
||||
with self.background_key_layer:
|
||||
for line_nb in xrange(1, layout_rows + 1):
|
||||
for pos, size in layout_geometry['LINE_%d' % line_nb]:
|
||||
BorderImage(texture=texture, pos=pos, size=size,
|
||||
border=self.key_border)
|
||||
|
||||
# then draw the text
|
||||
# calculate font_size
|
||||
|
|
|
@ -33,6 +33,24 @@ Our widget class is designed with a couple of principles in mind:
|
|||
You can also check if a widget collides with another widget with
|
||||
:meth:`Widget.collide_widget`.
|
||||
|
||||
|
||||
We also have some defaults that you should be aware of:
|
||||
|
||||
* A :class:`Widget` is not a :class:`Layout`: it will not change the position
|
||||
nor the size of its children. If you want a better positionning / sizing, use
|
||||
a :class:`Layout`.
|
||||
|
||||
* The default size is (100, 100), if the parent is not a :class:`Layout`. For
|
||||
example, adding a widget inside a :class:`Button`, :class:`Label`, will not
|
||||
inherit from the parent size or pos.
|
||||
|
||||
* The default size_hint is (1, 1). If the parent is a :class:`Layout`, then the
|
||||
widget size will be the parent/layout size.
|
||||
|
||||
* All the :meth:`Widget.on_touch_down`, :meth:`Widget.on_touch_move`,
|
||||
:meth:`Widget.on_touch_up` doesn't do any sort of collisions. If you want to
|
||||
know if the touch is inside your widget, use :meth:`Widget.collide_point`.
|
||||
|
||||
Using Properties
|
||||
----------------
|
||||
|
||||
|
@ -54,6 +72,8 @@ widget moves, you can bind your own callback function like this::
|
|||
wid = Widget()
|
||||
wid.bind(pos=callback_pos)
|
||||
|
||||
Read more about the :doc:`/api-kivy.properties`.
|
||||
|
||||
'''
|
||||
|
||||
__all__ = ('Widget', 'WidgetException')
|
||||
|
@ -121,7 +141,7 @@ class Widget(EventDispatcher):
|
|||
|
||||
# Create the default canvas if not exist
|
||||
if self.canvas is None:
|
||||
self.canvas = Canvas()
|
||||
self.canvas = Canvas(opacity=self.opacity)
|
||||
|
||||
# Apply all the styles
|
||||
if '__no_builder' not in kwargs:
|
||||
|
@ -566,7 +586,9 @@ class Widget(EventDispatcher):
|
|||
'''
|
||||
|
||||
def on_opacity(self, instance, value):
|
||||
self.canvas.opacity = value
|
||||
canvas = self.canvas
|
||||
if canvas is not None:
|
||||
canvas.opacity = value
|
||||
|
||||
canvas = None
|
||||
'''Canvas of the widget.
|
||||
|
|
Loading…
Reference in New Issue