Updated build system for Kivy 2.2.1
This commit is contained in:
parent
23e6b6e0c6
commit
67a8f61af8
|
@ -1,3 +1,4 @@
|
||||||
|
sbapp/kivymd_working
|
||||||
sbapp/.buildozer
|
sbapp/.buildozer
|
||||||
sbapp/requirements.txt
|
sbapp/requirements.txt
|
||||||
sbapp/venv
|
sbapp/venv
|
||||||
|
|
|
@ -30,12 +30,14 @@ patchsdl:
|
||||||
cp patches/PythonService.java .buildozer/android/platform/build-arm64-v8a_armeabi-v7a/dists/sideband/src/main/java/org/kivy/android/PythonService.java
|
cp patches/PythonService.java .buildozer/android/platform/build-arm64-v8a_armeabi-v7a/dists/sideband/src/main/java/org/kivy/android/PythonService.java
|
||||||
|
|
||||||
injectxml:
|
injectxml:
|
||||||
|
# mkdir /home/markqvist/.local/lib/python3.11/site-packages/pythonforandroid/bootstraps/sdl2/build/src/main/xml
|
||||||
# Inject XML on arm64-v8a
|
# Inject XML on arm64-v8a
|
||||||
mkdir -p .buildozer/android/platform/build-arm64-v8a_armeabi-v7a/dists/sideband/src/main/res/xml
|
mkdir -p .buildozer/android/platform/build-arm64-v8a_armeabi-v7a/dists/sideband/src/main/res/xml
|
||||||
mkdir -p .buildozer/android/platform/build-arm64-v8a_armeabi-v7a/dists/sideband/templates
|
mkdir -p .buildozer/android/platform/build-arm64-v8a_armeabi-v7a/dists/sideband/templates
|
||||||
cp patches/device_filter.xml .buildozer/android/platform/build-arm64-v8a_armeabi-v7a/dists/sideband/src/main/res/xml/
|
cp patches/device_filter.xml .buildozer/android/platform/build-arm64-v8a_armeabi-v7a/dists/sideband/src/main/res/xml/
|
||||||
cp patches/file_paths.xml .buildozer/android/platform/build-arm64-v8a_armeabi-v7a/dists/sideband/src/main/res/xml/
|
cp patches/file_paths.xml .buildozer/android/platform/build-arm64-v8a_armeabi-v7a/dists/sideband/src/main/res/xml/
|
||||||
cp patches/AndroidManifest.tmpl.xml .buildozer/android/platform/build-arm64-v8a_armeabi-v7a/dists/sideband/templates/
|
cp patches/AndroidManifest.tmpl.xml .buildozer/android/platform/build-arm64-v8a_armeabi-v7a/dists/sideband/templates/
|
||||||
|
cp patches/p4a_build.py .buildozer/android/platform/build-arm64-v8a_armeabi-v7a/dists/sideband/build.py
|
||||||
|
|
||||||
debug:
|
debug:
|
||||||
buildozer android debug
|
buildozer android debug
|
||||||
|
|
|
@ -12,9 +12,10 @@ version.regex = __version__ = ['"](.*)['"]
|
||||||
version.filename = %(source.dir)s/main.py
|
version.filename = %(source.dir)s/main.py
|
||||||
android.numeric_version = 20230204
|
android.numeric_version = 20230204
|
||||||
|
|
||||||
requirements = python3==3.9.5,hostpython3==3.9.5,cryptography,cffi,pycparser,kivy==2.1.0,pygments,sdl2,sdl2_ttf==2.0.15,pillow,qrcode==7.3.1,netifaces,libbz2,pydenticon,usb4a,usbserial4a
|
#requirements = python3==3.9.5,hostpython3==3.9.5,cryptography,cffi,pycparser,kivy==2.2.1,pygments,sdl2,sdl2_ttf==2.0.15,pillow,qrcode==7.3.1,netifaces,libbz2,pydenticon,usb4a,usbserial4a
|
||||||
|
requirements = kivy==2.2.1,libbz2,pillow,qrcode==7.3.1,usb4a,usbserial4a
|
||||||
|
|
||||||
p4a.local_recipes = ../Others/python-for-android/pythonforandroid/recipes
|
p4a.local_recipes = ../Others/python-for-android/pythonforandroid/recipes
|
||||||
requirements.source.kivymd = ../../Others/KivyMD-master
|
|
||||||
|
|
||||||
icon.filename = %(source.dir)s/assets/icon.png
|
icon.filename = %(source.dir)s/assets/icon.png
|
||||||
presplash.filename = %(source.dir)s/assets/presplash_small.png
|
presplash.filename = %(source.dir)s/assets/presplash_small.png
|
||||||
|
@ -27,7 +28,7 @@ fullscreen = 0
|
||||||
android.permissions = INTERNET,POST_NOTIFICATIONS,WAKE_LOCK,FOREGROUND_SERVICE,CHANGE_WIFI_MULTICAST_STATE,BLUETOOTH_CONNECT
|
android.permissions = INTERNET,POST_NOTIFICATIONS,WAKE_LOCK,FOREGROUND_SERVICE,CHANGE_WIFI_MULTICAST_STATE,BLUETOOTH_CONNECT
|
||||||
android.api = 30
|
android.api = 30
|
||||||
android.minapi = 24
|
android.minapi = 24
|
||||||
android.ndk = 23b
|
android.ndk = 25b
|
||||||
android.skip_update = False
|
android.skip_update = False
|
||||||
android.accept_sdk_license = True
|
android.accept_sdk_license = True
|
||||||
android.release_artifact = apk
|
android.release_artifact = apk
|
||||||
|
|
|
@ -26,11 +26,12 @@ import os
|
||||||
import kivy
|
import kivy
|
||||||
from kivy.logger import Logger
|
from kivy.logger import Logger
|
||||||
|
|
||||||
__version__ = "1.1.0.dev0"
|
__version__ = "1.2.0.dev0"
|
||||||
"""KivyMD version."""
|
"""KivyMD version."""
|
||||||
|
|
||||||
release = False
|
release = False
|
||||||
kivy.require("2.0.0")
|
if "READTHEDOCS" not in os.environ:
|
||||||
|
kivy.require("2.2.0")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from kivymd._version import __date__, __hash__, __short_hash__
|
from kivymd._version import __date__, __hash__, __short_hash__
|
||||||
|
@ -49,9 +50,6 @@ images_path = os.path.join(path, f"images{os.sep}")
|
||||||
uix_path = os.path.join(path, "uix")
|
uix_path = os.path.join(path, "uix")
|
||||||
"""Path to uix directory."""
|
"""Path to uix directory."""
|
||||||
|
|
||||||
glsl_path = os.path.join(path, "data", "glsl")
|
|
||||||
"""Path to glsl directory."""
|
|
||||||
|
|
||||||
_log_message = (
|
_log_message = (
|
||||||
"KivyMD:"
|
"KivyMD:"
|
||||||
+ (" Release" if release else "")
|
+ (" Release" if release else "")
|
||||||
|
|
|
@ -54,7 +54,7 @@ from kivymd.theming import ThemeManager
|
||||||
class FpsMonitoring:
|
class FpsMonitoring:
|
||||||
"""Implements a monitor to display the current FPS in the toolbar."""
|
"""Implements a monitor to display the current FPS in the toolbar."""
|
||||||
|
|
||||||
def fps_monitor_start(self) -> None:
|
def fps_monitor_start(self, anchor: str = "top") -> None:
|
||||||
"""Adds a monitor to the main application window."""
|
"""Adds a monitor to the main application window."""
|
||||||
|
|
||||||
def add_monitor(*args):
|
def add_monitor(*args):
|
||||||
|
@ -62,7 +62,7 @@ class FpsMonitoring:
|
||||||
|
|
||||||
from kivymd.utils.fpsmonitor import FpsMonitor
|
from kivymd.utils.fpsmonitor import FpsMonitor
|
||||||
|
|
||||||
monitor = FpsMonitor()
|
monitor = FpsMonitor(anchor=anchor)
|
||||||
monitor.start()
|
monitor.start()
|
||||||
Window.add_widget(monitor)
|
Window.add_widget(monitor)
|
||||||
|
|
||||||
|
|
|
@ -1,51 +0,0 @@
|
||||||
/*
|
|
||||||
The shader code has been refactored for the KivyMD library.
|
|
||||||
You can find the original code of this shaders at the links:
|
|
||||||
|
|
||||||
https://www.shadertoy.com/view/WtdSDs
|
|
||||||
https://www.shadertoy.com/view/fsdyzB
|
|
||||||
|
|
||||||
Additional thanks to iq for optimizing conditional block for individual
|
|
||||||
corner radius:
|
|
||||||
https://iquilezles.org/articles/distfunctions
|
|
||||||
*/
|
|
||||||
|
|
||||||
// For lower opengl version
|
|
||||||
|
|
||||||
float custom_smoothstep(float a, float b, float x) {
|
|
||||||
float t = clamp((x - a) / (b - a), 0.0, 1.0);
|
|
||||||
return t * t * (3.0 - 2.0 * t);
|
|
||||||
}
|
|
||||||
|
|
||||||
float roundedBoxSDF(vec2 centerPosition, vec2 size, vec4 radius) {
|
|
||||||
radius.xy = (centerPosition.x > 0.0) ? radius.xy : radius.zw;
|
|
||||||
radius.x = (centerPosition.y > 0.0) ? radius.x : radius.y;
|
|
||||||
|
|
||||||
vec2 q = abs(centerPosition) - (size - shadow_softness) + radius.x;
|
|
||||||
return min(max(q.x, q.y), 0.0) + length(max(q, 0.0)) - radius.x;
|
|
||||||
}
|
|
||||||
|
|
||||||
void mainImage(out vec4 fragColor, in vec2 fragCoord) {
|
|
||||||
// Smooth the result (free antialiasing).
|
|
||||||
float edge0 = 0.0;
|
|
||||||
float smoothedAlpha = 1.0 - custom_smoothstep(0.0, edge0, 1.0);
|
|
||||||
// Get the resultant shape.
|
|
||||||
vec4 quadColor = mix(
|
|
||||||
vec4(
|
|
||||||
shadow_color[0],
|
|
||||||
shadow_color[1],
|
|
||||||
shadow_color[2],
|
|
||||||
0.0
|
|
||||||
),
|
|
||||||
shadow_color,
|
|
||||||
smoothedAlpha
|
|
||||||
);
|
|
||||||
// Apply a drop shadow effect.
|
|
||||||
float shadowDistance = roundedBoxSDF(
|
|
||||||
fragCoord.xy - mouse.xy - (size / 2.0), size / 2.0, shadow_radius
|
|
||||||
);
|
|
||||||
float shadowAlpha = 1.0 - custom_smoothstep(
|
|
||||||
-shadow_softness, shadow_softness, shadowDistance
|
|
||||||
);
|
|
||||||
fragColor = mix(quadColor, shadow_color, shadowAlpha - smoothedAlpha);
|
|
||||||
}
|
|
|
@ -1,10 +0,0 @@
|
||||||
#ifdef GL_FRAGMENT_PRECISION_HIGH
|
|
||||||
precision highp float;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
uniform vec4 resolution;
|
|
||||||
uniform vec4 mouse;
|
|
||||||
uniform vec2 size;
|
|
||||||
uniform vec4 shadow_radius;
|
|
||||||
uniform float shadow_softness;
|
|
||||||
uniform vec4 shadow_color;
|
|
|
@ -1,10 +0,0 @@
|
||||||
vec2 gfc(in vec4 fc) {
|
|
||||||
vec2 canvas_pos = resolution.zw;
|
|
||||||
vec2 uv = fc.xy;
|
|
||||||
uv.y -= canvas_pos.y;
|
|
||||||
return uv;
|
|
||||||
}
|
|
||||||
|
|
||||||
void main(void) {
|
|
||||||
mainImage(gl_FragColor, gfc(gl_FragCoord));
|
|
||||||
}
|
|
|
@ -164,6 +164,9 @@ class FadingEdgeEffect(ThemableBehavior):
|
||||||
index,
|
index,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
self.update_canvas(
|
||||||
|
self, self.size, rectangle_top, rectangle_bottom, i
|
||||||
|
)
|
||||||
|
|
||||||
def update_canvas(
|
def update_canvas(
|
||||||
self,
|
self,
|
||||||
|
|
|
@ -5,6 +5,8 @@ Register KivyMD widgets to use without import.
|
||||||
from kivy.factory import Factory
|
from kivy.factory import Factory
|
||||||
|
|
||||||
register = Factory.register
|
register = Factory.register
|
||||||
|
register("MDSegmentedButton", module="kivymd.uix.segmentedbutton")
|
||||||
|
register("MDSegmentedButtonItem", module="kivymd.uix.segmentedbutton")
|
||||||
register("MDScrollView", module="kivymd.uix.scrollview")
|
register("MDScrollView", module="kivymd.uix.scrollview")
|
||||||
register("MDRecycleView", module="kivymd.uix.recycleview")
|
register("MDRecycleView", module="kivymd.uix.recycleview")
|
||||||
register("MDResponsiveLayout", module="kivymd.uix.responsivelayout")
|
register("MDResponsiveLayout", module="kivymd.uix.responsivelayout")
|
||||||
|
@ -37,6 +39,7 @@ register("FitImage", module="kivymd.uix.fitimage")
|
||||||
register("MDBackdrop", module="kivymd.uix.backdrop")
|
register("MDBackdrop", module="kivymd.uix.backdrop")
|
||||||
register("MDBanner", module="kivymd.uix.banner")
|
register("MDBanner", module="kivymd.uix.banner")
|
||||||
register("MDTooltip", module="kivymd.uix.tooltip")
|
register("MDTooltip", module="kivymd.uix.tooltip")
|
||||||
|
register("MDBottomSheet", module="kivymd.uix.bottomsheet")
|
||||||
register("MDBottomNavigation", module="kivymd.uix.bottomnavigation")
|
register("MDBottomNavigation", module="kivymd.uix.bottomnavigation")
|
||||||
register("MDBottomNavigationItem", module="kivymd.uix.bottomnavigation")
|
register("MDBottomNavigationItem", module="kivymd.uix.bottomnavigation")
|
||||||
register("MDToggleButton", module="kivymd.uix.behaviors.toggle_behavior")
|
register("MDToggleButton", module="kivymd.uix.behaviors.toggle_behavior")
|
||||||
|
|
Binary file not shown.
|
@ -12,7 +12,7 @@ Themes/Icon Definitions
|
||||||
List of icons from materialdesignicons.com. These expanded material design
|
List of icons from materialdesignicons.com. These expanded material design
|
||||||
icons are maintained by Austin Andrews (Templarian on Github).
|
icons are maintained by Austin Andrews (Templarian on Github).
|
||||||
|
|
||||||
LAST UPDATED: Version 7.0.96
|
LAST UPDATED: Version 7.1.96
|
||||||
|
|
||||||
To preview the icons and their names, you can use the following application:
|
To preview the icons and their names, you can use the following application:
|
||||||
----------------------------------------------------------------------------
|
----------------------------------------------------------------------------
|
||||||
|
@ -242,6 +242,8 @@ md_icons = {
|
||||||
"account-switch-outline": "\U000F04CB",
|
"account-switch-outline": "\U000F04CB",
|
||||||
"account-sync": "\U000F191B",
|
"account-sync": "\U000F191B",
|
||||||
"account-sync-outline": "\U000F191C",
|
"account-sync-outline": "\U000F191C",
|
||||||
|
"account-tag": "\U000F1C1B",
|
||||||
|
"account-tag-outline": "\U000F1C1C",
|
||||||
"account-tie": "\U000F0CE3",
|
"account-tie": "\U000F0CE3",
|
||||||
"account-tie-hat": "\U000F1898",
|
"account-tie-hat": "\U000F1898",
|
||||||
"account-tie-hat-outline": "\U000F1899",
|
"account-tie-hat-outline": "\U000F1899",
|
||||||
|
@ -774,6 +776,7 @@ md_icons = {
|
||||||
"audio-video": "\U000F093D",
|
"audio-video": "\U000F093D",
|
||||||
"audio-video-off": "\U000F11B6",
|
"audio-video-off": "\U000F11B6",
|
||||||
"augmented-reality": "\U000F0850",
|
"augmented-reality": "\U000F0850",
|
||||||
|
"aurora": "\U000F1BB9",
|
||||||
"auto-download": "\U000F137E",
|
"auto-download": "\U000F137E",
|
||||||
"auto-fix": "\U000F0068",
|
"auto-fix": "\U000F0068",
|
||||||
"auto-upload": "\U000F0069",
|
"auto-upload": "\U000F0069",
|
||||||
|
@ -852,6 +855,8 @@ md_icons = {
|
||||||
"bandage": "\U000F0DAF",
|
"bandage": "\U000F0DAF",
|
||||||
"bank": "\U000F0070",
|
"bank": "\U000F0070",
|
||||||
"bank-check": "\U000F1655",
|
"bank-check": "\U000F1655",
|
||||||
|
"bank-circle": "\U000F1C03",
|
||||||
|
"bank-circle-outline": "\U000F1C04",
|
||||||
"bank-minus": "\U000F0DB0",
|
"bank-minus": "\U000F0DB0",
|
||||||
"bank-off": "\U000F1656",
|
"bank-off": "\U000F1656",
|
||||||
"bank-off-outline": "\U000F1657",
|
"bank-off-outline": "\U000F1657",
|
||||||
|
@ -1443,6 +1448,8 @@ md_icons = {
|
||||||
"camera-image": "\U000F08CC",
|
"camera-image": "\U000F08CC",
|
||||||
"camera-iris": "\U000F0104",
|
"camera-iris": "\U000F0104",
|
||||||
"camera-lock": "\U000F1A14",
|
"camera-lock": "\U000F1A14",
|
||||||
|
"camera-lock-open": "\U000F1C0D",
|
||||||
|
"camera-lock-open-outline": "\U000F1C0E",
|
||||||
"camera-lock-outline": "\U000F1A15",
|
"camera-lock-outline": "\U000F1A15",
|
||||||
"camera-marker": "\U000F19A7",
|
"camera-marker": "\U000F19A7",
|
||||||
"camera-marker-outline": "\U000F19A8",
|
"camera-marker-outline": "\U000F19A8",
|
||||||
|
@ -1707,6 +1714,7 @@ md_icons = {
|
||||||
"chart-multiline": "\U000F08D4",
|
"chart-multiline": "\U000F08D4",
|
||||||
"chart-multiple": "\U000F1213",
|
"chart-multiple": "\U000F1213",
|
||||||
"chart-pie": "\U000F012B",
|
"chart-pie": "\U000F012B",
|
||||||
|
"chart-pie-outline": "\U000F1BDF",
|
||||||
"chart-ppf": "\U000F1380",
|
"chart-ppf": "\U000F1380",
|
||||||
"chart-sankey": "\U000F11DF",
|
"chart-sankey": "\U000F11DF",
|
||||||
"chart-sankey-variant": "\U000F11E0",
|
"chart-sankey-variant": "\U000F11E0",
|
||||||
|
@ -1978,22 +1986,53 @@ md_icons = {
|
||||||
"closed-caption-outline": "\U000F0DBD",
|
"closed-caption-outline": "\U000F0DBD",
|
||||||
"cloud": "\U000F015F",
|
"cloud": "\U000F015F",
|
||||||
"cloud-alert": "\U000F09E0",
|
"cloud-alert": "\U000F09E0",
|
||||||
|
"cloud-alert-outline": "\U000F1BE0",
|
||||||
|
"cloud-arrow-down": "\U000F1BE1",
|
||||||
|
"cloud-arrow-down-outline": "\U000F1BE2",
|
||||||
|
"cloud-arrow-left": "\U000F1BE3",
|
||||||
|
"cloud-arrow-left-outline": "\U000F1BE4",
|
||||||
|
"cloud-arrow-right": "\U000F1BE5",
|
||||||
|
"cloud-arrow-right-outline": "\U000F1BE6",
|
||||||
|
"cloud-arrow-up": "\U000F1BE7",
|
||||||
|
"cloud-arrow-up-outline": "\U000F1BE8",
|
||||||
"cloud-braces": "\U000F07B5",
|
"cloud-braces": "\U000F07B5",
|
||||||
"cloud-check": "\U000F0160",
|
"cloud-cancel": "\U000F1BE9",
|
||||||
"cloud-check-outline": "\U000F12CC",
|
"cloud-cancel-outline": "\U000F1BEA",
|
||||||
|
"cloud-check": "\U000F1BEB",
|
||||||
|
"cloud-check-outline": "\U000F1BEC",
|
||||||
|
"cloud-check-variant": "\U000F0160",
|
||||||
|
"cloud-check-variant-outline": "\U000F12CC",
|
||||||
"cloud-circle": "\U000F0161",
|
"cloud-circle": "\U000F0161",
|
||||||
|
"cloud-circle-outline": "\U000F1BED",
|
||||||
|
"cloud-clock": "\U000F1BEE",
|
||||||
|
"cloud-clock-outline": "\U000F1BEF",
|
||||||
|
"cloud-cog": "\U000F1BF0",
|
||||||
|
"cloud-cog-outline": "\U000F1BF1",
|
||||||
"cloud-download": "\U000F0162",
|
"cloud-download": "\U000F0162",
|
||||||
"cloud-download-outline": "\U000F0B7D",
|
"cloud-download-outline": "\U000F0B7D",
|
||||||
"cloud-lock": "\U000F11F1",
|
"cloud-lock": "\U000F11F1",
|
||||||
|
"cloud-lock-open": "\U000F1BF2",
|
||||||
|
"cloud-lock-open-outline": "\U000F1BF3",
|
||||||
"cloud-lock-outline": "\U000F11F2",
|
"cloud-lock-outline": "\U000F11F2",
|
||||||
|
"cloud-minus": "\U000F1BF4",
|
||||||
|
"cloud-minus-outline": "\U000F1BF5",
|
||||||
|
"cloud-off": "\U000F1BF6",
|
||||||
"cloud-off-outline": "\U000F0164",
|
"cloud-off-outline": "\U000F0164",
|
||||||
"cloud-outline": "\U000F0163",
|
"cloud-outline": "\U000F0163",
|
||||||
"cloud-percent": "\U000F1A35",
|
"cloud-percent": "\U000F1A35",
|
||||||
"cloud-percent-outline": "\U000F1A36",
|
"cloud-percent-outline": "\U000F1A36",
|
||||||
|
"cloud-plus": "\U000F1BF7",
|
||||||
|
"cloud-plus-outline": "\U000F1BF8",
|
||||||
"cloud-print": "\U000F0165",
|
"cloud-print": "\U000F0165",
|
||||||
"cloud-print-outline": "\U000F0166",
|
"cloud-print-outline": "\U000F0166",
|
||||||
"cloud-question": "\U000F0A39",
|
"cloud-question": "\U000F0A39",
|
||||||
"cloud-refresh": "\U000F052A",
|
"cloud-question-outline": "\U000F1BF9",
|
||||||
|
"cloud-refresh": "\U000F1BFA",
|
||||||
|
"cloud-refresh-outline": "\U000F1BFB",
|
||||||
|
"cloud-refresh-variant": "\U000F052A",
|
||||||
|
"cloud-refresh-variant-outline": "\U000F1BFC",
|
||||||
|
"cloud-remove": "\U000F1BFD",
|
||||||
|
"cloud-remove-outline": "\U000F1BFE",
|
||||||
"cloud-search": "\U000F0956",
|
"cloud-search": "\U000F0956",
|
||||||
"cloud-search-outline": "\U000F0957",
|
"cloud-search-outline": "\U000F0957",
|
||||||
"cloud-sync": "\U000F063F",
|
"cloud-sync": "\U000F063F",
|
||||||
|
@ -2311,6 +2350,7 @@ md_icons = {
|
||||||
"currency-rub": "\U000F01B1",
|
"currency-rub": "\U000F01B1",
|
||||||
"currency-rupee": "\U000F1976",
|
"currency-rupee": "\U000F1976",
|
||||||
"currency-sign": "\U000F07BE",
|
"currency-sign": "\U000F07BE",
|
||||||
|
"currency-thb": "\U000F1C05",
|
||||||
"currency-try": "\U000F01B2",
|
"currency-try": "\U000F01B2",
|
||||||
"currency-twd": "\U000F07BF",
|
"currency-twd": "\U000F07BF",
|
||||||
"currency-uah": "\U000F1B9B",
|
"currency-uah": "\U000F1B9B",
|
||||||
|
@ -2750,6 +2790,10 @@ md_icons = {
|
||||||
"eye-check-outline": "\U000F0D05",
|
"eye-check-outline": "\U000F0D05",
|
||||||
"eye-circle": "\U000F0B94",
|
"eye-circle": "\U000F0B94",
|
||||||
"eye-circle-outline": "\U000F0B95",
|
"eye-circle-outline": "\U000F0B95",
|
||||||
|
"eye-lock": "\U000F1C06",
|
||||||
|
"eye-lock-open": "\U000F1C07",
|
||||||
|
"eye-lock-open-outline": "\U000F1C08",
|
||||||
|
"eye-lock-outline": "\U000F1C09",
|
||||||
"eye-minus": "\U000F1026",
|
"eye-minus": "\U000F1026",
|
||||||
"eye-minus-outline": "\U000F1027",
|
"eye-minus-outline": "\U000F1027",
|
||||||
"eye-off": "\U000F0209",
|
"eye-off": "\U000F0209",
|
||||||
|
@ -2858,6 +2902,8 @@ md_icons = {
|
||||||
"file-document": "\U000F0219",
|
"file-document": "\U000F0219",
|
||||||
"file-document-alert": "\U000F1A97",
|
"file-document-alert": "\U000F1A97",
|
||||||
"file-document-alert-outline": "\U000F1A98",
|
"file-document-alert-outline": "\U000F1A98",
|
||||||
|
"file-document-arrow-right": "\U000F1C0F",
|
||||||
|
"file-document-arrow-right-outline": "\U000F1C10",
|
||||||
"file-document-check": "\U000F1A99",
|
"file-document-check": "\U000F1A99",
|
||||||
"file-document-check-outline": "\U000F1A9A",
|
"file-document-check-outline": "\U000F1A9A",
|
||||||
"file-document-edit": "\U000F0DC8",
|
"file-document-edit": "\U000F0DC8",
|
||||||
|
@ -3731,6 +3777,9 @@ md_icons = {
|
||||||
"helicopter": "\U000F0AC2",
|
"helicopter": "\U000F0AC2",
|
||||||
"help": "\U000F02D6",
|
"help": "\U000F02D6",
|
||||||
"help-box": "\U000F078B",
|
"help-box": "\U000F078B",
|
||||||
|
"help-box-multiple": "\U000F1C0A",
|
||||||
|
"help-box-multiple-outline": "\U000F1C0B",
|
||||||
|
"help-box-outline": "\U000F1C0C",
|
||||||
"help-circle": "\U000F02D7",
|
"help-circle": "\U000F02D7",
|
||||||
"help-circle-outline": "\U000F0625",
|
"help-circle-outline": "\U000F0625",
|
||||||
"help-network": "\U000F06F5",
|
"help-network": "\U000F06F5",
|
||||||
|
@ -3907,6 +3956,7 @@ md_icons = {
|
||||||
"image-filter-center-focus-strong-outline": "\U000F0F00",
|
"image-filter-center-focus-strong-outline": "\U000F0F00",
|
||||||
"image-filter-center-focus-weak": "\U000F02F2",
|
"image-filter-center-focus-weak": "\U000F02F2",
|
||||||
"image-filter-drama": "\U000F02F3",
|
"image-filter-drama": "\U000F02F3",
|
||||||
|
"image-filter-drama-outline": "\U000F1BFF",
|
||||||
"image-filter-frames": "\U000F02F4",
|
"image-filter-frames": "\U000F02F4",
|
||||||
"image-filter-hdr": "\U000F02F5",
|
"image-filter-hdr": "\U000F02F5",
|
||||||
"image-filter-none": "\U000F02F6",
|
"image-filter-none": "\U000F02F6",
|
||||||
|
@ -4021,6 +4071,7 @@ md_icons = {
|
||||||
"keyboard-backspace": "\U000F030D",
|
"keyboard-backspace": "\U000F030D",
|
||||||
"keyboard-caps": "\U000F030E",
|
"keyboard-caps": "\U000F030E",
|
||||||
"keyboard-close": "\U000F030F",
|
"keyboard-close": "\U000F030F",
|
||||||
|
"keyboard-close-outline": "\U000F1C00",
|
||||||
"keyboard-esc": "\U000F12B7",
|
"keyboard-esc": "\U000F12B7",
|
||||||
"keyboard-f1": "\U000F12AB",
|
"keyboard-f1": "\U000F12AB",
|
||||||
"keyboard-f10": "\U000F12B4",
|
"keyboard-f10": "\U000F12B4",
|
||||||
|
@ -4263,6 +4314,12 @@ md_icons = {
|
||||||
"lock-open-variant-outline": "\U000F0FC7",
|
"lock-open-variant-outline": "\U000F0FC7",
|
||||||
"lock-outline": "\U000F0341",
|
"lock-outline": "\U000F0341",
|
||||||
"lock-pattern": "\U000F06EA",
|
"lock-pattern": "\U000F06EA",
|
||||||
|
"lock-percent": "\U000F1C12",
|
||||||
|
"lock-percent-open": "\U000F1C13",
|
||||||
|
"lock-percent-open-outline": "\U000F1C14",
|
||||||
|
"lock-percent-open-variant": "\U000F1C15",
|
||||||
|
"lock-percent-open-variant-outline": "\U000F1C16",
|
||||||
|
"lock-percent-outline": "\U000F1C17",
|
||||||
"lock-plus": "\U000F05FB",
|
"lock-plus": "\U000F05FB",
|
||||||
"lock-plus-outline": "\U000F16B2",
|
"lock-plus-outline": "\U000F16B2",
|
||||||
"lock-question": "\U000F08EF",
|
"lock-question": "\U000F08EF",
|
||||||
|
@ -5072,6 +5129,7 @@ md_icons = {
|
||||||
"pencil-remove": "\U000F0DED",
|
"pencil-remove": "\U000F0DED",
|
||||||
"pencil-remove-outline": "\U000F0DEE",
|
"pencil-remove-outline": "\U000F0DEE",
|
||||||
"pencil-ruler": "\U000F1353",
|
"pencil-ruler": "\U000F1353",
|
||||||
|
"pencil-ruler-outline": "\U000F1C11",
|
||||||
"penguin": "\U000F0EC0",
|
"penguin": "\U000F0EC0",
|
||||||
"pentagon": "\U000F0701",
|
"pentagon": "\U000F0701",
|
||||||
"pentagon-outline": "\U000F0700",
|
"pentagon-outline": "\U000F0700",
|
||||||
|
@ -5309,6 +5367,41 @@ md_icons = {
|
||||||
"printer-off-outline": "\U000F1785",
|
"printer-off-outline": "\U000F1785",
|
||||||
"printer-outline": "\U000F1786",
|
"printer-outline": "\U000F1786",
|
||||||
"printer-pos": "\U000F1057",
|
"printer-pos": "\U000F1057",
|
||||||
|
"printer-pos-alert": "\U000F1BBC",
|
||||||
|
"printer-pos-alert-outline": "\U000F1BBD",
|
||||||
|
"printer-pos-cancel": "\U000F1BBE",
|
||||||
|
"printer-pos-cancel-outline": "\U000F1BBF",
|
||||||
|
"printer-pos-check": "\U000F1BC0",
|
||||||
|
"printer-pos-check-outline": "\U000F1BC1",
|
||||||
|
"printer-pos-cog": "\U000F1BC2",
|
||||||
|
"printer-pos-cog-outline": "\U000F1BC3",
|
||||||
|
"printer-pos-edit": "\U000F1BC4",
|
||||||
|
"printer-pos-edit-outline": "\U000F1BC5",
|
||||||
|
"printer-pos-minus": "\U000F1BC6",
|
||||||
|
"printer-pos-minus-outline": "\U000F1BC7",
|
||||||
|
"printer-pos-network": "\U000F1BC8",
|
||||||
|
"printer-pos-network-outline": "\U000F1BC9",
|
||||||
|
"printer-pos-off": "\U000F1BCA",
|
||||||
|
"printer-pos-off-outline": "\U000F1BCB",
|
||||||
|
"printer-pos-outline": "\U000F1BCC",
|
||||||
|
"printer-pos-pause": "\U000F1BCD",
|
||||||
|
"printer-pos-pause-outline": "\U000F1BCE",
|
||||||
|
"printer-pos-play": "\U000F1BCF",
|
||||||
|
"printer-pos-play-outline": "\U000F1BD0",
|
||||||
|
"printer-pos-plus": "\U000F1BD1",
|
||||||
|
"printer-pos-plus-outline": "\U000F1BD2",
|
||||||
|
"printer-pos-refresh": "\U000F1BD3",
|
||||||
|
"printer-pos-refresh-outline": "\U000F1BD4",
|
||||||
|
"printer-pos-remove": "\U000F1BD5",
|
||||||
|
"printer-pos-remove-outline": "\U000F1BD6",
|
||||||
|
"printer-pos-star": "\U000F1BD7",
|
||||||
|
"printer-pos-star-outline": "\U000F1BD8",
|
||||||
|
"printer-pos-stop": "\U000F1BD9",
|
||||||
|
"printer-pos-stop-outline": "\U000F1BDA",
|
||||||
|
"printer-pos-sync": "\U000F1BDB",
|
||||||
|
"printer-pos-sync-outline": "\U000F1BDC",
|
||||||
|
"printer-pos-wrench": "\U000F1BDD",
|
||||||
|
"printer-pos-wrench-outline": "\U000F1BDE",
|
||||||
"printer-search": "\U000F1457",
|
"printer-search": "\U000F1457",
|
||||||
"printer-settings": "\U000F0707",
|
"printer-settings": "\U000F0707",
|
||||||
"printer-wireless": "\U000F0A0B",
|
"printer-wireless": "\U000F0A0B",
|
||||||
|
@ -5497,7 +5590,10 @@ md_icons = {
|
||||||
"remote-off": "\U000F0EC4",
|
"remote-off": "\U000F0EC4",
|
||||||
"remote-tv": "\U000F0EC5",
|
"remote-tv": "\U000F0EC5",
|
||||||
"remote-tv-off": "\U000F0EC6",
|
"remote-tv-off": "\U000F0EC6",
|
||||||
|
"rename": "\U000F1C18",
|
||||||
"rename-box": "\U000F0455",
|
"rename-box": "\U000F0455",
|
||||||
|
"rename-box-outline": "\U000F1C19",
|
||||||
|
"rename-outline": "\U000F1C1A",
|
||||||
"reorder-horizontal": "\U000F0688",
|
"reorder-horizontal": "\U000F0688",
|
||||||
"reorder-vertical": "\U000F0689",
|
"reorder-vertical": "\U000F0689",
|
||||||
"repeat": "\U000F0456",
|
"repeat": "\U000F0456",
|
||||||
|
@ -5566,8 +5662,10 @@ md_icons = {
|
||||||
"robot-outline": "\U000F167A",
|
"robot-outline": "\U000F167A",
|
||||||
"robot-vacuum": "\U000F070D",
|
"robot-vacuum": "\U000F070D",
|
||||||
"robot-vacuum-alert": "\U000F1B5D",
|
"robot-vacuum-alert": "\U000F1B5D",
|
||||||
|
"robot-vacuum-off": "\U000F1C01",
|
||||||
"robot-vacuum-variant": "\U000F0908",
|
"robot-vacuum-variant": "\U000F0908",
|
||||||
"robot-vacuum-variant-alert": "\U000F1B5E",
|
"robot-vacuum-variant-alert": "\U000F1B5E",
|
||||||
|
"robot-vacuum-variant-off": "\U000F1C02",
|
||||||
"rocket": "\U000F0463",
|
"rocket": "\U000F0463",
|
||||||
"rocket-launch": "\U000F14DE",
|
"rocket-launch": "\U000F14DE",
|
||||||
"rocket-launch-outline": "\U000F14DF",
|
"rocket-launch-outline": "\U000F14DF",
|
||||||
|
@ -6605,6 +6703,8 @@ md_icons = {
|
||||||
"tooltip-outline": "\U000F0526",
|
"tooltip-outline": "\U000F0526",
|
||||||
"tooltip-plus": "\U000F0BD6",
|
"tooltip-plus": "\U000F0BD6",
|
||||||
"tooltip-plus-outline": "\U000F0527",
|
"tooltip-plus-outline": "\U000F0527",
|
||||||
|
"tooltip-question": "\U000F1BBA",
|
||||||
|
"tooltip-question-outline": "\U000F1BBB",
|
||||||
"tooltip-remove": "\U000F1560",
|
"tooltip-remove": "\U000F1560",
|
||||||
"tooltip-remove-outline": "\U000F1561",
|
"tooltip-remove-outline": "\U000F1561",
|
||||||
"tooltip-text": "\U000F0528",
|
"tooltip-text": "\U000F0528",
|
||||||
|
@ -7219,3 +7319,94 @@ md_icons = {
|
||||||
"zodiac-virgo": "\U000F0A88",
|
"zodiac-virgo": "\U000F0A88",
|
||||||
"blank": " ",
|
"blank": " ",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
from kivy.lang import Builder
|
||||||
|
from kivy.properties import StringProperty
|
||||||
|
from kivy.uix.screenmanager import Screen
|
||||||
|
|
||||||
|
from kivymd.app import MDApp
|
||||||
|
from kivymd.uix.list import OneLineIconListItem
|
||||||
|
|
||||||
|
Builder.load_string(
|
||||||
|
"""
|
||||||
|
#:import images_path kivymd.images_path
|
||||||
|
|
||||||
|
|
||||||
|
<CustomOneLineIconListItem>
|
||||||
|
|
||||||
|
IconLeftWidget:
|
||||||
|
icon: root.icon
|
||||||
|
|
||||||
|
|
||||||
|
<PreviousMDIcons>
|
||||||
|
|
||||||
|
MDBoxLayout:
|
||||||
|
orientation: 'vertical'
|
||||||
|
spacing: dp(10)
|
||||||
|
padding: dp(20)
|
||||||
|
|
||||||
|
MDBoxLayout:
|
||||||
|
adaptive_height: True
|
||||||
|
|
||||||
|
MDIconButton:
|
||||||
|
icon: 'magnify'
|
||||||
|
|
||||||
|
MDTextField:
|
||||||
|
id: search_field
|
||||||
|
hint_text: 'Search icon'
|
||||||
|
on_text: root.set_list_md_icons(self.text, True)
|
||||||
|
|
||||||
|
RecycleView:
|
||||||
|
id: rv
|
||||||
|
key_viewclass: 'viewclass'
|
||||||
|
key_size: 'height'
|
||||||
|
|
||||||
|
RecycleBoxLayout:
|
||||||
|
padding: dp(10)
|
||||||
|
default_size: None, dp(48)
|
||||||
|
default_size_hint: 1, None
|
||||||
|
size_hint_y: None
|
||||||
|
height: self.minimum_height
|
||||||
|
orientation: 'vertical'
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
|
class CustomOneLineIconListItem(OneLineIconListItem):
|
||||||
|
icon = StringProperty()
|
||||||
|
|
||||||
|
class PreviousMDIcons(Screen):
|
||||||
|
def set_list_md_icons(self, text="", search=False):
|
||||||
|
"""Builds a list of icons for the screen MDIcons."""
|
||||||
|
|
||||||
|
def add_icon_item(name_icon):
|
||||||
|
self.ids.rv.data.append(
|
||||||
|
{
|
||||||
|
"viewclass": "CustomOneLineIconListItem",
|
||||||
|
"icon": name_icon,
|
||||||
|
"text": name_icon,
|
||||||
|
"callback": lambda x: x,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
self.ids.rv.data = []
|
||||||
|
for name_icon in md_icons.keys():
|
||||||
|
if search:
|
||||||
|
if text in name_icon:
|
||||||
|
add_icon_item(name_icon)
|
||||||
|
else:
|
||||||
|
add_icon_item(name_icon)
|
||||||
|
|
||||||
|
class MainApp(MDApp):
|
||||||
|
def __init__(self, **kwargs):
|
||||||
|
super().__init__(**kwargs)
|
||||||
|
self.screen = PreviousMDIcons()
|
||||||
|
|
||||||
|
def build(self):
|
||||||
|
return self.screen
|
||||||
|
|
||||||
|
def on_start(self):
|
||||||
|
self.screen.set_list_md_icons()
|
||||||
|
|
||||||
|
MainApp().run()
|
||||||
|
|
|
@ -35,4 +35,30 @@ else:
|
||||||
PORTRAIT_TOOLBAR_HEIGHT = STANDARD_INCREMENT
|
PORTRAIT_TOOLBAR_HEIGHT = STANDARD_INCREMENT
|
||||||
LANDSCAPE_TOOLBAR_HEIGHT = STANDARD_INCREMENT
|
LANDSCAPE_TOOLBAR_HEIGHT = STANDARD_INCREMENT
|
||||||
|
|
||||||
|
# Elevation.
|
||||||
|
SEGMENT_CONTROL_SEGMENT_SWITCH_ELEVATION = 1
|
||||||
|
FILE_MANAGER_TOP_APP_BAR_ELEVATION = 1
|
||||||
|
FLOATING_ACTION_BUTTON_M2_ELEVATION = 1
|
||||||
|
FLOATING_ACTION_BUTTON_M3_ELEVATION = 0.5
|
||||||
|
CARD_STYLE_ELEVATED_M3_ELEVATION = 0.5
|
||||||
|
CARD_STYLE_OUTLINED_FILLED_M3_ELEVATION = 0
|
||||||
|
DATA_TABLE_ELEVATION = 4
|
||||||
|
DROP_DOWN_MENU_ELEVATION = 2
|
||||||
|
TOP_APP_BAR_ELEVATION = 2
|
||||||
|
SNACK_BAR_ELEVATION = 2
|
||||||
|
|
||||||
|
# Shadow softness.
|
||||||
|
RAISED_BUTTON_SOFTNESS = 4
|
||||||
|
FLOATING_ACTION_BUTTON_M3_SOFTNESS = 0
|
||||||
|
DATA_TABLE_SOFTNESS = 12
|
||||||
|
DROP_DOWN_MENU_SOFTNESS = 6
|
||||||
|
|
||||||
|
# Shadow offset.
|
||||||
|
RAISED_BUTTON_OFFSET = (0, -2)
|
||||||
|
FLOATING_ACTION_BUTTON_M2_OFFSET = (0, -1)
|
||||||
|
FLOATING_ACTION_BUTTON_M3_OFFSET = (0, -2)
|
||||||
|
DATA_TABLE_OFFSET = (0, -2)
|
||||||
|
DROP_DOWN_MENU_OFFSET = (0, -2)
|
||||||
|
SNACK_BAR_OFFSET = (0, -2)
|
||||||
|
|
||||||
TOUCH_TARGET_HEIGHT = dp(48)
|
TOUCH_TARGET_HEIGHT = dp(48)
|
||||||
|
|
|
@ -1,9 +0,0 @@
|
||||||
from kivy.tests.common import GraphicUnitTest
|
|
||||||
|
|
||||||
from kivymd.app import MDApp
|
|
||||||
|
|
||||||
|
|
||||||
class BaseTest(GraphicUnitTest):
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
super().__init__(*args, **kwargs)
|
|
||||||
self.app = MDApp() # NOQA
|
|
|
@ -1,94 +0,0 @@
|
||||||
"""
|
|
||||||
PyInstaller freezing test
|
|
||||||
=========================
|
|
||||||
|
|
||||||
PyInstaller must package KivyMD apps correctly.
|
|
||||||
"""
|
|
||||||
|
|
||||||
import subprocess
|
|
||||||
|
|
||||||
from PyInstaller import __main__ as pyi_main
|
|
||||||
|
|
||||||
|
|
||||||
def test_datas(tmp_path) -> None:
|
|
||||||
"""Test fonts and images."""
|
|
||||||
|
|
||||||
app_name = "userapp"
|
|
||||||
workpath = tmp_path / "build"
|
|
||||||
distpath = tmp_path / "dist"
|
|
||||||
app = tmp_path / (app_name + ".py")
|
|
||||||
app.write_text(
|
|
||||||
"""
|
|
||||||
import os
|
|
||||||
|
|
||||||
from kivy.core.text import LabelBase
|
|
||||||
|
|
||||||
import kivymd
|
|
||||||
|
|
||||||
fonts = os.listdir(kivymd.fonts_path)
|
|
||||||
print(fonts)
|
|
||||||
assert "Roboto-Regular.ttf" in fonts
|
|
||||||
assert "materialdesignicons-webfont.ttf" in fonts
|
|
||||||
print(LabelBase._fonts.keys())
|
|
||||||
assert "Roboto" in LabelBase._fonts.keys() # NOQA
|
|
||||||
assert "Icons" in LabelBase._fonts.keys() # NOQA
|
|
||||||
|
|
||||||
images = os.listdir(kivymd.images_path)
|
|
||||||
print(images)
|
|
||||||
assert "logo" in images
|
|
||||||
assert "alpha_layer.png" in images
|
|
||||||
assert "black.png" in images
|
|
||||||
assert "blue.png" in images
|
|
||||||
assert "red.png" in images
|
|
||||||
assert "green.png" in images
|
|
||||||
assert "yellow.png" in images
|
|
||||||
assert "folder.png" in images
|
|
||||||
assert "transparent.png" in images
|
|
||||||
"""
|
|
||||||
)
|
|
||||||
pyi_main.run(
|
|
||||||
[
|
|
||||||
"--workpath",
|
|
||||||
str(workpath),
|
|
||||||
"--distpath",
|
|
||||||
str(distpath),
|
|
||||||
"--specpath",
|
|
||||||
str(tmp_path),
|
|
||||||
str(app),
|
|
||||||
]
|
|
||||||
)
|
|
||||||
subprocess.run([str(distpath / app_name / app_name)], check=True)
|
|
||||||
|
|
||||||
|
|
||||||
def test_widgets(tmp_path) -> None:
|
|
||||||
"""Test that all widgets are accesible."""
|
|
||||||
|
|
||||||
app_name = "userapp"
|
|
||||||
workpath = tmp_path / "build"
|
|
||||||
distpath = tmp_path / "dist"
|
|
||||||
app = tmp_path / (app_name + ".py")
|
|
||||||
app.write_text(
|
|
||||||
"""
|
|
||||||
import os
|
|
||||||
|
|
||||||
import kivymd # NOQA
|
|
||||||
__import__("kivymd.uix.label")
|
|
||||||
__import__("kivymd.uix.button")
|
|
||||||
__import__("kivymd.uix.list")
|
|
||||||
__import__("kivymd.uix.navigationdrawer")
|
|
||||||
|
|
||||||
print(os.listdir(os.path.dirname(kivymd.uix.__path__[0])))
|
|
||||||
"""
|
|
||||||
)
|
|
||||||
pyi_main.run(
|
|
||||||
[
|
|
||||||
"--workpath",
|
|
||||||
str(workpath),
|
|
||||||
"--distpath",
|
|
||||||
str(distpath),
|
|
||||||
"--specpath",
|
|
||||||
str(tmp_path),
|
|
||||||
str(app),
|
|
||||||
]
|
|
||||||
)
|
|
||||||
subprocess.run([str(distpath / app_name / app_name)], check=True)
|
|
|
@ -1,21 +0,0 @@
|
||||||
from kivy import lang
|
|
||||||
from kivy.clock import Clock
|
|
||||||
from kivy.tests.common import GraphicUnitTest
|
|
||||||
|
|
||||||
from kivymd.app import MDApp
|
|
||||||
from kivymd.theming import ThemeManager
|
|
||||||
|
|
||||||
|
|
||||||
class AppTest(GraphicUnitTest):
|
|
||||||
def test_start_raw_app(self):
|
|
||||||
lang._delayed_start = None
|
|
||||||
a = MDApp()
|
|
||||||
Clock.schedule_once(a.stop, 0.1)
|
|
||||||
a.run()
|
|
||||||
|
|
||||||
def test_theme_manager_existance(self):
|
|
||||||
lang._delayed_start = None
|
|
||||||
a = MDApp()
|
|
||||||
Clock.schedule_once(a.stop, 0.1)
|
|
||||||
a.run()
|
|
||||||
assert isinstance(a.theme_cls, ThemeManager)
|
|
|
@ -1,24 +0,0 @@
|
||||||
from kivymd.tests.base_test import BaseTest
|
|
||||||
|
|
||||||
|
|
||||||
class BackdropTest(BaseTest):
|
|
||||||
def test_backdrop_raw_app(self):
|
|
||||||
from kivymd.uix.backdrop import MDBackdrop
|
|
||||||
from kivymd.uix.backdrop.backdrop import (
|
|
||||||
MDBackdropBackLayer,
|
|
||||||
MDBackdropFrontLayer,
|
|
||||||
)
|
|
||||||
from kivymd.uix.screen import MDScreen
|
|
||||||
from kivymd.uix.widget import MDWidget
|
|
||||||
|
|
||||||
self.render(
|
|
||||||
MDScreen(
|
|
||||||
MDBackdrop(
|
|
||||||
MDBackdropBackLayer(MDWidget()),
|
|
||||||
MDBackdropFrontLayer(MDWidget()),
|
|
||||||
id="backdrop",
|
|
||||||
title="Example Backdrop",
|
|
||||||
header_text="Menu:",
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
|
@ -1,32 +0,0 @@
|
||||||
from kivymd.tests.base_test import BaseTest
|
|
||||||
|
|
||||||
|
|
||||||
class BottomNavigationTest(BaseTest):
|
|
||||||
def test_bottom_navigation_m3_style_raw_app(self):
|
|
||||||
from kivymd.uix.bottomnavigation import (
|
|
||||||
MDBottomNavigation,
|
|
||||||
MDBottomNavigationItem,
|
|
||||||
)
|
|
||||||
from kivymd.uix.screen import MDScreen
|
|
||||||
|
|
||||||
self.app.theme_cls.material_style = "M3"
|
|
||||||
self.render(
|
|
||||||
MDScreen(
|
|
||||||
MDBottomNavigation(
|
|
||||||
MDBottomNavigationItem(
|
|
||||||
name="screen 1",
|
|
||||||
text="Mail",
|
|
||||||
icon="gmail",
|
|
||||||
),
|
|
||||||
MDBottomNavigationItem(
|
|
||||||
name="screen 2",
|
|
||||||
text="Twitter",
|
|
||||||
icon="twitter",
|
|
||||||
badge_icon="numeric-10",
|
|
||||||
),
|
|
||||||
panel_color="#eeeaea",
|
|
||||||
selected_color_background="#97ecf8",
|
|
||||||
text_color_active="red",
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
|
@ -1,25 +0,0 @@
|
||||||
from kivymd.tests.base_test import BaseTest
|
|
||||||
|
|
||||||
|
|
||||||
class CardTest(BaseTest):
|
|
||||||
def test_card_m3_style_raw_app(self):
|
|
||||||
from kivymd.uix.behaviors import RoundedRectangularElevationBehavior
|
|
||||||
from kivymd.uix.card import MDCard
|
|
||||||
from kivymd.uix.screen import MDScreen
|
|
||||||
|
|
||||||
class MD3Card(MDCard, RoundedRectangularElevationBehavior):
|
|
||||||
pass
|
|
||||||
|
|
||||||
self.app.theme_cls.material_style = "M3"
|
|
||||||
self.render(
|
|
||||||
MDScreen(
|
|
||||||
MD3Card(
|
|
||||||
size_hint=(None, None),
|
|
||||||
pos_hint={"center_x": 0.5, "center_y": 0.5},
|
|
||||||
size=("200dp", "100dp"),
|
|
||||||
line_color=(0.2, 0.2, 0.2, 0.8),
|
|
||||||
style="elevated",
|
|
||||||
md_bg_color="lightblue",
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
|
@ -1,16 +0,0 @@
|
||||||
from kivymd.tests.base_test import BaseTest
|
|
||||||
|
|
||||||
|
|
||||||
class ChipTest(BaseTest):
|
|
||||||
def test_chip_raw_app(self):
|
|
||||||
from kivymd.uix.chip import MDChip
|
|
||||||
from kivymd.uix.screen import MDScreen
|
|
||||||
|
|
||||||
self.render(
|
|
||||||
MDScreen(
|
|
||||||
MDChip(
|
|
||||||
text="Portland",
|
|
||||||
pos_hint={"center_x": 0.5, "center_y": 0.5},
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
|
@ -1,15 +0,0 @@
|
||||||
def test_create_project():
|
|
||||||
import os
|
|
||||||
|
|
||||||
os.system(
|
|
||||||
f"python3.10 -m kivymd.tools.patterns.create_project "
|
|
||||||
f"MVC "
|
|
||||||
f"{os.path.expanduser('~')} "
|
|
||||||
f"TestProject "
|
|
||||||
f"python3.10 "
|
|
||||||
f"stable "
|
|
||||||
f"--name_screen TestProjectScreen "
|
|
||||||
f"--name_database restdb "
|
|
||||||
f"--use_hotreload yes"
|
|
||||||
)
|
|
||||||
assert os.path.exists(os.path.join(os.path.expanduser("~"), "TestProject"))
|
|
|
@ -1,24 +0,0 @@
|
||||||
from kivymd.tests.base_test import BaseTest
|
|
||||||
|
|
||||||
|
|
||||||
class FitImageTest(BaseTest):
|
|
||||||
def test_fitimage_raw_app(self):
|
|
||||||
import os
|
|
||||||
|
|
||||||
from kivymd import images_path
|
|
||||||
from kivymd.uix.fitimage import FitImage
|
|
||||||
from kivymd.uix.screen import MDScreen
|
|
||||||
|
|
||||||
self.render(
|
|
||||||
MDScreen(
|
|
||||||
FitImage(
|
|
||||||
source=os.path.join(
|
|
||||||
images_path, "logo", "kivymd-icon-512.png"
|
|
||||||
),
|
|
||||||
size_hint=(0.5, 0.5),
|
|
||||||
pos_hint={"center_x": 0.5, "center_y": 0.5},
|
|
||||||
radius=[36, 36, 0, 0],
|
|
||||||
mipmap=True,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
|
@ -1,16 +0,0 @@
|
||||||
def test_fonts_registration():
|
|
||||||
# This should register fonts:
|
|
||||||
from kivy.core.text import LabelBase
|
|
||||||
|
|
||||||
import kivymd # NOQA
|
|
||||||
|
|
||||||
fonts = [
|
|
||||||
"Roboto",
|
|
||||||
"RobotoThin",
|
|
||||||
"RobotoLight",
|
|
||||||
"RobotoMedium",
|
|
||||||
"RobotoBlack",
|
|
||||||
"Icons",
|
|
||||||
]
|
|
||||||
for font in fonts:
|
|
||||||
assert font in LabelBase._fonts.keys()
|
|
|
@ -1,10 +0,0 @@
|
||||||
def test_icons_have_size():
|
|
||||||
from kivy.core.text import Label
|
|
||||||
|
|
||||||
from kivymd.icon_definitions import md_icons
|
|
||||||
|
|
||||||
lbl = Label(font_name="Icons")
|
|
||||||
for icon_name, icon_value in md_icons.items():
|
|
||||||
assert len(icon_value) == 1
|
|
||||||
lbl.refresh()
|
|
||||||
assert lbl.get_extents(icon_value) is not None
|
|
|
@ -1,39 +0,0 @@
|
||||||
from kivymd.tests.base_test import BaseTest
|
|
||||||
|
|
||||||
|
|
||||||
class ImageListTest(BaseTest):
|
|
||||||
def test_imagelist_raw_app(self):
|
|
||||||
import os
|
|
||||||
|
|
||||||
from kivymd import images_path
|
|
||||||
from kivymd.uix.button import MDIconButton
|
|
||||||
from kivymd.uix.imagelist import MDSmartTile
|
|
||||||
from kivymd.uix.label import MDLabel
|
|
||||||
from kivymd.uix.screen import MDScreen
|
|
||||||
|
|
||||||
self.render(
|
|
||||||
MDScreen(
|
|
||||||
MDSmartTile(
|
|
||||||
MDIconButton(
|
|
||||||
icon="heart-outline",
|
|
||||||
theme_icon_color="Custom",
|
|
||||||
icon_color="red",
|
|
||||||
pos_hint={"center_y": 0.5},
|
|
||||||
),
|
|
||||||
MDLabel(
|
|
||||||
text="Julia and Julie",
|
|
||||||
bold=True,
|
|
||||||
color="white",
|
|
||||||
),
|
|
||||||
radius=24,
|
|
||||||
box_radius=[0, 0, 24, 24],
|
|
||||||
box_color="grey",
|
|
||||||
source=os.path.join(
|
|
||||||
images_path, "logo", "kivymd-icon-512.png"
|
|
||||||
),
|
|
||||||
pos_hint={"center_x": 0.5, "center_y": 0.5},
|
|
||||||
size_hint=(None, None),
|
|
||||||
size=("320dp", "320dp"),
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
|
@ -1,67 +0,0 @@
|
||||||
from kivymd.tests.base_test import BaseTest
|
|
||||||
|
|
||||||
|
|
||||||
class ListTest(BaseTest):
|
|
||||||
def test_list_raw_app(self):
|
|
||||||
import os
|
|
||||||
|
|
||||||
from kivymd import images_path
|
|
||||||
from kivymd.uix.list import (
|
|
||||||
IconLeftWidget,
|
|
||||||
IconRightWidget,
|
|
||||||
ImageLeftWidget,
|
|
||||||
IRightBodyTouch,
|
|
||||||
MDList,
|
|
||||||
OneLineAvatarIconListItem,
|
|
||||||
OneLineAvatarListItem,
|
|
||||||
OneLineIconListItem,
|
|
||||||
OneLineListItem,
|
|
||||||
ThreeLineListItem,
|
|
||||||
TwoLineListItem,
|
|
||||||
)
|
|
||||||
from kivymd.uix.screen import MDScreen
|
|
||||||
from kivymd.uix.scrollview import MDScrollView
|
|
||||||
from kivymd.uix.selectioncontrol import MDCheckbox
|
|
||||||
|
|
||||||
class RightCheckbox(IRightBodyTouch, MDCheckbox):
|
|
||||||
pass
|
|
||||||
|
|
||||||
self.render(
|
|
||||||
MDScreen(
|
|
||||||
MDScrollView(
|
|
||||||
MDList(
|
|
||||||
OneLineListItem(text="Text"),
|
|
||||||
TwoLineListItem(
|
|
||||||
text="Text", secondary_text="secondary text"
|
|
||||||
),
|
|
||||||
ThreeLineListItem(
|
|
||||||
text="Text",
|
|
||||||
secondary_text="secondary text",
|
|
||||||
tertiary_text="tertiary text",
|
|
||||||
),
|
|
||||||
OneLineAvatarListItem(
|
|
||||||
ImageLeftWidget(
|
|
||||||
source=os.path.join(
|
|
||||||
images_path, "logo", "kivymd-icon-512.png"
|
|
||||||
)
|
|
||||||
),
|
|
||||||
text="Text",
|
|
||||||
),
|
|
||||||
OneLineIconListItem(
|
|
||||||
IconLeftWidget(icon="plus"),
|
|
||||||
text="Text",
|
|
||||||
),
|
|
||||||
OneLineAvatarIconListItem(
|
|
||||||
IconLeftWidget(icon="plus"),
|
|
||||||
IconRightWidget(icon="minus"),
|
|
||||||
text="Text",
|
|
||||||
),
|
|
||||||
OneLineAvatarIconListItem(
|
|
||||||
IconLeftWidget(icon="plus"),
|
|
||||||
RightCheckbox(),
|
|
||||||
text="Text",
|
|
||||||
),
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
|
@ -1,94 +0,0 @@
|
||||||
from kivymd.tests.base_test import BaseTest
|
|
||||||
|
|
||||||
|
|
||||||
class NavigationDrawerTest(BaseTest):
|
|
||||||
def test_navigationdrawer_raw_app(self):
|
|
||||||
from kivymd.uix.navigationdrawer import (
|
|
||||||
MDNavigationDrawer,
|
|
||||||
MDNavigationDrawerDivider,
|
|
||||||
MDNavigationDrawerHeader,
|
|
||||||
MDNavigationDrawerItem,
|
|
||||||
MDNavigationDrawerLabel,
|
|
||||||
MDNavigationDrawerMenu,
|
|
||||||
MDNavigationLayout,
|
|
||||||
)
|
|
||||||
from kivymd.uix.screen import MDScreen
|
|
||||||
from kivymd.uix.screenmanager import MDScreenManager
|
|
||||||
from kivymd.uix.toolbar import MDTopAppBar
|
|
||||||
|
|
||||||
class DrawerClickableItem(MDNavigationDrawerItem):
|
|
||||||
def __init__(self, **kwargs):
|
|
||||||
super().__init__(**kwargs)
|
|
||||||
self.focus_color = "#e7e4c0"
|
|
||||||
self.unfocus_color = "#f7f4e7"
|
|
||||||
self.text_color = "#4a4939"
|
|
||||||
self.icon_color = "#4a4939"
|
|
||||||
self.ripple_color = "#c5bdd2"
|
|
||||||
self.selected_color = "#0c6c4d"
|
|
||||||
|
|
||||||
class DrawerLabelItem(MDNavigationDrawerItem):
|
|
||||||
def __init__(self, **kwargs):
|
|
||||||
super().__init__(**kwargs)
|
|
||||||
self.bg_color = "#f7f4e7"
|
|
||||||
self.text_color = "#4a4939"
|
|
||||||
self.icon_color = "#4a4939"
|
|
||||||
_no_ripple_effect = True # NOQA
|
|
||||||
|
|
||||||
self.app.theme_cls.material_style = "M3"
|
|
||||||
self.render(
|
|
||||||
MDNavigationLayout(
|
|
||||||
MDScreenManager(
|
|
||||||
MDScreen(
|
|
||||||
MDTopAppBar(
|
|
||||||
title="Navigation Drawer",
|
|
||||||
elevation=10,
|
|
||||||
pos_hint={"top": 1},
|
|
||||||
md_bg_color="#e7e4c0",
|
|
||||||
specific_text_color="#4a4939",
|
|
||||||
left_action_items=[
|
|
||||||
["menu", lambda x: self.nav_drawer_open()]
|
|
||||||
],
|
|
||||||
)
|
|
||||||
)
|
|
||||||
),
|
|
||||||
MDNavigationDrawer(
|
|
||||||
MDNavigationDrawerMenu(
|
|
||||||
MDNavigationDrawerHeader(
|
|
||||||
title="Header title",
|
|
||||||
title_color="#4a4939",
|
|
||||||
text="Header text",
|
|
||||||
spacing="4dp",
|
|
||||||
padding=("12dp", 0, 0, "56dp"),
|
|
||||||
),
|
|
||||||
MDNavigationDrawerLabel(
|
|
||||||
text="Mail",
|
|
||||||
),
|
|
||||||
DrawerClickableItem(
|
|
||||||
icon="gmail",
|
|
||||||
right_text="+99",
|
|
||||||
text_right_color="#4a4939",
|
|
||||||
text="Inbox",
|
|
||||||
radius=24,
|
|
||||||
),
|
|
||||||
DrawerClickableItem(
|
|
||||||
icon="send",
|
|
||||||
text="Outbox",
|
|
||||||
radius=24,
|
|
||||||
),
|
|
||||||
MDNavigationDrawerDivider(),
|
|
||||||
MDNavigationDrawerLabel(
|
|
||||||
text="Labels",
|
|
||||||
),
|
|
||||||
DrawerLabelItem(
|
|
||||||
icon="information-outline",
|
|
||||||
text="Label",
|
|
||||||
),
|
|
||||||
DrawerLabelItem(
|
|
||||||
icon="information-outline",
|
|
||||||
text="Label",
|
|
||||||
),
|
|
||||||
),
|
|
||||||
id="nav_drawer",
|
|
||||||
),
|
|
||||||
)
|
|
||||||
)
|
|
|
@ -1,14 +0,0 @@
|
||||||
from kivymd.tests.base_test import BaseTest
|
|
||||||
|
|
||||||
|
|
||||||
class TabTest(BaseTest):
|
|
||||||
def test_tab_raw_app(self):
|
|
||||||
from kivymd.uix.floatlayout import MDFloatLayout
|
|
||||||
from kivymd.uix.tab import MDTabs, MDTabsBase
|
|
||||||
|
|
||||||
class Tab(MDFloatLayout, MDTabsBase):
|
|
||||||
pass
|
|
||||||
|
|
||||||
tab = MDTabs()
|
|
||||||
tab.add_widget(Tab(title="Tab"))
|
|
||||||
self.render(tab)
|
|
|
@ -1,72 +0,0 @@
|
||||||
# from kivy.clock import Clock
|
|
||||||
# from kivy.uix.textinput import TextInput
|
|
||||||
|
|
||||||
from kivymd.tests.base_test import BaseTest
|
|
||||||
|
|
||||||
|
|
||||||
class TextFieldTest(BaseTest):
|
|
||||||
def test_textfield_raw_app(self):
|
|
||||||
from kivymd.uix.boxlayout import MDBoxLayout
|
|
||||||
from kivymd.uix.button import MDFlatButton
|
|
||||||
from kivymd.uix.screen import MDScreen
|
|
||||||
from kivymd.uix.textfield import MDTextField
|
|
||||||
|
|
||||||
# def set_text():
|
|
||||||
# for widget in self.screen.ids.box.children:
|
|
||||||
# if issubclass(widget.__class__, TextInput):
|
|
||||||
# widget.text = "Input text"
|
|
||||||
|
|
||||||
self.render(
|
|
||||||
MDScreen(
|
|
||||||
MDBoxLayout(
|
|
||||||
MDTextField(
|
|
||||||
hint_text="Label",
|
|
||||||
helper_text="Error massage",
|
|
||||||
mode="rectangle",
|
|
||||||
max_text_length=5,
|
|
||||||
),
|
|
||||||
MDTextField(
|
|
||||||
icon_left="git",
|
|
||||||
hint_text="Label",
|
|
||||||
helper_text="Error massage",
|
|
||||||
mode="rectangle",
|
|
||||||
),
|
|
||||||
MDTextField(
|
|
||||||
icon_left="git",
|
|
||||||
hint_text="Label",
|
|
||||||
helper_text="Error massage",
|
|
||||||
mode="fill",
|
|
||||||
),
|
|
||||||
MDTextField(
|
|
||||||
hint_text="Label",
|
|
||||||
helper_text="Error massage",
|
|
||||||
mode="fill",
|
|
||||||
),
|
|
||||||
MDTextField(
|
|
||||||
hint_text="Label",
|
|
||||||
helper_text="Error massage",
|
|
||||||
),
|
|
||||||
MDTextField(
|
|
||||||
icon_left="git",
|
|
||||||
hint_text="Label",
|
|
||||||
helper_text="Error massage",
|
|
||||||
),
|
|
||||||
MDTextField(
|
|
||||||
hint_text="Round mode",
|
|
||||||
mode="round",
|
|
||||||
max_text_length=15,
|
|
||||||
helper_text="Massage",
|
|
||||||
),
|
|
||||||
MDFlatButton(
|
|
||||||
text="SET TEXT",
|
|
||||||
pos_hint={"center_x": 0.5},
|
|
||||||
),
|
|
||||||
id="box",
|
|
||||||
orientation="vertical",
|
|
||||||
spacing="20dp",
|
|
||||||
adaptive_height=True,
|
|
||||||
size_hint_x=0.8,
|
|
||||||
pos_hint={"center_x": 0.5, "center_y": 0.5},
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
|
@ -606,13 +606,16 @@ class ThemeManager(EventDispatcher):
|
||||||
readonly.
|
readonly.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
material_style = OptionProperty("M2", options=["M2", "M3"])
|
material_style = OptionProperty("M3", options=["M2", "M3"])
|
||||||
"""
|
"""
|
||||||
Material design style.
|
Material design style.
|
||||||
Available options are: 'M2', 'M3'.
|
Available options are: 'M2', 'M3'.
|
||||||
|
|
||||||
.. versionadded:: 1.0.0
|
.. versionadded:: 1.0.0
|
||||||
|
|
||||||
|
.. versionchanged:: 1.2.0
|
||||||
|
By default now `'M3'`.
|
||||||
|
|
||||||
.. seealso::
|
.. seealso::
|
||||||
|
|
||||||
`Material Design 2 <https://material.io/>`_ and
|
`Material Design 2 <https://material.io/>`_ and
|
||||||
|
@ -620,7 +623,7 @@ class ThemeManager(EventDispatcher):
|
||||||
|
|
||||||
|
|
||||||
:attr:`material_style` is an :class:`~kivy.properties.OptionProperty`
|
:attr:`material_style` is an :class:`~kivy.properties.OptionProperty`
|
||||||
and defaults to `'M2'`.
|
and defaults to `'M3'`.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
theme_style_switch_animation = BooleanProperty(False)
|
theme_style_switch_animation = BooleanProperty(False)
|
||||||
|
@ -647,9 +650,8 @@ class ThemeManager(EventDispatcher):
|
||||||
padding: 0, 0, 0 , "36dp"
|
padding: 0, 0, 0 , "36dp"
|
||||||
size_hint: .5, .5
|
size_hint: .5, .5
|
||||||
pos_hint: {"center_x": .5, "center_y": .5}
|
pos_hint: {"center_x": .5, "center_y": .5}
|
||||||
elevation: 4
|
elevation: 2
|
||||||
shadow_radius: 6
|
shadow_offset: 0, -2
|
||||||
shadow_offset: 0, 2
|
|
||||||
|
|
||||||
MDLabel:
|
MDLabel:
|
||||||
text: "Theme style - {}".format(app.theme_cls.theme_style)
|
text: "Theme style - {}".format(app.theme_cls.theme_style)
|
||||||
|
@ -720,9 +722,8 @@ class ThemeManager(EventDispatcher):
|
||||||
padding=(0, 0, 0, "36dp"),
|
padding=(0, 0, 0, "36dp"),
|
||||||
size_hint=(0.5, 0.5),
|
size_hint=(0.5, 0.5),
|
||||||
pos_hint={"center_x": 0.5, "center_y": 0.5},
|
pos_hint={"center_x": 0.5, "center_y": 0.5},
|
||||||
elevation=4,
|
elevation=2,
|
||||||
shadow_radius=6,
|
shadow_offset=(0, -2),
|
||||||
shadow_offset=(0, 2),
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
@ -1665,25 +1666,60 @@ class ThemableBehavior(EventDispatcher):
|
||||||
"https://github.com/kivymd/KivyMD/wiki/Modules-Material-App#exceptions"
|
"https://github.com/kivymd/KivyMD/wiki/Modules-Material-App#exceptions"
|
||||||
)
|
)
|
||||||
self.theme_cls = App.get_running_app().theme_cls
|
self.theme_cls = App.get_running_app().theme_cls
|
||||||
|
|
||||||
super().__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
|
|
||||||
# def dec_disabled(self, *args, **kwargs) -> None:
|
# Fix circular imports.
|
||||||
# callabacks = self.theme_cls.get_property_observers("theme_style")
|
from kivymd.uix.behaviors import CommonElevationBehavior
|
||||||
|
from kivymd.uix.label import MDLabel
|
||||||
|
from kivymd.uix.textfield import MDTextField
|
||||||
|
|
||||||
# for callaback in callabacks:
|
self.common_elevation_behavior = CommonElevationBehavior
|
||||||
# try:
|
self.md_label = MDLabel
|
||||||
# if hasattr(callaback, "proxy") and hasattr(
|
self.md_textfield = MDTextField
|
||||||
# callaback.proxy, "theme_cls"
|
|
||||||
# ):
|
|
||||||
# for property_name in self.unbind_properties:
|
|
||||||
# self.theme_cls.unbind(
|
|
||||||
# **{
|
|
||||||
# property_name: getattr(
|
|
||||||
# callaback.proxy, callaback.method_name
|
|
||||||
# )
|
|
||||||
# }
|
|
||||||
# )
|
|
||||||
# except ReferenceError:
|
|
||||||
# pass
|
|
||||||
|
|
||||||
# super().dec_disabled(*args, **kwargs)
|
def remove_widget(self, widget) -> None:
|
||||||
|
if not hasattr(widget, "theme_cls"):
|
||||||
|
super().remove_widget(widget)
|
||||||
|
return
|
||||||
|
|
||||||
|
callbacks = widget.theme_cls.get_property_observers("theme_style")
|
||||||
|
|
||||||
|
for callback in callbacks:
|
||||||
|
try:
|
||||||
|
if hasattr(callback, "proxy") and hasattr(
|
||||||
|
callback.proxy, "theme_cls"
|
||||||
|
):
|
||||||
|
if issubclass(widget.__class__, self.md_textfield):
|
||||||
|
widget.theme_cls.unbind(
|
||||||
|
**{
|
||||||
|
"theme_style": getattr(
|
||||||
|
callback.proxy, callback.method_name
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
for property_name in self.unbind_properties:
|
||||||
|
if widget == callback.proxy:
|
||||||
|
widget.theme_cls.unbind(
|
||||||
|
**{
|
||||||
|
property_name: getattr(
|
||||||
|
callback.proxy, callback.method_name
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
# KivyMD widgets may contain other MD widgets.
|
||||||
|
for children in widget.children:
|
||||||
|
if hasattr(children, "theme_cls"):
|
||||||
|
self.remove_widget(children)
|
||||||
|
except ReferenceError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# Canceling a scheduled method call on_window_touch for MDLabel
|
||||||
|
# objects.
|
||||||
|
if (
|
||||||
|
issubclass(widget.__class__, self.md_label)
|
||||||
|
and self.md_label.allow_selection
|
||||||
|
):
|
||||||
|
Window.unbind(on_touch_down=widget.on_window_touch)
|
||||||
|
|
||||||
|
super().remove_widget(widget)
|
||||||
|
|
|
@ -82,7 +82,7 @@ class Toast(BaseDialog):
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
super().__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
self.label_toast = Label(size_hint=(None, None), opacity=0)
|
self.label_toast = Label(size_hint=(None, None), markup=True, opacity=0)
|
||||||
self.label_toast.bind(texture_size=self.label_check_texture_size)
|
self.label_toast.bind(texture_size=self.label_check_texture_size)
|
||||||
self.add_widget(self.label_toast)
|
self.add_widget(self.label_toast)
|
||||||
|
|
||||||
|
|
|
@ -13,18 +13,6 @@ from pathlib import Path
|
||||||
import kivymd
|
import kivymd
|
||||||
|
|
||||||
datas = [
|
datas = [
|
||||||
# Add `.frag` files from the `kivymd/data/glsl/elevation` directory.
|
|
||||||
(
|
|
||||||
str(Path(kivymd.glsl_path).joinpath("elevation")) + os.sep,
|
|
||||||
str(
|
|
||||||
Path("kivymd").joinpath(
|
|
||||||
str(Path(kivymd.glsl_path)).split(str(Path("kivymd")) + os.sep)[
|
|
||||||
1
|
|
||||||
]
|
|
||||||
+ f"{os.sep}elevation"
|
|
||||||
)
|
|
||||||
),
|
|
||||||
),
|
|
||||||
# Add `.ttf` files from the `kivymd/fonts` directory.
|
# Add `.ttf` files from the `kivymd/fonts` directory.
|
||||||
(
|
(
|
||||||
kivymd.fonts_path,
|
kivymd.fonts_path,
|
||||||
|
|
|
@ -381,13 +381,12 @@ class {name_screen}Controller:
|
||||||
temp_base_screen = '''from kivy.properties import ObjectProperty
|
temp_base_screen = '''from kivy.properties import ObjectProperty
|
||||||
|
|
||||||
from kivymd.app import MDApp
|
from kivymd.app import MDApp
|
||||||
from kivymd.theming import ThemableBehavior
|
|
||||||
from kivymd.uix.screen import MDScreen
|
from kivymd.uix.screen import MDScreen
|
||||||
|
|
||||||
from Utility.observer import Observer
|
from Utility.observer import Observer
|
||||||
|
|
||||||
|
|
||||||
class BaseScreenView(ThemableBehavior, MDScreen, Observer):
|
class BaseScreenView(MDScreen, Observer):
|
||||||
"""
|
"""
|
||||||
A base class that implements a visual representation of the model data.
|
A base class that implements a visual representation of the model data.
|
||||||
The view class must be inherited from this class.
|
The view class must be inherited from this class.
|
||||||
|
|
|
@ -59,6 +59,8 @@ class MDAdaptiveWidget(SpecificBackgroundColorBehavior):
|
||||||
else:
|
else:
|
||||||
if not isinstance(self, (FloatLayout, Screen)):
|
if not isinstance(self, (FloatLayout, Screen)):
|
||||||
self.bind(minimum_height=self.setter("height"))
|
self.bind(minimum_height=self.setter("height"))
|
||||||
|
if not self.children:
|
||||||
|
self.height = 0
|
||||||
|
|
||||||
def on_adaptive_width(self, md_widget, value: bool) -> None:
|
def on_adaptive_width(self, md_widget, value: bool) -> None:
|
||||||
self.size_hint_x = None
|
self.size_hint_x = None
|
||||||
|
@ -71,6 +73,8 @@ class MDAdaptiveWidget(SpecificBackgroundColorBehavior):
|
||||||
else:
|
else:
|
||||||
if not isinstance(self, (FloatLayout, Screen)):
|
if not isinstance(self, (FloatLayout, Screen)):
|
||||||
self.bind(minimum_width=self.setter("width"))
|
self.bind(minimum_width=self.setter("width"))
|
||||||
|
if not self.children:
|
||||||
|
self.width = 0
|
||||||
|
|
||||||
def on_adaptive_size(self, md_widget, value: bool) -> None:
|
def on_adaptive_size(self, md_widget, value: bool) -> None:
|
||||||
self.size_hint = (None, None)
|
self.size_hint = (None, None)
|
||||||
|
@ -84,3 +88,5 @@ class MDAdaptiveWidget(SpecificBackgroundColorBehavior):
|
||||||
else:
|
else:
|
||||||
if not isinstance(self, (FloatLayout, Screen)):
|
if not isinstance(self, (FloatLayout, Screen)):
|
||||||
self.bind(minimum_size=self.setter("size"))
|
self.bind(minimum_size=self.setter("size"))
|
||||||
|
if not self.children:
|
||||||
|
self.size = (0, 0)
|
||||||
|
|
|
@ -33,11 +33,14 @@ __all__ = ("MDAnchorLayout",)
|
||||||
|
|
||||||
from kivy.uix.anchorlayout import AnchorLayout
|
from kivy.uix.anchorlayout import AnchorLayout
|
||||||
|
|
||||||
|
from kivymd.theming import ThemableBehavior
|
||||||
from kivymd.uix import MDAdaptiveWidget
|
from kivymd.uix import MDAdaptiveWidget
|
||||||
from kivymd.uix.behaviors import DeclarativeBehavior
|
from kivymd.uix.behaviors import DeclarativeBehavior
|
||||||
|
|
||||||
|
|
||||||
class MDAnchorLayout(DeclarativeBehavior, AnchorLayout, MDAdaptiveWidget):
|
class MDAnchorLayout(
|
||||||
|
DeclarativeBehavior, ThemableBehavior, AnchorLayout, MDAdaptiveWidget
|
||||||
|
):
|
||||||
"""
|
"""
|
||||||
Anchor layout class. For more information, see in the
|
Anchor layout class. For more information, see in the
|
||||||
:class:`~kivy.uix.anchorlayout.AnchorLayout` class documentation.
|
:class:`~kivy.uix.anchorlayout.AnchorLayout` class documentation.
|
||||||
|
|
|
@ -201,7 +201,6 @@ from kivy.properties import (
|
||||||
from kivy.uix.boxlayout import BoxLayout
|
from kivy.uix.boxlayout import BoxLayout
|
||||||
|
|
||||||
from kivymd import uix_path
|
from kivymd import uix_path
|
||||||
from kivymd.theming import ThemableBehavior
|
|
||||||
from kivymd.uix.boxlayout import MDBoxLayout
|
from kivymd.uix.boxlayout import MDBoxLayout
|
||||||
from kivymd.uix.card import MDCard
|
from kivymd.uix.card import MDCard
|
||||||
from kivymd.uix.floatlayout import MDFloatLayout
|
from kivymd.uix.floatlayout import MDFloatLayout
|
||||||
|
@ -214,8 +213,11 @@ with open(
|
||||||
Builder.load_string(kv_file.read())
|
Builder.load_string(kv_file.read())
|
||||||
|
|
||||||
|
|
||||||
class MDBackdrop(MDFloatLayout, ThemableBehavior):
|
class MDBackdrop(MDFloatLayout):
|
||||||
"""
|
"""
|
||||||
|
For more information, see in the
|
||||||
|
:class:`~kivymd.uix.floatlayout.MDFloatLayout` class documentation.
|
||||||
|
|
||||||
:Events:
|
:Events:
|
||||||
:attr:`on_open`
|
:attr:`on_open`
|
||||||
When the front layer drops.
|
When the front layer drops.
|
||||||
|
@ -277,7 +279,7 @@ class MDBackdrop(MDFloatLayout, ThemableBehavior):
|
||||||
|
|
||||||
back_layer_color = ColorProperty(None)
|
back_layer_color = ColorProperty(None)
|
||||||
"""
|
"""
|
||||||
Background color of back layer.
|
Background color of back layer in (r, g, b, a) or string format.
|
||||||
|
|
||||||
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/backdrop-back-layer-color.png
|
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/backdrop-back-layer-color.png
|
||||||
:align: center
|
:align: center
|
||||||
|
@ -288,7 +290,7 @@ class MDBackdrop(MDFloatLayout, ThemableBehavior):
|
||||||
|
|
||||||
front_layer_color = ColorProperty(None)
|
front_layer_color = ColorProperty(None)
|
||||||
"""
|
"""
|
||||||
Background color of front layer.
|
Background color of front layer in (r, g, b, a) or string format.
|
||||||
|
|
||||||
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/backdrop-front-layer-color.png
|
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/backdrop-front-layer-color.png
|
||||||
:align: center
|
:align: center
|
||||||
|
@ -512,15 +514,30 @@ class MDBackdrop(MDFloatLayout, ThemableBehavior):
|
||||||
|
|
||||||
|
|
||||||
class MDBackdropToolbar(MDTopAppBar):
|
class MDBackdropToolbar(MDTopAppBar):
|
||||||
"""Implements a toolbar for back content."""
|
"""
|
||||||
|
Implements a toolbar for back content.
|
||||||
|
|
||||||
|
For more information, see in the
|
||||||
|
:class:`~kivymd.uix.toolbar.toolbar.MDTopAppBar` classes documentation.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
class MDBackdropFrontLayer(MDBoxLayout):
|
class MDBackdropFrontLayer(MDBoxLayout):
|
||||||
"""Container for front content."""
|
"""
|
||||||
|
Container for front content.
|
||||||
|
|
||||||
|
For more information, see in the
|
||||||
|
:class:`~kivymd.uix.boxlayout.MDBoxLayout` classes documentation.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
class MDBackdropBackLayer(MDBoxLayout):
|
class MDBackdropBackLayer(MDBoxLayout):
|
||||||
"""Container for back content."""
|
"""
|
||||||
|
Container for back content.
|
||||||
|
|
||||||
|
For more information, see in the
|
||||||
|
:class:`~kivymd.uix.boxlayout.MDBoxLayout` class documentation.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
class _BackLayer(BoxLayout):
|
class _BackLayer(BoxLayout):
|
||||||
|
|
|
@ -177,6 +177,13 @@ with open(
|
||||||
|
|
||||||
|
|
||||||
class MDBanner(MDCard):
|
class MDBanner(MDCard):
|
||||||
|
"""
|
||||||
|
Banner class.
|
||||||
|
|
||||||
|
For more information, see in the :class:`~kivymd.uix.card.MDCard`
|
||||||
|
class documentation.
|
||||||
|
"""
|
||||||
|
|
||||||
vertical_pad = NumericProperty(dp(68))
|
vertical_pad = NumericProperty(dp(68))
|
||||||
"""
|
"""
|
||||||
Indent the banner at the top of the screen.
|
Indent the banner at the top of the screen.
|
||||||
|
|
|
@ -20,6 +20,11 @@ from .elevation import (
|
||||||
RectangularElevationBehavior,
|
RectangularElevationBehavior,
|
||||||
RoundedRectangularElevationBehavior,
|
RoundedRectangularElevationBehavior,
|
||||||
)
|
)
|
||||||
|
from .motion_behavior import (
|
||||||
|
MotionDialogBehavior,
|
||||||
|
MotionShackBehavior,
|
||||||
|
MotionDropDownMenuBehavior,
|
||||||
|
)
|
||||||
from .magic_behavior import MagicBehavior
|
from .magic_behavior import MagicBehavior
|
||||||
from .ripple_behavior import CircularRippleBehavior, RectangularRippleBehavior
|
from .ripple_behavior import CircularRippleBehavior, RectangularRippleBehavior
|
||||||
from .rotate_behavior import RotateBehavior
|
from .rotate_behavior import RotateBehavior
|
||||||
|
|
|
@ -5,9 +5,9 @@ Behaviors/Background Color
|
||||||
.. note:: The following classes are intended for in-house use of the library.
|
.. note:: The following classes are intended for in-house use of the library.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
__all__ = ("BackgroundColorBehavior", "SpecificBackgroundColorBehavior")
|
from __future__ import annotations
|
||||||
|
|
||||||
from typing import List, Union
|
__all__ = ("BackgroundColorBehavior", "SpecificBackgroundColorBehavior")
|
||||||
|
|
||||||
from kivy.animation import Animation
|
from kivy.animation import Animation
|
||||||
from kivy.lang import Builder
|
from kivy.lang import Builder
|
||||||
|
@ -49,6 +49,8 @@ Builder.load_string(
|
||||||
source: root.background
|
source: root.background
|
||||||
Color:
|
Color:
|
||||||
rgba: self.line_color if self.line_color else (0, 0, 0, 0)
|
rgba: self.line_color if self.line_color else (0, 0, 0, 0)
|
||||||
|
# TODO: maybe we should use SmoothLine,
|
||||||
|
# but this should be tested on all widgets.
|
||||||
Line:
|
Line:
|
||||||
width: root.line_width
|
width: root.line_width
|
||||||
rounded_rectangle:
|
rounded_rectangle:
|
||||||
|
@ -58,7 +60,6 @@ Builder.load_string(
|
||||||
self.width, \
|
self.width, \
|
||||||
self.height, \
|
self.height, \
|
||||||
*self.radius, \
|
*self.radius, \
|
||||||
100, \
|
|
||||||
]
|
]
|
||||||
PopMatrix
|
PopMatrix
|
||||||
""",
|
""",
|
||||||
|
@ -90,6 +91,8 @@ class BackgroundColorBehavior:
|
||||||
and defaults to `[0, 0, 0, 0]`.
|
and defaults to `[0, 0, 0, 0]`.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
# FIXME: in this case, we will not be able to animate this property
|
||||||
|
# using the `Animation` class.
|
||||||
md_bg_color = ColorProperty([1, 1, 1, 0])
|
md_bg_color = ColorProperty([1, 1, 1, 0])
|
||||||
"""
|
"""
|
||||||
The background color of the widget (:class:`~kivy.uix.widget.Widget`)
|
The background color of the widget (:class:`~kivy.uix.widget.Widget`)
|
||||||
|
@ -154,12 +157,34 @@ class BackgroundColorBehavior:
|
||||||
_background_y = NumericProperty(0)
|
_background_y = NumericProperty(0)
|
||||||
_background_origin = ReferenceListProperty(_background_x, _background_y)
|
_background_origin = ReferenceListProperty(_background_x, _background_y)
|
||||||
_md_bg_color = ColorProperty([0, 0, 0, 0])
|
_md_bg_color = ColorProperty([0, 0, 0, 0])
|
||||||
|
_origin_line_color = ColorProperty(None)
|
||||||
|
_origin_md_bg_color = ColorProperty(None)
|
||||||
|
|
||||||
def __init__(self, **kwarg):
|
def __init__(self, **kwarg):
|
||||||
super().__init__(**kwarg)
|
super().__init__(**kwarg)
|
||||||
self.bind(pos=self.update_background_origin)
|
self.bind(
|
||||||
|
pos=self.update_background_origin,
|
||||||
|
disabled=self.restore_color_origin,
|
||||||
|
)
|
||||||
|
|
||||||
|
def restore_color_origin(self, instance_md_widget, value: bool) -> None:
|
||||||
|
"""Called when the values of :attr:`disabled` change."""
|
||||||
|
|
||||||
|
if not value:
|
||||||
|
if self._origin_line_color:
|
||||||
|
self.line_color = self._origin_line_color
|
||||||
|
if self._origin_md_bg_color:
|
||||||
|
self.md_bg_color = self._origin_md_bg_color
|
||||||
|
|
||||||
|
def on_line_color(self, instance_md_widget, value: list | str) -> None:
|
||||||
|
"""Called when the values of :attr:`line_color` change."""
|
||||||
|
|
||||||
|
if not self.disabled:
|
||||||
|
self._origin_line_color = value
|
||||||
|
|
||||||
|
def on_md_bg_color(self, instance_md_widget, color: list | str):
|
||||||
|
"""Called when the values of :attr:`md_bg_color` change."""
|
||||||
|
|
||||||
def on_md_bg_color(self, instance_md_widget, color: Union[list, str]):
|
|
||||||
if (
|
if (
|
||||||
hasattr(self, "theme_cls")
|
hasattr(self, "theme_cls")
|
||||||
and self.theme_cls.theme_style_switch_animation
|
and self.theme_cls.theme_style_switch_animation
|
||||||
|
@ -172,9 +197,12 @@ class BackgroundColorBehavior:
|
||||||
else:
|
else:
|
||||||
self._md_bg_color = color
|
self._md_bg_color = color
|
||||||
|
|
||||||
def update_background_origin(
|
if not self.disabled:
|
||||||
self, instance_md_widget, pos: List[float]
|
self._origin_md_bg_color = color
|
||||||
) -> None:
|
|
||||||
|
def update_background_origin(self, instance_md_widget, pos: list) -> None:
|
||||||
|
"""Called when the values of :attr:`pos` change."""
|
||||||
|
|
||||||
if self.background_origin:
|
if self.background_origin:
|
||||||
self._background_origin = self.background_origin
|
self._background_origin = self.background_origin
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -41,8 +41,9 @@ For example, let's create a button with a rectangular elevation effect:
|
||||||
# With elevation effect
|
# With elevation effect
|
||||||
RectangularElevationButton:
|
RectangularElevationButton:
|
||||||
pos_hint: {"center_x": .5, "center_y": .6}
|
pos_hint: {"center_x": .5, "center_y": .6}
|
||||||
elevation: 4.5
|
elevation: 4
|
||||||
shadow_offset: 0, 6
|
shadow_offset: 0, -6
|
||||||
|
shadow_softness: 4
|
||||||
|
|
||||||
# Without elevation effect
|
# Without elevation effect
|
||||||
RectangularElevationButton:
|
RectangularElevationButton:
|
||||||
|
@ -102,8 +103,9 @@ For example, let's create a button with a rectangular elevation effect:
|
||||||
MDScreen(
|
MDScreen(
|
||||||
RectangularElevationButton(
|
RectangularElevationButton(
|
||||||
pos_hint={"center_x": .5, "center_y": .6},
|
pos_hint={"center_x": .5, "center_y": .6},
|
||||||
elevation=4.5,
|
elevation=4,
|
||||||
shadow_offset=(0, 6),
|
shadow_softness=4,
|
||||||
|
shadow_offset=(0, -6),
|
||||||
),
|
),
|
||||||
RectangularElevationButton(
|
RectangularElevationButton(
|
||||||
pos_hint={"center_x": .5, "center_y": .4},
|
pos_hint={"center_x": .5, "center_y": .4},
|
||||||
|
@ -164,6 +166,7 @@ Similarly, create a circular button:
|
||||||
CircularElevationButton:
|
CircularElevationButton:
|
||||||
pos_hint: {"center_x": .5, "center_y": .6}
|
pos_hint: {"center_x": .5, "center_y": .6}
|
||||||
elevation: 4
|
elevation: 4
|
||||||
|
shadow_softness: 4
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
|
||||||
|
@ -231,6 +234,7 @@ Similarly, create a circular button:
|
||||||
CircularElevationButton(
|
CircularElevationButton(
|
||||||
pos_hint={"center_x": .5, "center_y": .5},
|
pos_hint={"center_x": .5, "center_y": .5},
|
||||||
elevation=4,
|
elevation=4,
|
||||||
|
shadow_softness=4,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
@ -266,7 +270,7 @@ Animating the elevation
|
||||||
size_hint: None, None
|
size_hint: None, None
|
||||||
size: 100, 100
|
size: 100, 100
|
||||||
md_bg_color: 0, 0, 1, 1
|
md_bg_color: 0, 0, 1, 1
|
||||||
elevation: 4
|
elevation: 2
|
||||||
radius: 18
|
radius: 18
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
@ -336,7 +340,7 @@ Animating the elevation
|
||||||
size_hint=(None, None),
|
size_hint=(None, None),
|
||||||
size=(100, 100),
|
size=(100, 100),
|
||||||
md_bg_color="blue",
|
md_bg_color="blue",
|
||||||
elevation=4,
|
elevation=2,
|
||||||
radius=18,
|
radius=18,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
@ -360,32 +364,62 @@ __all__ = (
|
||||||
"FakeCircularElevationBehavior",
|
"FakeCircularElevationBehavior",
|
||||||
)
|
)
|
||||||
|
|
||||||
import os
|
|
||||||
|
|
||||||
from kivy import Logger
|
from kivy import Logger
|
||||||
from kivy.clock import Clock
|
from kivy.lang import Builder
|
||||||
from kivy.core.window import Window
|
|
||||||
from kivy.graphics import RenderContext, RoundedRectangle
|
|
||||||
from kivy.properties import (
|
from kivy.properties import (
|
||||||
AliasProperty,
|
|
||||||
BooleanProperty,
|
|
||||||
BoundedNumericProperty,
|
BoundedNumericProperty,
|
||||||
ColorProperty,
|
ColorProperty,
|
||||||
ListProperty,
|
ListProperty,
|
||||||
NumericProperty,
|
NumericProperty,
|
||||||
ObjectProperty,
|
|
||||||
VariableListProperty,
|
VariableListProperty,
|
||||||
)
|
)
|
||||||
from kivy.uix.widget import Widget
|
from kivy.uix.widget import Widget
|
||||||
|
|
||||||
from kivymd import glsl_path
|
Builder.load_string(
|
||||||
from kivymd.app import MDApp
|
"""
|
||||||
|
<CommonElevationBehavior>
|
||||||
|
canvas.before:
|
||||||
|
PushMatrix
|
||||||
|
Scale:
|
||||||
|
x: self.scale_value_x
|
||||||
|
y: self.scale_value_y
|
||||||
|
z: self.scale_value_x
|
||||||
|
origin:
|
||||||
|
self.center \
|
||||||
|
if not self.scale_value_center else \
|
||||||
|
self.scale_value_center
|
||||||
|
Rotate:
|
||||||
|
angle: self.rotate_value_angle
|
||||||
|
axis: tuple(self.rotate_value_axis)
|
||||||
|
origin: self.center
|
||||||
|
Color:
|
||||||
|
rgba:
|
||||||
|
(0, 0, 0, 0) \
|
||||||
|
if self.disabled or not self.elevation else \
|
||||||
|
root.shadow_color
|
||||||
|
BoxShadow:
|
||||||
|
pos: self.pos
|
||||||
|
size: self.size
|
||||||
|
offset: root.shadow_offset
|
||||||
|
spread_radius: -(root.shadow_softness), -(root.shadow_softness)
|
||||||
|
blur_radius: root.elevation * 10
|
||||||
|
border_radius:
|
||||||
|
(root.radius if hasattr(self, "radius") else [0, 0, 0, 0]) \
|
||||||
|
if root.shadow_radius == [0.0, 0.0, 0.0, 0.0] else \
|
||||||
|
root.shadow_radius
|
||||||
|
canvas.after:
|
||||||
|
PopMatrix
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
# FIXME: Add shadow manipulation with canvas instructions such as
|
|
||||||
# PushMatrix and PopMatrix.
|
|
||||||
class CommonElevationBehavior(Widget):
|
class CommonElevationBehavior(Widget):
|
||||||
"""Common base class for rectangular and circular elevation behavior."""
|
"""
|
||||||
|
Common base class for rectangular and circular elevation behavior.
|
||||||
|
|
||||||
|
For more information, see in the :class:`~kivy.uix.widget.Widget`
|
||||||
|
class documentation.
|
||||||
|
"""
|
||||||
|
|
||||||
elevation = BoundedNumericProperty(0, min=0, errorvalue=0)
|
elevation = BoundedNumericProperty(0, min=0, errorvalue=0)
|
||||||
"""
|
"""
|
||||||
|
@ -418,9 +452,9 @@ class CommonElevationBehavior(Widget):
|
||||||
radius: 12, 46, 12, 46
|
radius: 12, 46, 12, 46
|
||||||
size_hint: .5, .3
|
size_hint: .5, .3
|
||||||
pos_hint: {"center_x": .5, "center_y": .5}
|
pos_hint: {"center_x": .5, "center_y": .5}
|
||||||
elevation: 4
|
elevation: 2
|
||||||
shadow_softness: 8
|
shadow_softness: 4
|
||||||
shadow_offset: (-2, 2)
|
shadow_offset: (2, -2)
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
|
||||||
|
@ -434,21 +468,11 @@ class CommonElevationBehavior(Widget):
|
||||||
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/shadow-radius.png
|
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/shadow-radius.png
|
||||||
:align: center
|
:align: center
|
||||||
|
|
||||||
.. note::
|
|
||||||
However, if you want to use this parameter, remember that the angle
|
|
||||||
values for the radius of the Kivy widgets and the radius for the shader
|
|
||||||
are different.
|
|
||||||
|
|
||||||
.. code-block:: python
|
|
||||||
|
|
||||||
shadow_radius = ['top-right', 'bot-right', 'top-left', 'bot-left']
|
|
||||||
kivy_radius = ['top-left', 'top-right', 'bottom-right', 'bottom-left']
|
|
||||||
|
|
||||||
:attr:`shadow_radius` is an :class:`~kivy.properties.VariableListProperty`
|
:attr:`shadow_radius` is an :class:`~kivy.properties.VariableListProperty`
|
||||||
and defaults to `[0, 0, 0, 0]`.
|
and defaults to `[0, 0, 0, 0]`.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
shadow_softness = NumericProperty(12)
|
shadow_softness = NumericProperty(0.0)
|
||||||
"""
|
"""
|
||||||
Softness of the shadow.
|
Softness of the shadow.
|
||||||
|
|
||||||
|
@ -482,7 +506,9 @@ class CommonElevationBehavior(Widget):
|
||||||
|
|
||||||
|
|
||||||
class RectangularElevationButton(CommonElevationBehavior, BackgroundColorBehavior):
|
class RectangularElevationButton(CommonElevationBehavior, BackgroundColorBehavior):
|
||||||
md_bg_color = [0, 0, 1, 1]
|
def __init__(self, **kwargs):
|
||||||
|
super().__init__(**kwargs)
|
||||||
|
self.md_bg_color = "blue"
|
||||||
|
|
||||||
|
|
||||||
class Example(MDApp):
|
class Example(MDApp):
|
||||||
|
@ -499,7 +525,19 @@ class CommonElevationBehavior(Widget):
|
||||||
and defaults to `12`.
|
and defaults to `12`.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
shadow_offset = ListProperty((0, 2))
|
shadow_softness_size = BoundedNumericProperty(2, min=2, deprecated=True)
|
||||||
|
"""
|
||||||
|
The value of the softness of the shadow.
|
||||||
|
|
||||||
|
.. versionadded:: 1.1.0
|
||||||
|
|
||||||
|
.. deprecated:: 1.2.0
|
||||||
|
|
||||||
|
:attr:`shadow_softness_size` is an :class:`~kivy.properties.NumericProperty`
|
||||||
|
and defaults to `2`.
|
||||||
|
"""
|
||||||
|
|
||||||
|
shadow_offset = ListProperty((0, 0))
|
||||||
"""
|
"""
|
||||||
Offset of the shadow.
|
Offset of the shadow.
|
||||||
|
|
||||||
|
@ -523,14 +561,16 @@ class CommonElevationBehavior(Widget):
|
||||||
RectangularElevationButton:
|
RectangularElevationButton:
|
||||||
pos_hint: {"center_x": .5, "center_y": .5}
|
pos_hint: {"center_x": .5, "center_y": .5}
|
||||||
elevation: 6
|
elevation: 6
|
||||||
shadow_radius: 18
|
shadow_radius: 6
|
||||||
shadow_softness: 24
|
shadow_softness: 12
|
||||||
shadow_offset: 12, 12
|
shadow_offset: -12, -12
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
|
||||||
class RectangularElevationButton(CommonElevationBehavior, BackgroundColorBehavior):
|
class RectangularElevationButton(CommonElevationBehavior, BackgroundColorBehavior):
|
||||||
md_bg_color = [0, 0, 1, 1]
|
def __init__(self, **kwargs):
|
||||||
|
super().__init__(**kwargs)
|
||||||
|
self.md_bg_color = "blue"
|
||||||
|
|
||||||
|
|
||||||
class Example(MDApp):
|
class Example(MDApp):
|
||||||
|
@ -546,7 +586,7 @@ class CommonElevationBehavior(Widget):
|
||||||
.. code-block:: kv
|
.. code-block:: kv
|
||||||
|
|
||||||
RectangularElevationButton:
|
RectangularElevationButton:
|
||||||
shadow_offset: -12, 12
|
shadow_offset: 12, -12
|
||||||
|
|
||||||
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/shadow-offset-2.png
|
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/shadow-offset-2.png
|
||||||
:align: center
|
:align: center
|
||||||
|
@ -554,7 +594,7 @@ class CommonElevationBehavior(Widget):
|
||||||
.. code-block:: kv
|
.. code-block:: kv
|
||||||
|
|
||||||
RectangularElevationButton:
|
RectangularElevationButton:
|
||||||
shadow_offset: -12, -12
|
shadow_offset: 12, 12
|
||||||
|
|
||||||
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/shadow-offset-3.png
|
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/shadow-offset-3.png
|
||||||
:align: center
|
:align: center
|
||||||
|
@ -562,13 +602,13 @@ class CommonElevationBehavior(Widget):
|
||||||
.. code-block:: kv
|
.. code-block:: kv
|
||||||
|
|
||||||
RectangularElevationButton:
|
RectangularElevationButton:
|
||||||
shadow_offset: 12, -12
|
shadow_offset: -12, 12
|
||||||
|
|
||||||
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/shadow-offset-4.png
|
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/shadow-offset-4.png
|
||||||
:align: center
|
:align: center
|
||||||
|
|
||||||
:attr:`shadow_offset` is an :class:`~kivy.properties.ListProperty`
|
:attr:`shadow_offset` is an :class:`~kivy.properties.ListProperty`
|
||||||
and defaults to `(0, 2)`.
|
and defaults to `(0, 0)`.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
shadow_color = ColorProperty([0, 0, 0, 0.6])
|
shadow_color = ColorProperty([0, 0, 0, 0.6])
|
||||||
|
@ -586,252 +626,75 @@ class CommonElevationBehavior(Widget):
|
||||||
:align: center
|
:align: center
|
||||||
|
|
||||||
:attr:`shadow_color` is an :class:`~kivy.properties.ColorProperty`
|
:attr:`shadow_color` is an :class:`~kivy.properties.ColorProperty`
|
||||||
and defaults to `[0.4, 0.4, 0.4, 0.8]`.
|
and defaults to `[0, 0, 0, 0.6]`.
|
||||||
|
"""
|
||||||
|
|
||||||
|
scale_value_x = NumericProperty(1)
|
||||||
|
"""
|
||||||
|
X-axis value.
|
||||||
|
|
||||||
|
.. versionadded:: 1.2.0
|
||||||
|
|
||||||
|
:attr:`scale_value_x` is an :class:`~kivy.properties.NumericProperty`
|
||||||
|
and defaults to `1`.
|
||||||
|
"""
|
||||||
|
|
||||||
|
scale_value_y = NumericProperty(1)
|
||||||
|
"""
|
||||||
|
Y-axis value.
|
||||||
|
|
||||||
|
.. versionadded:: 1.2.0
|
||||||
|
|
||||||
|
:attr:`scale_value_y` is an :class:`~kivy.properties.NumericProperty`
|
||||||
|
and defaults to `1`.
|
||||||
|
"""
|
||||||
|
|
||||||
|
scale_value_z = NumericProperty(1)
|
||||||
|
"""
|
||||||
|
Z-axis value.
|
||||||
|
|
||||||
|
.. versionadded:: 1.2.0
|
||||||
|
|
||||||
|
:attr:`scale_value_z` is an :class:`~kivy.properties.NumericProperty`
|
||||||
|
and defaults to `1`.
|
||||||
|
"""
|
||||||
|
|
||||||
|
scale_value_center = ListProperty()
|
||||||
|
"""
|
||||||
|
Origin of the scale.
|
||||||
|
|
||||||
|
.. versionadded:: 1.2.0
|
||||||
|
|
||||||
|
The format of the origin can be either (x, y) or (x, y, z).
|
||||||
|
|
||||||
|
:attr:`scale_value_center` is an :class:`~kivy.properties.NumericProperty`
|
||||||
|
and defaults to `[]`.
|
||||||
|
"""
|
||||||
|
|
||||||
|
rotate_value_angle = NumericProperty(0)
|
||||||
|
"""
|
||||||
|
Property for getting/setting the angle of the rotation.
|
||||||
|
|
||||||
|
.. versionadded:: 1.2.0
|
||||||
|
|
||||||
|
:attr:`rotate_value_angle` is an :class:`~kivy.properties.NumericProperty`
|
||||||
|
and defaults to `0`.
|
||||||
|
"""
|
||||||
|
|
||||||
|
rotate_value_axis = ListProperty((0, 0, 1))
|
||||||
|
"""
|
||||||
|
Property for getting/setting the axis of the rotation.
|
||||||
|
|
||||||
|
.. versionadded:: 1.2.0
|
||||||
|
|
||||||
|
:attr:`rotate_value_axis` is an :class:`~kivy.properties.ListProperty`
|
||||||
|
and defaults to `(0, 0, 1)`.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
_transition_ref = ObjectProperty()
|
|
||||||
_has_relative_position = BooleanProperty(defaultvalue=False)
|
|
||||||
_elevation = 0
|
_elevation = 0
|
||||||
_shadow_color = [0.0, 0.0, 0.0, 0.0]
|
|
||||||
|
|
||||||
def _get_window_pos(self, *args):
|
|
||||||
window_pos = self.to_window(*self.pos)
|
|
||||||
# To list, so it can be compared to self.pos directly.
|
|
||||||
return [window_pos[0], window_pos[1]]
|
|
||||||
|
|
||||||
def _set_window_pos(self, value):
|
|
||||||
self.window_pos = value
|
|
||||||
|
|
||||||
window_pos = AliasProperty(_get_window_pos, _set_window_pos)
|
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
|
||||||
super().__init__(**kwargs)
|
|
||||||
|
|
||||||
if hasattr(MDApp.get_running_app(), "shaders_disabled") and MDApp.get_running_app().shaders_disabled:
|
|
||||||
self.shaders_disabled = True
|
|
||||||
else:
|
|
||||||
self.shaders_disabled = False
|
|
||||||
|
|
||||||
with self.canvas.before:
|
|
||||||
self.context = RenderContext(use_parent_projection=True)
|
|
||||||
with self.context:
|
|
||||||
if self.shaders_disabled:
|
|
||||||
self.rect = None
|
|
||||||
del self.rect
|
|
||||||
else:
|
|
||||||
self.rect = RoundedRectangle(pos=self.pos, size=self.size)
|
|
||||||
|
|
||||||
self.after_init()
|
|
||||||
|
|
||||||
def after_init(self, *args):
|
|
||||||
Clock.schedule_once(self.check_for_relative_behavior)
|
|
||||||
if not self.shaders_disabled:
|
|
||||||
Clock.schedule_once(self.set_shader_string)
|
|
||||||
Clock.schedule_once(lambda x: self.on_elevation(self, self.elevation))
|
|
||||||
self.on_pos()
|
|
||||||
|
|
||||||
def check_for_relative_behavior(self, *args) -> None:
|
|
||||||
"""
|
|
||||||
Checks if the widget has relative properties and if necessary
|
|
||||||
binds Window.on_draw and screen events to fix behavior
|
|
||||||
"""
|
|
||||||
|
|
||||||
if self.pos != self.window_pos:
|
|
||||||
self._has_relative_position = True
|
|
||||||
|
|
||||||
# Loops to check if its inside screenmanager or bottom_navigation.
|
|
||||||
widget = self
|
|
||||||
while True:
|
|
||||||
# Checks if has screen event function
|
|
||||||
# works for Screen and MDTab objects.
|
|
||||||
if hasattr(widget, "on_pre_enter"):
|
|
||||||
widget.bind(on_pre_enter=self.apply_correction)
|
|
||||||
widget.bind(on_pre_leave=self.apply_correction)
|
|
||||||
widget.bind(on_enter=self.reset_correction)
|
|
||||||
widget.bind(on_leave=self.reset_correction)
|
|
||||||
self._has_relative_position = True
|
|
||||||
|
|
||||||
# Save refs to objects with transition property.
|
|
||||||
if hasattr(widget, "header"): # specific to bottom_nav
|
|
||||||
self._transition_ref = widget.header.panel
|
|
||||||
elif hasattr(widget, "manager"): # specific to screen
|
|
||||||
if widget.manager: # manager cant be None
|
|
||||||
self._transition_ref = widget.manager
|
|
||||||
break
|
|
||||||
|
|
||||||
elif widget.parent and str(widget) != str(widget.parent):
|
|
||||||
widget = widget.parent
|
|
||||||
else:
|
|
||||||
break
|
|
||||||
|
|
||||||
if self._has_relative_position:
|
|
||||||
Window.bind(on_draw=self.update_window_position)
|
|
||||||
|
|
||||||
def apply_correction(self, *args):
|
|
||||||
if self._transition_ref:
|
|
||||||
transition = str(self._transition_ref.transition)
|
|
||||||
# Slide and Card transitions only need _has_relative_pos to be
|
|
||||||
# always on.
|
|
||||||
if (
|
|
||||||
"SlideTransition" in transition
|
|
||||||
or "CardTransition" in transition
|
|
||||||
):
|
|
||||||
self.context.use_parent_modelview = False
|
|
||||||
else:
|
|
||||||
self.context.use_parent_modelview = True
|
|
||||||
|
|
||||||
def reset_correction(self, *args):
|
|
||||||
self.context.use_parent_modelview = False
|
|
||||||
self.update_window_position()
|
|
||||||
|
|
||||||
def get_shader_string(self) -> str:
|
|
||||||
shader_string = ""
|
|
||||||
for name_file in ["header.frag", "elevation.frag", "main.frag"]:
|
|
||||||
with open(
|
|
||||||
os.path.join(glsl_path, "elevation", name_file),
|
|
||||||
encoding="utf-8",
|
|
||||||
) as file:
|
|
||||||
shader_string += f"{file.read()}\n\n"
|
|
||||||
|
|
||||||
return shader_string
|
|
||||||
|
|
||||||
def set_shader_string(self, *args) -> None:
|
|
||||||
self.context["shadow_radius"] = list(map(float, self.shadow_radius))
|
|
||||||
self.context["shadow_softness"] = float(self.shadow_softness)
|
|
||||||
self.context["shadow_color"] = list(map(float, self.shadow_color))[
|
|
||||||
:-1
|
|
||||||
] + [float(self.opacity)]
|
|
||||||
self.context["pos"] = list(map(float, self.rect.pos))
|
|
||||||
self.context.shader.fs = self.get_shader_string()
|
|
||||||
|
|
||||||
def update_resolution(self) -> None:
|
|
||||||
self.context["resolution"] = (*self.rect.size, *self.rect.pos)
|
|
||||||
|
|
||||||
def on_shadow_color(self, instance, value) -> None:
|
|
||||||
def on_shadow_color(*args):
|
|
||||||
self._shadow_color = list(map(float, value))[:-1] + [
|
|
||||||
float(self.opacity) if not self.disabled else 0
|
|
||||||
]
|
|
||||||
self.context["shadow_color"] = self._shadow_color
|
|
||||||
|
|
||||||
Clock.schedule_once(on_shadow_color)
|
|
||||||
|
|
||||||
def on_shadow_radius(self, instance, value) -> None:
|
|
||||||
def on_shadow_radius(*args):
|
|
||||||
if hasattr(self, "context"):
|
|
||||||
self.context["shadow_radius"] = list(map(float, value))
|
|
||||||
|
|
||||||
Clock.schedule_once(on_shadow_radius)
|
|
||||||
|
|
||||||
def on_shadow_softness(self, instance, value) -> None:
|
|
||||||
def on_shadow_softness(*args):
|
|
||||||
if hasattr(self, "context"):
|
|
||||||
self.context["shadow_softness"] = float(value)
|
|
||||||
|
|
||||||
Clock.schedule_once(on_shadow_softness)
|
|
||||||
|
|
||||||
def on_elevation(self, instance, value) -> None:
|
def on_elevation(self, instance, value) -> None:
|
||||||
def on_elevation(*args):
|
|
||||||
if hasattr(self, "context"):
|
|
||||||
self._elevation = value
|
self._elevation = value
|
||||||
self.hide_elevation(
|
|
||||||
True if (value <= 0 or self.disabled) else False
|
|
||||||
)
|
|
||||||
|
|
||||||
Clock.schedule_once(on_elevation)
|
|
||||||
|
|
||||||
def on_shadow_offset(self, instance, value) -> None:
|
|
||||||
self.on_size()
|
|
||||||
self.on_pos()
|
|
||||||
|
|
||||||
def update_window_position(self, *args) -> None:
|
|
||||||
"""
|
|
||||||
This function is used only when the widget has relative position
|
|
||||||
properties.
|
|
||||||
"""
|
|
||||||
|
|
||||||
self.on_pos()
|
|
||||||
|
|
||||||
def on_pos(self, *args) -> None:
|
|
||||||
if not hasattr(self, "rect"):
|
|
||||||
return
|
|
||||||
|
|
||||||
if (
|
|
||||||
self._has_relative_position
|
|
||||||
and not self.context.use_parent_modelview
|
|
||||||
):
|
|
||||||
pos = self.window_pos
|
|
||||||
else:
|
|
||||||
pos = self.pos
|
|
||||||
|
|
||||||
self.rect.pos = [
|
|
||||||
pos[0]
|
|
||||||
- ((self.rect.size[0] - self.width) / 2)
|
|
||||||
- self.shadow_offset[0],
|
|
||||||
pos[1]
|
|
||||||
- ((self.rect.size[1] - self.height) / 2)
|
|
||||||
- self.shadow_offset[1],
|
|
||||||
]
|
|
||||||
|
|
||||||
self.context["mouse"] = [self.rect.pos[0], 0.0, 0.0, 0.0]
|
|
||||||
self.context["pos"] = list(map(float, self.rect.pos))
|
|
||||||
self.update_resolution()
|
|
||||||
|
|
||||||
def on_size(self, *args) -> None:
|
|
||||||
if not hasattr(self, "rect"):
|
|
||||||
return
|
|
||||||
|
|
||||||
# If the elevation value is 0, set the canvas size to zero.
|
|
||||||
# Because even with a zero elevation value, the shadow is displayed
|
|
||||||
# under the widget. This is visible if we change the scale
|
|
||||||
# of the widget.
|
|
||||||
width = self.size[0] if self.elevation else 0
|
|
||||||
height = self.size[1] if self.elevation else 0
|
|
||||||
self.rect.size = (
|
|
||||||
width + (self._elevation * self.shadow_softness / 2),
|
|
||||||
height + (self._elevation * self.shadow_softness / 2),
|
|
||||||
)
|
|
||||||
|
|
||||||
self.context["mouse"] = [self.rect.pos[0], 0.0, 0.0, 0.0]
|
|
||||||
self.context["size"] = list(map(float, self.rect.size))
|
|
||||||
self.update_resolution()
|
|
||||||
|
|
||||||
def on_opacity(self, instance, value: int | float) -> None:
|
|
||||||
"""
|
|
||||||
Adjusts the transparency of the shadow according to the transparency
|
|
||||||
of the widget.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def on_opacity(*args):
|
|
||||||
self._shadow_color = list(map(float, self._shadow_color))[:-1] + [
|
|
||||||
float(value)
|
|
||||||
]
|
|
||||||
self.context["shadow_color"] = self._shadow_color
|
|
||||||
|
|
||||||
super().on_opacity(instance, value)
|
|
||||||
Clock.schedule_once(on_opacity)
|
|
||||||
|
|
||||||
def on_radius(self, instance, value) -> None:
|
|
||||||
self.shadow_radius = [value[1], value[2], value[0], value[3]]
|
|
||||||
|
|
||||||
def on_disabled(self, instance, value) -> None:
|
|
||||||
if value:
|
|
||||||
self._elevation = 0
|
|
||||||
self.hide_elevation(True)
|
|
||||||
else:
|
|
||||||
self.hide_elevation(False)
|
|
||||||
|
|
||||||
def hide_elevation(self, hide: bool) -> None:
|
|
||||||
if hide:
|
|
||||||
self._elevation = -self.elevation
|
|
||||||
self._shadow_color = [0.0, 0.0, 0.0, 0.0]
|
|
||||||
else:
|
|
||||||
self._elevation = self.elevation
|
|
||||||
self._shadow_color = self.shadow_color[:-1] + [float(self.opacity)]
|
|
||||||
|
|
||||||
self.on_shadow_color(self, self._shadow_color)
|
|
||||||
self.on_size()
|
|
||||||
self.on_pos()
|
|
||||||
|
|
||||||
|
|
||||||
class RectangularElevationBehavior(CommonElevationBehavior):
|
class RectangularElevationBehavior(CommonElevationBehavior):
|
||||||
|
|
|
@ -15,8 +15,9 @@ Usage
|
||||||
from kivy.lang import Builder
|
from kivy.lang import Builder
|
||||||
|
|
||||||
from kivymd.app import MDApp
|
from kivymd.app import MDApp
|
||||||
from kivymd.uix.behaviors import RectangularElevationBehavior, FocusBehavior
|
from kivymd.uix.behaviors import RectangularElevationBehavior
|
||||||
from kivymd.uix.boxlayout import MDBoxLayout
|
from kivymd.uix.boxlayout import MDBoxLayout
|
||||||
|
from kivymd.uix.behaviors.focus_behavior import FocusBehavior
|
||||||
|
|
||||||
KV = '''
|
KV = '''
|
||||||
MDScreen:
|
MDScreen:
|
||||||
|
@ -72,6 +73,18 @@ from kivymd.uix.behaviors import HoverBehavior
|
||||||
|
|
||||||
|
|
||||||
class FocusBehavior(HoverBehavior, ButtonBehavior):
|
class FocusBehavior(HoverBehavior, ButtonBehavior):
|
||||||
|
"""
|
||||||
|
Focus behavior class.
|
||||||
|
|
||||||
|
For more information, see in the :class:`~kivymd.uix.behavior.HoverBehavior`
|
||||||
|
and :class:`~kivy.uix.button.ButtonBehavior` classes documentation.
|
||||||
|
|
||||||
|
:Events:
|
||||||
|
:attr:`on_enter`
|
||||||
|
Called when mouse enters the bbox of the widget AND the widget is visible
|
||||||
|
:attr:`on_leave`
|
||||||
|
Called when the mouse exits the widget AND the widget is visible
|
||||||
|
"""
|
||||||
|
|
||||||
focus_behavior = BooleanProperty(True)
|
focus_behavior = BooleanProperty(True)
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -11,13 +11,13 @@ In `KV file`:
|
||||||
|
|
||||||
.. code-block:: kv
|
.. code-block:: kv
|
||||||
|
|
||||||
<HoverItem@MDBoxLayout+ThemableBehavior+HoverBehavior>
|
<HoverItem@MDBoxLayout+HoverBehavior>
|
||||||
|
|
||||||
In `python file`:
|
In `python file`:
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
class HoverItem(MDBoxLayout, ThemableBehavior, HoverBehavior):
|
class HoverItem(MDBoxLayout, HoverBehavior):
|
||||||
'''Custom item implementing hover behavior.'''
|
'''Custom item implementing hover behavior.'''
|
||||||
|
|
||||||
After creating a class, you must define two methods for it:
|
After creating a class, you must define two methods for it:
|
||||||
|
@ -38,7 +38,6 @@ the widget.
|
||||||
from kivymd.app import MDApp
|
from kivymd.app import MDApp
|
||||||
from kivymd.uix.behaviors import HoverBehavior
|
from kivymd.uix.behaviors import HoverBehavior
|
||||||
from kivymd.uix.boxlayout import MDBoxLayout
|
from kivymd.uix.boxlayout import MDBoxLayout
|
||||||
from kivymd.theming import ThemableBehavior
|
|
||||||
|
|
||||||
KV = '''
|
KV = '''
|
||||||
Screen
|
Screen
|
||||||
|
@ -51,7 +50,7 @@ the widget.
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
|
||||||
class HoverItem(MDBoxLayout, ThemableBehavior, HoverBehavior):
|
class HoverItem(MDBoxLayout, HoverBehavior):
|
||||||
'''Custom item implementing hover behavior.'''
|
'''Custom item implementing hover behavior.'''
|
||||||
|
|
||||||
def on_enter(self, *args):
|
def on_enter(self, *args):
|
||||||
|
|
|
@ -118,7 +118,6 @@ Builder.load_string(
|
||||||
|
|
||||||
|
|
||||||
class MagicBehavior:
|
class MagicBehavior:
|
||||||
|
|
||||||
magic_speed = NumericProperty(1)
|
magic_speed = NumericProperty(1)
|
||||||
"""
|
"""
|
||||||
Animation playback speed.
|
Animation playback speed.
|
||||||
|
|
|
@ -0,0 +1,287 @@
|
||||||
|
"""
|
||||||
|
Behaviors/Motion
|
||||||
|
================
|
||||||
|
|
||||||
|
.. rubric:: Use motion to make a UI expressive and easy to use.
|
||||||
|
|
||||||
|
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/motion.png
|
||||||
|
:align: center
|
||||||
|
|
||||||
|
.. versionadded:: 1.2.0
|
||||||
|
|
||||||
|
Classes of the `Motion` type implement the display behavior of widgets such
|
||||||
|
as dialogs, dropdown menu, snack bars, and so on.
|
||||||
|
"""
|
||||||
|
|
||||||
|
__all__ = (
|
||||||
|
"MotionBase",
|
||||||
|
"MotionDropDownMenuBehavior",
|
||||||
|
"MotionDialogBehavior",
|
||||||
|
"MotionShackBehavior",
|
||||||
|
)
|
||||||
|
|
||||||
|
from kivy.animation import Animation
|
||||||
|
from kivy.clock import Clock
|
||||||
|
from kivy.core.window import Window
|
||||||
|
from kivy.properties import StringProperty, NumericProperty
|
||||||
|
|
||||||
|
from kivymd.uix.behaviors.stencil_behavior import StencilBehavior
|
||||||
|
|
||||||
|
|
||||||
|
class MotionBase:
|
||||||
|
"""Base class for widget display movement behavior."""
|
||||||
|
|
||||||
|
show_transition = StringProperty("linear")
|
||||||
|
"""
|
||||||
|
The type of transition of the widget opening.
|
||||||
|
|
||||||
|
:attr:`show_transition` is a :class:`~kivy.properties.StringProperty`
|
||||||
|
and defaults to `'linear'`.
|
||||||
|
"""
|
||||||
|
|
||||||
|
show_duration = NumericProperty(0.2)
|
||||||
|
"""
|
||||||
|
Duration of widget display transition.
|
||||||
|
|
||||||
|
:attr:`show_duration` is a :class:`~kivy.properties.NumericProperty`
|
||||||
|
and defaults to `0.2`.
|
||||||
|
"""
|
||||||
|
|
||||||
|
hide_transition = StringProperty("linear")
|
||||||
|
"""
|
||||||
|
The type of transition of the widget closing.
|
||||||
|
|
||||||
|
:attr:`hide_transition` is a :class:`~kivy.properties.StringProperty`
|
||||||
|
and defaults to `'linear'`.
|
||||||
|
"""
|
||||||
|
|
||||||
|
hide_duration = NumericProperty(0.2)
|
||||||
|
"""
|
||||||
|
Duration of widget closing transition.
|
||||||
|
|
||||||
|
:attr:`hide_duration` is a :class:`~kivy.properties.NumericProperty`
|
||||||
|
and defaults to `0.2`.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class MotionDropDownMenuBehavior(MotionBase):
|
||||||
|
"""
|
||||||
|
Base class for the dropdown menu movement behavior.
|
||||||
|
|
||||||
|
For more information, see in the :class:`~MotionBase` class documentation.
|
||||||
|
"""
|
||||||
|
|
||||||
|
show_transition = StringProperty("out_back")
|
||||||
|
"""
|
||||||
|
The type of transition of the widget opening.
|
||||||
|
|
||||||
|
:attr:`show_transition` is a :class:`~kivy.properties.StringProperty`
|
||||||
|
and defaults to `'out_back'`.
|
||||||
|
"""
|
||||||
|
|
||||||
|
show_duration = NumericProperty(0.4)
|
||||||
|
"""
|
||||||
|
Duration of widget display transition.
|
||||||
|
|
||||||
|
:attr:`show_duration` is a :class:`~kivy.properties.NumericProperty`
|
||||||
|
and defaults to `0.2`.
|
||||||
|
"""
|
||||||
|
|
||||||
|
hide_transition = StringProperty("out_cubic")
|
||||||
|
"""
|
||||||
|
The type of transition of the widget closing.
|
||||||
|
|
||||||
|
:attr:`hide_transition` is a :class:`~kivy.properties.StringProperty`
|
||||||
|
and defaults to `'out_cubic'`.
|
||||||
|
"""
|
||||||
|
|
||||||
|
_scale_x = NumericProperty(None)
|
||||||
|
"""
|
||||||
|
Default X-axis scaling values.
|
||||||
|
|
||||||
|
:attr:`_scale_x` is a :class:`~kivy.properties.NumericProperty`
|
||||||
|
and defaults to `None`.
|
||||||
|
"""
|
||||||
|
|
||||||
|
_scale_y = NumericProperty(None)
|
||||||
|
"""
|
||||||
|
Default Y-axis scaling values.
|
||||||
|
|
||||||
|
:attr:`_scale_y` is a :class:`~kivy.properties.NumericProperty`
|
||||||
|
and defaults to `None`.
|
||||||
|
"""
|
||||||
|
|
||||||
|
_opacity = NumericProperty(None)
|
||||||
|
"""
|
||||||
|
Menu transparency values.
|
||||||
|
|
||||||
|
:attr:`_opacity` is a :class:`~kivy.properties.NumericProperty`
|
||||||
|
and defaults to `None`.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, **kwargs):
|
||||||
|
super().__init__(**kwargs)
|
||||||
|
self.set_scale()
|
||||||
|
# self.set_opacity()
|
||||||
|
|
||||||
|
def set_opacity(self) -> None:
|
||||||
|
self._opacity = 0
|
||||||
|
|
||||||
|
def set_scale(self) -> None:
|
||||||
|
self._scale_x = 0
|
||||||
|
self._scale_y = 0
|
||||||
|
|
||||||
|
def on_dismiss(self) -> None:
|
||||||
|
Window.remove_widget(self)
|
||||||
|
# anim = Animation(
|
||||||
|
# _scale_x=0,
|
||||||
|
# _scale_y=0,
|
||||||
|
# # _opacity=0,
|
||||||
|
# duration=self.hide_duration,
|
||||||
|
# transition=self.hide_transition,
|
||||||
|
# )
|
||||||
|
# anim.bind(on_complete=lambda *args: Window.remove_widget(self))
|
||||||
|
# anim.start(self)
|
||||||
|
|
||||||
|
def on_open(self, *args):
|
||||||
|
pass
|
||||||
|
anim = Animation(
|
||||||
|
_scale_y=1,
|
||||||
|
# _opacity=1,
|
||||||
|
duration=0.0,
|
||||||
|
transition=self.show_transition,
|
||||||
|
)
|
||||||
|
anim &= Animation(
|
||||||
|
_scale_x=1,
|
||||||
|
duration=0.0,
|
||||||
|
transition="out_quad",
|
||||||
|
)
|
||||||
|
anim.start(self)
|
||||||
|
|
||||||
|
def on__opacity(self, instance, value):
|
||||||
|
self.opacity = value
|
||||||
|
|
||||||
|
def on__scale_x(self, instance, value):
|
||||||
|
self.scale_value_x = value
|
||||||
|
|
||||||
|
def on__scale_y(self, instance, value):
|
||||||
|
self.scale_value_y = value
|
||||||
|
|
||||||
|
|
||||||
|
class MotionDialogBehavior(MotionBase):
|
||||||
|
"""
|
||||||
|
Base class for dialog movement behavior.
|
||||||
|
|
||||||
|
For more information, see in the :class:`~MotionBase` class documentation.
|
||||||
|
"""
|
||||||
|
|
||||||
|
show_duration = NumericProperty(0.0)
|
||||||
|
"""
|
||||||
|
Duration of widget display transition.
|
||||||
|
|
||||||
|
:attr:`show_duration` is a :class:`~kivy.properties.NumericProperty`
|
||||||
|
and defaults to `0.1`.
|
||||||
|
"""
|
||||||
|
|
||||||
|
scale_x = NumericProperty(1.0)
|
||||||
|
"""
|
||||||
|
Default X-axis scaling values.
|
||||||
|
|
||||||
|
:attr:`scale_x` is a :class:`~kivy.properties.NumericProperty`
|
||||||
|
and defaults to `1.5`.
|
||||||
|
"""
|
||||||
|
|
||||||
|
scale_y = NumericProperty(1.0)
|
||||||
|
"""
|
||||||
|
Default Y-axis scaling values.
|
||||||
|
|
||||||
|
:attr:`scale_y` is a :class:`~kivy.properties.NumericProperty`
|
||||||
|
and defaults to `1.5`.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, **kwargs):
|
||||||
|
super().__init__(**kwargs)
|
||||||
|
self.set_default_values()
|
||||||
|
|
||||||
|
def set_default_values(self):
|
||||||
|
"""Sets default scaled and transparency values."""
|
||||||
|
|
||||||
|
self.scale_value_x = self.scale_x
|
||||||
|
self.scale_value_y = self.scale_y
|
||||||
|
self.opacity = 0
|
||||||
|
|
||||||
|
def on_dismiss(self, *args):
|
||||||
|
"""Called when a dialog closed."""
|
||||||
|
|
||||||
|
self.set_default_values()
|
||||||
|
|
||||||
|
def on_open(self, *args):
|
||||||
|
"""Called when a dialog opened."""
|
||||||
|
|
||||||
|
Animation(
|
||||||
|
opacity=1,
|
||||||
|
scale_value_x=1,
|
||||||
|
scale_value_y=1,
|
||||||
|
t=self.show_transition,
|
||||||
|
d=self.show_duration,
|
||||||
|
).start(self)
|
||||||
|
|
||||||
|
|
||||||
|
class MotionShackBehavior(StencilBehavior, MotionBase):
|
||||||
|
"""
|
||||||
|
The base class for the behavior of the movement of snack bars.
|
||||||
|
|
||||||
|
For more information, see in the
|
||||||
|
:class:`~MotionBase` class and
|
||||||
|
:class:`~kivy.uix.behaviors.stencil_behavior.StencilBehavior` class
|
||||||
|
documentation.
|
||||||
|
"""
|
||||||
|
|
||||||
|
_interval = 0
|
||||||
|
_height = 0
|
||||||
|
|
||||||
|
def on_dismiss(self, *args):
|
||||||
|
"""Called when a snackbar closed."""
|
||||||
|
|
||||||
|
def remove_snackbar(*args):
|
||||||
|
Window.parent.remove_widget(self)
|
||||||
|
self.height = self._height
|
||||||
|
self.dispatch("on_dismiss")
|
||||||
|
|
||||||
|
Clock.unschedule(self._wait_interval)
|
||||||
|
anim = Animation(
|
||||||
|
opacity=0,
|
||||||
|
height=0,
|
||||||
|
t=self.hide_transition,
|
||||||
|
d=self.hide_duration,
|
||||||
|
)
|
||||||
|
anim.bind(on_complete=remove_snackbar)
|
||||||
|
anim.start(self)
|
||||||
|
|
||||||
|
def on_open(self, *args):
|
||||||
|
"""Called when a snackbar opened."""
|
||||||
|
|
||||||
|
def open(*args):
|
||||||
|
self._height = self.height
|
||||||
|
self.height = 0
|
||||||
|
anim = Animation(
|
||||||
|
opacity=1,
|
||||||
|
height=self._height,
|
||||||
|
t=self.show_transition,
|
||||||
|
d=self.show_duration,
|
||||||
|
)
|
||||||
|
anim.bind(
|
||||||
|
on_complete=lambda *args: Clock.schedule_interval(
|
||||||
|
self._wait_interval, 1
|
||||||
|
)
|
||||||
|
)
|
||||||
|
anim.start(self)
|
||||||
|
|
||||||
|
Clock.schedule_once(open)
|
||||||
|
self.dispatch("on_open")
|
||||||
|
|
||||||
|
def _wait_interval(self, interval):
|
||||||
|
self._interval += interval
|
||||||
|
if self._interval > self.duration:
|
||||||
|
self.dismiss()
|
||||||
|
self._interval = 0
|
|
@ -413,7 +413,12 @@ class CommonRipple:
|
||||||
|
|
||||||
|
|
||||||
class RectangularRippleBehavior(CommonRipple):
|
class RectangularRippleBehavior(CommonRipple):
|
||||||
"""Class implements a rectangular ripple effect."""
|
"""
|
||||||
|
Class implements a rectangular ripple effect.
|
||||||
|
|
||||||
|
For more information, see in the :class:`~kivymd.uix.behavior.CommonRipple`
|
||||||
|
class documentation.
|
||||||
|
"""
|
||||||
|
|
||||||
ripple_scale = NumericProperty(2.75)
|
ripple_scale = NumericProperty(2.75)
|
||||||
"""
|
"""
|
||||||
|
@ -472,7 +477,12 @@ class RectangularRippleBehavior(CommonRipple):
|
||||||
|
|
||||||
|
|
||||||
class CircularRippleBehavior(CommonRipple):
|
class CircularRippleBehavior(CommonRipple):
|
||||||
"""Class implements a circular ripple effect."""
|
"""
|
||||||
|
Class implements a circular ripple effect.
|
||||||
|
|
||||||
|
For more information, see in the :class:`~kivymd.uix.behavior.CommonRipple`
|
||||||
|
class documentation.
|
||||||
|
"""
|
||||||
|
|
||||||
ripple_scale = NumericProperty(1)
|
ripple_scale = NumericProperty(1)
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -91,6 +91,10 @@ KivyMD
|
||||||
|
|
||||||
|
|
||||||
Test().run()
|
Test().run()
|
||||||
|
|
||||||
|
.. warning:: Do not use `RotateBehavior` class with classes that inherited`
|
||||||
|
from `CommonElevationBehavior` class. `CommonElevationBehavior` classes
|
||||||
|
by default contains attributes for rotate widget.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
__all__ = ("RotateBehavior",)
|
__all__ = ("RotateBehavior",)
|
||||||
|
|
|
@ -105,12 +105,16 @@ KivyMD
|
||||||
|
|
||||||
|
|
||||||
Test().run()
|
Test().run()
|
||||||
|
|
||||||
|
.. warning:: Do not use `ScaleBehavior` class with classes that inherited`
|
||||||
|
from `CommonElevationBehavior` class. `CommonElevationBehavior` classes
|
||||||
|
by default contains attributes for scale widget.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
__all__ = ("ScaleBehavior",)
|
__all__ = ("ScaleBehavior",)
|
||||||
|
|
||||||
from kivy.lang import Builder
|
from kivy.lang import Builder
|
||||||
from kivy.properties import NumericProperty
|
from kivy.properties import ListProperty, NumericProperty
|
||||||
|
|
||||||
Builder.load_string(
|
Builder.load_string(
|
||||||
"""
|
"""
|
||||||
|
@ -120,8 +124,11 @@ Builder.load_string(
|
||||||
Scale:
|
Scale:
|
||||||
x: self.scale_value_x
|
x: self.scale_value_x
|
||||||
y: self.scale_value_y
|
y: self.scale_value_y
|
||||||
z: self.scale_value_x
|
z: self.scale_value_z
|
||||||
origin: self.center
|
origin:
|
||||||
|
self.center \
|
||||||
|
if not self.scale_value_center else \
|
||||||
|
self.scale_value_center
|
||||||
canvas.after:
|
canvas.after:
|
||||||
PopMatrix
|
PopMatrix
|
||||||
"""
|
"""
|
||||||
|
@ -154,3 +161,15 @@ class ScaleBehavior:
|
||||||
:attr:`scale_value_z` is an :class:`~kivy.properties.NumericProperty`
|
:attr:`scale_value_z` is an :class:`~kivy.properties.NumericProperty`
|
||||||
and defaults to `1`.
|
and defaults to `1`.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
scale_value_center = ListProperty()
|
||||||
|
"""
|
||||||
|
Origin of the scale.
|
||||||
|
|
||||||
|
.. versionadded:: 1.2.0
|
||||||
|
|
||||||
|
The format of the origin can be either (x, y) or (x, y, z).
|
||||||
|
|
||||||
|
:attr:`scale_value_center` is an :class:`~kivy.properties.NumericProperty`
|
||||||
|
and defaults to `[]`.
|
||||||
|
"""
|
||||||
|
|
|
@ -22,7 +22,7 @@ Usage
|
||||||
from kivymd.uix.button import MDRaisedButton
|
from kivymd.uix.button import MDRaisedButton
|
||||||
|
|
||||||
KV = '''
|
KV = '''
|
||||||
Screen:
|
MDScreen:
|
||||||
|
|
||||||
MyButton:
|
MyButton:
|
||||||
text: "PRESS ME"
|
text: "PRESS ME"
|
||||||
|
@ -74,6 +74,7 @@ class TouchBehavior:
|
||||||
|
|
||||||
def create_clock(self, widget, touch, *args):
|
def create_clock(self, widget, touch, *args):
|
||||||
if self.collide_point(touch.x, touch.y):
|
if self.collide_point(touch.x, touch.y):
|
||||||
|
if "event" not in touch.ud:
|
||||||
callback = partial(self.on_long_touch, touch)
|
callback = partial(self.on_long_touch, touch)
|
||||||
Clock.schedule_once(callback, self.duration_long_touch)
|
Clock.schedule_once(callback, self.duration_long_touch)
|
||||||
touch.ud["event"] = callback
|
touch.ud["event"] = callback
|
||||||
|
@ -85,10 +86,9 @@ class TouchBehavior:
|
||||||
|
|
||||||
def delete_clock(self, widget, touch, *args):
|
def delete_clock(self, widget, touch, *args):
|
||||||
if self.collide_point(touch.x, touch.y):
|
if self.collide_point(touch.x, touch.y):
|
||||||
try:
|
if "event" in touch.ud:
|
||||||
Clock.unschedule(touch.ud["event"])
|
Clock.unschedule(touch.ud["event"])
|
||||||
except KeyError:
|
del touch.ud["event"]
|
||||||
pass
|
|
||||||
|
|
||||||
def on_long_touch(self, touch, *args):
|
def on_long_touch(self, touch, *args):
|
||||||
"""Called when the widget is pressed for a long time."""
|
"""Called when the widget is pressed for a long time."""
|
||||||
|
|
|
@ -274,12 +274,19 @@ with open(
|
||||||
Builder.load_string(kv_file.read())
|
Builder.load_string(kv_file.read())
|
||||||
|
|
||||||
|
|
||||||
class MDBottomNavigationHeader(
|
class MDBottomNavigationHeader(ButtonBehavior, MDAnchorLayout):
|
||||||
ThemableBehavior, ButtonBehavior, MDAnchorLayout
|
"""
|
||||||
):
|
Bottom navigation header class.
|
||||||
|
|
||||||
|
For more information, see in the
|
||||||
|
:class:`~kivy.uix.behaviors.ButtonBehavior` and
|
||||||
|
:class:`~kivymd.uix.anchorlayout.MDAnchorLayout`
|
||||||
|
classes documentation.
|
||||||
|
"""
|
||||||
|
|
||||||
panel_color = ColorProperty([1, 1, 1, 0])
|
panel_color = ColorProperty([1, 1, 1, 0])
|
||||||
"""
|
"""
|
||||||
Panel color of bottom navigation.
|
Panel color of bottom navigation in (r, g, b, a) or string format.
|
||||||
|
|
||||||
:attr:`panel_color` is an :class:`~kivy.properties.ColorProperty`
|
:attr:`panel_color` is an :class:`~kivy.properties.ColorProperty`
|
||||||
and defaults to `[1, 1, 1, 0]`.
|
and defaults to `[1, 1, 1, 0]`.
|
||||||
|
@ -307,7 +314,8 @@ class MDBottomNavigationHeader(
|
||||||
|
|
||||||
text_color_normal = ColorProperty([1, 1, 1, 1])
|
text_color_normal = ColorProperty([1, 1, 1, 1])
|
||||||
"""
|
"""
|
||||||
Text color of the label when it is not selected.
|
Text color in (r, g, b, a) or string format of the label when it is not
|
||||||
|
selected.
|
||||||
|
|
||||||
:attr:`text_color_normal` is an :class:`~kivy.properties.ColorProperty`
|
:attr:`text_color_normal` is an :class:`~kivy.properties.ColorProperty`
|
||||||
and defaults to `[1, 1, 1, 1]`.
|
and defaults to `[1, 1, 1, 1]`.
|
||||||
|
@ -315,7 +323,7 @@ class MDBottomNavigationHeader(
|
||||||
|
|
||||||
text_color_active = ColorProperty([1, 1, 1, 1])
|
text_color_active = ColorProperty([1, 1, 1, 1])
|
||||||
"""
|
"""
|
||||||
Text color of the label when it is selected.
|
Text color in (r, g, b, a) or string format of the label when it is selected.
|
||||||
|
|
||||||
:attr:`text_color_active` is an :class:`~kivy.properties.ColorProperty`
|
:attr:`text_color_active` is an :class:`~kivy.properties.ColorProperty`
|
||||||
and defaults to `[1, 1, 1, 1]`.
|
and defaults to `[1, 1, 1, 1]`.
|
||||||
|
@ -323,7 +331,8 @@ class MDBottomNavigationHeader(
|
||||||
|
|
||||||
selected_color_background = ColorProperty(None)
|
selected_color_background = ColorProperty(None)
|
||||||
"""
|
"""
|
||||||
The background color of the highlighted item when using Material Design v3.
|
The background color in (r, g, b, a) or string format of the highlighted
|
||||||
|
item when using Material Design v3.
|
||||||
|
|
||||||
.. versionadded:: 1.0.0
|
.. versionadded:: 1.0.0
|
||||||
|
|
||||||
|
@ -384,10 +393,13 @@ class MDBottomNavigationHeader(
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class MDTab(MDScreen, ThemableBehavior):
|
class MDTab(MDScreen):
|
||||||
"""
|
"""
|
||||||
A tab is simply a screen with meta information that defines the content
|
A tab is simply a screen with meta information that defines the content
|
||||||
that goes in the tab header.
|
that goes in the tab header.
|
||||||
|
|
||||||
|
For more information, see in the
|
||||||
|
:class:`~kivymd.uix.screen.MDScreen` class documentation.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
__events__ = (
|
__events__ = (
|
||||||
|
@ -524,6 +536,10 @@ class TabbedPanelBase(
|
||||||
A class that contains all variables a :class:`~kivy.properties.TabPannel`
|
A class that contains all variables a :class:`~kivy.properties.TabPannel`
|
||||||
must have. It is here so I (zingballyhoo) don't get mad about
|
must have. It is here so I (zingballyhoo) don't get mad about
|
||||||
the :class:`~kivy.properties.TabbedPannels` not being DRY.
|
the :class:`~kivy.properties.TabbedPannels` not being DRY.
|
||||||
|
|
||||||
|
For more information, see in the :class:`~kivymd.theming.ThemableBehavior`
|
||||||
|
and :class:`~kivymd.uix.behaviors.SpecificBackgroundColorBehavior`
|
||||||
|
and :class:`~kivy.uix.boxlayout.BoxLayout` classes documentation.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
current = StringProperty(None)
|
current = StringProperty(None)
|
||||||
|
@ -555,6 +571,10 @@ class MDBottomNavigation(DeclarativeBehavior, TabbedPanelBase):
|
||||||
A bottom navigation that is implemented by delegating all items to a
|
A bottom navigation that is implemented by delegating all items to a
|
||||||
:class:`~kivy.uix.screenmanager.ScreenManager`.
|
:class:`~kivy.uix.screenmanager.ScreenManager`.
|
||||||
|
|
||||||
|
For more information, see in the
|
||||||
|
:class:`~kivymd.uix.behaviors.DeclarativeBehavior` and
|
||||||
|
:class:`~TabbedPanelBase` classes documentation.
|
||||||
|
|
||||||
:Events:
|
:Events:
|
||||||
:attr:`on_switch_tabs`
|
:attr:`on_switch_tabs`
|
||||||
Called when switching tabs. Returns the object of the tab to be
|
Called when switching tabs. Returns the object of the tab to be
|
||||||
|
@ -856,7 +876,5 @@ class MDBottomNavigation(DeclarativeBehavior, TabbedPanelBase):
|
||||||
return bottom_navigation_item
|
return bottom_navigation_item
|
||||||
|
|
||||||
|
|
||||||
class MDBottomNavigationBar(
|
class MDBottomNavigationBar(CommonElevationBehavior, MDFloatLayout):
|
||||||
ThemableBehavior, CommonElevationBehavior, MDFloatLayout
|
|
||||||
):
|
|
||||||
pass
|
pass
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
# NOQA F401
|
# NOQA F401
|
||||||
from .bottomsheet import (
|
from .bottomsheet import (
|
||||||
GridBottomSheetItem,
|
|
||||||
MDBottomSheet,
|
MDBottomSheet,
|
||||||
|
MDBottomSheetContent,
|
||||||
|
MDBottomSheetDragHandle,
|
||||||
|
MDBottomSheetDragHandleButton,
|
||||||
|
MDBottomSheetDragHandleTitle,
|
||||||
MDCustomBottomSheet,
|
MDCustomBottomSheet,
|
||||||
MDGridBottomSheet,
|
MDGridBottomSheet,
|
||||||
MDListBottomSheet,
|
MDListBottomSheet,
|
||||||
|
|
|
@ -1,73 +1,42 @@
|
||||||
#:import Window kivy.core.window.Window
|
<MDBottomSheetContent>
|
||||||
|
size_hint_y: None
|
||||||
|
height: self.minimum_height
|
||||||
|
|
||||||
|
|
||||||
<SheetList>
|
<MDBottomSheetDragHandle>
|
||||||
|
orientation: "vertical"
|
||||||
|
size_hint_y: None
|
||||||
|
height: self.minimum_height
|
||||||
|
padding: "16dp", "8dp", "16dp", "16dp"
|
||||||
|
|
||||||
MDGridLayout:
|
BottomSheetDragHandle:
|
||||||
id: box_sheet_list
|
md_bg_color:
|
||||||
cols: 1
|
app.theme_cls.disabled_hint_text_color \
|
||||||
adaptive_height: True
|
if not root.drag_handle_color else \
|
||||||
padding: 0, 0, 0, "96dp"
|
root.drag_handle_color
|
||||||
|
size_hint: None, None
|
||||||
|
size: "32dp", "4dp"
|
||||||
|
radius: 4
|
||||||
|
pos_hint: {"center_x": .5}
|
||||||
|
|
||||||
|
BottomSheetDragHandleContainer:
|
||||||
|
id: header_container
|
||||||
|
size_hint_y: None
|
||||||
|
height: self.minimum_height
|
||||||
|
|
||||||
|
|
||||||
<MDBottomSheet>
|
<MDBottomSheet>
|
||||||
md_bg_color: root.value_transparent
|
orientation: "vertical"
|
||||||
_upper_padding: _upper_padding
|
md_bg_color: root.bg_color if root.bg_color else app.theme_cls.bg_darkest
|
||||||
_gl_content: _gl_content
|
radius: 16, 16, 0, 0
|
||||||
_position_content: Window.height
|
padding: 0, "8dp", 0, 0
|
||||||
|
|
||||||
MDBoxLayout:
|
MDBoxLayout:
|
||||||
orientation: "vertical"
|
id: drag_handle_container
|
||||||
padding: 0, 1, 0, 0
|
|
||||||
|
|
||||||
BsPadding:
|
|
||||||
id: _upper_padding
|
|
||||||
size_hint_y: None
|
size_hint_y: None
|
||||||
height: root.height - min(root.width * 9 / 16, root._gl_content.height)
|
height: self.minimum_height
|
||||||
on_release: root.dismiss()
|
|
||||||
|
|
||||||
BottomSheetContent:
|
MDBoxLayout:
|
||||||
id: _gl_content
|
id: container
|
||||||
size_hint_y: None
|
size_hint_y: None
|
||||||
cols: 1
|
height: self.minimum_height
|
||||||
md_bg_color: 0, 0, 0, 0
|
|
||||||
|
|
||||||
canvas:
|
|
||||||
Color:
|
|
||||||
rgba: root.theme_cls.bg_normal if not root.bg_color else root.bg_color
|
|
||||||
RoundedRectangle:
|
|
||||||
pos: self.pos
|
|
||||||
size: self.size
|
|
||||||
radius:
|
|
||||||
[
|
|
||||||
(root.radius, root.radius) if root.radius_from == "top_left" or root.radius_from == "top" else (0, 0),
|
|
||||||
(root.radius, root.radius) if root.radius_from == "top_right" or root.radius_from == "top" else (0, 0),
|
|
||||||
(root.radius, root.radius) if root.radius_from == "bottom_right" or root.radius_from == "bottom" else (0, 0),
|
|
||||||
(root.radius, root.radius) if root.radius_from == "bottom_left" or root.radius_from == "bottom" else (0, 0)
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
<ListBottomSheetIconLeft>
|
|
||||||
theme_text_color: "Primary"
|
|
||||||
pos_hint: {"center_x": .5, "center_y": .5}
|
|
||||||
|
|
||||||
|
|
||||||
<GridBottomSheetItem>
|
|
||||||
orientation: "vertical"
|
|
||||||
padding: 0, dp(24), 0, 0
|
|
||||||
size_hint_y: None
|
|
||||||
size: dp(64), dp(96)
|
|
||||||
|
|
||||||
AnchorLayout:
|
|
||||||
anchor_x: "center"
|
|
||||||
|
|
||||||
MDIconButton:
|
|
||||||
icon: root.source
|
|
||||||
user_font_size: root.icon_size
|
|
||||||
on_release: root.dispatch("on_release")
|
|
||||||
|
|
||||||
MDLabel:
|
|
||||||
font_style: "Caption"
|
|
||||||
theme_text_color: "Secondary"
|
|
||||||
text: root.caption
|
|
||||||
halign: "center"
|
|
File diff suppressed because it is too large
Load Diff
|
@ -87,11 +87,14 @@ __all__ = ("MDBoxLayout",)
|
||||||
|
|
||||||
from kivy.uix.boxlayout import BoxLayout
|
from kivy.uix.boxlayout import BoxLayout
|
||||||
|
|
||||||
|
from kivymd.theming import ThemableBehavior
|
||||||
from kivymd.uix import MDAdaptiveWidget
|
from kivymd.uix import MDAdaptiveWidget
|
||||||
from kivymd.uix.behaviors import DeclarativeBehavior
|
from kivymd.uix.behaviors import DeclarativeBehavior
|
||||||
|
|
||||||
|
|
||||||
class MDBoxLayout(DeclarativeBehavior, BoxLayout, MDAdaptiveWidget):
|
class MDBoxLayout(
|
||||||
|
DeclarativeBehavior, ThemableBehavior, BoxLayout, MDAdaptiveWidget
|
||||||
|
):
|
||||||
"""
|
"""
|
||||||
Box layout class.
|
Box layout class.
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
canvas:
|
canvas:
|
||||||
Clear
|
Clear
|
||||||
Color:
|
Color:
|
||||||
|
group: "bg-color"
|
||||||
rgba:
|
rgba:
|
||||||
self._md_bg_color \
|
self._md_bg_color \
|
||||||
if not self.disabled else \
|
if not self.disabled else \
|
||||||
|
@ -12,6 +13,7 @@
|
||||||
source: self.source if hasattr(self, "source") else ""
|
source: self.source if hasattr(self, "source") else ""
|
||||||
radius: [root._radius, ]
|
radius: [root._radius, ]
|
||||||
Color:
|
Color:
|
||||||
|
group: "outline-color"
|
||||||
rgba:
|
rgba:
|
||||||
root._line_color \
|
root._line_color \
|
||||||
if not root.disabled else \
|
if not root.disabled else \
|
||||||
|
@ -92,9 +94,11 @@
|
||||||
root.theme_cls.disabled_hint_text_color \
|
root.theme_cls.disabled_hint_text_color \
|
||||||
if not root.disabled_color else \
|
if not root.disabled_color else \
|
||||||
root.disabled_color
|
root.disabled_color
|
||||||
|
# Fix https://github.com/kivymd/KivyMD/issues/1448
|
||||||
on_icon:
|
# TODO: Perhaps this change may affect other widgets.
|
||||||
if self.icon not in md_icons.keys(): self.size_hint = (1, 1)
|
# You need to create tests.
|
||||||
|
# on_icon:
|
||||||
|
# if self.icon not in md_icons.keys(): self.size_hint = (1, 1)
|
||||||
theme_text_color: root._theme_icon_color
|
theme_text_color: root._theme_icon_color
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -679,6 +679,15 @@ from kivy.weakproxy import WeakProxy
|
||||||
from kivymd import uix_path
|
from kivymd import uix_path
|
||||||
from kivymd.color_definitions import text_colors
|
from kivymd.color_definitions import text_colors
|
||||||
from kivymd.font_definitions import theme_font_styles
|
from kivymd.font_definitions import theme_font_styles
|
||||||
|
from kivymd.material_resources import (
|
||||||
|
FLOATING_ACTION_BUTTON_M2_ELEVATION,
|
||||||
|
FLOATING_ACTION_BUTTON_M2_OFFSET,
|
||||||
|
FLOATING_ACTION_BUTTON_M3_ELEVATION,
|
||||||
|
FLOATING_ACTION_BUTTON_M3_OFFSET,
|
||||||
|
FLOATING_ACTION_BUTTON_M3_SOFTNESS,
|
||||||
|
RAISED_BUTTON_OFFSET,
|
||||||
|
RAISED_BUTTON_SOFTNESS,
|
||||||
|
)
|
||||||
from kivymd.theming import ThemableBehavior
|
from kivymd.theming import ThemableBehavior
|
||||||
from kivymd.uix.behaviors import (
|
from kivymd.uix.behaviors import (
|
||||||
CommonElevationBehavior,
|
CommonElevationBehavior,
|
||||||
|
@ -704,62 +713,6 @@ theme_text_color_options = (
|
||||||
"ContrastParentBackground",
|
"ContrastParentBackground",
|
||||||
)
|
)
|
||||||
|
|
||||||
# FIXME: If you set a new elevation value for the button
|
|
||||||
# (press the "Set elevation" button), then disable the button
|
|
||||||
# (press the "Disabled" button), and then enable the button
|
|
||||||
# (press the "Undisabled" button), then the previously set elevation value is
|
|
||||||
# reset to zero.
|
|
||||||
# In addition, if you set a new elevation value
|
|
||||||
# (press the "Set elevation" button) and click on the button for which we set
|
|
||||||
# the elevation value, then the new elevation value will receive the previous
|
|
||||||
# elevation value. This problem is only related to the buttons.
|
|
||||||
# For example, there is no such problem for the MDCard widget.
|
|
||||||
|
|
||||||
"""
|
|
||||||
from kivy.lang import Builder
|
|
||||||
|
|
||||||
from kivymd.app import MDApp
|
|
||||||
|
|
||||||
KV = '''
|
|
||||||
MDScreen:
|
|
||||||
|
|
||||||
MDRaisedButton:
|
|
||||||
size_hint: .5, .5
|
|
||||||
id: button
|
|
||||||
pos_hint: {"center_x": .5, "center_y": .5}
|
|
||||||
elevation: 0
|
|
||||||
|
|
||||||
MDBoxLayout:
|
|
||||||
adaptive_size: True
|
|
||||||
pos_hint: {"center_x": .5}
|
|
||||||
spacing: 12
|
|
||||||
padding: 12
|
|
||||||
|
|
||||||
MDRaisedButton:
|
|
||||||
text: "Set elevation"
|
|
||||||
pos_hint: {"center_x": .5, "bottom": 1}
|
|
||||||
on_release: button.elevation = 4
|
|
||||||
|
|
||||||
MDRaisedButton:
|
|
||||||
text: "Disabled"
|
|
||||||
pos_hint: {"center_x": .5, "bottom": 1}
|
|
||||||
on_release: button.disabled = True
|
|
||||||
|
|
||||||
MDRaisedButton:
|
|
||||||
text: "Undisabled"
|
|
||||||
pos_hint: {"center_x": .5, "bottom": 1}
|
|
||||||
on_release: button.disabled = False
|
|
||||||
'''
|
|
||||||
|
|
||||||
|
|
||||||
class Test(MDApp):
|
|
||||||
def build(self):
|
|
||||||
return Builder.load_string(KV)
|
|
||||||
|
|
||||||
|
|
||||||
Test().run()
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
class BaseButton(
|
class BaseButton(
|
||||||
DeclarativeBehavior,
|
DeclarativeBehavior,
|
||||||
|
@ -772,7 +725,12 @@ class BaseButton(
|
||||||
Base class for all buttons.
|
Base class for all buttons.
|
||||||
|
|
||||||
For more information, see in the
|
For more information, see in the
|
||||||
:class:`~kivy.uix.anchorlayout.AnchorLayout` class documentation.
|
:class:`~kivymd.uix.behaviors.DeclarativeBehavior` and
|
||||||
|
:class:`~kivymd.uix.behaviors.RectangularRippleBehavior` and
|
||||||
|
:class:`~kivymd.theming.ThemableBehavior` and
|
||||||
|
:class:`~kivy.uix.behaviors.ButtonBehavior` and
|
||||||
|
:class:`~kivy.uix.anchorlayout.AnchorLayout`
|
||||||
|
classes documentation.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
padding = VariableListProperty([dp(16), dp(8), dp(16), dp(8)])
|
padding = VariableListProperty([dp(16), dp(8), dp(16), dp(8)])
|
||||||
|
@ -1208,7 +1166,7 @@ class ButtonElevationBehaviour(CommonElevationBehavior):
|
||||||
|
|
||||||
_elevation_raised = NumericProperty()
|
_elevation_raised = NumericProperty()
|
||||||
_anim_raised = ObjectProperty(None, allownone=True)
|
_anim_raised = ObjectProperty(None, allownone=True)
|
||||||
_default_elevation = 3
|
_default_elevation = 2
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
super().__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
|
@ -1220,7 +1178,8 @@ class ButtonElevationBehaviour(CommonElevationBehavior):
|
||||||
self.on_disabled(self, self.disabled)
|
self.on_disabled(self, self.disabled)
|
||||||
|
|
||||||
def create_anim_raised(self, *args) -> None:
|
def create_anim_raised(self, *args) -> None:
|
||||||
self._elevation_raised = self.elevation + 1.2
|
if self.elevation:
|
||||||
|
self._elevation_raised = self.elevation
|
||||||
self._anim_raised = Animation(elevation=self.elevation + 1, d=0.15)
|
self._anim_raised = Animation(elevation=self.elevation + 1, d=0.15)
|
||||||
|
|
||||||
def on_touch_down(self, touch):
|
def on_touch_down(self, touch):
|
||||||
|
@ -1231,21 +1190,21 @@ class ButtonElevationBehaviour(CommonElevationBehavior):
|
||||||
return False
|
return False
|
||||||
if self in touch.ud:
|
if self in touch.ud:
|
||||||
return False
|
return False
|
||||||
if self._anim_raised:
|
if self._anim_raised and self.elevation:
|
||||||
self._anim_raised.start(self)
|
self._anim_raised.start(self)
|
||||||
return super().on_touch_down(touch)
|
return super().on_touch_down(touch)
|
||||||
|
|
||||||
def on_touch_up(self, touch):
|
def on_touch_up(self, touch):
|
||||||
if not self.disabled:
|
if not self.disabled:
|
||||||
if touch.grab_current is not self:
|
if self in touch.ud:
|
||||||
self.stop_elevation_anim()
|
self.stop_elevation_anim()
|
||||||
return super().on_touch_up(touch)
|
return super().on_touch_up(touch)
|
||||||
self.stop_elevation_anim()
|
|
||||||
return super().on_touch_up(touch)
|
return super().on_touch_up(touch)
|
||||||
|
|
||||||
def stop_elevation_anim(self):
|
def stop_elevation_anim(self):
|
||||||
Animation.cancel_all(self, "elevation")
|
Animation.cancel_all(self, "elevation")
|
||||||
self.elevation = self._elevation_raised - 1
|
if self._anim_raised and self.elevation:
|
||||||
|
self.elevation = self._elevation_raised
|
||||||
|
|
||||||
|
|
||||||
class ButtonContentsText:
|
class ButtonContentsText:
|
||||||
|
@ -1313,6 +1272,10 @@ class MDFlatButton(BaseButton, ButtonContentsText):
|
||||||
"""
|
"""
|
||||||
A flat rectangular button with (by default) no border or background.
|
A flat rectangular button with (by default) no border or background.
|
||||||
Text is the default text color.
|
Text is the default text color.
|
||||||
|
|
||||||
|
For more information, see in the
|
||||||
|
:class:`~BaseButton` and :class:`~ButtonContentsText`
|
||||||
|
classes documentation.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
padding = VariableListProperty([dp(8), dp(8), dp(8), dp(8)])
|
padding = VariableListProperty([dp(8), dp(8), dp(8), dp(8)])
|
||||||
|
@ -1334,6 +1297,12 @@ class MDRaisedButton(BaseButton, ButtonElevationBehaviour, ButtonContentsText):
|
||||||
"""
|
"""
|
||||||
A flat button with (by default) a primary color fill and matching
|
A flat button with (by default) a primary color fill and matching
|
||||||
color text.
|
color text.
|
||||||
|
|
||||||
|
For more information, see in the
|
||||||
|
:class:`~BaseButton` and
|
||||||
|
:class:`~ButtonElevationBehaviour` and
|
||||||
|
:class:`~ButtonContentsText`
|
||||||
|
classes documentation.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# FIXME: Move the underlying attributes to the :class:`~BaseButton` class.
|
# FIXME: Move the underlying attributes to the :class:`~BaseButton` class.
|
||||||
|
@ -1345,15 +1314,19 @@ class MDRaisedButton(BaseButton, ButtonElevationBehaviour, ButtonContentsText):
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
self.shadow_softness = 8
|
self.shadow_softness = RAISED_BUTTON_SOFTNESS
|
||||||
self.shadow_offset = (0, 2)
|
self.shadow_offset = RAISED_BUTTON_OFFSET
|
||||||
self.shadow_radius = self._radius * 2
|
# self.shadow_radius = self._radius * 2
|
||||||
|
|
||||||
|
|
||||||
class MDRectangleFlatButton(BaseButton, ButtonContentsText):
|
class MDRectangleFlatButton(BaseButton, ButtonContentsText):
|
||||||
"""
|
"""
|
||||||
A flat button with (by default) a primary color border and primary
|
A flat button with (by default) a primary color border and primary
|
||||||
color text.
|
color text.
|
||||||
|
|
||||||
|
For more information, see in the
|
||||||
|
:class:`~BaseButton` and :class:`~ButtonContentsText`
|
||||||
|
classes documentation.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
_default_line_color = None
|
_default_line_color = None
|
||||||
|
@ -1368,6 +1341,12 @@ class MDRectangleFlatIconButton(
|
||||||
"""
|
"""
|
||||||
A flat button with (by default) a primary color border, primary color text
|
A flat button with (by default) a primary color border, primary color text
|
||||||
and a primary color icon on the left.
|
and a primary color icon on the left.
|
||||||
|
|
||||||
|
For more information, see in the
|
||||||
|
:class:`~BaseButton` and
|
||||||
|
:class:`~OldButtonIconMixin` and
|
||||||
|
:class:`~ButtonContentsIconText`
|
||||||
|
classes documentation.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
_default_line_color = None
|
_default_line_color = None
|
||||||
|
@ -1382,6 +1361,10 @@ class MDRoundFlatButton(BaseButton, ButtonContentsText):
|
||||||
"""
|
"""
|
||||||
A flat button with (by default) fully rounded corners, a primary
|
A flat button with (by default) fully rounded corners, a primary
|
||||||
color border and primary color text.
|
color border and primary color text.
|
||||||
|
|
||||||
|
For more information, see in the
|
||||||
|
:class:`~BaseButton` and :class:`~ButtonContentsText`
|
||||||
|
classes documentation.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
_default_line_color = None
|
_default_line_color = None
|
||||||
|
@ -1400,6 +1383,12 @@ class MDRoundFlatIconButton(
|
||||||
"""
|
"""
|
||||||
A flat button with (by default) rounded corners, a primary color border,
|
A flat button with (by default) rounded corners, a primary color border,
|
||||||
primary color text and a primary color icon on the left.
|
primary color text and a primary color icon on the left.
|
||||||
|
|
||||||
|
For more information, see in the
|
||||||
|
:class:`~BaseButton` and
|
||||||
|
:class:`~OldButtonIconMixin` and
|
||||||
|
:class:`~ButtonContentsIconText`
|
||||||
|
classes documentation.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
_default_line_color = None
|
_default_line_color = None
|
||||||
|
@ -1418,6 +1407,10 @@ class MDFillRoundFlatButton(BaseButton, ButtonContentsText):
|
||||||
"""
|
"""
|
||||||
A flat button with (by default) rounded corners, a primary color fill
|
A flat button with (by default) rounded corners, a primary color fill
|
||||||
and primary color text.
|
and primary color text.
|
||||||
|
|
||||||
|
For more information, see in the
|
||||||
|
:class:`~BaseButton` and :class:`~ButtonContentsText`
|
||||||
|
classes documentation.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
_default_md_bg_color = None
|
_default_md_bg_color = None
|
||||||
|
@ -1436,6 +1429,12 @@ class MDFillRoundFlatIconButton(
|
||||||
"""
|
"""
|
||||||
A flat button with (by default) rounded corners, a primary color fill,
|
A flat button with (by default) rounded corners, a primary color fill,
|
||||||
primary color text and a primary color icon on the left.
|
primary color text and a primary color icon on the left.
|
||||||
|
|
||||||
|
For more information, see in the
|
||||||
|
:class:`~BaseButton` and
|
||||||
|
:class:`~OldButtonIconMixin` and
|
||||||
|
:class:`~ButtonContentsIconText`
|
||||||
|
classes documentation.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
_default_md_bg_color = None
|
_default_md_bg_color = None
|
||||||
|
@ -1451,7 +1450,14 @@ class MDFillRoundFlatIconButton(
|
||||||
|
|
||||||
|
|
||||||
class MDIconButton(BaseButton, OldButtonIconMixin, ButtonContentsIcon):
|
class MDIconButton(BaseButton, OldButtonIconMixin, ButtonContentsIcon):
|
||||||
"""A simple rounded icon button."""
|
"""
|
||||||
|
A simple rounded icon button.
|
||||||
|
|
||||||
|
For more information, see in the
|
||||||
|
:class:`~BaseButton` and
|
||||||
|
:class:`~OldButtonIconMixin` and
|
||||||
|
:class:`~ButtonContentsIcon` classes documentation.
|
||||||
|
"""
|
||||||
|
|
||||||
icon = StringProperty("checkbox-blank-circle")
|
icon = StringProperty("checkbox-blank-circle")
|
||||||
"""
|
"""
|
||||||
|
@ -1489,6 +1495,12 @@ class MDFloatingActionButton(
|
||||||
Implementation
|
Implementation
|
||||||
`FAB <https://m3.material.io/components/floating-action-button/overview>`_
|
`FAB <https://m3.material.io/components/floating-action-button/overview>`_
|
||||||
button.
|
button.
|
||||||
|
|
||||||
|
For more information, see in the
|
||||||
|
:class:`~BaseButton` and
|
||||||
|
:class:`~OldButtonIconMixin` and
|
||||||
|
:class:`~ButtonElevationBehaviour` and
|
||||||
|
:class:`~ButtonContentsIcon` classes documentation.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
type = OptionProperty("standard", options=["small", "large", "standard"])
|
type = OptionProperty("standard", options=["small", "large", "standard"])
|
||||||
|
@ -1530,10 +1542,13 @@ class MDFloatingActionButton(
|
||||||
def set__radius(self, *args) -> None:
|
def set__radius(self, *args) -> None:
|
||||||
if self.theme_cls.material_style == "M2":
|
if self.theme_cls.material_style == "M2":
|
||||||
self.shadow_radius = self.height / 2
|
self.shadow_radius = self.height / 2
|
||||||
|
self.elevation = FLOATING_ACTION_BUTTON_M2_ELEVATION
|
||||||
|
self.shadow_offset = FLOATING_ACTION_BUTTON_M2_OFFSET
|
||||||
self.rounded_button = True
|
self.rounded_button = True
|
||||||
else:
|
else:
|
||||||
self.shadow_softness = 8
|
self.shadow_softness = FLOATING_ACTION_BUTTON_M3_SOFTNESS
|
||||||
self.shadow_offset = (0, 2)
|
self.shadow_offset = FLOATING_ACTION_BUTTON_M3_OFFSET
|
||||||
|
self.elevation = FLOATING_ACTION_BUTTON_M3_ELEVATION
|
||||||
self.rounded_button = False
|
self.rounded_button = False
|
||||||
|
|
||||||
if self.type == "small":
|
if self.type == "small":
|
||||||
|
@ -1566,6 +1581,14 @@ class MDFloatingActionButton(
|
||||||
|
|
||||||
|
|
||||||
class MDTextButton(ButtonBehavior, MDLabel):
|
class MDTextButton(ButtonBehavior, MDLabel):
|
||||||
|
"""
|
||||||
|
Text button class.
|
||||||
|
|
||||||
|
For more information, see in the
|
||||||
|
:class:`~kivy.uix.behaviors.ButtonBehavior` and
|
||||||
|
:class:`~kivymd.uix.label.MDLabel` classes documentation.
|
||||||
|
"""
|
||||||
|
|
||||||
color = ColorProperty(None)
|
color = ColorProperty(None)
|
||||||
"""
|
"""
|
||||||
Button color in (r, g, b, a) or string format.
|
Button color in (r, g, b, a) or string format.
|
||||||
|
@ -1638,6 +1661,12 @@ class MDFloatingActionButtonSpeedDial(
|
||||||
For more information, see in the
|
For more information, see in the
|
||||||
:class:`~kivy.uix.floatlayout.FloatLayout` class documentation.
|
:class:`~kivy.uix.floatlayout.FloatLayout` class documentation.
|
||||||
|
|
||||||
|
For more information, see in the
|
||||||
|
:class:`~kivymd.uix.behaviors.DeclarativeBehavior` and
|
||||||
|
:class:`~kivymd.theming.ThemableBehavior` and
|
||||||
|
:class:`~kivy.uix.floatlayout.FloatLayout`
|
||||||
|
lasses documentation.
|
||||||
|
|
||||||
:Events:
|
:Events:
|
||||||
:attr:`on_open`
|
:attr:`on_open`
|
||||||
Called when a stack is opened.
|
Called when a stack is opened.
|
||||||
|
@ -1868,7 +1897,7 @@ class MDFloatingActionButtonSpeedDial(
|
||||||
"""
|
"""
|
||||||
Background color of root button in (r, g, b, a) or string format.
|
Background color of root button in (r, g, b, a) or string format.
|
||||||
|
|
||||||
.. code-clock:: kv
|
.. code-block:: kv
|
||||||
|
|
||||||
MDFloatingActionButtonSpeedDial:
|
MDFloatingActionButtonSpeedDial:
|
||||||
bg_color_root_button: "red"
|
bg_color_root_button: "red"
|
||||||
|
@ -1884,7 +1913,7 @@ class MDFloatingActionButtonSpeedDial(
|
||||||
"""
|
"""
|
||||||
Background color of the stack buttons in (r, g, b, a) or string format.
|
Background color of the stack buttons in (r, g, b, a) or string format.
|
||||||
|
|
||||||
.. code-clock:: kv
|
.. code-block:: kv
|
||||||
|
|
||||||
MDFloatingActionButtonSpeedDial:
|
MDFloatingActionButtonSpeedDial:
|
||||||
bg_color_root_button: "red"
|
bg_color_root_button: "red"
|
||||||
|
@ -1901,7 +1930,7 @@ class MDFloatingActionButtonSpeedDial(
|
||||||
"""
|
"""
|
||||||
The color icon of the stack buttons in (r, g, b, a) or string format.
|
The color icon of the stack buttons in (r, g, b, a) or string format.
|
||||||
|
|
||||||
.. code-clock:: kv
|
.. code-block:: kv
|
||||||
|
|
||||||
MDFloatingActionButtonSpeedDial:
|
MDFloatingActionButtonSpeedDial:
|
||||||
bg_color_root_button: "red"
|
bg_color_root_button: "red"
|
||||||
|
@ -1919,7 +1948,7 @@ class MDFloatingActionButtonSpeedDial(
|
||||||
"""
|
"""
|
||||||
The color icon of the root button in (r, g, b, a) or string format.
|
The color icon of the root button in (r, g, b, a) or string format.
|
||||||
|
|
||||||
.. code-clock:: kv
|
.. code-block:: kv
|
||||||
|
|
||||||
MDFloatingActionButtonSpeedDial:
|
MDFloatingActionButtonSpeedDial:
|
||||||
bg_color_root_button: "red"
|
bg_color_root_button: "red"
|
||||||
|
@ -1939,7 +1968,7 @@ class MDFloatingActionButtonSpeedDial(
|
||||||
Background color for the floating text of the buttons in (r, g, b, a)
|
Background color for the floating text of the buttons in (r, g, b, a)
|
||||||
or string format.
|
or string format.
|
||||||
|
|
||||||
.. code-clock:: kv
|
.. code-block:: kv
|
||||||
|
|
||||||
MDFloatingActionButtonSpeedDial:
|
MDFloatingActionButtonSpeedDial:
|
||||||
bg_hint_color: "red"
|
bg_hint_color: "red"
|
||||||
|
|
|
@ -94,6 +94,7 @@ An example of the implementation of a card in the style of material design versi
|
||||||
style=style,
|
style=style,
|
||||||
text=style.capitalize(),
|
text=style.capitalize(),
|
||||||
md_bg_color=styles[style],
|
md_bg_color=styles[style],
|
||||||
|
shadow_offset=(0, -1),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -152,10 +153,9 @@ An example of the implementation of a card in the style of material design versi
|
||||||
),
|
),
|
||||||
line_color=(0.2, 0.2, 0.2, 0.8),
|
line_color=(0.2, 0.2, 0.2, 0.8),
|
||||||
style=style,
|
style=style,
|
||||||
padding="4dp",
|
text=style.capitalize(),
|
||||||
size_hint=(None, None),
|
|
||||||
size=("200dp", "100dp"),
|
|
||||||
md_bg_color=styles[style],
|
md_bg_color=styles[style],
|
||||||
|
shadow_offset=(0, -1),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -699,7 +699,12 @@ from kivy.utils import get_color_from_hex
|
||||||
|
|
||||||
from kivymd import uix_path
|
from kivymd import uix_path
|
||||||
from kivymd.color_definitions import colors
|
from kivymd.color_definitions import colors
|
||||||
|
from kivymd.material_resources import (
|
||||||
|
CARD_STYLE_ELEVATED_M3_ELEVATION,
|
||||||
|
CARD_STYLE_OUTLINED_FILLED_M3_ELEVATION,
|
||||||
|
)
|
||||||
from kivymd.theming import ThemableBehavior
|
from kivymd.theming import ThemableBehavior
|
||||||
|
from kivymd.uix import MDAdaptiveWidget
|
||||||
from kivymd.uix.behaviors import (
|
from kivymd.uix.behaviors import (
|
||||||
BackgroundColorBehavior,
|
BackgroundColorBehavior,
|
||||||
CommonElevationBehavior,
|
CommonElevationBehavior,
|
||||||
|
@ -716,12 +721,17 @@ with open(
|
||||||
Builder.load_string(kv_file.read())
|
Builder.load_string(kv_file.read())
|
||||||
|
|
||||||
|
|
||||||
class MDSeparator(ThemableBehavior, MDBoxLayout):
|
class MDSeparator(MDBoxLayout):
|
||||||
"""A separator line."""
|
"""
|
||||||
|
A separator line.
|
||||||
|
|
||||||
|
For more information, see in the
|
||||||
|
:class:`~kivymd.uix.boxlayout.MDBoxLayout` class documentation.
|
||||||
|
"""
|
||||||
|
|
||||||
color = ColorProperty(None)
|
color = ColorProperty(None)
|
||||||
"""
|
"""
|
||||||
Separator color.
|
Separator color in (r, g, b, a) or string format.
|
||||||
|
|
||||||
:attr:`color` is a :class:`~kivy.properties.ColorProperty`
|
:attr:`color` is a :class:`~kivy.properties.ColorProperty`
|
||||||
and defaults to `None`.
|
and defaults to `None`.
|
||||||
|
@ -743,6 +753,7 @@ class MDSeparator(ThemableBehavior, MDBoxLayout):
|
||||||
|
|
||||||
class MDCard(
|
class MDCard(
|
||||||
DeclarativeBehavior,
|
DeclarativeBehavior,
|
||||||
|
MDAdaptiveWidget,
|
||||||
ThemableBehavior,
|
ThemableBehavior,
|
||||||
BackgroundColorBehavior,
|
BackgroundColorBehavior,
|
||||||
RectangularRippleBehavior,
|
RectangularRippleBehavior,
|
||||||
|
@ -750,6 +761,21 @@ class MDCard(
|
||||||
FocusBehavior,
|
FocusBehavior,
|
||||||
BoxLayout,
|
BoxLayout,
|
||||||
):
|
):
|
||||||
|
"""
|
||||||
|
Card class.
|
||||||
|
|
||||||
|
For more information, see in the
|
||||||
|
:class:`~kivymd.uix.behaviors.DeclarativeBehavior` and
|
||||||
|
:class:`~kivymd.uix.MDAdaptiveWidget` and
|
||||||
|
:class:`~kivymd.theming.ThemableBehavior` and
|
||||||
|
:class:`~kivymd.uix.behaviors.BackgroundColorBehavior` and
|
||||||
|
:class:`~kivymd.uix.behaviors.RectangularRippleBehavior` and
|
||||||
|
:class:`~kivymd.uix.behaviors.CommonElevationBehavior` and
|
||||||
|
:class:`~kivymd.uix.behaviors.FocusBehavior` and
|
||||||
|
:class:`~kivy.uix.boxlayout.BoxLayout` and
|
||||||
|
classes documentation.
|
||||||
|
"""
|
||||||
|
|
||||||
focus_behavior = BooleanProperty(False)
|
focus_behavior = BooleanProperty(False)
|
||||||
"""
|
"""
|
||||||
Using focus when hovering over a card.
|
Using focus when hovering over a card.
|
||||||
|
@ -824,9 +850,9 @@ class MDCard(
|
||||||
def set_elevation(self) -> None:
|
def set_elevation(self) -> None:
|
||||||
if self.theme_cls.material_style == "M3":
|
if self.theme_cls.material_style == "M3":
|
||||||
if self.style == "outlined" or self.style == "filled":
|
if self.style == "outlined" or self.style == "filled":
|
||||||
self.elevation = 0
|
self.elevation = CARD_STYLE_OUTLINED_FILLED_M3_ELEVATION
|
||||||
elif self.style == "elevated":
|
elif self.style == "elevated":
|
||||||
self.elevation = 2
|
self.elevation = CARD_STYLE_ELEVATED_M3_ELEVATION
|
||||||
|
|
||||||
def set_radius(self) -> None:
|
def set_radius(self) -> None:
|
||||||
if (
|
if (
|
||||||
|
@ -848,6 +874,11 @@ class MDCard(
|
||||||
|
|
||||||
class MDCardSwipe(MDRelativeLayout):
|
class MDCardSwipe(MDRelativeLayout):
|
||||||
"""
|
"""
|
||||||
|
Card swipe class.
|
||||||
|
|
||||||
|
For more information, see in the
|
||||||
|
:class:`~kivymd.uix.relativelayout.MDRelativeLayout` class documentation.
|
||||||
|
|
||||||
:Events:
|
:Events:
|
||||||
:attr:`on_swipe_complete`
|
:attr:`on_swipe_complete`
|
||||||
Called when a swipe of card is completed.
|
Called when a swipe of card is completed.
|
||||||
|
@ -1065,7 +1096,11 @@ class MDCardSwipe(MDRelativeLayout):
|
||||||
|
|
||||||
|
|
||||||
class MDCardSwipeFrontBox(MDCard):
|
class MDCardSwipeFrontBox(MDCard):
|
||||||
pass
|
"""
|
||||||
|
Card swipe front box.
|
||||||
|
|
||||||
|
For more information, see in the :class:`~MDCard` class documentation.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
class MDCardSwipeLayerBox(MDBoxLayout):
|
class MDCardSwipeLayerBox(MDBoxLayout):
|
||||||
|
|
|
@ -57,10 +57,11 @@ MDCarousel
|
||||||
from kivy.animation import Animation
|
from kivy.animation import Animation
|
||||||
from kivy.uix.carousel import Carousel
|
from kivy.uix.carousel import Carousel
|
||||||
|
|
||||||
|
from kivymd.theming import ThemableBehavior
|
||||||
from kivymd.uix.behaviors import DeclarativeBehavior
|
from kivymd.uix.behaviors import DeclarativeBehavior
|
||||||
|
|
||||||
|
|
||||||
class MDCarousel(DeclarativeBehavior, Carousel):
|
class MDCarousel(DeclarativeBehavior, ThemableBehavior, Carousel):
|
||||||
"""
|
"""
|
||||||
based on kivy's carousel.
|
based on kivy's carousel.
|
||||||
|
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
from .chip import MDChip # NOQA F401
|
from .chip import MDChip, MDChipText # NOQA F401
|
||||||
|
|
|
@ -1,110 +1,38 @@
|
||||||
<MDScalableCheckIcon>
|
|
||||||
scale_value_x: 0
|
|
||||||
scale_value_y: 0
|
|
||||||
scale_value_z: 0
|
|
||||||
|
|
||||||
|
|
||||||
<MDChip>
|
<MDChip>
|
||||||
size_hint_y: None
|
size_hint_y: None
|
||||||
height: "32dp"
|
height: "32dp"
|
||||||
spacing: "8dp"
|
|
||||||
adaptive_width: True
|
adaptive_width: True
|
||||||
radius: 16 if self.radius == [0, 0, 0, 0] else self.radius
|
radius:
|
||||||
padding:
|
16 \
|
||||||
"12dp" if not self.icon_left else "4dp", \
|
if self.radius == [0, 0, 0, 0] else \
|
||||||
0, \
|
(max(self.radius) if max(self.radius) < self.height / 2 else 16)
|
||||||
"12dp" if not self.icon_right else "8dp", \
|
|
||||||
0
|
|
||||||
md_bg_color:
|
md_bg_color:
|
||||||
|
( \
|
||||||
( \
|
( \
|
||||||
app.theme_cls.bg_darkest \
|
app.theme_cls.bg_darkest \
|
||||||
if app.theme_cls.theme_style == "Light" else \
|
if app.theme_cls.theme_style == "Light" else \
|
||||||
app.theme_cls.bg_light \
|
app.theme_cls.bg_light \
|
||||||
) \
|
) \
|
||||||
if not self.disabled else app.theme_cls.disabled_hint_text_color
|
if not self._origin_md_bg_color else \
|
||||||
|
self._origin_md_bg_color
|
||||||
canvas.before:
|
) \
|
||||||
Color:
|
if not self.disabled else app.theme_cls.disabled_primary_color
|
||||||
rgba:
|
line_color:
|
||||||
|
app.theme_cls.disabled_hint_text_color \
|
||||||
|
if self.disabled else ( \
|
||||||
|
self._origin_line_color \
|
||||||
|
if self._origin_line_color else \
|
||||||
self.line_color \
|
self.line_color \
|
||||||
if not self.disabled else \
|
|
||||||
app.theme_cls.disabled_hint_text_color
|
|
||||||
Line:
|
|
||||||
width: 1
|
|
||||||
rounded_rectangle:
|
|
||||||
( \
|
|
||||||
self.x, \
|
|
||||||
self.y, \
|
|
||||||
self.width, \
|
|
||||||
self.height, \
|
|
||||||
*self.radius, \
|
|
||||||
self.height \
|
|
||||||
)
|
)
|
||||||
|
|
||||||
MDRelativeLayout:
|
LeadingIconContainer:
|
||||||
id: relative_box
|
id: leading_icon_container
|
||||||
size_hint: None, None
|
adaptive_width: True
|
||||||
size: ("24dp", "24dp") if root.icon_left else (0, 0)
|
|
||||||
pos_hint: {"center_y": .5}
|
|
||||||
radius: [int(self.height / 2),]
|
|
||||||
|
|
||||||
MDIcon:
|
LabelTextContainer:
|
||||||
id: icon_left
|
id: label_container
|
||||||
icon: root.icon_left
|
adaptive_width: True
|
||||||
size_hint: None, None
|
|
||||||
size: ("28dp", "28dp") if root.icon_left else (0, 0)
|
|
||||||
theme_text_color: "Custom"
|
|
||||||
pos_hint: {"center_y": .5}
|
|
||||||
pos: 0, -2
|
|
||||||
text_color:
|
|
||||||
( \
|
|
||||||
root.icon_left_color \
|
|
||||||
if root.icon_left_color else \
|
|
||||||
root.theme_cls.disabled_hint_text_color \
|
|
||||||
) \
|
|
||||||
if not self.disabled else app.theme_cls.disabled_hint_text_color
|
|
||||||
|
|
||||||
MDBoxLayout:
|
TrailingIconContainer:
|
||||||
id: icon_left_box
|
id: trailing_icon_container
|
||||||
size_hint: None, None
|
adaptive_width: True
|
||||||
radius: [int(self.height / 2),]
|
|
||||||
size: ("28dp", "28dp") if root.icon_left else (0, 0)
|
|
||||||
pos: 0, -2
|
|
||||||
|
|
||||||
MDScalableCheckIcon:
|
|
||||||
id: check_icon
|
|
||||||
icon: "check"
|
|
||||||
size_hint: None, None
|
|
||||||
size: "28dp", "28dp"
|
|
||||||
color: (1, 1, 1, 1) if not root.icon_check_color else root.icon_check_color
|
|
||||||
pos: 2, -2
|
|
||||||
|
|
||||||
MDLabel:
|
|
||||||
id: label
|
|
||||||
text: root.text
|
|
||||||
adaptive_size: True
|
|
||||||
markup: True
|
|
||||||
pos_hint: {"center_y": .5}
|
|
||||||
color:
|
|
||||||
( \
|
|
||||||
root.text_color \
|
|
||||||
if root.text_color else \
|
|
||||||
root.theme_cls.disabled_hint_text_color \
|
|
||||||
) \
|
|
||||||
if not self.disabled else app.theme_cls.disabled_hint_text_color
|
|
||||||
|
|
||||||
MDIcon:
|
|
||||||
id: icon_right
|
|
||||||
icon: root.icon_right
|
|
||||||
size_hint: None, None
|
|
||||||
size: ("18dp", "18dp") if root.icon_right else (0, 0)
|
|
||||||
font_size: "18sp" if root.icon_right else 0
|
|
||||||
theme_text_color: "Custom"
|
|
||||||
pos_hint: {"center_y": .5}
|
|
||||||
text_color:
|
|
||||||
( \
|
|
||||||
root.icon_right_color \
|
|
||||||
if root.icon_right_color else \
|
|
||||||
root.theme_cls.disabled_hint_text_color \
|
|
||||||
) \
|
|
||||||
if not self.disabled else app.theme_cls.disabled_hint_text_color
|
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -53,7 +53,6 @@ from kivymd.uix.floatlayout import MDFloatLayout
|
||||||
|
|
||||||
|
|
||||||
class MDCircularLayout(MDFloatLayout):
|
class MDCircularLayout(MDFloatLayout):
|
||||||
|
|
||||||
degree_spacing = NumericProperty(30)
|
degree_spacing = NumericProperty(30)
|
||||||
"""
|
"""
|
||||||
The space between children in degree.
|
The space between children in degree.
|
||||||
|
|
|
@ -231,4 +231,11 @@
|
||||||
id: container
|
id: container
|
||||||
orientation: "vertical"
|
orientation: "vertical"
|
||||||
elevation: root.elevation
|
elevation: root.elevation
|
||||||
|
shadow_radius: root.shadow_radius
|
||||||
|
shadow_softness: root.shadow_softness
|
||||||
|
shadow_offset: root.shadow_offset
|
||||||
|
shadow_color: root.shadow_color
|
||||||
|
shadow_color: root.shadow_color
|
||||||
|
shadow_softness_size: root.shadow_softness_size
|
||||||
padding: "24dp", "24dp", "8dp", "8dp"
|
padding: "24dp", "24dp", "8dp", "8dp"
|
||||||
|
md_bg_color: app.theme_cls.bg_normal
|
||||||
|
|
|
@ -37,6 +37,7 @@ from kivy.lang import Builder
|
||||||
from kivy.metrics import dp
|
from kivy.metrics import dp
|
||||||
from kivy.properties import (
|
from kivy.properties import (
|
||||||
BooleanProperty,
|
BooleanProperty,
|
||||||
|
BoundedNumericProperty,
|
||||||
ColorProperty,
|
ColorProperty,
|
||||||
DictProperty,
|
DictProperty,
|
||||||
ListProperty,
|
ListProperty,
|
||||||
|
@ -44,6 +45,7 @@ from kivy.properties import (
|
||||||
ObjectProperty,
|
ObjectProperty,
|
||||||
OptionProperty,
|
OptionProperty,
|
||||||
StringProperty,
|
StringProperty,
|
||||||
|
VariableListProperty,
|
||||||
)
|
)
|
||||||
from kivy.uix.anchorlayout import AnchorLayout
|
from kivy.uix.anchorlayout import AnchorLayout
|
||||||
from kivy.uix.behaviors import ButtonBehavior, FocusBehavior
|
from kivy.uix.behaviors import ButtonBehavior, FocusBehavior
|
||||||
|
@ -56,6 +58,11 @@ from kivy.uix.scrollview import ScrollView
|
||||||
|
|
||||||
from kivymd import uix_path
|
from kivymd import uix_path
|
||||||
from kivymd.effects.stiffscroll import StiffScrollEffect
|
from kivymd.effects.stiffscroll import StiffScrollEffect
|
||||||
|
from kivymd.material_resources import (
|
||||||
|
DATA_TABLE_ELEVATION,
|
||||||
|
DATA_TABLE_OFFSET,
|
||||||
|
DATA_TABLE_SOFTNESS,
|
||||||
|
)
|
||||||
from kivymd.theming import ThemableBehavior
|
from kivymd.theming import ThemableBehavior
|
||||||
from kivymd.uix.behaviors import HoverBehavior
|
from kivymd.uix.behaviors import HoverBehavior
|
||||||
from kivymd.uix.boxlayout import MDBoxLayout
|
from kivymd.uix.boxlayout import MDBoxLayout
|
||||||
|
@ -758,7 +765,7 @@ class TableData(RecycleView):
|
||||||
# instance_pagination.ids.button_forward.disabled = True
|
# instance_pagination.ids.button_forward.disabled = True
|
||||||
|
|
||||||
|
|
||||||
class TablePagination(ThemableBehavior, MDBoxLayout):
|
class TablePagination(MDBoxLayout):
|
||||||
"""Pagination Container."""
|
"""Pagination Container."""
|
||||||
|
|
||||||
table_data = ObjectProperty()
|
table_data = ObjectProperty()
|
||||||
|
@ -772,8 +779,11 @@ class TablePagination(ThemableBehavior, MDBoxLayout):
|
||||||
|
|
||||||
class MDDataTable(ThemableBehavior, AnchorLayout):
|
class MDDataTable(ThemableBehavior, AnchorLayout):
|
||||||
"""
|
"""
|
||||||
See :class:`~kivy.uix.anchorlayout.AnchorLayout` class documentation for
|
Datatable class.
|
||||||
more information.
|
|
||||||
|
For more information, see in the
|
||||||
|
:class:`~kivymd.theming.ThemableBehavior` and
|
||||||
|
:class:`~kivy.uix.anchorlayout.AnchorLayout` classes documentation.
|
||||||
|
|
||||||
:Events:
|
:Events:
|
||||||
:attr:`on_row_press`
|
:attr:`on_row_press`
|
||||||
|
@ -1297,14 +1307,70 @@ class MDDataTable(ThemableBehavior, AnchorLayout):
|
||||||
and defaults to `False`.
|
and defaults to `False`.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
elevation = NumericProperty(4)
|
elevation = NumericProperty(DATA_TABLE_ELEVATION)
|
||||||
"""
|
"""
|
||||||
Table elevation.
|
See :attr:`kivymd.uix.behaviors.elevation.CommonElevationBehavior.elevation`
|
||||||
|
attribute.
|
||||||
|
|
||||||
:attr:`elevation` is an :class:`~kivy.properties.NumericProperty`
|
:attr:`elevation` is an :class:`~kivy.properties.NumericProperty`
|
||||||
and defaults to `4`.
|
and defaults to `4`.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
shadow_radius = VariableListProperty([6], length=4)
|
||||||
|
"""
|
||||||
|
See :attr:`kivymd.uix.behaviors.elevation.CommonElevationBehavior.shadow_radius`
|
||||||
|
attribute.
|
||||||
|
|
||||||
|
.. versionadded:: 1.2.0
|
||||||
|
|
||||||
|
:attr:`shadow_radius` is an :class:`~kivy.properties.VariableListProperty`
|
||||||
|
and defaults to `[6]`.
|
||||||
|
"""
|
||||||
|
|
||||||
|
shadow_softness = NumericProperty(DATA_TABLE_SOFTNESS)
|
||||||
|
"""
|
||||||
|
See :attr:`kivymd.uix.behaviors.elevation.CommonElevationBehavior.shadow_softness`
|
||||||
|
attribute.
|
||||||
|
|
||||||
|
.. versionadded:: 1.2.0
|
||||||
|
|
||||||
|
:attr:`shadow_softness` is an :class:`~kivy.properties.NumericProperty`
|
||||||
|
and defaults to `12`.
|
||||||
|
"""
|
||||||
|
|
||||||
|
shadow_softness_size = BoundedNumericProperty(2, min=2)
|
||||||
|
"""
|
||||||
|
See :attr:`kivymd.uix.behaviors.elevation.CommonElevationBehavior.shadow_softness_size`
|
||||||
|
attribute.
|
||||||
|
|
||||||
|
.. versionadded:: 1.2.0
|
||||||
|
|
||||||
|
:attr:`shadow_softness_size` is an :class:`~kivy.properties.BoundedNumericProperty`
|
||||||
|
and defaults to `2`.
|
||||||
|
"""
|
||||||
|
|
||||||
|
shadow_offset = ListProperty(DATA_TABLE_OFFSET)
|
||||||
|
"""
|
||||||
|
See :attr:`kivymd.uix.behaviors.elevation.CommonElevationBehavior.shadow_offset`
|
||||||
|
attribute.
|
||||||
|
|
||||||
|
.. versionadded:: 1.2.0
|
||||||
|
|
||||||
|
:attr:`shadow_offset` is an :class:`~kivy.properties.ListProperty`
|
||||||
|
and defaults to `(0, 2)`.
|
||||||
|
"""
|
||||||
|
|
||||||
|
shadow_color = ColorProperty([0, 0, 0, 0.6])
|
||||||
|
"""
|
||||||
|
See :attr:`kivymd.uix.behaviors.elevation.CommonElevationBehavior.shadow_color`
|
||||||
|
attribute.
|
||||||
|
|
||||||
|
.. versionadded:: 1.2.0
|
||||||
|
|
||||||
|
:attr:`shadow_color` is an :class:`~kivy.properties.ColorProperty`
|
||||||
|
and defaults to `[0, 0, 0, 0.6]`.
|
||||||
|
"""
|
||||||
|
|
||||||
rows_num = NumericProperty(5)
|
rows_num = NumericProperty(5)
|
||||||
"""
|
"""
|
||||||
The number of rows displayed on one page of the table.
|
The number of rows displayed on one page of the table.
|
||||||
|
@ -1626,6 +1692,77 @@ class MDDataTable(ThemableBehavior, AnchorLayout):
|
||||||
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/data-tables-add-remove-row.gif
|
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/data-tables-add-remove-row.gif
|
||||||
:align: center
|
:align: center
|
||||||
|
|
||||||
|
Deleting checked rows
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
from kivy.metrics import dp
|
||||||
|
from kivy.lang import Builder
|
||||||
|
from kivy.clock import Clock
|
||||||
|
|
||||||
|
from kivymd.app import MDApp
|
||||||
|
from kivymd.uix.datatables import MDDataTable
|
||||||
|
from kivymd.uix.screen import MDScreen
|
||||||
|
|
||||||
|
KV = '''
|
||||||
|
MDBoxLayout:
|
||||||
|
orientation: "vertical"
|
||||||
|
padding: "56dp"
|
||||||
|
spacing: "24dp"
|
||||||
|
|
||||||
|
MDData:
|
||||||
|
id: table_screen
|
||||||
|
|
||||||
|
MDRaisedButton:
|
||||||
|
text: "DELETE CHECKED ROWS"
|
||||||
|
on_release: table_screen.delete_checked_rows()
|
||||||
|
'''
|
||||||
|
|
||||||
|
|
||||||
|
class MDData(MDScreen):
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
self.data = [
|
||||||
|
["1", "Asep Sudrajat", "Male", "Soccer"],
|
||||||
|
["2", "Egy", "Male", "Soccer"],
|
||||||
|
["3", "Tanos", "Demon", "Soccer"],
|
||||||
|
]
|
||||||
|
self.data_tables = MDDataTable(
|
||||||
|
use_pagination=True,
|
||||||
|
check=True,
|
||||||
|
column_data=[
|
||||||
|
("No", dp(30)),
|
||||||
|
("No Urut.", dp(30)),
|
||||||
|
("Alamat Pengirim", dp(30)),
|
||||||
|
("No Surat", dp(60)),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
self.data_tables.row_data = self.data
|
||||||
|
self.add_widget(self.data_tables)
|
||||||
|
|
||||||
|
def delete_checked_rows(self):
|
||||||
|
def deselect_rows(*args):
|
||||||
|
self.data_tables.table_data.select_all("normal")
|
||||||
|
|
||||||
|
for data in self.data_tables.get_row_checks():
|
||||||
|
self.data_tables.remove_row(data)
|
||||||
|
|
||||||
|
Clock.schedule_once(deselect_rows)
|
||||||
|
|
||||||
|
|
||||||
|
class MyApp(MDApp):
|
||||||
|
def build(self):
|
||||||
|
self.theme_cls.theme_style = "Dark"
|
||||||
|
self.theme_cls.primary_palette = "Orange"
|
||||||
|
return Builder.load_string(KV)
|
||||||
|
|
||||||
|
|
||||||
|
MyApp().run()
|
||||||
|
|
||||||
|
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/data-tables-deleting-checked-rows.gif
|
||||||
|
:align: center
|
||||||
|
|
||||||
.. versionadded:: 1.0.0
|
.. versionadded:: 1.0.0
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
@ -1760,7 +1897,6 @@ class MDDataTable(ThemableBehavior, AnchorLayout):
|
||||||
|
|
||||||
|
|
||||||
class CellRow(
|
class CellRow(
|
||||||
ThemableBehavior,
|
|
||||||
RecycleDataViewBehavior,
|
RecycleDataViewBehavior,
|
||||||
HoverBehavior,
|
HoverBehavior,
|
||||||
ButtonBehavior,
|
ButtonBehavior,
|
||||||
|
|
|
@ -32,7 +32,7 @@
|
||||||
orientation: "vertical"
|
orientation: "vertical"
|
||||||
size_hint_y: None
|
size_hint_y: None
|
||||||
height: self.minimum_height
|
height: self.minimum_height
|
||||||
padding: "24dp", "24dp", "16dp", "8dp"
|
padding: "24dp", "24dp", "8dp", "8dp"
|
||||||
radius: root.radius
|
radius: root.radius
|
||||||
md_bg_color:
|
md_bg_color:
|
||||||
root.theme_cls.bg_dark \
|
root.theme_cls.bg_dark \
|
||||||
|
|
|
@ -89,7 +89,7 @@ from kivy.uix.modalview import ModalView
|
||||||
from kivymd import uix_path
|
from kivymd import uix_path
|
||||||
from kivymd.material_resources import DEVICE_TYPE
|
from kivymd.material_resources import DEVICE_TYPE
|
||||||
from kivymd.theming import ThemableBehavior
|
from kivymd.theming import ThemableBehavior
|
||||||
from kivymd.uix.behaviors import CommonElevationBehavior
|
from kivymd.uix.behaviors import CommonElevationBehavior, MotionDialogBehavior
|
||||||
from kivymd.uix.button import BaseButton
|
from kivymd.uix.button import BaseButton
|
||||||
from kivymd.uix.card import MDSeparator
|
from kivymd.uix.card import MDSeparator
|
||||||
from kivymd.uix.list import BaseListItem
|
from kivymd.uix.list import BaseListItem
|
||||||
|
@ -100,7 +100,9 @@ with open(
|
||||||
Builder.load_string(kv_file.read())
|
Builder.load_string(kv_file.read())
|
||||||
|
|
||||||
|
|
||||||
class BaseDialog(ThemableBehavior, ModalView, CommonElevationBehavior):
|
class BaseDialog(
|
||||||
|
ThemableBehavior, MotionDialogBehavior, ModalView, CommonElevationBehavior
|
||||||
|
):
|
||||||
elevation = NumericProperty(3)
|
elevation = NumericProperty(3)
|
||||||
"""
|
"""
|
||||||
See :attr:`kivymd.uix.behaviors.elevation.CommonElevationBehavior.elevation`
|
See :attr:`kivymd.uix.behaviors.elevation.CommonElevationBehavior.elevation`
|
||||||
|
@ -159,6 +161,16 @@ class BaseDialog(ThemableBehavior, ModalView, CommonElevationBehavior):
|
||||||
|
|
||||||
|
|
||||||
class MDDialog(BaseDialog):
|
class MDDialog(BaseDialog):
|
||||||
|
"""
|
||||||
|
Dialog class.
|
||||||
|
|
||||||
|
For more information, see in the
|
||||||
|
:class:`~kivymd.theming.ThemableBehavior` and
|
||||||
|
:class:`~kivy.uix.modalview.ModalView` and
|
||||||
|
:class:`~kivymd.uix.behaviors.CommonElevationBehavior`
|
||||||
|
classes documentation.
|
||||||
|
"""
|
||||||
|
|
||||||
title = StringProperty()
|
title = StringProperty()
|
||||||
"""
|
"""
|
||||||
Title dialog.
|
Title dialog.
|
||||||
|
@ -422,7 +434,8 @@ class MDDialog(BaseDialog):
|
||||||
|
|
||||||
content_cls = ObjectProperty()
|
content_cls = ObjectProperty()
|
||||||
"""
|
"""
|
||||||
Custom content class.
|
Custom content class. This attribute is only available when :attr:`type` is
|
||||||
|
set to `'custom'`.
|
||||||
|
|
||||||
.. tabs::
|
.. tabs::
|
||||||
|
|
||||||
|
@ -637,6 +650,7 @@ class MDDialog(BaseDialog):
|
||||||
def on_open(self) -> None:
|
def on_open(self) -> None:
|
||||||
# TODO: Add scrolling text.
|
# TODO: Add scrolling text.
|
||||||
self.height = self.ids.container.height
|
self.height = self.ids.container.height
|
||||||
|
super().on_open()
|
||||||
|
|
||||||
def get_normal_height(self) -> float:
|
def get_normal_height(self) -> float:
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -68,6 +68,17 @@ class _Triangle(Widget):
|
||||||
class MDDropDownItem(
|
class MDDropDownItem(
|
||||||
DeclarativeBehavior, ThemableBehavior, ButtonBehavior, BoxLayout
|
DeclarativeBehavior, ThemableBehavior, ButtonBehavior, BoxLayout
|
||||||
):
|
):
|
||||||
|
"""
|
||||||
|
Dropdown item class.
|
||||||
|
|
||||||
|
For more information, see in the
|
||||||
|
:class:`~kivymd.uix.behaviors.DeclarativeBehavior` and
|
||||||
|
:class:`~kivymd.theming.ThemableBehavior` and
|
||||||
|
:class:`~kivy.uix.behaviors.ButtonBehavior` and
|
||||||
|
:class:`~kivy.uix.boxlayout.BoxLayout`
|
||||||
|
classes documentation.
|
||||||
|
"""
|
||||||
|
|
||||||
text = StringProperty()
|
text = StringProperty()
|
||||||
"""
|
"""
|
||||||
Text item.
|
Text item.
|
||||||
|
|
|
@ -185,21 +185,39 @@ class MDExpansionChevronRight(IRightBodyTouch, MDIconButton):
|
||||||
|
|
||||||
|
|
||||||
class MDExpansionPanelOneLine(OneLineAvatarIconListItem):
|
class MDExpansionPanelOneLine(OneLineAvatarIconListItem):
|
||||||
"""Single line panel."""
|
"""
|
||||||
|
Single line panel.
|
||||||
|
|
||||||
|
For more information, see in the
|
||||||
|
:class:`~kivymd.uix.list.OneLineAvatarIconListItem` class documentation.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
class MDExpansionPanelTwoLine(TwoLineAvatarIconListItem):
|
class MDExpansionPanelTwoLine(TwoLineAvatarIconListItem):
|
||||||
"""Two-line panel."""
|
"""
|
||||||
|
Two-line panel.
|
||||||
|
|
||||||
|
For more information, see in the
|
||||||
|
:class:`~kivymd.uix.list.TwoLineAvatarIconListItem` class documentation.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
class MDExpansionPanelThreeLine(ThreeLineAvatarIconListItem):
|
class MDExpansionPanelThreeLine(ThreeLineAvatarIconListItem):
|
||||||
"""Three-line panel."""
|
"""
|
||||||
|
Three-line panel.
|
||||||
|
|
||||||
|
For more information, see in the
|
||||||
|
:class:`~kivymd.uix.list.ThreeLineAvatarIconListItem` class documentation.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
class MDExpansionPanelLabel(TwoLineListItem):
|
class MDExpansionPanelLabel(TwoLineListItem):
|
||||||
"""
|
"""
|
||||||
Label panel.
|
Label panel.
|
||||||
|
|
||||||
|
For more information, see in the
|
||||||
|
:class:`~kivymd.uix.list.TwoLineListItem` class documentation.
|
||||||
|
|
||||||
..warning:: This class is created for use in the
|
..warning:: This class is created for use in the
|
||||||
:class:`~kivymd.uix.stepper.MDStepperVertical` and
|
:class:`~kivymd.uix.stepper.MDStepperVertical` and
|
||||||
:class:`~kivymd.uix.stepper.MDStepper` classes, and has not
|
:class:`~kivymd.uix.stepper.MDStepper` classes, and has not
|
||||||
|
@ -217,6 +235,11 @@ class MDExpansionPanelLabel(TwoLineListItem):
|
||||||
|
|
||||||
class MDExpansionPanel(RelativeLayout):
|
class MDExpansionPanel(RelativeLayout):
|
||||||
"""
|
"""
|
||||||
|
Expansion panel class.
|
||||||
|
|
||||||
|
For more information, see in the
|
||||||
|
:class:`~kivy.uix.relativelayout.RelativeLayout` classes documentation.
|
||||||
|
|
||||||
:Events:
|
:Events:
|
||||||
:attr:`on_open`
|
:attr:`on_open`
|
||||||
Called when a panel is opened.
|
Called when a panel is opened.
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
#:import os os
|
#:import os os
|
||||||
|
#:import FILE_MANAGER_TOP_APP_BAR_ELEVATION kivymd.material_resources.FILE_MANAGER_TOP_APP_BAR_ELEVATION
|
||||||
|
|
||||||
|
|
||||||
<BodyManager>
|
<BodyManager>
|
||||||
icon: "folder"
|
icon: "folder"
|
||||||
|
@ -74,7 +76,7 @@
|
||||||
title: root.current_path
|
title: root.current_path
|
||||||
right_action_items: [["close-box", lambda x: root.exit_manager(1)]]
|
right_action_items: [["close-box", lambda x: root.exit_manager(1)]]
|
||||||
left_action_items: [["chevron-left", lambda x: root.back()]]
|
left_action_items: [["chevron-left", lambda x: root.back()]]
|
||||||
elevation: 3
|
elevation: FILE_MANAGER_TOP_APP_BAR_ELEVATION
|
||||||
md_bg_color:
|
md_bg_color:
|
||||||
app.theme_cls.primary_color \
|
app.theme_cls.primary_color \
|
||||||
if not root.background_color_toolbar else \
|
if not root.background_color_toolbar else \
|
||||||
|
|
|
@ -158,7 +158,6 @@ from kivy.uix.behaviors import ButtonBehavior
|
||||||
from kivy.uix.modalview import ModalView
|
from kivy.uix.modalview import ModalView
|
||||||
|
|
||||||
from kivymd import images_path, uix_path
|
from kivymd import images_path, uix_path
|
||||||
from kivymd.theming import ThemableBehavior
|
|
||||||
from kivymd.uix.behaviors import CircularRippleBehavior
|
from kivymd.uix.behaviors import CircularRippleBehavior
|
||||||
from kivymd.uix.boxlayout import MDBoxLayout
|
from kivymd.uix.boxlayout import MDBoxLayout
|
||||||
from kivymd.uix.button import MDFloatingActionButton
|
from kivymd.uix.button import MDFloatingActionButton
|
||||||
|
@ -197,7 +196,7 @@ class ModifiedOneLineIconListItem(BaseListItem):
|
||||||
self.height = dp(48)
|
self.height = dp(48)
|
||||||
|
|
||||||
|
|
||||||
class MDFileManager(MDRelativeLayout, ThemableBehavior):
|
class MDFileManager(MDRelativeLayout):
|
||||||
"""
|
"""
|
||||||
Implements a modal dialog with a file manager.
|
Implements a modal dialog with a file manager.
|
||||||
|
|
||||||
|
@ -248,7 +247,8 @@ class MDFileManager(MDRelativeLayout, ThemableBehavior):
|
||||||
|
|
||||||
background_color_selection_button = ColorProperty(None)
|
background_color_selection_button = ColorProperty(None)
|
||||||
"""
|
"""
|
||||||
Background color of the current directory/path selection button.
|
Background color in (r, g, b, a) or string format of the current
|
||||||
|
directory/path selection button.
|
||||||
|
|
||||||
.. versionadded:: 1.1.0
|
.. versionadded:: 1.1.0
|
||||||
|
|
||||||
|
@ -268,7 +268,7 @@ class MDFileManager(MDRelativeLayout, ThemableBehavior):
|
||||||
|
|
||||||
background_color_toolbar = ColorProperty(None)
|
background_color_toolbar = ColorProperty(None)
|
||||||
"""
|
"""
|
||||||
Background color of the file manager toolbar.
|
Background color in (r, g, b, a) or string format of the file manager toolbar.
|
||||||
|
|
||||||
.. versionadded:: 1.1.0
|
.. versionadded:: 1.1.0
|
||||||
|
|
||||||
|
@ -307,7 +307,8 @@ class MDFileManager(MDRelativeLayout, ThemableBehavior):
|
||||||
|
|
||||||
icon_color = ColorProperty(None)
|
icon_color = ColorProperty(None)
|
||||||
"""
|
"""
|
||||||
Color of the folder icon when the :attr:`preview` property is set to False.
|
Color in (r, g, b, a) or string format of the folder icon when the
|
||||||
|
:attr:`preview` property is set to False.
|
||||||
|
|
||||||
.. versionadded:: 1.1.0
|
.. versionadded:: 1.1.0
|
||||||
|
|
||||||
|
|
|
@ -137,6 +137,14 @@ from kivymd.uix.boxlayout import MDBoxLayout
|
||||||
|
|
||||||
|
|
||||||
class FitImage(MDBoxLayout, StencilBehavior):
|
class FitImage(MDBoxLayout, StencilBehavior):
|
||||||
|
"""
|
||||||
|
Fit image class.
|
||||||
|
|
||||||
|
For more information, see in the
|
||||||
|
:class:`~kivymd.uix.boxlayout.MDLayout` and
|
||||||
|
:class:`~kivymd.uix.behaviors.StencilBehavior` classes documentation.
|
||||||
|
"""
|
||||||
|
|
||||||
source = ObjectProperty()
|
source = ObjectProperty()
|
||||||
"""
|
"""
|
||||||
Filename/source of your image.
|
Filename/source of your image.
|
||||||
|
|
|
@ -35,11 +35,14 @@ MDFloatLayout
|
||||||
|
|
||||||
from kivy.uix.floatlayout import FloatLayout
|
from kivy.uix.floatlayout import FloatLayout
|
||||||
|
|
||||||
|
from kivymd.theming import ThemableBehavior
|
||||||
from kivymd.uix import MDAdaptiveWidget
|
from kivymd.uix import MDAdaptiveWidget
|
||||||
from kivymd.uix.behaviors import DeclarativeBehavior
|
from kivymd.uix.behaviors import DeclarativeBehavior
|
||||||
|
|
||||||
|
|
||||||
class MDFloatLayout(DeclarativeBehavior, FloatLayout, MDAdaptiveWidget):
|
class MDFloatLayout(
|
||||||
|
DeclarativeBehavior, ThemableBehavior, FloatLayout, MDAdaptiveWidget
|
||||||
|
):
|
||||||
"""
|
"""
|
||||||
Float layout class. For more information, see in the
|
Float layout class. For more information, see in the
|
||||||
:class:`~kivy.uix.floatlayout.FloatLayout` class documentation.
|
:class:`~kivy.uix.floatlayout.FloatLayout` class documentation.
|
||||||
|
|
|
@ -85,11 +85,14 @@ Equivalent
|
||||||
|
|
||||||
from kivy.uix.gridlayout import GridLayout
|
from kivy.uix.gridlayout import GridLayout
|
||||||
|
|
||||||
|
from kivymd.theming import ThemableBehavior
|
||||||
from kivymd.uix import MDAdaptiveWidget
|
from kivymd.uix import MDAdaptiveWidget
|
||||||
from kivymd.uix.behaviors import DeclarativeBehavior
|
from kivymd.uix.behaviors import DeclarativeBehavior
|
||||||
|
|
||||||
|
|
||||||
class MDGridLayout(DeclarativeBehavior, GridLayout, MDAdaptiveWidget):
|
class MDGridLayout(
|
||||||
|
DeclarativeBehavior, ThemableBehavior, GridLayout, MDAdaptiveWidget
|
||||||
|
):
|
||||||
"""
|
"""
|
||||||
Grid layout class. For more information, see in the
|
Grid layout class. For more information, see in the
|
||||||
:class:`~kivy.uix.gridlayout.GridLayout` class documentation.
|
:class:`~kivy.uix.gridlayout.GridLayout` class documentation.
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
(0, 0)
|
(0, 0)
|
||||||
on_release: root.dispatch("on_release")
|
on_release: root.dispatch("on_release")
|
||||||
on_press: root.dispatch("on_press")
|
on_press: root.dispatch("on_press")
|
||||||
|
_no_ripple_effect: root._no_ripple_effect
|
||||||
|
|
||||||
SmartTileOverlayBox:
|
SmartTileOverlayBox:
|
||||||
id: box
|
id: box
|
||||||
|
|
|
@ -71,6 +71,7 @@ __all__ = [
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
|
||||||
|
from kivy.clock import Clock
|
||||||
from kivy.lang import Builder
|
from kivy.lang import Builder
|
||||||
from kivy.properties import (
|
from kivy.properties import (
|
||||||
BooleanProperty,
|
BooleanProperty,
|
||||||
|
@ -82,7 +83,6 @@ from kivy.properties import (
|
||||||
from kivy.uix.behaviors import ButtonBehavior
|
from kivy.uix.behaviors import ButtonBehavior
|
||||||
|
|
||||||
from kivymd import uix_path
|
from kivymd import uix_path
|
||||||
from kivymd.theming import ThemableBehavior
|
|
||||||
from kivymd.uix.behaviors import RectangularRippleBehavior
|
from kivymd.uix.behaviors import RectangularRippleBehavior
|
||||||
from kivymd.uix.boxlayout import MDBoxLayout
|
from kivymd.uix.boxlayout import MDBoxLayout
|
||||||
from kivymd.uix.fitimage import FitImage
|
from kivymd.uix.fitimage import FitImage
|
||||||
|
@ -103,10 +103,13 @@ class SmartTileOverlayBox(MDBoxLayout):
|
||||||
"""Implements a container for custom widgets to be added to the tile."""
|
"""Implements a container for custom widgets to be added to the tile."""
|
||||||
|
|
||||||
|
|
||||||
class MDSmartTile(MDRelativeLayout, ThemableBehavior):
|
class MDSmartTile(MDRelativeLayout):
|
||||||
"""
|
"""
|
||||||
A tile for more complex needs.
|
A tile for more complex needs.
|
||||||
|
|
||||||
|
For more information, see in the
|
||||||
|
:class:`~kivymd.uix.relativelayout.MDRelativeLayout` class documentation.
|
||||||
|
|
||||||
Includes an image, a container to place overlays and a box that can act
|
Includes an image, a container to place overlays and a box that can act
|
||||||
as a header or a footer, as described in the Material Design specs.
|
as a header or a footer, as described in the Material Design specs.
|
||||||
|
|
||||||
|
@ -139,7 +142,8 @@ class MDSmartTile(MDRelativeLayout, ThemableBehavior):
|
||||||
|
|
||||||
box_color = ColorProperty((0, 0, 0, 0.5))
|
box_color = ColorProperty((0, 0, 0, 0.5))
|
||||||
"""
|
"""
|
||||||
Sets the color and opacity for the information box.
|
Sets the color in (r, g, b, a) or string format and opacity for the
|
||||||
|
information box.
|
||||||
|
|
||||||
.. code-block:: kv
|
.. code-block:: kv
|
||||||
|
|
||||||
|
@ -249,6 +253,8 @@ class MDSmartTile(MDRelativeLayout, ThemableBehavior):
|
||||||
and defaults to `False`.
|
and defaults to `False`.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
_no_ripple_effect = BooleanProperty(False)
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
self.register_event_type("on_release")
|
self.register_event_type("on_release")
|
||||||
|
@ -270,4 +276,4 @@ class MDSmartTile(MDRelativeLayout, ThemableBehavior):
|
||||||
if isinstance(widget, MDLabel):
|
if isinstance(widget, MDLabel):
|
||||||
widget.shorten = True
|
widget.shorten = True
|
||||||
widget.shorten_from = "right"
|
widget.shorten_from = "right"
|
||||||
self.ids.box.add_widget(widget)
|
Clock.schedule_once(lambda x: self.ids.box.add_widget(widget))
|
||||||
|
|
|
@ -3,12 +3,10 @@
|
||||||
|
|
||||||
<MDLabel>
|
<MDLabel>
|
||||||
disabled_color: self.theme_cls.disabled_hint_text_color
|
disabled_color: self.theme_cls.disabled_hint_text_color
|
||||||
# FIXME: Overriding the values of this property greatly affects application
|
text_size:
|
||||||
# performance. Especially when the application window is resized and a
|
(self.width if not self.adaptive_width else None) \
|
||||||
# custom font is used. Performance is especially slow when you are using
|
if not self.adaptive_size else None, \
|
||||||
# `PIL` as your text processing provider - os.environ ['KIVY_TEXT'] = 'pil'.
|
None
|
||||||
# Priority - CRITICAL.
|
|
||||||
text_size: self.width, None
|
|
||||||
|
|
||||||
|
|
||||||
<MDIcon>:
|
<MDIcon>:
|
||||||
|
@ -16,6 +14,7 @@
|
||||||
Color:
|
Color:
|
||||||
rgba: (1, 1, 1, 1) if self.source else (0, 0, 0, 0)
|
rgba: (1, 1, 1, 1) if self.source else (0, 0, 0, 0)
|
||||||
Rectangle:
|
Rectangle:
|
||||||
|
group: "rectangle"
|
||||||
source: self.source if self.source else None
|
source: self.source if self.source else None
|
||||||
pos:
|
pos:
|
||||||
self.pos \
|
self.pos \
|
||||||
|
@ -32,6 +31,7 @@
|
||||||
|
|
||||||
# Badge icon.
|
# Badge icon.
|
||||||
MDLabel:
|
MDLabel:
|
||||||
|
id: badge
|
||||||
font_style: "Icon"
|
font_style: "Icon"
|
||||||
adaptive_size: True
|
adaptive_size: True
|
||||||
opposite_icon_color: True
|
opposite_icon_color: True
|
||||||
|
@ -62,6 +62,7 @@
|
||||||
if root.badge_icon else \
|
if root.badge_icon else \
|
||||||
(0, 0, 0, 0)
|
(0, 0, 0, 0)
|
||||||
RoundedRectangle:
|
RoundedRectangle:
|
||||||
|
group: "badge"
|
||||||
radius: [self.width / 2,]
|
radius: [self.width / 2,]
|
||||||
pos: self.pos
|
pos: self.pos
|
||||||
size: self.size
|
size: self.size
|
||||||
|
|
|
@ -18,6 +18,10 @@ Class :class:`MDLabel` inherited from the :class:`~kivy.uix.label.Label` class
|
||||||
but for :class:`MDLabel` the ``text_size`` parameter is ``(self.width, None)``
|
but for :class:`MDLabel` the ``text_size`` parameter is ``(self.width, None)``
|
||||||
and default is positioned on the left:
|
and default is positioned on the left:
|
||||||
|
|
||||||
|
.. tabs::
|
||||||
|
|
||||||
|
.. tab:: Declarative KV style
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
from kivy.lang import Builder
|
from kivy.lang import Builder
|
||||||
|
@ -27,12 +31,6 @@ and default is positioned on the left:
|
||||||
KV = '''
|
KV = '''
|
||||||
MDScreen:
|
MDScreen:
|
||||||
|
|
||||||
MDBoxLayout:
|
|
||||||
orientation: "vertical"
|
|
||||||
|
|
||||||
MDTopAppBar:
|
|
||||||
title: "MDLabel"
|
|
||||||
|
|
||||||
MDLabel:
|
MDLabel:
|
||||||
text: "MDLabel"
|
text: "MDLabel"
|
||||||
'''
|
'''
|
||||||
|
@ -40,11 +38,39 @@ and default is positioned on the left:
|
||||||
|
|
||||||
class Test(MDApp):
|
class Test(MDApp):
|
||||||
def build(self):
|
def build(self):
|
||||||
|
self.theme_cls.theme_style = "Dark"
|
||||||
|
self.theme_cls.primary_palette = "Orange"
|
||||||
return Builder.load_string(KV)
|
return Builder.load_string(KV)
|
||||||
|
|
||||||
|
|
||||||
Test().run()
|
Test().run()
|
||||||
|
|
||||||
|
.. tab:: Declarative Python style
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
from kivy.lang import Builder
|
||||||
|
|
||||||
|
from kivymd.app import MDApp
|
||||||
|
from kivymd.uix.screen import MDScreen
|
||||||
|
from kivymd.uix.label import MDLabel
|
||||||
|
|
||||||
|
|
||||||
|
class Test(MDApp):
|
||||||
|
def build(self):
|
||||||
|
self.theme_cls.theme_style = "Dark"
|
||||||
|
self.theme_cls.primary_palette = "Orange"
|
||||||
|
return (
|
||||||
|
MDScreen(
|
||||||
|
MDLabel(
|
||||||
|
text="MDLabel"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
Test().run()
|
||||||
|
|
||||||
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/md-label-to-left.png
|
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/md-label-to-left.png
|
||||||
:align: center
|
:align: center
|
||||||
|
|
||||||
|
@ -74,20 +100,16 @@ and default is positioned on the left:
|
||||||
from kivymd.uix.label import MDLabel
|
from kivymd.uix.label import MDLabel
|
||||||
|
|
||||||
KV = '''
|
KV = '''
|
||||||
MDScreen:
|
|
||||||
|
|
||||||
MDBoxLayout:
|
MDBoxLayout:
|
||||||
id: box
|
|
||||||
orientation: "vertical"
|
orientation: "vertical"
|
||||||
|
|
||||||
MDTopAppBar:
|
|
||||||
title: "MDLabel"
|
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
|
||||||
class Test(MDApp):
|
class Test(MDApp):
|
||||||
def build(self):
|
def build(self):
|
||||||
|
self.theme_cls.theme_style = "Dark"
|
||||||
screen = Builder.load_string(KV)
|
screen = Builder.load_string(KV)
|
||||||
|
|
||||||
# Names of standard color themes.
|
# Names of standard color themes.
|
||||||
for name_theme in [
|
for name_theme in [
|
||||||
"Primary",
|
"Primary",
|
||||||
|
@ -96,7 +118,7 @@ and default is positioned on the left:
|
||||||
"Error",
|
"Error",
|
||||||
"ContrastParentBackground",
|
"ContrastParentBackground",
|
||||||
]:
|
]:
|
||||||
screen.ids.box.add_widget(
|
screen.add_widget(
|
||||||
MDLabel(
|
MDLabel(
|
||||||
text=name_theme,
|
text=name_theme,
|
||||||
halign="center",
|
halign="center",
|
||||||
|
@ -121,7 +143,7 @@ in the ``text_color`` parameter:
|
||||||
text: "Custom color"
|
text: "Custom color"
|
||||||
halign: "center"
|
halign: "center"
|
||||||
theme_text_color: "Custom"
|
theme_text_color: "Custom"
|
||||||
text_color: 0, 0, 1, 1
|
text_color: "blue"
|
||||||
|
|
||||||
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/md-label-custom-color.png
|
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/md-label-custom-color.png
|
||||||
:align: center
|
:align: center
|
||||||
|
@ -138,26 +160,20 @@ parameter:
|
||||||
from kivymd.uix.label import MDLabel
|
from kivymd.uix.label import MDLabel
|
||||||
from kivymd.font_definitions import theme_font_styles
|
from kivymd.font_definitions import theme_font_styles
|
||||||
|
|
||||||
|
|
||||||
KV = '''
|
KV = '''
|
||||||
MDScreen:
|
MDScrollView:
|
||||||
|
|
||||||
MDBoxLayout:
|
|
||||||
orientation: "vertical"
|
|
||||||
|
|
||||||
MDTopAppBar:
|
|
||||||
title: "MDLabel"
|
|
||||||
|
|
||||||
ScrollView:
|
|
||||||
|
|
||||||
MDList:
|
MDList:
|
||||||
id: box
|
id: box
|
||||||
|
spacing: "8dp"
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
|
||||||
class Test(MDApp):
|
class Test(MDApp):
|
||||||
def build(self):
|
def build(self):
|
||||||
|
self.theme_cls.theme_style = "Dark"
|
||||||
screen = Builder.load_string(KV)
|
screen = Builder.load_string(KV)
|
||||||
|
|
||||||
# Names of standard font styles.
|
# Names of standard font styles.
|
||||||
for name_style in theme_font_styles[:-1]:
|
for name_style in theme_font_styles[:-1]:
|
||||||
screen.ids.box.add_widget(
|
screen.ids.box.add_widget(
|
||||||
|
@ -165,6 +181,7 @@ parameter:
|
||||||
text=f"{name_style} style",
|
text=f"{name_style} style",
|
||||||
halign="center",
|
halign="center",
|
||||||
font_style=name_style,
|
font_style=name_style,
|
||||||
|
adaptive_height=True,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
return screen
|
return screen
|
||||||
|
@ -172,7 +189,273 @@ parameter:
|
||||||
|
|
||||||
Test().run()
|
Test().run()
|
||||||
|
|
||||||
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/md-label-font-style.gif
|
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/md-label-font-style.png
|
||||||
|
:align: center
|
||||||
|
|
||||||
|
Highlighting and copying labels
|
||||||
|
===============================
|
||||||
|
|
||||||
|
You can highlight labels by double tap on the label:
|
||||||
|
----------------------------------------------------
|
||||||
|
|
||||||
|
.. tabs::
|
||||||
|
|
||||||
|
.. tab:: Declarative KV style
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
from kivy.lang.builder import Builder
|
||||||
|
|
||||||
|
from kivymd.app import MDApp
|
||||||
|
|
||||||
|
KV = '''
|
||||||
|
MDScreen:
|
||||||
|
|
||||||
|
MDLabel:
|
||||||
|
adaptive_size: True
|
||||||
|
pos_hint: {"center_x": .5, "center_y": .5}
|
||||||
|
text: "MDLabel"
|
||||||
|
allow_selection: True
|
||||||
|
padding: "4dp", "4dp"
|
||||||
|
'''
|
||||||
|
|
||||||
|
|
||||||
|
class Example(MDApp):
|
||||||
|
def build(self):
|
||||||
|
self.theme_cls.theme_style = "Dark"
|
||||||
|
self.theme_cls.primary_palette = "Orange"
|
||||||
|
return Builder.load_string(KV)
|
||||||
|
|
||||||
|
|
||||||
|
Example().run()
|
||||||
|
|
||||||
|
.. tab:: Declarative Python style
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
from kivy.lang.builder import Builder
|
||||||
|
|
||||||
|
from kivymd.app import MDApp
|
||||||
|
from kivymd.uix.label import MDLabel
|
||||||
|
from kivymd.uix.screen import MDScreen
|
||||||
|
|
||||||
|
|
||||||
|
class Example(MDApp):
|
||||||
|
def build(self):
|
||||||
|
self.theme_cls.theme_style = "Dark"
|
||||||
|
self.theme_cls.primary_palette = "Orange"
|
||||||
|
return (
|
||||||
|
MDScreen(
|
||||||
|
MDLabel(
|
||||||
|
adaptive_size=True,
|
||||||
|
pos_hint={"center_x": .5, "center_y": .5},
|
||||||
|
text="MDLabel",
|
||||||
|
allow_selection=True,
|
||||||
|
padding=("4dp", "4dp"),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
Example().run()
|
||||||
|
|
||||||
|
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/md-label-allow-selection.gif
|
||||||
|
:align: center
|
||||||
|
|
||||||
|
You can copy the label text by double clicking on it:
|
||||||
|
-----------------------------------------------------
|
||||||
|
|
||||||
|
.. tabs::
|
||||||
|
|
||||||
|
.. tab:: Declarative KV style
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
from kivy.lang.builder import Builder
|
||||||
|
|
||||||
|
from kivymd.app import MDApp
|
||||||
|
|
||||||
|
KV = '''
|
||||||
|
MDScreen:
|
||||||
|
|
||||||
|
MDLabel:
|
||||||
|
adaptive_size: True
|
||||||
|
pos_hint: {"center_x": .5, "center_y": .5}
|
||||||
|
text: "MDLabel"
|
||||||
|
padding: "4dp", "4dp"
|
||||||
|
allow_selection: True
|
||||||
|
allow_copy: True
|
||||||
|
on_copy: print("The text is copied to the clipboard")
|
||||||
|
'''
|
||||||
|
|
||||||
|
|
||||||
|
class Example(MDApp):
|
||||||
|
def build(self):
|
||||||
|
self.theme_cls.theme_style = "Dark"
|
||||||
|
self.theme_cls.primary_palette = "Orange"
|
||||||
|
return Builder.load_string(KV)
|
||||||
|
|
||||||
|
|
||||||
|
Example().run()
|
||||||
|
|
||||||
|
.. tab:: Declarative Python style
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
from kivy.lang.builder import Builder
|
||||||
|
|
||||||
|
from kivymd.app import MDApp
|
||||||
|
from kivymd.uix.label import MDLabel
|
||||||
|
from kivymd.uix.screen import MDScreen
|
||||||
|
|
||||||
|
|
||||||
|
class Example(MDApp):
|
||||||
|
def build(self):
|
||||||
|
self.theme_cls.theme_style = "Dark"
|
||||||
|
self.theme_cls.primary_palette = "Orange"
|
||||||
|
return (
|
||||||
|
MDScreen(
|
||||||
|
MDLabel(
|
||||||
|
id="label",
|
||||||
|
adaptive_size=True,
|
||||||
|
pos_hint={"center_x": .5, "center_y": .5},
|
||||||
|
text="MDLabel",
|
||||||
|
allow_selection=True,
|
||||||
|
allow_copy=True,
|
||||||
|
padding=("4dp", "4dp"),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
def on_start(self):
|
||||||
|
self.root.ids.label.bind(on_copy=self.on_copy)
|
||||||
|
|
||||||
|
def on_copy(self, instance_label: MDLabel):
|
||||||
|
print("The text is copied to the clipboard")
|
||||||
|
|
||||||
|
|
||||||
|
Example().run()
|
||||||
|
|
||||||
|
Example of copying/cutting labels using the context menu
|
||||||
|
--------------------------------------------------------
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
from kivy.core.clipboard import Clipboard
|
||||||
|
from kivy.lang.builder import Builder
|
||||||
|
from kivy.metrics import dp
|
||||||
|
|
||||||
|
from kivymd.app import MDApp
|
||||||
|
from kivymd.uix.label import MDLabel
|
||||||
|
from kivymd.uix.menu import MDDropdownMenu
|
||||||
|
from kivymd.toast import toast
|
||||||
|
|
||||||
|
KV = '''
|
||||||
|
MDBoxLayout:
|
||||||
|
orientation: "vertical"
|
||||||
|
spacing: "12dp"
|
||||||
|
padding: "24dp"
|
||||||
|
|
||||||
|
MDScrollView:
|
||||||
|
|
||||||
|
MDBoxLayout:
|
||||||
|
id: box
|
||||||
|
orientation: "vertical"
|
||||||
|
padding: "24dp"
|
||||||
|
spacing: "12dp"
|
||||||
|
adaptive_height: True
|
||||||
|
|
||||||
|
MDTextField:
|
||||||
|
max_height: "200dp"
|
||||||
|
mode: "fill"
|
||||||
|
multiline: True
|
||||||
|
|
||||||
|
MDWidget:
|
||||||
|
'''
|
||||||
|
|
||||||
|
data = [
|
||||||
|
"Lorem ipsum dolor sit amet, consectetur adipiscing elit.",
|
||||||
|
"Sed blandit libero volutpat sed cras ornare arcu. Nisl vel pretium "
|
||||||
|
"lectus quam id leo in. Tincidunt arcu non sodales neque sodales ut etiam.",
|
||||||
|
"Elit scelerisque mauris pellentesque pulvinar pellentesque habitant. "
|
||||||
|
"Nisl rhoncus mattis rhoncus urna neque. Orci nulla pellentesque "
|
||||||
|
"dignissim enim. Ac auctor augue mauris augue neque gravida in fermentum. "
|
||||||
|
"Lacus suspendisse faucibus interdum posuere."
|
||||||
|
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class CopyLabel(MDLabel):
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
self.allow_selection = True
|
||||||
|
self.adaptive_height = True
|
||||||
|
self.theme_text_color = "Custom"
|
||||||
|
self.text_color = self.theme_cls.text_color
|
||||||
|
|
||||||
|
|
||||||
|
class Example(MDApp):
|
||||||
|
context_menu = None
|
||||||
|
|
||||||
|
def build(self):
|
||||||
|
self.theme_cls.theme_style = "Dark"
|
||||||
|
self.theme_cls.primary_palette = "Orange"
|
||||||
|
return Builder.load_string(KV)
|
||||||
|
|
||||||
|
def on_start(self):
|
||||||
|
for text in data:
|
||||||
|
copy_label = CopyLabel(text=text)
|
||||||
|
copy_label.bind(
|
||||||
|
on_selection=self.open_context_menu,
|
||||||
|
on_cancel_selection=self.restore_text_color,
|
||||||
|
)
|
||||||
|
self.root.ids.box.add_widget(copy_label)
|
||||||
|
|
||||||
|
def click_item_context_menu(
|
||||||
|
self, type_click: str, instance_label: CopyLabel
|
||||||
|
) -> None:
|
||||||
|
Clipboard.copy(instance_label.text)
|
||||||
|
|
||||||
|
if type_click == "copy":
|
||||||
|
toast("Copied")
|
||||||
|
elif type_click == "cut":
|
||||||
|
self.root.ids.box.remove_widget(instance_label)
|
||||||
|
toast("Cut")
|
||||||
|
if self.context_menu:
|
||||||
|
self.context_menu.dismiss()
|
||||||
|
|
||||||
|
def restore_text_color(self, instance_label: CopyLabel) -> None:
|
||||||
|
instance_label.text_color = self.theme_cls.text_color
|
||||||
|
|
||||||
|
def open_context_menu(self, instance_label: CopyLabel) -> None:
|
||||||
|
instance_label.text_color = "black"
|
||||||
|
menu_items = [
|
||||||
|
{
|
||||||
|
"text": "Copy text",
|
||||||
|
"viewclass": "OneLineListItem",
|
||||||
|
"height": dp(48),
|
||||||
|
"on_release": lambda: self.click_item_context_menu(
|
||||||
|
"copy", instance_label
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"text": "Cut text",
|
||||||
|
"viewclass": "OneLineListItem",
|
||||||
|
"height": dp(48),
|
||||||
|
"on_release": lambda: self.click_item_context_menu(
|
||||||
|
"cut", instance_label
|
||||||
|
),
|
||||||
|
},
|
||||||
|
]
|
||||||
|
self.context_menu = MDDropdownMenu(
|
||||||
|
caller=instance_label, items=menu_items, width_mult=3
|
||||||
|
)
|
||||||
|
self.context_menu.open()
|
||||||
|
|
||||||
|
|
||||||
|
Example().run()
|
||||||
|
|
||||||
|
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/copying-cutting-labels-using-context-menu.gif
|
||||||
:align: center
|
:align: center
|
||||||
|
|
||||||
.. MDIcon:
|
.. MDIcon:
|
||||||
|
@ -217,6 +500,8 @@ MDIcon with badge icon
|
||||||
:align: center
|
:align: center
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
__all__ = ("MDLabel", "MDIcon")
|
__all__ = ("MDLabel", "MDIcon")
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
@ -224,6 +509,8 @@ from typing import Union
|
||||||
|
|
||||||
from kivy.animation import Animation
|
from kivy.animation import Animation
|
||||||
from kivy.clock import Clock
|
from kivy.clock import Clock
|
||||||
|
from kivy.core.clipboard import Clipboard
|
||||||
|
from kivy.core.window import Window
|
||||||
from kivy.graphics import Color, Rectangle
|
from kivy.graphics import Color, Rectangle
|
||||||
from kivy.lang import Builder
|
from kivy.lang import Builder
|
||||||
from kivy.metrics import sp
|
from kivy.metrics import sp
|
||||||
|
@ -243,7 +530,7 @@ from kivymd import uix_path
|
||||||
from kivymd.theming import ThemableBehavior
|
from kivymd.theming import ThemableBehavior
|
||||||
from kivymd.theming_dynamic_text import get_contrast_text_color
|
from kivymd.theming_dynamic_text import get_contrast_text_color
|
||||||
from kivymd.uix import MDAdaptiveWidget
|
from kivymd.uix import MDAdaptiveWidget
|
||||||
from kivymd.uix.behaviors import DeclarativeBehavior
|
from kivymd.uix.behaviors import DeclarativeBehavior, TouchBehavior
|
||||||
from kivymd.uix.floatlayout import MDFloatLayout
|
from kivymd.uix.floatlayout import MDFloatLayout
|
||||||
|
|
||||||
__MDLabel_colors__ = {
|
__MDLabel_colors__ = {
|
||||||
|
@ -264,7 +551,36 @@ with open(
|
||||||
Builder.load_string(kv_file.read())
|
Builder.load_string(kv_file.read())
|
||||||
|
|
||||||
|
|
||||||
class MDLabel(DeclarativeBehavior, ThemableBehavior, Label, MDAdaptiveWidget):
|
class MDLabel(
|
||||||
|
DeclarativeBehavior,
|
||||||
|
ThemableBehavior,
|
||||||
|
Label,
|
||||||
|
MDAdaptiveWidget,
|
||||||
|
TouchBehavior,
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
Label class.
|
||||||
|
|
||||||
|
For more information, see in the
|
||||||
|
:class:`~kivymd.uix.behaviors.DeclarativeBehavior` and
|
||||||
|
:class:`~kivymd.theming.ThemableBehavior` and
|
||||||
|
:class:`~kivy.uix.label.Label` and
|
||||||
|
:class:`~kivymd.uix.MDAdaptiveWidget` and
|
||||||
|
:class:`~kivymd.uix.behaviors.TouchBehavior`
|
||||||
|
classes documentation.
|
||||||
|
|
||||||
|
:Events:
|
||||||
|
`on_ref_press`
|
||||||
|
Called when the user clicks on a word referenced with a
|
||||||
|
``[ref]`` tag in a text markup.
|
||||||
|
`on_copy`
|
||||||
|
Called when double-tapping on the label.
|
||||||
|
`on_selection`
|
||||||
|
Called when double-tapping on the label.
|
||||||
|
`on_cancel_selection`
|
||||||
|
Called when the highlighting is removed from the label text.
|
||||||
|
"""
|
||||||
|
|
||||||
font_style = StringProperty("Body1")
|
font_style = StringProperty("Body1")
|
||||||
"""
|
"""
|
||||||
Label font style.
|
Label font style.
|
||||||
|
@ -316,29 +632,84 @@ class MDLabel(DeclarativeBehavior, ThemableBehavior, Label, MDAdaptiveWidget):
|
||||||
|
|
||||||
text_color = ColorProperty(None)
|
text_color = ColorProperty(None)
|
||||||
"""
|
"""
|
||||||
Label text color in (r, g, b, a) format.
|
Label text color in (r, g, b, a) or string format.
|
||||||
|
|
||||||
:attr:`text_color` is an :class:`~kivy.properties.ColorProperty`
|
:attr:`text_color` is an :class:`~kivy.properties.ColorProperty`
|
||||||
and defaults to `None`.
|
and defaults to `None`.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
allow_copy = BooleanProperty(False)
|
||||||
|
"""
|
||||||
|
Allows you to copy text to the clipboard by double-clicking on the label.
|
||||||
|
|
||||||
|
.. versionadded:: 1.2.0
|
||||||
|
|
||||||
|
:attr:`allow_copy` is an :class:`~kivy.properties.BooleanProperty`
|
||||||
|
and defaults to `False`.
|
||||||
|
"""
|
||||||
|
|
||||||
|
allow_selection = BooleanProperty(False)
|
||||||
|
"""
|
||||||
|
Allows to highlight text by double-clicking on the label.
|
||||||
|
|
||||||
|
.. versionadded:: 1.2.0
|
||||||
|
|
||||||
|
:attr:`allow_selection` is an :class:`~kivy.properties.BooleanProperty`
|
||||||
|
and defaults to `False`.
|
||||||
|
"""
|
||||||
|
|
||||||
|
color_selection = ColorProperty(None)
|
||||||
|
"""
|
||||||
|
The color in (r, g, b, a) or string format of the text selection when the
|
||||||
|
value of the :attr:`allow_selection` attribute is True.
|
||||||
|
|
||||||
|
.. versionadded:: 1.2.0
|
||||||
|
|
||||||
|
:attr:`color_selection` is an :class:`~kivy.properties.ColorProperty`
|
||||||
|
and defaults to `None`.
|
||||||
|
"""
|
||||||
|
|
||||||
|
color_deselection = ColorProperty(None)
|
||||||
|
"""
|
||||||
|
The color in (r, g, b, a) or string format of the text deselection when the
|
||||||
|
value of the :attr:`allow_selection` attribute is True.
|
||||||
|
|
||||||
|
.. versionadded:: 1.2.0
|
||||||
|
|
||||||
|
:attr:`color_deselection` is an :class:`~kivy.properties.ColorProperty`
|
||||||
|
and defaults to `None`.
|
||||||
|
"""
|
||||||
|
|
||||||
|
is_selected = BooleanProperty(False)
|
||||||
|
"""
|
||||||
|
Is the label text highlighted.
|
||||||
|
|
||||||
|
.. versionadded:: 1.2.0
|
||||||
|
|
||||||
|
:attr:`is_selected` is an :class:`~kivy.properties.BooleanProperty`
|
||||||
|
and defaults to `False`.
|
||||||
|
"""
|
||||||
|
|
||||||
_text_color_str = StringProperty()
|
_text_color_str = StringProperty()
|
||||||
|
|
||||||
parent_background = ColorProperty(None)
|
parent_background = ColorProperty(None)
|
||||||
can_capitalize = BooleanProperty(True)
|
can_capitalize = BooleanProperty(True)
|
||||||
canvas_bg = ObjectProperty()
|
canvas_bg = ObjectProperty()
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super().__init__(**kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
self.bind(
|
self.bind(
|
||||||
font_style=self.update_font_style,
|
font_style=self.update_font_style,
|
||||||
can_capitalize=self.update_font_style,
|
can_capitalize=self.update_font_style,
|
||||||
)
|
)
|
||||||
|
self.theme_cls.bind(theme_style=self._do_update_theme_color)
|
||||||
|
self.register_event_type("on_copy")
|
||||||
|
self.register_event_type("on_selection")
|
||||||
|
self.register_event_type("on_cancel_selection")
|
||||||
self.on_theme_text_color(None, self.theme_text_color)
|
self.on_theme_text_color(None, self.theme_text_color)
|
||||||
self.update_font_style(None, "")
|
self.update_font_style(None, "")
|
||||||
self.on_opposite_colors(None, self.opposite_colors)
|
self.on_opposite_colors(None, self.opposite_colors)
|
||||||
Clock.schedule_once(self.check_font_styles)
|
Clock.schedule_once(self.check_font_styles)
|
||||||
self.theme_cls.bind(theme_style=self._do_update_theme_color)
|
|
||||||
|
|
||||||
def check_font_styles(self, interval: Union[int, float] = 0) -> bool:
|
def check_font_styles(self, interval: Union[int, float] = 0) -> bool:
|
||||||
if self.font_style not in list(self.theme_cls.font_styles.keys()):
|
if self.font_style not in list(self.theme_cls.font_styles.keys()):
|
||||||
|
@ -353,6 +724,7 @@ class MDLabel(DeclarativeBehavior, ThemableBehavior, Label, MDAdaptiveWidget):
|
||||||
if self.check_font_styles() is True:
|
if self.check_font_styles() is True:
|
||||||
font_info = self.theme_cls.font_styles[self.font_style]
|
font_info = self.theme_cls.font_styles[self.font_style]
|
||||||
self.font_name = font_info[0]
|
self.font_name = font_info[0]
|
||||||
|
if self.font_style in list(self.theme_cls.font_styles.keys())[0:14]:
|
||||||
self.font_size = sp(font_info[1])
|
self.font_size = sp(font_info[1])
|
||||||
|
|
||||||
if font_info[2] and self.can_capitalize:
|
if font_info[2] and self.can_capitalize:
|
||||||
|
@ -363,6 +735,64 @@ class MDLabel(DeclarativeBehavior, ThemableBehavior, Label, MDAdaptiveWidget):
|
||||||
# TODO: Add letter spacing change
|
# TODO: Add letter spacing change
|
||||||
# self.letter_spacing = font_info[3]
|
# self.letter_spacing = font_info[3]
|
||||||
|
|
||||||
|
def do_selection(self) -> None:
|
||||||
|
if not self.is_selected:
|
||||||
|
self.md_bg_color = (
|
||||||
|
self.theme_cls.primary_light
|
||||||
|
if not self.color_selection
|
||||||
|
else self.color_selection
|
||||||
|
)
|
||||||
|
|
||||||
|
def cancel_selection(self) -> None:
|
||||||
|
if self.is_selected:
|
||||||
|
self.md_bg_color = (
|
||||||
|
self.theme_cls.bg_normal
|
||||||
|
if not self.color_deselection
|
||||||
|
else self.color_deselection
|
||||||
|
)
|
||||||
|
self.dispatch("on_cancel_selection")
|
||||||
|
self.is_selected = False
|
||||||
|
|
||||||
|
def on_double_tap(self, touch, *args) -> None:
|
||||||
|
if self.allow_copy and self.collide_point(*touch.pos):
|
||||||
|
Clipboard.copy(self.text)
|
||||||
|
self.dispatch("on_copy")
|
||||||
|
if self.allow_selection and self.collide_point(*touch.pos):
|
||||||
|
self.do_selection()
|
||||||
|
self.dispatch("on_selection")
|
||||||
|
self.is_selected = True
|
||||||
|
|
||||||
|
def on_window_touch(self, *args):
|
||||||
|
if self.is_selected:
|
||||||
|
self.cancel_selection()
|
||||||
|
|
||||||
|
def on_copy(self, *args) -> None:
|
||||||
|
"""
|
||||||
|
Called when double-tapping on the label.
|
||||||
|
|
||||||
|
.. versionadded:: 1.2.0
|
||||||
|
"""
|
||||||
|
|
||||||
|
def on_selection(self, *args) -> None:
|
||||||
|
"""
|
||||||
|
Called when double-tapping on the label.
|
||||||
|
|
||||||
|
.. versionadded:: 1.2.0
|
||||||
|
"""
|
||||||
|
|
||||||
|
def on_cancel_selection(self, *args) -> None:
|
||||||
|
"""
|
||||||
|
Called when the highlighting is removed from the label text.
|
||||||
|
|
||||||
|
.. versionadded:: 1.2.0
|
||||||
|
"""
|
||||||
|
|
||||||
|
def on_allow_selection(self, instance_label, selection: bool) -> None:
|
||||||
|
if selection:
|
||||||
|
Window.bind(on_touch_down=self.on_window_touch)
|
||||||
|
else:
|
||||||
|
Window.unbind(on_touch_down=self.on_window_touch)
|
||||||
|
|
||||||
def on_theme_text_color(
|
def on_theme_text_color(
|
||||||
self, instance_label, theme_text_color: str
|
self, instance_label, theme_text_color: str
|
||||||
) -> None:
|
) -> None:
|
||||||
|
@ -414,6 +844,7 @@ class MDLabel(DeclarativeBehavior, ThemableBehavior, Label, MDAdaptiveWidget):
|
||||||
|
|
||||||
def on_md_bg_color(self, instance_label, color: Union[list, str]) -> None:
|
def on_md_bg_color(self, instance_label, color: Union[list, str]) -> None:
|
||||||
self.canvas.remove_group("Background_instruction")
|
self.canvas.remove_group("Background_instruction")
|
||||||
|
self.canvas.before.clear()
|
||||||
with self.canvas.before:
|
with self.canvas.before:
|
||||||
Color(rgba=color)
|
Color(rgba=color)
|
||||||
self.canvas_bg = Rectangle(pos=self.pos, size=self.size)
|
self.canvas_bg = Rectangle(pos=self.pos, size=self.size)
|
||||||
|
@ -445,6 +876,13 @@ class MDLabel(DeclarativeBehavior, ThemableBehavior, Label, MDAdaptiveWidget):
|
||||||
|
|
||||||
|
|
||||||
class MDIcon(MDFloatLayout, MDLabel):
|
class MDIcon(MDFloatLayout, MDLabel):
|
||||||
|
"""
|
||||||
|
Icon class.
|
||||||
|
|
||||||
|
For more information, see in the :class:`~MDLabel` and
|
||||||
|
:class:`~kivymd.uix.floatlayout.MDFloatLayout` classes documentation.
|
||||||
|
"""
|
||||||
|
|
||||||
icon = StringProperty("android")
|
icon = StringProperty("android")
|
||||||
"""
|
"""
|
||||||
Label icon name.
|
Label icon name.
|
||||||
|
@ -465,7 +903,7 @@ class MDIcon(MDFloatLayout, MDLabel):
|
||||||
|
|
||||||
badge_icon_color = ColorProperty([1, 1, 1, 1])
|
badge_icon_color = ColorProperty([1, 1, 1, 1])
|
||||||
"""
|
"""
|
||||||
Badge icon color in (r, g, b, a) format.
|
Badge icon color in (r, g, b, a) or string format.
|
||||||
|
|
||||||
.. versionadded:: 1.0.0
|
.. versionadded:: 1.0.0
|
||||||
|
|
||||||
|
@ -475,7 +913,7 @@ class MDIcon(MDFloatLayout, MDLabel):
|
||||||
|
|
||||||
badge_bg_color = ColorProperty(None)
|
badge_bg_color = ColorProperty(None)
|
||||||
"""
|
"""
|
||||||
Badge icon background color in (r, g, b, a) format.
|
Badge icon background color in (r, g, b, a) or string format.
|
||||||
|
|
||||||
.. versionadded:: 1.0.0
|
.. versionadded:: 1.0.0
|
||||||
|
|
||||||
|
|
|
@ -984,6 +984,9 @@ class MDList(MDGridLayout):
|
||||||
|
|
||||||
When adding (or removing) a widget, it will resize itself to fit its
|
When adding (or removing) a widget, it will resize itself to fit its
|
||||||
children, plus top and bottom paddings as described by the `MD` spec.
|
children, plus top and bottom paddings as described by the `MD` spec.
|
||||||
|
|
||||||
|
For more information, see in the
|
||||||
|
:class:`~kivymd.uix.gridlayout.MDGridLayout` classes documentation.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
_list_vertical_padding = NumericProperty("8dp")
|
_list_vertical_padding = NumericProperty("8dp")
|
||||||
|
@ -1002,6 +1005,13 @@ class BaseListItem(
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
Base class to all ListItems. Not supposed to be instantiated on its own.
|
Base class to all ListItems. Not supposed to be instantiated on its own.
|
||||||
|
|
||||||
|
For more information, see in the
|
||||||
|
:class:`~kivymd.uix.behaviors.DeclarativeBehavior` and
|
||||||
|
:class:`~kivymd.theming.ThemableBehavior` and
|
||||||
|
:class:`~kivymd.uix.behaviors.RectangularRippleBehavior` and
|
||||||
|
:class:`~kivy.uix.behaviors.ButtonBehavior` and
|
||||||
|
:class:`~kivy.uix.floatlayout.FloatLayout` classes documentation.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
text = StringProperty()
|
text = StringProperty()
|
||||||
|
@ -1242,7 +1252,12 @@ class IRightBodyTouch:
|
||||||
|
|
||||||
|
|
||||||
class OneLineListItem(BaseListItem):
|
class OneLineListItem(BaseListItem):
|
||||||
"""A one line list item."""
|
"""
|
||||||
|
A one line list item.
|
||||||
|
|
||||||
|
For more information, see in the :class:`~BaseListItem`
|
||||||
|
classes documentation.
|
||||||
|
"""
|
||||||
|
|
||||||
_txt_top_pad = NumericProperty("16dp")
|
_txt_top_pad = NumericProperty("16dp")
|
||||||
_txt_bot_pad = NumericProperty("15dp")
|
_txt_bot_pad = NumericProperty("15dp")
|
||||||
|
@ -1255,7 +1270,12 @@ class OneLineListItem(BaseListItem):
|
||||||
|
|
||||||
|
|
||||||
class TwoLineListItem(BaseListItem):
|
class TwoLineListItem(BaseListItem):
|
||||||
"""A two line list item."""
|
"""
|
||||||
|
A two line list item.
|
||||||
|
|
||||||
|
For more information, see in the :class:`~BaseListItem`
|
||||||
|
classes documentation.
|
||||||
|
"""
|
||||||
|
|
||||||
_txt_top_pad = NumericProperty("20dp")
|
_txt_top_pad = NumericProperty("20dp")
|
||||||
_txt_bot_pad = NumericProperty("15dp")
|
_txt_bot_pad = NumericProperty("15dp")
|
||||||
|
@ -1267,7 +1287,12 @@ class TwoLineListItem(BaseListItem):
|
||||||
|
|
||||||
|
|
||||||
class ThreeLineListItem(BaseListItem):
|
class ThreeLineListItem(BaseListItem):
|
||||||
"""A three line list item."""
|
"""
|
||||||
|
A three line list item.
|
||||||
|
|
||||||
|
For more information, see in the :class:`~BaseListItem`
|
||||||
|
classes documentation.
|
||||||
|
"""
|
||||||
|
|
||||||
_txt_top_pad = NumericProperty("16dp")
|
_txt_top_pad = NumericProperty("16dp")
|
||||||
_txt_bot_pad = NumericProperty("15dp")
|
_txt_bot_pad = NumericProperty("15dp")
|
||||||
|
@ -1280,6 +1305,13 @@ class ThreeLineListItem(BaseListItem):
|
||||||
|
|
||||||
|
|
||||||
class OneLineAvatarListItem(BaseListItem):
|
class OneLineAvatarListItem(BaseListItem):
|
||||||
|
"""
|
||||||
|
A one line list item with left image.
|
||||||
|
|
||||||
|
For more information, see in the :class:`~BaseListItem`
|
||||||
|
classes documentation.
|
||||||
|
"""
|
||||||
|
|
||||||
_txt_left_pad = NumericProperty("72dp")
|
_txt_left_pad = NumericProperty("72dp")
|
||||||
_txt_top_pad = NumericProperty("20dp")
|
_txt_top_pad = NumericProperty("20dp")
|
||||||
_txt_bot_pad = NumericProperty("19dp")
|
_txt_bot_pad = NumericProperty("19dp")
|
||||||
|
@ -1292,6 +1324,13 @@ class OneLineAvatarListItem(BaseListItem):
|
||||||
|
|
||||||
|
|
||||||
class TwoLineAvatarListItem(OneLineAvatarListItem):
|
class TwoLineAvatarListItem(OneLineAvatarListItem):
|
||||||
|
"""
|
||||||
|
A two line list item with left image.
|
||||||
|
|
||||||
|
For more information, see in the :class:`~OneLineAvatarListItem`
|
||||||
|
classes documentation.
|
||||||
|
"""
|
||||||
|
|
||||||
_txt_top_pad = NumericProperty("20dp")
|
_txt_top_pad = NumericProperty("20dp")
|
||||||
_txt_bot_pad = NumericProperty("15dp")
|
_txt_bot_pad = NumericProperty("15dp")
|
||||||
_height = NumericProperty()
|
_height = NumericProperty()
|
||||||
|
@ -1303,6 +1342,13 @@ class TwoLineAvatarListItem(OneLineAvatarListItem):
|
||||||
|
|
||||||
|
|
||||||
class ThreeLineAvatarListItem(ThreeLineListItem):
|
class ThreeLineAvatarListItem(ThreeLineListItem):
|
||||||
|
"""
|
||||||
|
A three line list item with left image.
|
||||||
|
|
||||||
|
For more information, see in the :class:`~ThreeLineListItem`
|
||||||
|
classes documentation.
|
||||||
|
"""
|
||||||
|
|
||||||
_txt_left_pad = NumericProperty("72dp")
|
_txt_left_pad = NumericProperty("72dp")
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
|
@ -1310,10 +1356,24 @@ class ThreeLineAvatarListItem(ThreeLineListItem):
|
||||||
|
|
||||||
|
|
||||||
class OneLineIconListItem(OneLineListItem):
|
class OneLineIconListItem(OneLineListItem):
|
||||||
|
"""
|
||||||
|
A one line list item with left icon.
|
||||||
|
|
||||||
|
For more information, see in the :class:`~OneLineListItem`
|
||||||
|
classes documentation.
|
||||||
|
"""
|
||||||
|
|
||||||
_txt_left_pad = NumericProperty("72dp")
|
_txt_left_pad = NumericProperty("72dp")
|
||||||
|
|
||||||
|
|
||||||
class TwoLineIconListItem(OneLineIconListItem):
|
class TwoLineIconListItem(OneLineIconListItem):
|
||||||
|
"""
|
||||||
|
A two line list item with left icon.
|
||||||
|
|
||||||
|
For more information, see in the :class:`~OneLineIconListItem`
|
||||||
|
classes documentation.
|
||||||
|
"""
|
||||||
|
|
||||||
_txt_top_pad = NumericProperty("20dp")
|
_txt_top_pad = NumericProperty("20dp")
|
||||||
_txt_bot_pad = NumericProperty("15dp")
|
_txt_bot_pad = NumericProperty("15dp")
|
||||||
_height = NumericProperty()
|
_height = NumericProperty()
|
||||||
|
@ -1325,10 +1385,24 @@ class TwoLineIconListItem(OneLineIconListItem):
|
||||||
|
|
||||||
|
|
||||||
class ThreeLineIconListItem(ThreeLineListItem):
|
class ThreeLineIconListItem(ThreeLineListItem):
|
||||||
|
"""
|
||||||
|
A three line list item with left icon.
|
||||||
|
|
||||||
|
For more information, see in the :class:`~ThreeLineListItem`
|
||||||
|
classes documentation.
|
||||||
|
"""
|
||||||
|
|
||||||
_txt_left_pad = NumericProperty("72dp")
|
_txt_left_pad = NumericProperty("72dp")
|
||||||
|
|
||||||
|
|
||||||
class OneLineRightIconListItem(OneLineListItem):
|
class OneLineRightIconListItem(OneLineListItem):
|
||||||
|
"""
|
||||||
|
A one line list item with right icon/image.
|
||||||
|
|
||||||
|
For more information, see in the :class:`~OneLineListItem`
|
||||||
|
classes documentation.
|
||||||
|
"""
|
||||||
|
|
||||||
_txt_right_pad = NumericProperty("40dp")
|
_txt_right_pad = NumericProperty("40dp")
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
|
@ -1337,6 +1411,13 @@ class OneLineRightIconListItem(OneLineListItem):
|
||||||
|
|
||||||
|
|
||||||
class TwoLineRightIconListItem(OneLineRightIconListItem):
|
class TwoLineRightIconListItem(OneLineRightIconListItem):
|
||||||
|
"""
|
||||||
|
A two line list item with right icon/image.
|
||||||
|
|
||||||
|
For more information, see in the :class:`~OneLineRightIconListItem`
|
||||||
|
classes documentation.
|
||||||
|
"""
|
||||||
|
|
||||||
_txt_top_pad = NumericProperty("20dp")
|
_txt_top_pad = NumericProperty("20dp")
|
||||||
_txt_bot_pad = NumericProperty("15dp")
|
_txt_bot_pad = NumericProperty("15dp")
|
||||||
_height = NumericProperty()
|
_height = NumericProperty()
|
||||||
|
@ -1348,6 +1429,13 @@ class TwoLineRightIconListItem(OneLineRightIconListItem):
|
||||||
|
|
||||||
|
|
||||||
class ThreeLineRightIconListItem(ThreeLineListItem):
|
class ThreeLineRightIconListItem(ThreeLineListItem):
|
||||||
|
"""
|
||||||
|
A three line list item with right icon/image.
|
||||||
|
|
||||||
|
For more information, see in the :class:`~ThreeLineRightIconListItem`
|
||||||
|
classes documentation.
|
||||||
|
"""
|
||||||
|
|
||||||
_txt_right_pad = NumericProperty("40dp")
|
_txt_right_pad = NumericProperty("40dp")
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
|
@ -1356,6 +1444,13 @@ class ThreeLineRightIconListItem(ThreeLineListItem):
|
||||||
|
|
||||||
|
|
||||||
class OneLineAvatarIconListItem(OneLineAvatarListItem):
|
class OneLineAvatarIconListItem(OneLineAvatarListItem):
|
||||||
|
"""
|
||||||
|
A one line list item with left/right icon/image/widget.
|
||||||
|
|
||||||
|
For more information, see in the :class:`~OneLineAvatarListItem`
|
||||||
|
classes documentation.
|
||||||
|
"""
|
||||||
|
|
||||||
_txt_right_pad = NumericProperty("40dp")
|
_txt_right_pad = NumericProperty("40dp")
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
|
@ -1364,6 +1459,13 @@ class OneLineAvatarIconListItem(OneLineAvatarListItem):
|
||||||
|
|
||||||
|
|
||||||
class TwoLineAvatarIconListItem(TwoLineAvatarListItem):
|
class TwoLineAvatarIconListItem(TwoLineAvatarListItem):
|
||||||
|
"""
|
||||||
|
A two line list item with left/right icon/image/widget.
|
||||||
|
|
||||||
|
For more information, see in the :class:`~TwoLineAvatarListItem`
|
||||||
|
classes documentation.
|
||||||
|
"""
|
||||||
|
|
||||||
_txt_right_pad = NumericProperty("40dp")
|
_txt_right_pad = NumericProperty("40dp")
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
|
@ -1372,6 +1474,13 @@ class TwoLineAvatarIconListItem(TwoLineAvatarListItem):
|
||||||
|
|
||||||
|
|
||||||
class ThreeLineAvatarIconListItem(ThreeLineAvatarListItem):
|
class ThreeLineAvatarIconListItem(ThreeLineAvatarListItem):
|
||||||
|
"""
|
||||||
|
A three line list item with left/right icon/image/widget.
|
||||||
|
|
||||||
|
For more information, see in the :class:`~ThreeLineAvatarListItem`
|
||||||
|
classes documentation.
|
||||||
|
"""
|
||||||
|
|
||||||
_txt_right_pad = NumericProperty("40dp")
|
_txt_right_pad = NumericProperty("40dp")
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
|
@ -1388,13 +1497,31 @@ class TouchBehavior:
|
||||||
class ImageLeftWidget(
|
class ImageLeftWidget(
|
||||||
CircularRippleBehavior, ButtonBehavior, ILeftBodyTouch, FitImage
|
CircularRippleBehavior, ButtonBehavior, ILeftBodyTouch, FitImage
|
||||||
):
|
):
|
||||||
pass
|
"""
|
||||||
|
The widget implements the left image for use in ListItem classes.
|
||||||
|
|
||||||
|
For more information, see in the
|
||||||
|
:class:`~kivymd.uix.behaviors.CircularRippleBehavior` and
|
||||||
|
:class:`~kivy.uix.behaviors.ButtonBehavior` and
|
||||||
|
:class:`~ILeftBodyTouch` and
|
||||||
|
:class:`~kivymd.uix.fitimage.FitImage` classes documentation.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
class ImageLeftWidgetWithoutTouch(
|
class ImageLeftWidgetWithoutTouch(
|
||||||
CircularRippleBehavior, TouchBehavior, ButtonBehavior, ILeftBody, FitImage
|
CircularRippleBehavior, TouchBehavior, ButtonBehavior, ILeftBody, FitImage
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
|
Disables the image event.
|
||||||
|
The widget implements the left image for use in `ListItem` classes.
|
||||||
|
|
||||||
|
For more information, see in the
|
||||||
|
:class:`~kivymd.uix.behaviors.CircularRippleBehavior` and
|
||||||
|
:class:`~TouchBehavior` and
|
||||||
|
:class:`~kivy.uix.behaviors.ButtonBehavior` and
|
||||||
|
:class:`~ILeftBody` and
|
||||||
|
:class:`~kivymd.uix.fitimage.FitImage` classes documentation.
|
||||||
|
|
||||||
.. versionadded:: 1.0.0
|
.. versionadded:: 1.0.0
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
@ -1404,13 +1531,31 @@ class ImageLeftWidgetWithoutTouch(
|
||||||
class ImageRightWidget(
|
class ImageRightWidget(
|
||||||
CircularRippleBehavior, ButtonBehavior, IRightBodyTouch, FitImage
|
CircularRippleBehavior, ButtonBehavior, IRightBodyTouch, FitImage
|
||||||
):
|
):
|
||||||
pass
|
"""
|
||||||
|
The widget implements the right image for use in ListItem classes.
|
||||||
|
|
||||||
|
For more information, see in the
|
||||||
|
:class:`~kivymd.uix.behaviors.CircularRippleBehavior` and
|
||||||
|
:class:`~kivy.uix.behaviors.ButtonBehavior` and
|
||||||
|
:class:`~IRightBodyTouch` and
|
||||||
|
:class:`~kivymd.uix.fitimage.FitImage` classes documentation.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
class ImageRightWidgetWithoutTouch(
|
class ImageRightWidgetWithoutTouch(
|
||||||
CircularRippleBehavior, TouchBehavior, ButtonBehavior, IRightBody, FitImage
|
CircularRippleBehavior, TouchBehavior, ButtonBehavior, IRightBody, FitImage
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
|
Disables the image event.
|
||||||
|
The widget implements the right image for use in `ListItem` classes.
|
||||||
|
|
||||||
|
For more information, see in the
|
||||||
|
:class:`~kivymd.uix.behaviors.CircularRippleBehavior` and
|
||||||
|
:class:`~TouchBehavior` and
|
||||||
|
:class:`~kivy.uix.behaviors.ButtonBehavior` and
|
||||||
|
:class:`~IRightBody` and
|
||||||
|
:class:`~kivymd.uix.fitimage.FitImage` classes documentation.
|
||||||
|
|
||||||
.. versionadded:: 1.0.0
|
.. versionadded:: 1.0.0
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
@ -1418,11 +1563,29 @@ class ImageRightWidgetWithoutTouch(
|
||||||
|
|
||||||
|
|
||||||
class IconRightWidget(IRightBodyTouch, MDIconButton):
|
class IconRightWidget(IRightBodyTouch, MDIconButton):
|
||||||
|
"""
|
||||||
|
The widget implements the right icon for use in ListItem classes.
|
||||||
|
|
||||||
|
For more information, see in the
|
||||||
|
:class:`~IRightBodyTouch` and
|
||||||
|
:class:`~kivymd.uix.button.MDIconButton`
|
||||||
|
classes documentation.
|
||||||
|
"""
|
||||||
|
|
||||||
pos_hint = {"center_y": 0.5}
|
pos_hint = {"center_y": 0.5}
|
||||||
|
|
||||||
|
|
||||||
class IconRightWidgetWithoutTouch(TouchBehavior, IRightBody, MDIconButton):
|
class IconRightWidgetWithoutTouch(TouchBehavior, IRightBody, MDIconButton):
|
||||||
"""
|
"""
|
||||||
|
Disables the icon event.
|
||||||
|
The widget implements the right icon for use in ListItem classes.
|
||||||
|
|
||||||
|
For more information, see in the
|
||||||
|
:class:`~TouchBehavior` and
|
||||||
|
:class:`~IRightBody` and
|
||||||
|
:class:`~kivymd.uix.button.MDIconButton`
|
||||||
|
classes documentation.
|
||||||
|
|
||||||
.. versionadded:: 1.0.0
|
.. versionadded:: 1.0.0
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
@ -1431,11 +1594,29 @@ class IconRightWidgetWithoutTouch(TouchBehavior, IRightBody, MDIconButton):
|
||||||
|
|
||||||
|
|
||||||
class IconLeftWidget(ILeftBodyTouch, MDIconButton):
|
class IconLeftWidget(ILeftBodyTouch, MDIconButton):
|
||||||
|
"""
|
||||||
|
The widget implements the left icon for use in ListItem classes.
|
||||||
|
|
||||||
|
For more information, see in the
|
||||||
|
:class:`~ILeftBodyTouch` and
|
||||||
|
:class:`~kivymd.uix.button.MDIconButton`
|
||||||
|
classes documentation.
|
||||||
|
"""
|
||||||
|
|
||||||
pos_hint = {"center_y": 0.5}
|
pos_hint = {"center_y": 0.5}
|
||||||
|
|
||||||
|
|
||||||
class IconLeftWidgetWithoutTouch(TouchBehavior, ILeftBody, MDIconButton):
|
class IconLeftWidgetWithoutTouch(TouchBehavior, ILeftBody, MDIconButton):
|
||||||
"""
|
"""
|
||||||
|
Disables the icon event.
|
||||||
|
The widget implements the left icon for use in ListItem classes.
|
||||||
|
|
||||||
|
For more information, see in the
|
||||||
|
:class:`~TouchBehavior` and
|
||||||
|
:class:`~ILeftBody` and
|
||||||
|
:class:`~kivymd.uix.button.MDIconButton`
|
||||||
|
classes documentation.
|
||||||
|
|
||||||
.. versionadded:: 1.0.0
|
.. versionadded:: 1.0.0
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
@ -1444,4 +1625,11 @@ class IconLeftWidgetWithoutTouch(TouchBehavior, ILeftBody, MDIconButton):
|
||||||
|
|
||||||
|
|
||||||
class CheckboxLeftWidget(ILeftBodyTouch, MDCheckbox):
|
class CheckboxLeftWidget(ILeftBodyTouch, MDCheckbox):
|
||||||
pass
|
"""
|
||||||
|
The widget implements the left checkbox element for use in ListItem classes.
|
||||||
|
|
||||||
|
For more information, see in the
|
||||||
|
:class:`~ILeftBodyTouch` and
|
||||||
|
:class:`~kivymd.uix.selectioncontrol.MDCheckbox`
|
||||||
|
classes documentation.
|
||||||
|
"""
|
||||||
|
|
|
@ -1,26 +1,9 @@
|
||||||
#:import STANDARD_INCREMENT kivymd.material_resources.STANDARD_INCREMENT
|
|
||||||
|
|
||||||
|
|
||||||
<RightContent>
|
|
||||||
adaptive_width: True
|
|
||||||
|
|
||||||
|
|
||||||
<MDMenuItemIcon>
|
|
||||||
|
|
||||||
IconLeftWidget:
|
|
||||||
id: icon_widget
|
|
||||||
icon: root.icon
|
|
||||||
|
|
||||||
|
|
||||||
<MDMenu>
|
<MDMenu>
|
||||||
size_hint: None, None
|
|
||||||
width: root.width_mult * STANDARD_INCREMENT
|
|
||||||
bar_width: 0
|
bar_width: 0
|
||||||
key_viewclass: "viewclass"
|
key_viewclass: "viewclass"
|
||||||
key_size: "height"
|
key_size: "height"
|
||||||
|
|
||||||
RecycleBoxLayout:
|
RecycleBoxLayout:
|
||||||
padding: 0, "4dp", 0, "4dp"
|
|
||||||
default_size: None, dp(48)
|
default_size: None, dp(48)
|
||||||
default_size_hint: 1, None
|
default_size_hint: 1, None
|
||||||
size_hint_y: None
|
size_hint_y: None
|
||||||
|
@ -28,23 +11,473 @@
|
||||||
orientation: "vertical"
|
orientation: "vertical"
|
||||||
|
|
||||||
|
|
||||||
<MenuContainer@MDCard>
|
<MDDropdownTrailingTextItem>
|
||||||
|
orientation: "vertical"
|
||||||
|
|
||||||
|
MDBoxLayout:
|
||||||
|
id: container
|
||||||
|
spacing: "12dp"
|
||||||
|
padding: "12dp", 0, "12dp", 0
|
||||||
|
|
||||||
|
MDLabel:
|
||||||
|
text: root.text
|
||||||
|
pos_hint: {"center_y": .5}
|
||||||
|
theme_text_color: "Custom" if root.text_color else "Primary"
|
||||||
|
shorten: True
|
||||||
|
shorten_from: "right"
|
||||||
|
size_hint_x: None
|
||||||
|
width:
|
||||||
|
root.width - \
|
||||||
|
( \
|
||||||
|
+ trailing_container.width \
|
||||||
|
+ container.padding[0] \
|
||||||
|
+ container.padding[2] \
|
||||||
|
+ container.spacing \
|
||||||
|
)
|
||||||
|
text_color:
|
||||||
|
root.text_color \
|
||||||
|
if root.text_color else \
|
||||||
|
app.theme_cls.text_color
|
||||||
|
|
||||||
|
MDTrailingTextContainer:
|
||||||
|
id: trailing_container
|
||||||
|
text: root.trailing_text
|
||||||
|
adaptive_width: True
|
||||||
|
theme_text_color: "Custom" if root.trailing_text_color else "Primary"
|
||||||
|
text_color:
|
||||||
|
root.trailing_text_color \
|
||||||
|
if root.trailing_text_color else \
|
||||||
|
app.theme_cls.text_color
|
||||||
|
|
||||||
|
MDSeparator:
|
||||||
|
md_bg_color:
|
||||||
|
( \
|
||||||
|
self.theme_cls.divider_color \
|
||||||
|
if not root.divider_color \
|
||||||
|
else root.divider_color \
|
||||||
|
) \
|
||||||
|
if root.divider else \
|
||||||
|
(0, 0, 0, 0)
|
||||||
|
|
||||||
|
|
||||||
|
<MDDropdownLeadingIconTrailingTextItem>
|
||||||
|
orientation: "vertical"
|
||||||
|
|
||||||
|
MDBoxLayout:
|
||||||
|
id: container
|
||||||
|
spacing: "12dp"
|
||||||
|
padding: "10dp", 0, "16dp", 0
|
||||||
|
|
||||||
|
MDIcon:
|
||||||
|
id: leading_icon
|
||||||
|
icon: root.leading_icon
|
||||||
|
size_hint: None, None
|
||||||
|
size: "48dp", "48dp"
|
||||||
|
pos_hint: {"center_y": .5}
|
||||||
|
theme_text_color: "Custom" if root.leading_icon_color else "Primary"
|
||||||
|
text_color:
|
||||||
|
root.leading_icon_color \
|
||||||
|
if root.leading_icon_color else \
|
||||||
|
app.theme_cls.text_color
|
||||||
|
|
||||||
|
MDLabel:
|
||||||
|
text: root.text
|
||||||
|
pos_hint: {"center_y": .5}
|
||||||
|
theme_text_color: "Custom" if root.text_color else "Primary"
|
||||||
|
shorten: True
|
||||||
|
shorten_from: "right"
|
||||||
|
size_hint_x: None
|
||||||
|
width:
|
||||||
|
root.width - \
|
||||||
|
( \
|
||||||
|
leading_icon.width \
|
||||||
|
+ trailing_container.width \
|
||||||
|
+ container.padding[0] \
|
||||||
|
+ container.padding[2] \
|
||||||
|
+ container.spacing \
|
||||||
|
+ dp(18) \
|
||||||
|
)
|
||||||
|
text_color:
|
||||||
|
root.text_color \
|
||||||
|
if root.text_color else \
|
||||||
|
app.theme_cls.text_color
|
||||||
|
|
||||||
|
Widget:
|
||||||
|
|
||||||
|
MDTrailingTextContainer:
|
||||||
|
id: trailing_container
|
||||||
|
text: root.trailing_text
|
||||||
|
adaptive_width: True
|
||||||
|
theme_text_color: "Custom" if root.trailing_text_color else "Primary"
|
||||||
|
text_color:
|
||||||
|
root.trailing_text_color \
|
||||||
|
if root.trailing_text_color else \
|
||||||
|
app.theme_cls.text_color
|
||||||
|
|
||||||
|
MDSeparator:
|
||||||
|
md_bg_color:
|
||||||
|
( \
|
||||||
|
self.theme_cls.divider_color \
|
||||||
|
if not root.divider_color \
|
||||||
|
else root.divider_color \
|
||||||
|
) \
|
||||||
|
if root.divider else \
|
||||||
|
(0, 0, 0, 0)
|
||||||
|
|
||||||
|
|
||||||
|
<MDDropdownTrailingIconItem>
|
||||||
|
orientation: "vertical"
|
||||||
|
|
||||||
|
MDBoxLayout:
|
||||||
|
id: container
|
||||||
|
spacing: "12dp"
|
||||||
|
padding: "12dp", 0, "12dp", 0
|
||||||
|
|
||||||
|
MDLabel:
|
||||||
|
id: label
|
||||||
|
text: root.text
|
||||||
|
shorten: True
|
||||||
|
size_hint_x: None
|
||||||
|
shorten_from: "right"
|
||||||
|
pos_hint: {"center_y": .5}
|
||||||
|
theme_text_color: "Custom" if root.text_color else "Primary"
|
||||||
|
shorten: True
|
||||||
|
shorten_from: "right"
|
||||||
|
width:
|
||||||
|
root.width - \
|
||||||
|
( \
|
||||||
|
+ trailing_icon.width \
|
||||||
|
+ container.padding[0] \
|
||||||
|
+ container.padding[2] \
|
||||||
|
+ container.spacing \
|
||||||
|
+ dp(18) \
|
||||||
|
)
|
||||||
|
text_color:
|
||||||
|
root.text_color \
|
||||||
|
if root.text_color else \
|
||||||
|
app.theme_cls.text_color
|
||||||
|
|
||||||
|
Widget:
|
||||||
|
|
||||||
|
MDIcon:
|
||||||
|
id: trailing_icon
|
||||||
|
size_hint: None, None
|
||||||
|
size: "48dp", "48dp"
|
||||||
|
pos_hint: {"center_y": .5}
|
||||||
|
icon: root.trailing_icon
|
||||||
|
theme_text_color: "Custom" if root.trailing_icon_color else "Primary"
|
||||||
|
text_color:
|
||||||
|
root.trailing_icon_color \
|
||||||
|
if root.trailing_icon_color else \
|
||||||
|
app.theme_cls.text_color
|
||||||
|
|
||||||
|
MDSeparator:
|
||||||
|
md_bg_color:
|
||||||
|
( \
|
||||||
|
self.theme_cls.divider_color \
|
||||||
|
if not root.divider_color \
|
||||||
|
else root.divider_color \
|
||||||
|
) \
|
||||||
|
if root.divider else \
|
||||||
|
(0, 0, 0, 0)
|
||||||
|
|
||||||
|
|
||||||
|
<MDTrailingIconTextContainer>
|
||||||
|
adaptive_width: True
|
||||||
|
|
||||||
|
MDIcon:
|
||||||
|
icon: root.trailing_icon
|
||||||
|
size_hint: None, None
|
||||||
|
size: "48dp", "48dp"
|
||||||
|
pos_hint: {"center_y": .5}
|
||||||
|
theme_text_color: "Custom" if root.trailing_icon_color else "Primary"
|
||||||
|
text_color:
|
||||||
|
root.trailing_icon_color \
|
||||||
|
if root.trailing_icon_color else \
|
||||||
|
app.theme_cls.text_color
|
||||||
|
|
||||||
|
MDLabel:
|
||||||
|
text: root.trailing_text
|
||||||
|
adaptive_size: True
|
||||||
|
pos_hint: {"center_y": .5}
|
||||||
|
theme_text_color: "Custom" if root.trailing_text_color else "Primary"
|
||||||
|
text_color:
|
||||||
|
root.trailing_text_color \
|
||||||
|
if root.trailing_text_color else \
|
||||||
|
app.theme_cls.text_color
|
||||||
|
|
||||||
|
|
||||||
|
<MDDropdownTrailingIconTextItem>
|
||||||
|
orientation: "vertical"
|
||||||
|
|
||||||
|
MDBoxLayout:
|
||||||
|
id: container
|
||||||
|
spacing: "12dp"
|
||||||
|
padding: "12dp", 0, "12dp", 0
|
||||||
|
|
||||||
|
MDLabel:
|
||||||
|
id: label
|
||||||
|
text: root.text
|
||||||
|
shorten: True
|
||||||
|
size_hint_x: None
|
||||||
|
shorten_from: "right"
|
||||||
|
pos_hint: {"center_y": .5}
|
||||||
|
theme_text_color: "Custom" if root.text_color else "Primary"
|
||||||
|
shorten: True
|
||||||
|
shorten_from: "right"
|
||||||
|
width:
|
||||||
|
root.width - \
|
||||||
|
( \
|
||||||
|
+ trailing_container.width \
|
||||||
|
+ container.padding[0] \
|
||||||
|
+ container.padding[2] \
|
||||||
|
+ container.spacing \
|
||||||
|
)
|
||||||
|
text_color:
|
||||||
|
root.text_color \
|
||||||
|
if root.text_color else \
|
||||||
|
app.theme_cls.text_color
|
||||||
|
|
||||||
|
MDTrailingIconTextContainer:
|
||||||
|
id: trailing_container
|
||||||
|
trailing_icon: root.trailing_icon
|
||||||
|
trailing_text: root.trailing_text
|
||||||
|
trailing_text_color: root.trailing_text_color
|
||||||
|
trailing_icon_color: root.trailing_icon_color
|
||||||
|
|
||||||
|
MDSeparator:
|
||||||
|
md_bg_color:
|
||||||
|
( \
|
||||||
|
self.theme_cls.divider_color \
|
||||||
|
if not root.divider_color \
|
||||||
|
else root.divider_color \
|
||||||
|
) \
|
||||||
|
if root.divider else \
|
||||||
|
(0, 0, 0, 0)
|
||||||
|
|
||||||
|
|
||||||
|
<MDDropdownTextItem>
|
||||||
|
orientation: "vertical"
|
||||||
|
|
||||||
|
MDLabel:
|
||||||
|
text: root.text
|
||||||
|
valign: "center"
|
||||||
|
padding_x: "12dp"
|
||||||
|
theme_text_color: "Custom" if root.text_color else "Primary"
|
||||||
|
shorten: True
|
||||||
|
shorten_from: "right"
|
||||||
|
text_color:
|
||||||
|
root.text_color \
|
||||||
|
if root.text_color else \
|
||||||
|
app.theme_cls.text_color
|
||||||
|
|
||||||
|
MDSeparator:
|
||||||
|
md_bg_color:
|
||||||
|
( \
|
||||||
|
self.theme_cls.divider_color \
|
||||||
|
if not root.divider_color \
|
||||||
|
else root.divider_color \
|
||||||
|
) \
|
||||||
|
if root.divider else \
|
||||||
|
(0, 0, 0, 0)
|
||||||
|
|
||||||
|
|
||||||
|
<MDDropdownLeadingTrailingIconTextItem>
|
||||||
|
orientation: "vertical"
|
||||||
|
|
||||||
|
MDBoxLayout:
|
||||||
|
id: container
|
||||||
|
spacing: "12dp"
|
||||||
|
padding: "10dp", 0, "16dp", 0
|
||||||
|
|
||||||
|
MDIcon:
|
||||||
|
id: leading_icon
|
||||||
|
icon: root.leading_icon
|
||||||
|
size_hint: None, None
|
||||||
|
size: "48dp", "48dp"
|
||||||
|
pos_hint: {"center_y": .5}
|
||||||
|
theme_text_color: "Custom" if root.leading_icon_color else "Primary"
|
||||||
|
text_color:
|
||||||
|
root.leading_icon_color \
|
||||||
|
if root.leading_icon_color else \
|
||||||
|
app.theme_cls.text_color
|
||||||
|
|
||||||
|
MDLabel:
|
||||||
|
text: root.text
|
||||||
|
pos_hint: {"center_y": .5}
|
||||||
|
theme_text_color: "Custom" if root.text_color else "Primary"
|
||||||
|
shorten: True
|
||||||
|
shorten_from: "right"
|
||||||
|
size_hint_x: None
|
||||||
|
width:
|
||||||
|
root.width - \
|
||||||
|
( \
|
||||||
|
leading_icon.width \
|
||||||
|
+ trailing_container.width \
|
||||||
|
+ container.padding[0] \
|
||||||
|
+ container.padding[2] \
|
||||||
|
+ container.spacing \
|
||||||
|
+ dp(18) \
|
||||||
|
)
|
||||||
|
text_color:
|
||||||
|
root.text_color \
|
||||||
|
if root.text_color else \
|
||||||
|
app.theme_cls.text_color
|
||||||
|
|
||||||
|
Widget:
|
||||||
|
|
||||||
|
MDTrailingIconTextContainer:
|
||||||
|
id: trailing_container
|
||||||
|
trailing_icon: root.trailing_icon
|
||||||
|
trailing_text: root.trailing_text
|
||||||
|
trailing_icon_color: root.trailing_icon_color
|
||||||
|
trailing_text_color: root.trailing_text_color
|
||||||
|
|
||||||
|
MDSeparator:
|
||||||
|
md_bg_color:
|
||||||
|
( \
|
||||||
|
self.theme_cls.divider_color \
|
||||||
|
if not root.divider_color \
|
||||||
|
else root.divider_color \
|
||||||
|
) \
|
||||||
|
if root.divider else \
|
||||||
|
(0, 0, 0, 0)
|
||||||
|
|
||||||
|
|
||||||
|
<MDDropdownLeadingTrailingIconItem>
|
||||||
|
orientation: "vertical"
|
||||||
|
|
||||||
|
MDBoxLayout:
|
||||||
|
id: container
|
||||||
|
spacing: "12dp"
|
||||||
|
padding: "10dp", 0, "12dp", 0
|
||||||
|
|
||||||
|
MDIcon:
|
||||||
|
id: leading_icon
|
||||||
|
icon: root.leading_icon
|
||||||
|
size_hint: None, None
|
||||||
|
size: "48dp", "48dp"
|
||||||
|
pos_hint: {"center_y": .5}
|
||||||
|
theme_text_color: "Custom" if root.leading_icon_color else "Primary"
|
||||||
|
text_color:
|
||||||
|
root.leading_icon_color \
|
||||||
|
if root.leading_icon_color else \
|
||||||
|
app.theme_cls.text_color
|
||||||
|
|
||||||
|
MDLabel:
|
||||||
|
id: label
|
||||||
|
text: root.text
|
||||||
|
shorten: True
|
||||||
|
size_hint_x: None
|
||||||
|
shorten_from: "right"
|
||||||
|
pos_hint: {"center_y": .5}
|
||||||
|
theme_text_color: "Custom" if root.text_color else "Primary"
|
||||||
|
shorten: True
|
||||||
|
shorten_from: "right"
|
||||||
|
width:
|
||||||
|
root.width - \
|
||||||
|
( \
|
||||||
|
leading_icon.width \
|
||||||
|
+ trailing_icon.width \
|
||||||
|
+ container.padding[0] \
|
||||||
|
+ container.padding[2] \
|
||||||
|
+ container.spacing \
|
||||||
|
+ dp(18) \
|
||||||
|
)
|
||||||
|
text_color:
|
||||||
|
root.text_color \
|
||||||
|
if root.text_color else \
|
||||||
|
app.theme_cls.text_color
|
||||||
|
|
||||||
|
Widget:
|
||||||
|
|
||||||
|
MDIcon:
|
||||||
|
id: trailing_icon
|
||||||
|
size_hint: None, None
|
||||||
|
size: "48dp", "48dp"
|
||||||
|
pos_hint: {"center_y": .5}
|
||||||
|
icon: root.trailing_icon
|
||||||
|
theme_text_color: "Custom" if root.trailing_icon_color else "Primary"
|
||||||
|
text_color:
|
||||||
|
root.trailing_icon_color \
|
||||||
|
if root.trailing_icon_color else \
|
||||||
|
app.theme_cls.text_color
|
||||||
|
|
||||||
|
MDSeparator:
|
||||||
|
md_bg_color:
|
||||||
|
( \
|
||||||
|
self.theme_cls.divider_color \
|
||||||
|
if not root.divider_color \
|
||||||
|
else root.divider_color \
|
||||||
|
) \
|
||||||
|
if root.divider else \
|
||||||
|
(0, 0, 0, 0)
|
||||||
|
|
||||||
|
|
||||||
|
<MDDropdownLeadingIconItem>
|
||||||
|
orientation: "vertical"
|
||||||
|
|
||||||
|
MDBoxLayout:
|
||||||
|
id: container
|
||||||
|
spacing: "12dp"
|
||||||
|
padding: "12dp", 0, "12dp", 0
|
||||||
|
|
||||||
|
MDIcon:
|
||||||
|
id: leading_icon
|
||||||
|
icon: root.leading_icon
|
||||||
|
size_hint: None, None
|
||||||
|
size: "48dp", "48dp"
|
||||||
|
pos_hint: {"center_y": .5}
|
||||||
|
theme_text_color: "Custom" if root.leading_icon_color else "Primary"
|
||||||
|
text_color:
|
||||||
|
root.leading_icon_color \
|
||||||
|
if root.leading_icon_color else \
|
||||||
|
app.theme_cls.text_color
|
||||||
|
|
||||||
|
MDLabel:
|
||||||
|
id: label
|
||||||
|
text: root.text
|
||||||
|
shorten: True
|
||||||
|
size_hint_x: None
|
||||||
|
shorten_from: "right"
|
||||||
|
pos_hint: {"center_y": .5}
|
||||||
|
theme_text_color: "Custom" if root.text_color else "Primary"
|
||||||
|
shorten: True
|
||||||
|
shorten_from: "right"
|
||||||
|
width:
|
||||||
|
root.width - \
|
||||||
|
( \
|
||||||
|
leading_icon.width \
|
||||||
|
+ container.padding[0] \
|
||||||
|
+ container.padding[2] \
|
||||||
|
+ container.spacing \
|
||||||
|
)
|
||||||
|
text_color:
|
||||||
|
root.text_color \
|
||||||
|
if root.text_color else \
|
||||||
|
app.theme_cls.text_color
|
||||||
|
|
||||||
|
MDSeparator:
|
||||||
|
md_bg_color:
|
||||||
|
( \
|
||||||
|
self.theme_cls.divider_color \
|
||||||
|
if not root.divider_color \
|
||||||
|
else root.divider_color \
|
||||||
|
) \
|
||||||
|
if root.divider else \
|
||||||
|
(0, 0, 0, 0)
|
||||||
|
|
||||||
|
|
||||||
<MDDropdownMenu>
|
<MDDropdownMenu>
|
||||||
|
|
||||||
MenuContainer:
|
|
||||||
id: card
|
|
||||||
orientation: "vertical"
|
orientation: "vertical"
|
||||||
elevation: root.elevation
|
elevation: root.elevation
|
||||||
size_hint: None, None
|
shadow_radius: root.shadow_radius
|
||||||
size: md_menu.size[0], md_menu.size[1] + content_header.height
|
shadow_softness: root.shadow_softness
|
||||||
pos: md_menu.pos
|
shadow_offset: root.shadow_offset
|
||||||
opacity: md_menu.opacity
|
shadow_color: root.shadow_color
|
||||||
|
shadow_color: root.shadow_color
|
||||||
radius: root.radius
|
radius: root.radius
|
||||||
md_bg_color:
|
size_hint: None, None
|
||||||
root.background_color \
|
|
||||||
if root.background_color else root.theme_cls.bg_dark
|
|
||||||
|
|
||||||
MDBoxLayout:
|
MDBoxLayout:
|
||||||
id: content_header
|
id: content_header
|
||||||
|
@ -53,7 +486,3 @@
|
||||||
MDMenu:
|
MDMenu:
|
||||||
id: md_menu
|
id: md_menu
|
||||||
drop_cls: root
|
drop_cls: root
|
||||||
width_mult: root.width_mult
|
|
||||||
size_hint: None, None
|
|
||||||
size: 0, 0
|
|
||||||
opacity: 0
|
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -575,8 +575,8 @@ class NavigationDrawerContentError(Exception):
|
||||||
|
|
||||||
class MDNavigationLayout(MDFloatLayout):
|
class MDNavigationLayout(MDFloatLayout):
|
||||||
"""
|
"""
|
||||||
For more information, see in the :class:`~kivymd.uix.floatlayout.MDFloatLayout`
|
For more information, see in the
|
||||||
class documentation.
|
:class:`~kivymd.uix.floatlayout.MDFloatLayout` class documentation.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
_scrim_color = ObjectProperty(None)
|
_scrim_color = ObjectProperty(None)
|
||||||
|
@ -737,7 +737,7 @@ class MDNavigationDrawerDivider(MDBoxLayout):
|
||||||
|
|
||||||
color = ColorProperty(None)
|
color = ColorProperty(None)
|
||||||
"""
|
"""
|
||||||
Divider color in ``rgba`` format.
|
Divider color in (r, g, b, a) or string format.
|
||||||
|
|
||||||
:attr:`color` is a :class:`~kivy.properties.ColorProperty`
|
:attr:`color` is a :class:`~kivy.properties.ColorProperty`
|
||||||
and defaults to `None`.
|
and defaults to `None`.
|
||||||
|
@ -811,7 +811,7 @@ class MDNavigationDrawerHeader(MDBoxLayout):
|
||||||
|
|
||||||
title_color = ColorProperty(None)
|
title_color = ColorProperty(None)
|
||||||
"""
|
"""
|
||||||
Title text color.
|
Title text color in (r, g, b, a) or string format.
|
||||||
|
|
||||||
:attr:`title_color` is a :class:`~kivy.properties.ColorProperty`
|
:attr:`title_color` is a :class:`~kivy.properties.ColorProperty`
|
||||||
and defaults to `None`.
|
and defaults to `None`.
|
||||||
|
@ -851,7 +851,7 @@ class MDNavigationDrawerHeader(MDBoxLayout):
|
||||||
|
|
||||||
text_color = ColorProperty(None)
|
text_color = ColorProperty(None)
|
||||||
"""
|
"""
|
||||||
Title text color.
|
Title text color in (r, g, b, a) or string format.
|
||||||
|
|
||||||
:attr:`text_color` is a :class:`~kivy.properties.ColorProperty`
|
:attr:`text_color` is a :class:`~kivy.properties.ColorProperty`
|
||||||
and defaults to `None`.
|
and defaults to `None`.
|
||||||
|
@ -893,7 +893,9 @@ class MDNavigationDrawerItem(OneLineAvatarIconListItem, FocusBehavior):
|
||||||
Implements an item for the :class:`~MDNavigationDrawer` menu list.
|
Implements an item for the :class:`~MDNavigationDrawer` menu list.
|
||||||
|
|
||||||
For more information, see in the
|
For more information, see in the
|
||||||
:class:`~kivymd.uix.list.OneLineAvatarIconListItem` class documentation.
|
:class:`~kivymd.uix.list.OneLineAvatarIconListItem` and
|
||||||
|
:class:`~kivymd.uix.behaviors.FocusBehavior`
|
||||||
|
class documentation.
|
||||||
|
|
||||||
.. versionadded:: 1.0.0
|
.. versionadded:: 1.0.0
|
||||||
|
|
||||||
|
@ -936,7 +938,7 @@ class MDNavigationDrawerItem(OneLineAvatarIconListItem, FocusBehavior):
|
||||||
|
|
||||||
icon_color = ColorProperty(None)
|
icon_color = ColorProperty(None)
|
||||||
"""
|
"""
|
||||||
Icon color item.
|
Icon color in (r, g, b, a) or string format item.
|
||||||
|
|
||||||
:attr:`icon_color` is a :class:`~kivy.properties.ColorProperty`
|
:attr:`icon_color` is a :class:`~kivy.properties.ColorProperty`
|
||||||
and defaults to `None`.
|
and defaults to `None`.
|
||||||
|
@ -944,7 +946,8 @@ class MDNavigationDrawerItem(OneLineAvatarIconListItem, FocusBehavior):
|
||||||
|
|
||||||
selected_color = ColorProperty([0, 0, 0, 1])
|
selected_color = ColorProperty([0, 0, 0, 1])
|
||||||
"""
|
"""
|
||||||
The color of the icon and text of the selected item.
|
The color in (r, g, b, a) or string format of the icon and text of the
|
||||||
|
selected item.
|
||||||
|
|
||||||
:attr:`selected_color` is a :class:`~kivy.properties.ColorProperty`
|
:attr:`selected_color` is a :class:`~kivy.properties.ColorProperty`
|
||||||
and defaults to `[0, 0, 0, 1]`.
|
and defaults to `[0, 0, 0, 1]`.
|
||||||
|
@ -960,7 +963,7 @@ class MDNavigationDrawerItem(OneLineAvatarIconListItem, FocusBehavior):
|
||||||
|
|
||||||
text_right_color = ColorProperty(None)
|
text_right_color = ColorProperty(None)
|
||||||
"""
|
"""
|
||||||
Right text color item.
|
Right text color item in (r, g, b, a) or string format.
|
||||||
|
|
||||||
:attr:`text_right_color` is a :class:`~kivy.properties.ColorProperty`
|
:attr:`text_right_color` is a :class:`~kivy.properties.ColorProperty`
|
||||||
and defaults to `None`.
|
and defaults to `None`.
|
||||||
|
@ -1095,9 +1098,9 @@ class MDNavigationDrawer(MDCard):
|
||||||
# FIXME: Doesn't work in Kivy v2.1.0.
|
# FIXME: Doesn't work in Kivy v2.1.0.
|
||||||
scrim_color = ColorProperty([0, 0, 0, 0.5])
|
scrim_color = ColorProperty([0, 0, 0, 0.5])
|
||||||
"""
|
"""
|
||||||
Color for scrim. Alpha channel will be multiplied with
|
Color for scrim in (r, g, b, a) or string format. Alpha channel will be
|
||||||
:attr:`_scrim_alpha`. Set fourth channel to 0 if you want to disable
|
multiplied with :attr:`_scrim_alpha`. Set fourth channel to 0 if you want
|
||||||
scrim.
|
to disable scrim.
|
||||||
|
|
||||||
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/navigation-drawer-scrim-color.png
|
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/navigation-drawer-scrim-color.png
|
||||||
:align: center
|
:align: center
|
||||||
|
|
|
@ -8,26 +8,14 @@ Components/NavigationRail
|
||||||
|
|
||||||
`Material Design spec, Navigation rail <https://m3.material.io/components/navigation-rail/specs>`_
|
`Material Design spec, Navigation rail <https://m3.material.io/components/navigation-rail/specs>`_
|
||||||
|
|
||||||
.. rubric::
|
.. rubric:: Navigation rails provide access to primary destinations in apps
|
||||||
|
when using tablet and desktop screens.
|
||||||
Navigation rails provide access to primary destinations in apps when using
|
|
||||||
tablet and desktop screens.
|
|
||||||
|
|
||||||
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/navigation-rail.png
|
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/navigation-rail.png
|
||||||
:align: center
|
:align: center
|
||||||
|
|
||||||
Usage
|
Usage
|
||||||
=====
|
-----
|
||||||
|
|
||||||
.. code-block:: kv
|
|
||||||
|
|
||||||
MDNavigationRail:
|
|
||||||
|
|
||||||
MDNavigationRailItem:
|
|
||||||
|
|
||||||
MDNavigationRailItem:
|
|
||||||
|
|
||||||
MDNavigationRailItem:
|
|
||||||
|
|
||||||
.. tabs::
|
.. tabs::
|
||||||
|
|
||||||
|
@ -113,6 +101,21 @@ Usage
|
||||||
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/navigation-rail-usage.png
|
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/navigation-rail-usage.png
|
||||||
:align: center
|
:align: center
|
||||||
|
|
||||||
|
Anatomy
|
||||||
|
-------
|
||||||
|
|
||||||
|
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/navigation-rail-anatomy.png
|
||||||
|
:align: center
|
||||||
|
|
||||||
|
1. Container
|
||||||
|
2. Label text (optional)
|
||||||
|
3. Icon
|
||||||
|
4. Active indicator
|
||||||
|
5. Badge (optional)
|
||||||
|
6. Large badge (optional)
|
||||||
|
7. Large badge label (optional)
|
||||||
|
8. Menu icon (optional)
|
||||||
|
|
||||||
Example
|
Example
|
||||||
=======
|
=======
|
||||||
|
|
||||||
|
@ -137,9 +140,8 @@ Example
|
||||||
|
|
||||||
|
|
||||||
<ExtendedButton>
|
<ExtendedButton>
|
||||||
elevation: 3.5
|
elevation: 1
|
||||||
shadow_radius: 12
|
shadow_radius: 12
|
||||||
shadow_softness: 4
|
|
||||||
-height: "56dp"
|
-height: "56dp"
|
||||||
|
|
||||||
|
|
||||||
|
@ -207,9 +209,9 @@ Example
|
||||||
|
|
||||||
MDNavigationDrawer:
|
MDNavigationDrawer:
|
||||||
id: nav_drawer
|
id: nav_drawer
|
||||||
radius: (0, 16, 16, 0)
|
radius: 0, 16, 16, 0
|
||||||
md_bg_color: "#fffcf4"
|
md_bg_color: "#fffcf4"
|
||||||
elevation: 4
|
elevation: 2
|
||||||
width: "240dp"
|
width: "240dp"
|
||||||
|
|
||||||
MDNavigationDrawerMenu:
|
MDNavigationDrawerMenu:
|
||||||
|
@ -218,11 +220,15 @@ Example
|
||||||
orientation: "vertical"
|
orientation: "vertical"
|
||||||
adaptive_height: True
|
adaptive_height: True
|
||||||
spacing: "12dp"
|
spacing: "12dp"
|
||||||
padding: "3dp", 0, 0, "12dp"
|
padding: 0, 0, 0, "12dp"
|
||||||
|
|
||||||
MDIconButton:
|
MDIconButton:
|
||||||
icon: "menu"
|
icon: "menu"
|
||||||
|
|
||||||
|
MDBoxLayout:
|
||||||
|
adaptive_height: True
|
||||||
|
padding: "12dp", 0, 0, 0
|
||||||
|
|
||||||
ExtendedButton:
|
ExtendedButton:
|
||||||
text: "Compose"
|
text: "Compose"
|
||||||
icon: "pencil"
|
icon: "pencil"
|
||||||
|
@ -269,7 +275,9 @@ Example
|
||||||
|
|
||||||
def set_radius(self, *args):
|
def set_radius(self, *args):
|
||||||
if self.rounded_button:
|
if self.rounded_button:
|
||||||
self._radius = self.radius = self.height / 4
|
value = self.height / 4
|
||||||
|
self.radius = [value, value, value, value]
|
||||||
|
self._radius = value
|
||||||
|
|
||||||
|
|
||||||
class Example(MDApp):
|
class Example(MDApp):
|
||||||
|
@ -357,9 +365,8 @@ Example
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
self.padding = "16dp"
|
self.padding = "16dp"
|
||||||
self.elevation = 3.5
|
self.elevation = 1
|
||||||
self.shadow_radius = 12
|
self.shadow_radius = 12
|
||||||
self.shadow_softness = 4
|
|
||||||
self.height = dp(56)
|
self.height = dp(56)
|
||||||
Clock.schedule_once(self.set_spacing)
|
Clock.schedule_once(self.set_spacing)
|
||||||
|
|
||||||
|
@ -439,10 +446,14 @@ Example
|
||||||
MDIconButton(
|
MDIconButton(
|
||||||
icon="menu",
|
icon="menu",
|
||||||
),
|
),
|
||||||
|
MDBoxLayout(
|
||||||
ExtendedButton(
|
ExtendedButton(
|
||||||
text="Compose",
|
text="Compose",
|
||||||
icon="pencil",
|
icon="pencil",
|
||||||
),
|
),
|
||||||
|
adaptive_height=True,
|
||||||
|
padding=["12dp", 0, 0, 0],
|
||||||
|
),
|
||||||
orientation="vertical",
|
orientation="vertical",
|
||||||
adaptive_height=True,
|
adaptive_height=True,
|
||||||
spacing="12dp",
|
spacing="12dp",
|
||||||
|
@ -540,6 +551,7 @@ from typing import Union
|
||||||
|
|
||||||
from kivy.animation import Animation
|
from kivy.animation import Animation
|
||||||
from kivy.clock import Clock
|
from kivy.clock import Clock
|
||||||
|
from kivy.core.window import Window
|
||||||
from kivy.lang import Builder
|
from kivy.lang import Builder
|
||||||
from kivy.logger import Logger
|
from kivy.logger import Logger
|
||||||
from kivy.metrics import dp
|
from kivy.metrics import dp
|
||||||
|
@ -556,7 +568,6 @@ from kivy.properties import (
|
||||||
from kivy.uix.behaviors import ButtonBehavior
|
from kivy.uix.behaviors import ButtonBehavior
|
||||||
|
|
||||||
from kivymd import uix_path
|
from kivymd import uix_path
|
||||||
from kivymd.theming import ThemableBehavior
|
|
||||||
from kivymd.uix.behaviors import ScaleBehavior
|
from kivymd.uix.behaviors import ScaleBehavior
|
||||||
from kivymd.uix.boxlayout import MDBoxLayout
|
from kivymd.uix.boxlayout import MDBoxLayout
|
||||||
from kivymd.uix.button import MDFloatingActionButton, MDIconButton
|
from kivymd.uix.button import MDFloatingActionButton, MDIconButton
|
||||||
|
@ -591,7 +602,12 @@ class RippleWidget(MDWidget, ScaleBehavior):
|
||||||
|
|
||||||
|
|
||||||
class MDNavigationRailFabButton(MDFloatingActionButton):
|
class MDNavigationRailFabButton(MDFloatingActionButton):
|
||||||
"""Implements an optional floating action button (FAB)."""
|
"""
|
||||||
|
Implements an optional floating action button (FAB).
|
||||||
|
|
||||||
|
For more information, see in the
|
||||||
|
:class:`~kivymd.uix.button.MDFloatingActionButton` class documentation.
|
||||||
|
"""
|
||||||
|
|
||||||
icon = StringProperty("pencil")
|
icon = StringProperty("pencil")
|
||||||
"""
|
"""
|
||||||
|
@ -613,7 +629,12 @@ class MDNavigationRailFabButton(MDFloatingActionButton):
|
||||||
|
|
||||||
|
|
||||||
class MDNavigationRailMenuButton(MDIconButton):
|
class MDNavigationRailMenuButton(MDIconButton):
|
||||||
"""Implements a menu button."""
|
"""
|
||||||
|
Implements a menu button.
|
||||||
|
|
||||||
|
For more information, see in the
|
||||||
|
:class:`~kivymd.uix.button.MDIconButton` classes documentation.
|
||||||
|
"""
|
||||||
|
|
||||||
icon = StringProperty("menu")
|
icon = StringProperty("menu")
|
||||||
"""
|
"""
|
||||||
|
@ -634,8 +655,15 @@ class MDNavigationRailMenuButton(MDIconButton):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
class MDNavigationRailItem(ThemableBehavior, ButtonBehavior, MDBoxLayout):
|
class MDNavigationRailItem(ButtonBehavior, MDBoxLayout):
|
||||||
"""Implements a menu item with an icon and text."""
|
"""
|
||||||
|
Implements a menu item with an icon and text.
|
||||||
|
|
||||||
|
For more information, see in the
|
||||||
|
:class:`~kivy.uix.behaviors.ButtonBehavior` and
|
||||||
|
:class:`~kivymd.uix.boxlayout.MDBoxLayout`
|
||||||
|
classes documentation.
|
||||||
|
"""
|
||||||
|
|
||||||
navigation_rail = ObjectProperty()
|
navigation_rail = ObjectProperty()
|
||||||
"""
|
"""
|
||||||
|
@ -814,6 +842,11 @@ class MDNavigationRailItem(ThemableBehavior, ButtonBehavior, MDBoxLayout):
|
||||||
|
|
||||||
class MDNavigationRail(MDCard):
|
class MDNavigationRail(MDCard):
|
||||||
"""
|
"""
|
||||||
|
Navigation rail class.
|
||||||
|
|
||||||
|
For more information, see in the
|
||||||
|
:class:`~kivymd.uix.card.MDCard` class documentation.
|
||||||
|
|
||||||
:Events:
|
:Events:
|
||||||
:attr:`on_item_press`
|
:attr:`on_item_press`
|
||||||
Called on the `on_press` event of menu item -
|
Called on the `on_press` event of menu item -
|
||||||
|
@ -941,7 +974,8 @@ class MDNavigationRail(MDCard):
|
||||||
|
|
||||||
text_color_item_normal = ColorProperty(None)
|
text_color_item_normal = ColorProperty(None)
|
||||||
"""
|
"""
|
||||||
The text color of the normal menu item (:class:`~MDNavigationRailItem`).
|
The text color in (r, g, b, a) or string format of the normal menu item
|
||||||
|
(:class:`~MDNavigationRailItem`).
|
||||||
|
|
||||||
.. code-block:: kv
|
.. code-block:: kv
|
||||||
|
|
||||||
|
@ -960,7 +994,8 @@ class MDNavigationRail(MDCard):
|
||||||
|
|
||||||
text_color_item_active = ColorProperty(None)
|
text_color_item_active = ColorProperty(None)
|
||||||
"""
|
"""
|
||||||
The text color of the active menu item (:class:`~MDNavigationRailItem`).
|
The text color in (r, g, b, a) or string format of the active menu item
|
||||||
|
(:class:`~MDNavigationRailItem`).
|
||||||
|
|
||||||
.. code-block:: kv
|
.. code-block:: kv
|
||||||
|
|
||||||
|
@ -979,7 +1014,8 @@ class MDNavigationRail(MDCard):
|
||||||
|
|
||||||
icon_color_item_normal = ColorProperty(None)
|
icon_color_item_normal = ColorProperty(None)
|
||||||
"""
|
"""
|
||||||
The icon color of the normal menu item (:class:`~MDNavigationRailItem`).
|
The icon color in (r, g, b, a) or string format of the normal menu item
|
||||||
|
(:class:`~MDNavigationRailItem`).
|
||||||
|
|
||||||
.. code-block:: kv
|
.. code-block:: kv
|
||||||
|
|
||||||
|
@ -998,7 +1034,8 @@ class MDNavigationRail(MDCard):
|
||||||
|
|
||||||
icon_color_item_active = ColorProperty(None)
|
icon_color_item_active = ColorProperty(None)
|
||||||
"""
|
"""
|
||||||
The icon color of the active menu item (:class:`~MDNavigationRailItem`).
|
The icon color in (r, g, b, a) or string format of the active menu item
|
||||||
|
(:class:`~MDNavigationRailItem`).
|
||||||
|
|
||||||
.. code-block:: kv
|
.. code-block:: kv
|
||||||
|
|
||||||
|
@ -1110,6 +1147,9 @@ class MDNavigationRail(MDCard):
|
||||||
self.register_event_type("on_item_press")
|
self.register_event_type("on_item_press")
|
||||||
self.register_event_type("on_item_release")
|
self.register_event_type("on_item_release")
|
||||||
|
|
||||||
|
def on_size(self, *args):
|
||||||
|
Clock.schedule_once(self.set_pos_menu_fab_buttons)
|
||||||
|
|
||||||
def on_item_press(self, *args) -> None:
|
def on_item_press(self, *args) -> None:
|
||||||
"""
|
"""
|
||||||
Called on the `on_press` event of menu item -
|
Called on the `on_press` event of menu item -
|
||||||
|
@ -1188,7 +1228,7 @@ class MDNavigationRail(MDCard):
|
||||||
items[index].dispatch("on_press")
|
items[index].dispatch("on_press")
|
||||||
items[index].dispatch("on_release")
|
items[index].dispatch("on_release")
|
||||||
|
|
||||||
def set_pos_menu_fab_buttons(self, interval: Union[int, float]) -> None:
|
def set_pos_menu_fab_buttons(self, *args) -> None:
|
||||||
"""
|
"""
|
||||||
Sets the position of the :class:`~MDNavigationRailFabButton` and
|
Sets the position of the :class:`~MDNavigationRailFabButton` and
|
||||||
:class:`~MDNavigationRailMenuButton` buttons on the panel.
|
:class:`~MDNavigationRailMenuButton` buttons on the panel.
|
||||||
|
|
|
@ -100,7 +100,6 @@ from PIL import ImageDraw
|
||||||
from kivymd import uix_path
|
from kivymd import uix_path
|
||||||
from kivymd.color_definitions import colors as _colors
|
from kivymd.color_definitions import colors as _colors
|
||||||
from kivymd.color_definitions import text_colors
|
from kivymd.color_definitions import text_colors
|
||||||
from kivymd.theming import ThemableBehavior
|
|
||||||
from kivymd.uix.behaviors import RectangularRippleBehavior
|
from kivymd.uix.behaviors import RectangularRippleBehavior
|
||||||
from kivymd.uix.behaviors.toggle_behavior import MDToggleButton
|
from kivymd.uix.behaviors.toggle_behavior import MDToggleButton
|
||||||
from kivymd.uix.boxlayout import MDBoxLayout
|
from kivymd.uix.boxlayout import MDBoxLayout
|
||||||
|
@ -123,9 +122,11 @@ class TypeColorButton(MDRaisedButton, MDToggleButton):
|
||||||
'RGBA', 'HEX', 'RGB'.
|
'RGBA', 'HEX', 'RGB'.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
theme_text_color = "Custom"
|
def __init__(self, *args, **kwargs):
|
||||||
text_color = (0, 0, 0, 1)
|
super().__init__(*args, **kwargs)
|
||||||
elevation = 0
|
self.theme_text_color = "Custom"
|
||||||
|
self.text_color = (0, 0, 0, 1)
|
||||||
|
self.elevation = 0
|
||||||
|
|
||||||
|
|
||||||
class SelectAlphaChannelWidget(MDBoxLayout):
|
class SelectAlphaChannelWidget(MDBoxLayout):
|
||||||
|
@ -186,7 +187,7 @@ class SliderTab(MDBoxLayout):
|
||||||
"""Basic event handler for changing the slider value."""
|
"""Basic event handler for changing the slider value."""
|
||||||
|
|
||||||
|
|
||||||
class GradientTab(ThemableBehavior, MDBoxLayout):
|
class GradientTab(MDBoxLayout):
|
||||||
"""
|
"""
|
||||||
The class implements a tab with a gradient, a color selection scale and
|
The class implements a tab with a gradient, a color selection scale and
|
||||||
a scale for setting the transparency value of the selected color.
|
a scale for setting the transparency value of the selected color.
|
||||||
|
@ -398,8 +399,8 @@ class MDColorPicker(BaseDialog):
|
||||||
|
|
||||||
default_color = ColorProperty(None, allownone=True)
|
default_color = ColorProperty(None, allownone=True)
|
||||||
"""
|
"""
|
||||||
Default color value The set color value will be used when you open the
|
Default color value in (r, g, b, a) or string format. The set color value
|
||||||
dialog.
|
will be used when you open the dialog.
|
||||||
|
|
||||||
:attr:`default_color` is an :class:`~kivy.properties.ColorProperty`
|
:attr:`default_color` is an :class:`~kivy.properties.ColorProperty`
|
||||||
and defaults to `None`.
|
and defaults to `None`.
|
||||||
|
@ -416,7 +417,8 @@ class MDColorPicker(BaseDialog):
|
||||||
|
|
||||||
background_down_button_selected_type_color = ColorProperty([1, 1, 1, 0.3])
|
background_down_button_selected_type_color = ColorProperty([1, 1, 1, 0.3])
|
||||||
"""
|
"""
|
||||||
Button background for choosing a color type ('RGBA', 'HEX', 'HSL', 'RGB').
|
Button background for choosing a color type ('RGBA', 'HEX', 'HSL', 'RGB')
|
||||||
|
in (r, g, b, a) or string format.
|
||||||
|
|
||||||
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/color-picker-background-down-button-selected-type-color.png
|
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/color-picker-background-down-button-selected-type-color.png
|
||||||
:align: center
|
:align: center
|
||||||
|
|
|
@ -31,9 +31,7 @@
|
||||||
|
|
||||||
canvas:
|
canvas:
|
||||||
Color:
|
Color:
|
||||||
rgb:
|
rgb: root.primary_color or app.theme_cls.primary_color
|
||||||
app.theme_cls.primary_color \
|
|
||||||
if not root.primary_color else root.primary_color
|
|
||||||
RoundedRectangle:
|
RoundedRectangle:
|
||||||
size:
|
size:
|
||||||
(dp(328), dp(120)) \
|
(dp(328), dp(120)) \
|
||||||
|
@ -48,9 +46,7 @@
|
||||||
if root.theme_cls.device_orientation == "portrait" \
|
if root.theme_cls.device_orientation == "portrait" \
|
||||||
else (root.radius[0], dp(0), dp(0), root.radius[3])
|
else (root.radius[0], dp(0), dp(0), root.radius[3])
|
||||||
Color:
|
Color:
|
||||||
rgba:
|
rgba: root.accent_color or app.theme_cls.bg_normal
|
||||||
app.theme_cls.bg_normal \
|
|
||||||
if not root.accent_color else root.accent_color
|
|
||||||
RoundedRectangle:
|
RoundedRectangle:
|
||||||
size:
|
size:
|
||||||
(dp(328), dp(512) - dp(120) - root._shift_dialog_height) \
|
(dp(328), dp(512) - dp(120) - root._shift_dialog_height) \
|
||||||
|
@ -79,9 +75,7 @@
|
||||||
(dp(24), root.height - self.height - dp(18)) \
|
(dp(24), root.height - self.height - dp(18)) \
|
||||||
if root.theme_cls.device_orientation == "portrait" \
|
if root.theme_cls.device_orientation == "portrait" \
|
||||||
else (dp(24), root.height - self.height - dp(24))
|
else (dp(24), root.height - self.height - dp(24))
|
||||||
text_color:
|
text_color: root.text_toolbar_color or root.specific_text_color
|
||||||
root.specific_text_color \
|
|
||||||
if not root.text_toolbar_color else root.text_toolbar_color
|
|
||||||
|
|
||||||
MDLabel:
|
MDLabel:
|
||||||
id: label_full_date
|
id: label_full_date
|
||||||
|
@ -100,28 +94,13 @@
|
||||||
dp(24) if not root._input_date_dialog_open else dp(168) + dp(24), \
|
dp(24) if not root._input_date_dialog_open else dp(168) + dp(24), \
|
||||||
root.height - self.height - dp(96) \
|
root.height - self.height - dp(96) \
|
||||||
)
|
)
|
||||||
text:
|
text: root._date_label_text
|
||||||
root.set_text_full_date(root.sel_year, root.sel_month, root.sel_day, \
|
|
||||||
root.theme_cls.device_orientation)
|
|
||||||
text_color:
|
text_color:
|
||||||
( \
|
root.text_toolbar_color or root.specific_text_color \
|
||||||
root.specific_text_color \
|
if root.theme_cls.device_orientation == "portrait" else \
|
||||||
if not root.text_toolbar_color else root.text_toolbar_color \
|
root.primary_color or self.theme_cls.primary_color \
|
||||||
) \
|
if root._input_date_dialog_open else \
|
||||||
if root.theme_cls.device_orientation == "portrait" \
|
root.text_toolbar_color or root.specific_text_color
|
||||||
else \
|
|
||||||
( \
|
|
||||||
( \
|
|
||||||
self.theme_cls.primary_color \
|
|
||||||
if not root.primary_color else root.primary_color \
|
|
||||||
) \
|
|
||||||
if root._input_date_dialog_open \
|
|
||||||
else \
|
|
||||||
( \
|
|
||||||
root.specific_text_color \
|
|
||||||
if not root.text_toolbar_color else root.text_toolbar_color \
|
|
||||||
) \
|
|
||||||
)
|
|
||||||
|
|
||||||
RecycleView:
|
RecycleView:
|
||||||
id: _year_layout
|
id: _year_layout
|
||||||
|
@ -164,9 +143,7 @@
|
||||||
(root.height - dp(120) + dp(12)) \
|
(root.height - dp(120) + dp(12)) \
|
||||||
if root.theme_cls.device_orientation == "portrait" \
|
if root.theme_cls.device_orientation == "portrait" \
|
||||||
else dp(12)
|
else dp(12)
|
||||||
text_color:
|
text_color: root.text_toolbar_color or root.specific_text_color
|
||||||
root.specific_text_color \
|
|
||||||
if not root.text_toolbar_color else root.text_toolbar_color
|
|
||||||
|
|
||||||
MDLabel:
|
MDLabel:
|
||||||
id: label_month_selector
|
id: label_month_selector
|
||||||
|
@ -180,9 +157,7 @@
|
||||||
(dp(24), root.height - dp(120) - self.height - dp(20)) \
|
(dp(24), root.height - dp(120) - self.height - dp(20)) \
|
||||||
if root.theme_cls.device_orientation == "portrait" \
|
if root.theme_cls.device_orientation == "portrait" \
|
||||||
else (dp(168) + dp(24), label_title.y)
|
else (dp(168) + dp(24), label_title.y)
|
||||||
text_color:
|
text_color: root.text_color or app.theme_cls.text_color
|
||||||
app.theme_cls.text_color \
|
|
||||||
if not root.text_color else root.text_color
|
|
||||||
|
|
||||||
DatePickerIconTooltipButton:
|
DatePickerIconTooltipButton:
|
||||||
id: triangle
|
id: triangle
|
||||||
|
@ -199,9 +174,7 @@
|
||||||
(label_month_selector.width + dp(14), root.height - dp(123) - self.height) \
|
(label_month_selector.width + dp(14), root.height - dp(123) - self.height) \
|
||||||
if root.theme_cls.device_orientation == "portrait" \
|
if root.theme_cls.device_orientation == "portrait" \
|
||||||
else (dp(180) + label_month_selector.width, label_title.y - dp(14))
|
else (dp(180) + label_month_selector.width, label_title.y - dp(14))
|
||||||
text_color:
|
text_color: root.text_color or app.theme_cls.text_color
|
||||||
app.theme_cls.text_color \
|
|
||||||
if not root.text_color else root.text_color
|
|
||||||
md_bg_color_disabled: 0, 0, 0, 0
|
md_bg_color_disabled: 0, 0, 0, 0
|
||||||
|
|
||||||
DatePickerIconTooltipButton:
|
DatePickerIconTooltipButton:
|
||||||
|
@ -218,9 +191,7 @@
|
||||||
root.height - dp(120) - self.height / 2 - dp(30) \
|
root.height - dp(120) - self.height / 2 - dp(30) \
|
||||||
if root.theme_cls.device_orientation == "portrait" \
|
if root.theme_cls.device_orientation == "portrait" \
|
||||||
else dp(272)
|
else dp(272)
|
||||||
text_color:
|
text_color: root.text_color or app.theme_cls.text_color
|
||||||
app.theme_cls.text_color \
|
|
||||||
if not root.text_color else root.text_color
|
|
||||||
|
|
||||||
DatePickerIconTooltipButton:
|
DatePickerIconTooltipButton:
|
||||||
id: chevron_right
|
id: chevron_right
|
||||||
|
@ -236,9 +207,7 @@
|
||||||
root.height - dp(120) - self.height / 2 - dp(30) \
|
root.height - dp(120) - self.height / 2 - dp(30) \
|
||||||
if root.theme_cls.device_orientation == "portrait" \
|
if root.theme_cls.device_orientation == "portrait" \
|
||||||
else dp(272)
|
else dp(272)
|
||||||
text_color:
|
text_color: root.text_color or app.theme_cls.text_color
|
||||||
app.theme_cls.text_color \
|
|
||||||
if not root.text_color else root.text_color
|
|
||||||
|
|
||||||
# TODO: Replace the GridLayout with a RecycleView
|
# TODO: Replace the GridLayout with a RecycleView
|
||||||
# if it improves performance.
|
# if it improves performance.
|
||||||
|
@ -280,10 +249,7 @@
|
||||||
text: "OK"
|
text: "OK"
|
||||||
theme_text_color: "Custom"
|
theme_text_color: "Custom"
|
||||||
font_name: root.font_name
|
font_name: root.font_name
|
||||||
text_color:
|
text_color: root.text_button_color or root.theme_cls.primary_color
|
||||||
root.theme_cls.primary_color \
|
|
||||||
if not root.text_button_color else \
|
|
||||||
root.text_button_color
|
|
||||||
on_release: root.on_ok_button_pressed()
|
on_release: root.on_ok_button_pressed()
|
||||||
|
|
||||||
MDFlatButton:
|
MDFlatButton:
|
||||||
|
@ -293,10 +259,7 @@
|
||||||
theme_text_color: "Custom"
|
theme_text_color: "Custom"
|
||||||
pos: root.width - self.width - ok_button.width - dp(10), dp(10)
|
pos: root.width - self.width - ok_button.width - dp(10), dp(10)
|
||||||
font_name: root.font_name
|
font_name: root.font_name
|
||||||
text_color:
|
text_color: root.text_button_color or root.theme_cls.primary_color
|
||||||
root.theme_cls.primary_color \
|
|
||||||
if not root.text_button_color else \
|
|
||||||
root.text_button_color
|
|
||||||
|
|
||||||
|
|
||||||
<DatePickerDaySelectableItem>
|
<DatePickerDaySelectableItem>
|
||||||
|
@ -312,14 +275,8 @@
|
||||||
canvas.before:
|
canvas.before:
|
||||||
Color:
|
Color:
|
||||||
rgba:
|
rgba:
|
||||||
(\
|
(self.owner.selector_color or self.theme_cls.primary_color)[:-1] + [.3] \
|
||||||
self.owner.selector_color[:-1] + [.3] \
|
if self.is_in_range \
|
||||||
if self.owner.selector_color \
|
|
||||||
else self.theme_cls.primary_color[:-1] + [.3] \
|
|
||||||
) \
|
|
||||||
if not self.disabled \
|
|
||||||
and self.text \
|
|
||||||
and self.check_date(self.owner.year, self.owner.month, int(self.text)) \
|
|
||||||
else (0, 0, 0, 0)
|
else (0, 0, 0, 0)
|
||||||
RoundedRectangle:
|
RoundedRectangle:
|
||||||
size:
|
size:
|
||||||
|
@ -327,55 +284,24 @@
|
||||||
if root.theme_cls.device_orientation == "portrait" \
|
if root.theme_cls.device_orientation == "portrait" \
|
||||||
else \
|
else \
|
||||||
(dp(32), dp(28)) \
|
(dp(32), dp(28)) \
|
||||||
if self.index in [6, 13, 20, 27, 34] or self.owner._date_range \
|
if self.is_range_end or self.is_week_end or self.is_month_end \
|
||||||
and self.text and self.owner._date_range[-1] == date( \
|
|
||||||
self.current_year, \
|
|
||||||
self.current_month, \
|
|
||||||
int(self.text) \
|
|
||||||
) \
|
|
||||||
or self.text and int(self.text) == \
|
|
||||||
calendar.monthrange(self.current_year, self.current_month)[1] \
|
|
||||||
else (dp(46), dp(28))
|
else (dp(46), dp(28))
|
||||||
pos:
|
pos:
|
||||||
(self.x - dp(1.5), self.y + dp(5)) \
|
(self.x - dp(1.5), self.y + dp(5)) \
|
||||||
if root.theme_cls.device_orientation == "portrait" else \
|
if root.theme_cls.device_orientation == "portrait" else \
|
||||||
(self.x, self.y + 1)
|
(self.x, self.y + 1)
|
||||||
radius:
|
radius:
|
||||||
[0, 0, 0, 0] if not self.owner._date_range else \
|
[
|
||||||
( \
|
self.width / 2 if self.is_range_start else 0,
|
||||||
[self.width / 2, 0, 0, self.width / 2] \
|
self.width / 2 if self.is_range_end else 0,
|
||||||
if self.text and self.owner._date_range[0] == date( \
|
self.width / 2 if self.is_range_end else 0,
|
||||||
self.current_year, \
|
self.width / 2 if self.is_range_start else 0,
|
||||||
self.current_month, \
|
]
|
||||||
int(self.text) \
|
|
||||||
) \
|
|
||||||
or (self.index in [0, 7, 14, 21, 28] and root.is_selected) \
|
|
||||||
else \
|
|
||||||
( \
|
|
||||||
[0, 0, 0, 0] if self.text \
|
|
||||||
and self.owner._date_range[-1] != date( \
|
|
||||||
self.current_year, \
|
|
||||||
self.current_month, \
|
|
||||||
int(self.text) \
|
|
||||||
) \
|
|
||||||
and self.index not in [6, 13, 20, 27, 30] \
|
|
||||||
else [0, self.width / 2, self.width, 0] \
|
|
||||||
if root.is_selected or self.text \
|
|
||||||
and self.owner._date_range[-1] == date( \
|
|
||||||
self.current_year, \
|
|
||||||
self.current_month, \
|
|
||||||
int(self.text) \
|
|
||||||
) \
|
|
||||||
else [0, 0, 0, 0]) \
|
|
||||||
)
|
|
||||||
|
|
||||||
# Selection circle.
|
# Selection circle.
|
||||||
Color:
|
Color:
|
||||||
rgba:
|
rgba:
|
||||||
( \
|
root.owner.selector_color or self.theme_cls.primary_color \
|
||||||
self.theme_cls.primary_color if not root.owner.selector_color \
|
|
||||||
else root.owner.selector_color \
|
|
||||||
) \
|
|
||||||
if root.is_selected and not self.disabled \
|
if root.is_selected and not self.disabled \
|
||||||
else (0, 0, 0, 0)
|
else (0, 0, 0, 0)
|
||||||
Ellipse:
|
Ellipse:
|
||||||
|
@ -393,25 +319,11 @@
|
||||||
font_name: root.owner.font_name
|
font_name: root.owner.font_name
|
||||||
theme_text_color: "Custom"
|
theme_text_color: "Custom"
|
||||||
text_color:
|
text_color:
|
||||||
( \
|
root.owner.accent_color or root.theme_cls.bg_normal \
|
||||||
root.theme_cls.primary_color \
|
if root.is_selected else \
|
||||||
if not root.owner.text_current_color \
|
root.owner.text_current_color or root.theme_cls.primary_color \
|
||||||
else root.owner.text_current_color \
|
if root.is_today else \
|
||||||
) \
|
root.owner.text_color or root.theme_cls.text_color
|
||||||
if root.is_today and not root.is_selected \
|
|
||||||
else ( \
|
|
||||||
( \
|
|
||||||
root.theme_cls.text_color \
|
|
||||||
if not root.is_selected or root.owner.mode == "range" \
|
|
||||||
else (1, 1, 1, 1) \
|
|
||||||
) \
|
|
||||||
if not root.owner.text_color \
|
|
||||||
else \
|
|
||||||
( \
|
|
||||||
root.owner.text_color \
|
|
||||||
if not root.is_selected else (1, 1, 1, 1)) \
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
<DatePickerWeekdayLabel>
|
<DatePickerWeekdayLabel>
|
||||||
font_style: "Caption"
|
font_style: "Caption"
|
||||||
|
@ -425,9 +337,7 @@
|
||||||
size:
|
size:
|
||||||
(dp(40), dp(40)) if root.theme_cls.device_orientation == "portrait" \
|
(dp(40), dp(40)) if root.theme_cls.device_orientation == "portrait" \
|
||||||
else (dp(32), dp(32))
|
else (dp(32), dp(32))
|
||||||
text_color:
|
text_color: root.owner.text_weekday_color or app.theme_cls.disabled_hint_text_color
|
||||||
app.theme_cls.disabled_hint_text_color \
|
|
||||||
if not root.owner.text_weekday_color else root.owner.text_weekday_color
|
|
||||||
|
|
||||||
|
|
||||||
<DatePickerYearSelectableItem>
|
<DatePickerYearSelectableItem>
|
||||||
|
@ -436,13 +346,21 @@
|
||||||
valign: "middle"
|
valign: "middle"
|
||||||
halign: "center"
|
halign: "center"
|
||||||
text: root.text
|
text: root.text
|
||||||
|
theme_text_color: "Custom"
|
||||||
|
text_color:
|
||||||
|
(0, 0, 0, 0) \
|
||||||
|
if self.owner is None else \
|
||||||
|
self.owner.accent_color or self.owner.theme_cls.bg_normal \
|
||||||
|
if self.selected else \
|
||||||
|
self.owner.text_color or self.owner.theme_cls.text_color
|
||||||
on_text: root.font_name = root.owner.font_name
|
on_text: root.font_name = root.owner.font_name
|
||||||
|
|
||||||
canvas.before:
|
canvas.before:
|
||||||
Color:
|
Color:
|
||||||
rgba:
|
rgba:
|
||||||
root.selected_color if root.selected_color \
|
self.owner.selector_color or self.theme_cls.primary_color \
|
||||||
else self.theme_cls.primary_color
|
if self.selected else \
|
||||||
|
(0, 0, 0, 0)
|
||||||
RoundedRectangle:
|
RoundedRectangle:
|
||||||
pos: self.x + dp(12), self.y
|
pos: self.x + dp(12), self.y
|
||||||
size: self.width - dp(24), self.height
|
size: self.width - dp(24), self.height
|
||||||
|
@ -453,6 +371,7 @@
|
||||||
adaptive_height: True
|
adaptive_height: True
|
||||||
size_hint_x: None
|
size_hint_x: None
|
||||||
spacing: dp(8)
|
spacing: dp(8)
|
||||||
|
opacity: 0
|
||||||
width:
|
width:
|
||||||
self.owner.width - dp(48) \
|
self.owner.width - dp(48) \
|
||||||
if root.owner.theme_cls.device_orientation == "portrait" \
|
if root.owner.theme_cls.device_orientation == "portrait" \
|
||||||
|
@ -468,10 +387,6 @@
|
||||||
|
|
||||||
<DatePickerInputField>
|
<DatePickerInputField>
|
||||||
mode: "fill"
|
mode: "fill"
|
||||||
opacity: 0
|
|
||||||
hint_text: "dd/mm/yyyy"
|
hint_text: "dd/mm/yyyy"
|
||||||
input_filter: root.input_filter
|
input_filter: root.input_filter
|
||||||
fill_color:
|
fill_color: root.owner.input_field_background_color or (0, 0, 0, .15)
|
||||||
(0, 0, 0, .15) \
|
|
||||||
if not self.owner.input_field_background_color \
|
|
||||||
else root.owner.input_field_background_color
|
|
||||||
|
|
|
@ -203,7 +203,6 @@ from datetime import date
|
||||||
from itertools import zip_longest
|
from itertools import zip_longest
|
||||||
from typing import Union
|
from typing import Union
|
||||||
|
|
||||||
from kivy import Logger
|
|
||||||
from kivy.animation import Animation
|
from kivy.animation import Animation
|
||||||
from kivy.lang import Builder
|
from kivy.lang import Builder
|
||||||
from kivy.metrics import dp
|
from kivy.metrics import dp
|
||||||
|
@ -253,6 +252,12 @@ class BaseDialogPicker(
|
||||||
Base class for :class:`~kivymd.uix.picker.MDDatePicker` and
|
Base class for :class:`~kivymd.uix.picker.MDDatePicker` and
|
||||||
:class:`~kivymd.uix.picker.MDTimePicker` classes.
|
:class:`~kivymd.uix.picker.MDTimePicker` classes.
|
||||||
|
|
||||||
|
For more information, see in the
|
||||||
|
:class:`~kivymd.uix.dialog.BaseDialog` and
|
||||||
|
:class:`~kivymd.uix.behaviors.CommonElevationBehavior` and
|
||||||
|
:class:`~kivymd.uix.behaviors.SpecificBackgroundColorBehavior`
|
||||||
|
classes documentation.
|
||||||
|
|
||||||
:Events:
|
:Events:
|
||||||
`on_save`
|
`on_save`
|
||||||
Events called when the "OK" dialog box button is clicked.
|
Events called when the "OK" dialog box button is clicked.
|
||||||
|
@ -644,11 +649,30 @@ class DatePickerTypeDateError(Exception):
|
||||||
|
|
||||||
|
|
||||||
class DatePickerInputField(MDTextField):
|
class DatePickerInputField(MDTextField):
|
||||||
"""Implements date input in dd/mm/yyyy format."""
|
"""
|
||||||
|
Implements date input in dd/mm/yyyy format.
|
||||||
|
|
||||||
|
For more information, see in the
|
||||||
|
:class:`~kivymd.uix.textfield.MDTextField` class documentation.
|
||||||
|
"""
|
||||||
|
|
||||||
helper_text_mode = StringProperty("on_error")
|
helper_text_mode = StringProperty("on_error")
|
||||||
owner = ObjectProperty() # MDDatePicker object
|
owner = ObjectProperty() # MDDatePicker object
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
self.bind(text=self._on_text_check_errors)
|
||||||
|
|
||||||
|
def _on_text_check_errors(self, widget, text):
|
||||||
|
if text == "":
|
||||||
|
self.error = False
|
||||||
|
return
|
||||||
|
try:
|
||||||
|
datetime.datetime.strptime(text, "%d/%m/%Y")
|
||||||
|
self.error = False
|
||||||
|
except ValueError:
|
||||||
|
self.error = True
|
||||||
|
|
||||||
def set_error(self):
|
def set_error(self):
|
||||||
"""Sets a text field to an error state."""
|
"""Sets a text field to an error state."""
|
||||||
|
|
||||||
|
@ -699,53 +723,13 @@ class DatePickerDaySelectableItem(
|
||||||
owner = ObjectProperty()
|
owner = ObjectProperty()
|
||||||
is_today = BooleanProperty(False)
|
is_today = BooleanProperty(False)
|
||||||
is_selected = BooleanProperty(False)
|
is_selected = BooleanProperty(False)
|
||||||
current_month = NumericProperty()
|
is_in_range = BooleanProperty(False)
|
||||||
current_year = NumericProperty()
|
is_range_start = BooleanProperty(False)
|
||||||
index = NumericProperty(0)
|
is_range_end = BooleanProperty(False)
|
||||||
|
is_month_end = BooleanProperty(False)
|
||||||
def check_date(self, year: int, month: int, day: int):
|
is_week_end = BooleanProperty(False)
|
||||||
try:
|
|
||||||
return date(year, month, day) in self.owner._date_range
|
|
||||||
except ValueError as error:
|
|
||||||
if str(error) == "day is out of range for month":
|
|
||||||
return False
|
|
||||||
|
|
||||||
def on_release(self):
|
def on_release(self):
|
||||||
if (
|
|
||||||
self.owner.mode == "range"
|
|
||||||
and self.owner._end_range_date
|
|
||||||
and self.owner._start_range_date
|
|
||||||
):
|
|
||||||
return
|
|
||||||
if (
|
|
||||||
not self.owner._input_date_dialog_open
|
|
||||||
and not self.owner._select_year_dialog_open
|
|
||||||
):
|
|
||||||
if self.owner.mode == "range" and not self.owner._start_range_date:
|
|
||||||
self.owner._start_range_date = date(
|
|
||||||
self.current_year, self.current_month, int(self.text)
|
|
||||||
)
|
|
||||||
self.owner.min_date = self.owner._start_range_date
|
|
||||||
elif (
|
|
||||||
self.owner.mode == "range"
|
|
||||||
and not self.owner._end_range_date
|
|
||||||
and self.owner._start_range_date
|
|
||||||
):
|
|
||||||
self.owner._end_range_date = date(
|
|
||||||
self.current_year, self.current_month, int(self.text)
|
|
||||||
)
|
|
||||||
if self.owner._end_range_date <= self.owner.min_date:
|
|
||||||
toast(self.owner.date_range_text_error)
|
|
||||||
Logger.error(
|
|
||||||
"`Data Picker: max_date` value cannot be less than "
|
|
||||||
"or equal to 'min_date' value."
|
|
||||||
)
|
|
||||||
self.owner._start_range_date = 0
|
|
||||||
self.owner._end_range_date = 0
|
|
||||||
return
|
|
||||||
self.owner.max_date = self.owner._end_range_date
|
|
||||||
self.owner.update_calendar_for_date_range()
|
|
||||||
|
|
||||||
self.owner.set_selected_widget(self)
|
self.owner.set_selected_widget(self)
|
||||||
|
|
||||||
def on_touch_down(self, touch):
|
def on_touch_down(self, touch):
|
||||||
|
@ -760,7 +744,7 @@ class DatePickerYearSelectableItem(RecycleDataViewBehavior, MDLabel):
|
||||||
"""Implements an item for a pick list of the year."""
|
"""Implements an item for a pick list of the year."""
|
||||||
|
|
||||||
index = None
|
index = None
|
||||||
selected_color = ColorProperty([0, 0, 0, 0])
|
selected = BooleanProperty(False)
|
||||||
owner = ObjectProperty()
|
owner = ObjectProperty()
|
||||||
|
|
||||||
def refresh_view_attrs(self, rv, index, data):
|
def refresh_view_attrs(self, rv, index, data):
|
||||||
|
@ -772,32 +756,10 @@ class DatePickerYearSelectableItem(RecycleDataViewBehavior, MDLabel):
|
||||||
return True
|
return True
|
||||||
if self.collide_point(*touch.pos):
|
if self.collide_point(*touch.pos):
|
||||||
self.owner.year = int(self.text)
|
self.owner.year = int(self.text)
|
||||||
# self.owner.sel_year = self.owner.year
|
|
||||||
self.owner.ids.label_full_date.text = self.owner.set_text_full_date(
|
|
||||||
self.owner.sel_year,
|
|
||||||
self.owner.sel_month,
|
|
||||||
self.owner.sel_day,
|
|
||||||
self.owner.theme_cls.device_orientation,
|
|
||||||
)
|
|
||||||
return self.parent.select_with_touch(self.index, touch)
|
return self.parent.select_with_touch(self.index, touch)
|
||||||
|
|
||||||
def apply_selection(self, table_data, index, is_selected):
|
def apply_selection(self, table_data, index, is_selected):
|
||||||
if is_selected:
|
self.selected = is_selected
|
||||||
self.selected_color = (
|
|
||||||
self.owner.selector_color
|
|
||||||
if self.owner.selector_color
|
|
||||||
else self.theme_cls.primary_color
|
|
||||||
)
|
|
||||||
self.text_color = (1, 1, 1, 1)
|
|
||||||
else:
|
|
||||||
if int(self.text) == self.owner.sel_year:
|
|
||||||
self.text_color = (
|
|
||||||
self.theme_cls.primary_color
|
|
||||||
if not self.owner.text_current_color
|
|
||||||
else self.owner.text_current_color
|
|
||||||
)
|
|
||||||
self.selected_color = [0, 0, 0, 0]
|
|
||||||
self.text_color = (0, 0, 0, 1)
|
|
||||||
|
|
||||||
|
|
||||||
# TODO: Add the feature to embed the `MDDatePicker` class in other layouts
|
# TODO: Add the feature to embed the `MDDatePicker` class in other layouts
|
||||||
|
@ -889,7 +851,7 @@ class MDDatePicker(BaseDialogPicker):
|
||||||
and defaults to `picker`.
|
and defaults to `picker`.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
min_date = ObjectProperty()
|
min_date = ObjectProperty(allownone=True)
|
||||||
"""
|
"""
|
||||||
The minimum value of the date range for the `'mode`' parameter.
|
The minimum value of the date range for the `'mode`' parameter.
|
||||||
Must be an object <class 'datetime.date'>.
|
Must be an object <class 'datetime.date'>.
|
||||||
|
@ -900,7 +862,7 @@ class MDDatePicker(BaseDialogPicker):
|
||||||
and defaults to `None`.
|
and defaults to `None`.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
max_date = ObjectProperty()
|
max_date = ObjectProperty(allownone=True)
|
||||||
"""
|
"""
|
||||||
The minimum value of the date range for the `'mode`' parameter.
|
The minimum value of the date range for the `'mode`' parameter.
|
||||||
Must be an object <class 'datetime.date'>.
|
Must be an object <class 'datetime.date'>.
|
||||||
|
@ -955,17 +917,13 @@ class MDDatePicker(BaseDialogPicker):
|
||||||
|
|
||||||
_calendar_layout = ObjectProperty()
|
_calendar_layout = ObjectProperty()
|
||||||
_calendar_list = None
|
_calendar_list = None
|
||||||
_enter_data_field = None
|
_fields_container = None
|
||||||
_enter_data_field_two = None
|
|
||||||
_enter_data_field_container = None
|
|
||||||
_date_range = []
|
|
||||||
_scale_calendar_layout = NumericProperty(1)
|
_scale_calendar_layout = NumericProperty(1)
|
||||||
_scale_year_layout = NumericProperty(0)
|
_scale_year_layout = NumericProperty(0)
|
||||||
_shift_dialog_height = NumericProperty(0)
|
_shift_dialog_height = NumericProperty(0)
|
||||||
_input_date_dialog_open = BooleanProperty(False)
|
_input_date_dialog_open = BooleanProperty(False)
|
||||||
_select_year_dialog_open = False
|
_select_year_dialog_open = False
|
||||||
_start_range_date = 0
|
_date_label_text = StringProperty()
|
||||||
_end_range_date = 0
|
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
|
@ -996,7 +954,6 @@ class MDDatePicker(BaseDialogPicker):
|
||||||
"'max_date' must be of class <class 'datetime.date'>"
|
"'max_date' must be of class <class 'datetime.date'>"
|
||||||
)
|
)
|
||||||
self.compare_date_range()
|
self.compare_date_range()
|
||||||
self._date_range = self.get_date_range()
|
|
||||||
|
|
||||||
self.generate_list_widgets_days()
|
self.generate_list_widgets_days()
|
||||||
self.update_calendar(self.sel_year, self.sel_month)
|
self.update_calendar(self.sel_year, self.sel_month)
|
||||||
|
@ -1006,6 +963,8 @@ class MDDatePicker(BaseDialogPicker):
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Called when the device's screen orientation changes."""
|
"""Called when the device's screen orientation changes."""
|
||||||
|
|
||||||
|
# Separators of the label text depend on the orientation.
|
||||||
|
self._update_date_label_text()
|
||||||
if self._input_date_dialog_open:
|
if self._input_date_dialog_open:
|
||||||
if orientation_value == "portrait":
|
if orientation_value == "portrait":
|
||||||
self._shift_dialog_height = dp(250)
|
self._shift_dialog_height = dp(250)
|
||||||
|
@ -1017,21 +976,12 @@ class MDDatePicker(BaseDialogPicker):
|
||||||
Called when the 'OK' button is pressed to confirm the date entered.
|
Called when the 'OK' button is pressed to confirm the date entered.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if self._enter_data_field and not self.is_date_valaid(
|
if self._input_date_dialog_open and not self._try_apply_input():
|
||||||
self._enter_data_field.text
|
|
||||||
):
|
|
||||||
self._enter_data_field.set_error()
|
|
||||||
return
|
return
|
||||||
if self._enter_data_field_two and not self.is_date_valaid(
|
|
||||||
self._enter_data_field_two.text
|
|
||||||
):
|
|
||||||
self._enter_data_field_two.set_error()
|
|
||||||
return
|
|
||||||
|
|
||||||
self.dispatch(
|
self.dispatch(
|
||||||
"on_save",
|
"on_save",
|
||||||
date(self.sel_year, self.sel_month, self.sel_day),
|
date(self.sel_year, self.sel_month, self.sel_day),
|
||||||
self._date_range,
|
self.get_date_range(),
|
||||||
)
|
)
|
||||||
|
|
||||||
def is_date_valaid(self, date: str) -> bool:
|
def is_date_valaid(self, date: str) -> bool:
|
||||||
|
@ -1085,56 +1035,25 @@ class MDDatePicker(BaseDialogPicker):
|
||||||
self.ids._year_layout.children[0].clear_selection()
|
self.ids._year_layout.children[0].clear_selection()
|
||||||
|
|
||||||
def transformation_to_dialog_input_date(self) -> None:
|
def transformation_to_dialog_input_date(self) -> None:
|
||||||
def set_date_to_input_field():
|
|
||||||
if not self._enter_data_field_two:
|
|
||||||
# Date of current day.
|
|
||||||
self._enter_data_field.text = (
|
|
||||||
f"{'' if self.sel_day >= 10 else '0'}"
|
|
||||||
f"{self.sel_day}/"
|
|
||||||
f"{'' if self.sel_month >= 10 else '0'}"
|
|
||||||
f"{self.sel_month}/{self.sel_year}"
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
# Range start date.
|
|
||||||
self._enter_data_field.text = (
|
|
||||||
f"{'' if self.min_date.day >= 10 else '0'}"
|
|
||||||
f"{self.min_date.day}/"
|
|
||||||
f"{'' if self.min_date.month >= 10 else '0'}"
|
|
||||||
f"{self.min_date.month}/{self.min_date.year}"
|
|
||||||
)
|
|
||||||
|
|
||||||
def set_date_to_input_field_two() -> None:
|
|
||||||
# Range end date.
|
|
||||||
self._enter_data_field_two.text = (
|
|
||||||
f"{'' if self.max_date.day >= 10 else '0'}"
|
|
||||||
f"{self.max_date.day}/"
|
|
||||||
f"{'' if self.max_date.month >= 10 else '0'}"
|
|
||||||
f"{self.max_date.month}/{self.max_date.year}"
|
|
||||||
)
|
|
||||||
|
|
||||||
self.ids.triangle.disabled = True
|
self.ids.triangle.disabled = True
|
||||||
if self._select_year_dialog_open:
|
if self._select_year_dialog_open:
|
||||||
self.transformation_from_dialog_select_year()
|
self.transformation_from_dialog_select_year()
|
||||||
self._input_date_dialog_open = True
|
self._input_date_dialog_open = True
|
||||||
|
|
||||||
self._enter_data_field_container = DatePickerInputFieldContainer(
|
|
||||||
owner=self
|
|
||||||
)
|
|
||||||
self._enter_data_field = self.get_field()
|
|
||||||
if self.min_date and self.max_date:
|
|
||||||
self._enter_data_field_two = self.get_field()
|
|
||||||
set_date_to_input_field_two()
|
|
||||||
set_date_to_input_field()
|
|
||||||
self._enter_data_field_container.add_widget(self._enter_data_field)
|
|
||||||
if self._enter_data_field_two:
|
|
||||||
self._enter_data_field_container.add_widget(
|
|
||||||
self._enter_data_field_two
|
|
||||||
)
|
|
||||||
|
|
||||||
self.ids.container.add_widget(self._enter_data_field_container)
|
|
||||||
self.ids.edit_icon.icon = "calendar"
|
self.ids.edit_icon.icon = "calendar"
|
||||||
self.ids.label_title.text = self.title_input
|
self.ids.label_title.text = self.title_input
|
||||||
|
|
||||||
|
self._fields_container = DatePickerInputFieldContainer(owner=self)
|
||||||
|
if self.mode == "picker":
|
||||||
|
selected_date = date(self.sel_year, self.sel_month, self.sel_day)
|
||||||
|
selected_dates = [selected_date]
|
||||||
|
else:
|
||||||
|
selected_dates = [self.min_date, self.max_date]
|
||||||
|
for selected_date in selected_dates:
|
||||||
|
field = self.get_field(selected_date)
|
||||||
|
field.bind(text=self._on_date_field_text_changes)
|
||||||
|
self._fields_container.add_widget(field)
|
||||||
|
self.ids.container.add_widget(self._fields_container)
|
||||||
|
|
||||||
Animation(
|
Animation(
|
||||||
_shift_dialog_height=dp(250)
|
_shift_dialog_height=dp(250)
|
||||||
if self.theme_cls.device_orientation == "portrait"
|
if self.theme_cls.device_orientation == "portrait"
|
||||||
|
@ -1152,28 +1071,22 @@ class MDDatePicker(BaseDialogPicker):
|
||||||
).start(self.ids.chevron_right)
|
).start(self.ids.chevron_right)
|
||||||
Animation(opacity=0, d=0.15).start(self.ids.label_month_selector)
|
Animation(opacity=0, d=0.15).start(self.ids.label_month_selector)
|
||||||
Animation(opacity=0, d=0.15).start(self.ids.triangle)
|
Animation(opacity=0, d=0.15).start(self.ids.triangle)
|
||||||
Animation(opacity=1, d=0.15).start(self._enter_data_field)
|
Animation(opacity=1, d=0.15).start(self._fields_container)
|
||||||
if self._enter_data_field_two:
|
# The label text separator in landscape orientation depends on the
|
||||||
Animation(opacity=1, d=0.15).start(self._enter_data_field_two)
|
# open dialog.
|
||||||
self.ids.label_full_date.text = self.set_text_full_date(
|
self._update_date_label_text()
|
||||||
self.sel_year,
|
|
||||||
self.sel_month,
|
|
||||||
self.sel_day,
|
|
||||||
self.theme_cls.device_orientation,
|
|
||||||
)
|
|
||||||
|
|
||||||
def transformation_from_dialog_input_date(
|
def transformation_from_dialog_input_date(
|
||||||
self, interval: Union[int, float]
|
self, interval: Union[int, float]
|
||||||
) -> None:
|
) -> None:
|
||||||
|
if not self._try_apply_input():
|
||||||
|
return
|
||||||
self._input_date_dialog_open = False
|
self._input_date_dialog_open = False
|
||||||
self.ids.label_full_date.text = self.set_text_full_date(
|
|
||||||
self.sel_year,
|
|
||||||
self.sel_month,
|
|
||||||
self.sel_day,
|
|
||||||
self.theme_cls.device_orientation,
|
|
||||||
)
|
|
||||||
self.ids.triangle.disabled = False
|
self.ids.triangle.disabled = False
|
||||||
self.ids.container.remove_widget(self._enter_data_field_container)
|
self.ids.edit_icon.icon = "pencil"
|
||||||
|
self.ids.label_title.text = self.title
|
||||||
|
self.ids.container.remove_widget(self._fields_container)
|
||||||
|
self._fields_container = None
|
||||||
Animation(
|
Animation(
|
||||||
_shift_dialog_height=dp(0), _scale_calendar_layout=1, d=0.15
|
_shift_dialog_height=dp(0), _scale_calendar_layout=1, d=0.15
|
||||||
).start(self)
|
).start(self)
|
||||||
|
@ -1187,41 +1100,67 @@ class MDDatePicker(BaseDialogPicker):
|
||||||
).start(self.ids.chevron_right)
|
).start(self.ids.chevron_right)
|
||||||
Animation(opacity=1, d=0.15).start(self.ids.label_month_selector)
|
Animation(opacity=1, d=0.15).start(self.ids.label_month_selector)
|
||||||
Animation(opacity=1, d=0.15).start(self.ids.triangle)
|
Animation(opacity=1, d=0.15).start(self.ids.triangle)
|
||||||
Animation(opacity=0, d=0.15).start(self._enter_data_field)
|
# The label text separator in landscape orientation depends on the
|
||||||
self.ids.edit_icon.icon = "pencil"
|
# open dialog.
|
||||||
self.ids.label_title.text = self.title
|
self._update_date_label_text()
|
||||||
|
|
||||||
if not self.min_date and not self.max_date:
|
def _get_dates_from_fields(self):
|
||||||
list_date = self._enter_data_field.get_list_date()
|
"""
|
||||||
if len(list_date) == 3 and len(list_date[2]) == 4:
|
Return a list of dates entered by the user in the input fields.
|
||||||
self.sel_day = int(list_date[0])
|
|
||||||
self.sel_month = int(list_date[1])
|
If there is an error in the field or the field is empty, None will be
|
||||||
self.sel_year = int(list_date[2])
|
in its place in the list. The length of the list will be 0 if the input
|
||||||
|
dialog is closed, otherwise 1 in picker mode or 2 in range mode.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if not self._fields_container:
|
||||||
|
return []
|
||||||
|
|
||||||
|
dates = []
|
||||||
|
# Widgets are arranged in the reverse order of their addition.
|
||||||
|
for field in reversed(self._fields_container.children):
|
||||||
|
try:
|
||||||
|
date = datetime.datetime.strptime(field.text, "%d/%m/%Y").date()
|
||||||
|
except ValueError:
|
||||||
|
date = None
|
||||||
|
dates.append(date)
|
||||||
|
|
||||||
|
return dates
|
||||||
|
|
||||||
|
def _try_apply_input(self) -> bool:
|
||||||
|
"""
|
||||||
|
Apply the dates entered by the user, update the calendar and return
|
||||||
|
True. If there are errors in the fields, do nothing and return False.
|
||||||
|
"""
|
||||||
|
|
||||||
|
dates = self._get_dates_from_fields()
|
||||||
|
if not dates:
|
||||||
|
return True
|
||||||
|
|
||||||
|
# Widgets are arranged in the reverse order of their addition.
|
||||||
|
fields = reversed(self._fields_container.children)
|
||||||
|
if any(d is None and f.text for f, d in zip(fields, dates)):
|
||||||
|
return False
|
||||||
|
|
||||||
|
if self.mode == "picker":
|
||||||
|
selected_date = date(self.sel_year, self.sel_month, self.sel_day)
|
||||||
|
selected_date = dates[0] or selected_date
|
||||||
|
self.sel_year = selected_date.year
|
||||||
|
self.sel_month = selected_date.month
|
||||||
|
self.sel_day = selected_date.day
|
||||||
self.update_calendar(self.sel_year, self.sel_month)
|
self.update_calendar(self.sel_year, self.sel_month)
|
||||||
elif self.min_date and self.max_date:
|
elif self.mode == "range":
|
||||||
list_min_date = self._enter_data_field.get_list_date()
|
date1, date2 = dates[0] or self.min_date, dates[1] or self.max_date
|
||||||
list_max_date = self._enter_data_field_two.get_list_date()
|
ends = list(filter(bool, [date1, date2]))
|
||||||
|
if ends:
|
||||||
|
self.min_date = min(ends)
|
||||||
|
self.max_date = max(ends)
|
||||||
|
self.update_calendar(self.year, self.month)
|
||||||
|
|
||||||
if len(list_min_date) == 3 and len(list_min_date[2]) == 4:
|
return True
|
||||||
self.min_date = date(
|
|
||||||
int(list_min_date[2]),
|
|
||||||
int(list_min_date[1]),
|
|
||||||
int(list_min_date[0]),
|
|
||||||
)
|
|
||||||
if len(list_max_date) == 3 and len(list_max_date[2]) == 4:
|
|
||||||
self.max_date = date(
|
|
||||||
int(list_max_date[2]),
|
|
||||||
int(list_max_date[1]),
|
|
||||||
int(list_max_date[0]),
|
|
||||||
)
|
|
||||||
|
|
||||||
self.update_calendar_for_date_range()
|
def _on_date_field_text_changes(self, *args):
|
||||||
self.ids.label_full_date.text = self.set_text_full_date(
|
self._update_date_label_text()
|
||||||
int(list_max_date[2]),
|
|
||||||
int(list_max_date[1]),
|
|
||||||
int(list_max_date[0]),
|
|
||||||
self.theme_cls.device_orientation,
|
|
||||||
)
|
|
||||||
|
|
||||||
def compare_date_range(self) -> None:
|
def compare_date_range(self) -> None:
|
||||||
# TODO: Add behavior if the minimum date range exceeds the maximum
|
# TODO: Add behavior if the minimum date range exceeds the maximum
|
||||||
|
@ -1233,8 +1172,7 @@ class MDDatePicker(BaseDialogPicker):
|
||||||
)
|
)
|
||||||
|
|
||||||
def update_calendar_for_date_range(self) -> None:
|
def update_calendar_for_date_range(self) -> None:
|
||||||
# self.compare_date_range()
|
# This method is no longer used, use update_calendar instead.
|
||||||
self._date_range = self.get_date_range()
|
|
||||||
self.update_calendar(self.year, self.month)
|
self.update_calendar(self.year, self.month)
|
||||||
|
|
||||||
def update_text_full_date(self, list_date) -> None:
|
def update_text_full_date(self, list_date) -> None:
|
||||||
|
@ -1243,27 +1181,13 @@ class MDDatePicker(BaseDialogPicker):
|
||||||
in an open date input dialog.
|
in an open date input dialog.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if len(list_date) == 1 and len(list_date[0]) == 2:
|
# This method no longer used, use update_calendar instead.
|
||||||
self.ids.label_full_date.text = self.set_text_full_date(
|
year = int(list_date[2]) if len(list_date) > 2 else self.sel_year
|
||||||
self.sel_year,
|
month = int(list_date[1]) if len(list_date) > 1 else self.sel_month
|
||||||
self.sel_month,
|
day = int(list_date[0]) if len(list_date) > 0 else self.sel_day
|
||||||
list_date[0],
|
day = min(day, calendar.monthrange(year, month)[1])
|
||||||
self.theme_cls.device_orientation,
|
self.sel_year, self.sel_month, self.sel_day = year, month, day
|
||||||
)
|
self.update_calendar(year, month)
|
||||||
if len(list_date) == 2 and len(list_date[1]) == 2:
|
|
||||||
self.ids.label_full_date.text = self.set_text_full_date(
|
|
||||||
self.sel_year,
|
|
||||||
int(list_date[1]),
|
|
||||||
int(list_date[0]),
|
|
||||||
self.theme_cls.device_orientation,
|
|
||||||
)
|
|
||||||
if len(list_date) == 3 and len(list_date[2]) == 4:
|
|
||||||
self.ids.label_full_date.text = self.set_text_full_date(
|
|
||||||
int(list_date[2]),
|
|
||||||
int(list_date[1]),
|
|
||||||
int(list_date[0]),
|
|
||||||
self.theme_cls.device_orientation,
|
|
||||||
)
|
|
||||||
|
|
||||||
def update_calendar(self, year, month) -> None:
|
def update_calendar(self, year, month) -> None:
|
||||||
self.year, self.month = year, month
|
self.year, self.month = year, month
|
||||||
|
@ -1271,7 +1195,10 @@ class MDDatePicker(BaseDialogPicker):
|
||||||
selected_date = date(self.sel_year, self.sel_month, self.sel_day)
|
selected_date = date(self.sel_year, self.sel_month, self.sel_day)
|
||||||
selected_dates = {selected_date}
|
selected_dates = {selected_date}
|
||||||
else:
|
else:
|
||||||
selected_dates = {self._start_range_date, self._end_range_date}
|
selected_dates = {self.min_date, self.max_date}
|
||||||
|
# The label text depends on the selected date or date range.
|
||||||
|
self._update_date_label_text()
|
||||||
|
month_end = date(year, month, calendar.monthrange(year, month)[1])
|
||||||
dates = self.calendar.itermonthdates(year, month)
|
dates = self.calendar.itermonthdates(year, month)
|
||||||
for widget, widget_date in zip_longest(self._calendar_list, dates):
|
for widget, widget_date in zip_longest(self._calendar_list, dates):
|
||||||
# Only widgets whose dates are in the displayed month are visible.
|
# Only widgets whose dates are in the displayed month are visible.
|
||||||
|
@ -1281,21 +1208,31 @@ class MDDatePicker(BaseDialogPicker):
|
||||||
and widget_date.year == year
|
and widget_date.year == year
|
||||||
)
|
)
|
||||||
widget.text = str(widget_date.day) if visible else ""
|
widget.text = str(widget_date.day) if visible else ""
|
||||||
widget.current_year = year
|
|
||||||
widget.current_month = month
|
|
||||||
widget.is_today = visible and widget_date == self.today
|
widget.is_today = visible and widget_date == self.today
|
||||||
widget.is_selected = visible and widget_date in selected_dates
|
widget.is_selected = visible and widget_date in selected_dates
|
||||||
# I don't understand why, but this line is important. Without this
|
# I don't understand why, but this line is important. Without this
|
||||||
# line, some widgets that we are trying to disable remain enabled.
|
# line, some widgets that we are trying to disable remain enabled.
|
||||||
widget.disabled = False
|
widget.disabled = False
|
||||||
widget.disabled = (
|
widget.disabled = not visible
|
||||||
not visible
|
widget.is_in_range = (
|
||||||
or self.mode == "range"
|
visible
|
||||||
and self._date_range
|
and self.min_date is not None
|
||||||
and widget_date not in self._date_range
|
and self.max_date is not None
|
||||||
|
and self.min_date <= widget_date <= self.max_date
|
||||||
)
|
)
|
||||||
|
widget.is_range_start = (
|
||||||
|
visible
|
||||||
|
and self.min_date is not None
|
||||||
|
and widget_date == self.min_date
|
||||||
|
)
|
||||||
|
widget.is_range_end = (
|
||||||
|
visible
|
||||||
|
and self.max_date is not None
|
||||||
|
and widget_date == self.max_date
|
||||||
|
)
|
||||||
|
widget.is_month_end = widget_date == month_end
|
||||||
|
|
||||||
def get_field(self) -> MDTextField:
|
def get_field(self, date=None) -> MDTextField:
|
||||||
"""Creates and returns a text field object used to enter dates."""
|
"""Creates and returns a text field object used to enter dates."""
|
||||||
|
|
||||||
if issubclass(self.input_field_cls, MDTextField):
|
if issubclass(self.input_field_cls, MDTextField):
|
||||||
|
@ -1322,6 +1259,7 @@ class MDDatePicker(BaseDialogPicker):
|
||||||
|
|
||||||
field = self.input_field_cls(
|
field = self.input_field_cls(
|
||||||
owner=self,
|
owner=self,
|
||||||
|
text=date.strftime("%d/%m/%Y") if date else "",
|
||||||
helper_text=self.helper_text,
|
helper_text=self.helper_text,
|
||||||
fill_color_normal=fill_color_normal,
|
fill_color_normal=fill_color_normal,
|
||||||
fill_color_focus=fill_color_focus,
|
fill_color_focus=fill_color_focus,
|
||||||
|
@ -1340,6 +1278,8 @@ class MDDatePicker(BaseDialogPicker):
|
||||||
)
|
)
|
||||||
|
|
||||||
def get_date_range(self) -> list:
|
def get_date_range(self) -> list:
|
||||||
|
if not self.min_date or not self.max_date:
|
||||||
|
return []
|
||||||
date_range = [
|
date_range = [
|
||||||
self.min_date + datetime.timedelta(days=x)
|
self.min_date + datetime.timedelta(days=x)
|
||||||
for x in range((self.max_date - self.min_date).days + 1)
|
for x in range((self.max_date - self.min_date).days + 1)
|
||||||
|
@ -1353,118 +1293,73 @@ class MDDatePicker(BaseDialogPicker):
|
||||||
a date range.
|
a date range.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if 12 < int(month) < 0:
|
# In portrait orientation, the label is stretched in width, so we
|
||||||
raise ValueError(
|
# should not insert line breaks. When the input dialog is open, the
|
||||||
"set_text_full_date:\n\t" f"Month [{month}] out of range."
|
# label moves to the right and also stretches in width.
|
||||||
)
|
horizontal = orientation == "portrait" or self._input_date_dialog_open
|
||||||
if int(day) > calendar.monthrange(int(year), (month))[1]:
|
|
||||||
return ""
|
|
||||||
date = datetime.date(int(year), int(month), int(day))
|
|
||||||
separator = (
|
|
||||||
"\n"
|
|
||||||
if (orientation == "landscape" and not self._input_date_dialog_open)
|
|
||||||
else " "
|
|
||||||
)
|
|
||||||
|
|
||||||
|
def date_repr(date):
|
||||||
|
return date.strftime("%b").capitalize() + " " + str(date.day)
|
||||||
|
|
||||||
|
input_dates = self._get_dates_from_fields()
|
||||||
if self.mode == "picker":
|
if self.mode == "picker":
|
||||||
if not self.min_date and not self.max_date:
|
selected_date = date(self.sel_year, self.sel_month, self.sel_day)
|
||||||
return (
|
if input_dates:
|
||||||
date.strftime("%a,").capitalize()
|
selected_date = input_dates[0] or selected_date
|
||||||
+ separator
|
weekday_repr = selected_date.strftime("%a").capitalize()
|
||||||
+ date.strftime("%b ").capitalize()
|
separator = ", " if horizontal else ",\n"
|
||||||
+ str(day).lstrip("0")
|
return weekday_repr + separator + date_repr(selected_date)
|
||||||
)
|
|
||||||
else:
|
|
||||||
return (
|
|
||||||
self.min_date.strftime("%b ").capitalize()
|
|
||||||
+ str(self.min_date.day).lstrip("0")
|
|
||||||
+ (
|
|
||||||
" - "
|
|
||||||
if orientation == "portrait"
|
|
||||||
else (
|
|
||||||
",\n" if not self._input_date_dialog_open else ", "
|
|
||||||
)
|
|
||||||
)
|
|
||||||
+ self.max_date.strftime("%b ").capitalize()
|
|
||||||
+ str(self.max_date.day).lstrip("0")
|
|
||||||
)
|
|
||||||
elif self.mode == "range":
|
elif self.mode == "range":
|
||||||
if self._start_range_date and self._end_range_date:
|
start, end = self.min_date, self.max_date
|
||||||
if (
|
if input_dates:
|
||||||
orientation == "landscape"
|
start, end = input_dates[0] or start, input_dates[1] or end
|
||||||
and "-" in self.ids.label_full_date.text
|
ends = [end for end in (start, end) if end]
|
||||||
):
|
if len(ends) == 0:
|
||||||
return (
|
start_repr, end_repr = "Start", "End"
|
||||||
self.ids.label_full_date.text.split("-")[0].strip()
|
|
||||||
+ (",\n" if not self._input_date_dialog_open else " - ")
|
|
||||||
+ date.strftime("%b ").capitalize()
|
|
||||||
+ str(day).lstrip("0")
|
|
||||||
)
|
|
||||||
else:
|
else:
|
||||||
if (
|
start, end = min(ends), max(ends)
|
||||||
orientation == "landscape"
|
start_repr, end_repr = date_repr(start), date_repr(end)
|
||||||
and "," in self.ids.label_full_date.text
|
separator = " — " if horizontal else ",\n"
|
||||||
):
|
return start_repr + separator + end_repr
|
||||||
return (
|
|
||||||
self.ids.label_full_date.text.split(",")[0].strip()
|
def _update_date_label_text(self):
|
||||||
+ (
|
self._date_label_text = self.set_text_full_date(
|
||||||
",\n"
|
self.sel_year,
|
||||||
if not self._input_date_dialog_open
|
self.sel_month,
|
||||||
else "-"
|
self.sel_day,
|
||||||
)
|
self.theme_cls.device_orientation,
|
||||||
+ date.strftime("%b ").capitalize()
|
|
||||||
+ str(day).lstrip("0")
|
|
||||||
)
|
|
||||||
if (
|
|
||||||
orientation == "portrait"
|
|
||||||
and "," in self.ids.label_full_date.text
|
|
||||||
):
|
|
||||||
return (
|
|
||||||
self.ids.label_full_date.text.split(",")[0].strip()
|
|
||||||
+ "-"
|
|
||||||
+ date.strftime("%b ").capitalize()
|
|
||||||
+ str(day).lstrip("0")
|
|
||||||
)
|
|
||||||
if (
|
|
||||||
orientation == "portrait"
|
|
||||||
and "-" in self.ids.label_full_date.text
|
|
||||||
):
|
|
||||||
return (
|
|
||||||
self.ids.label_full_date.text.split("-")[0].strip()
|
|
||||||
+ " - "
|
|
||||||
+ date.strftime("%b ").capitalize()
|
|
||||||
+ str(day).lstrip("0")
|
|
||||||
)
|
|
||||||
elif self._start_range_date and not self._end_range_date:
|
|
||||||
return (
|
|
||||||
(
|
|
||||||
date.strftime("%b ").capitalize()
|
|
||||||
+ str(day).lstrip("0")
|
|
||||||
+ " - End"
|
|
||||||
)
|
|
||||||
if orientation != "landscape"
|
|
||||||
else (
|
|
||||||
date.strftime("%b ").capitalize()
|
|
||||||
+ str(day).lstrip("0")
|
|
||||||
+ "{}End".format(
|
|
||||||
",\n" if not self._input_date_dialog_open else " - "
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
elif not self._start_range_date and not self._end_range_date:
|
|
||||||
return (
|
|
||||||
"Start - End"
|
|
||||||
if orientation != "landscape"
|
|
||||||
else "Start{}End".format(
|
|
||||||
",\n" if not self._input_date_dialog_open else " - "
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
def set_selected_widget(self, widget) -> None:
|
def set_selected_widget(self, widget) -> None:
|
||||||
self.sel_year = self.year
|
if self._select_year_dialog_open or self._input_date_dialog_open:
|
||||||
self.sel_month = self.month
|
return
|
||||||
self.sel_day = int(widget.text)
|
try:
|
||||||
|
widget_date = date(self.year, self.month, int(widget.text))
|
||||||
|
except ValueError:
|
||||||
|
return
|
||||||
|
if self.mode == "picker":
|
||||||
|
self.sel_year = widget_date.year
|
||||||
|
self.sel_month = widget_date.month
|
||||||
|
self.sel_day = widget_date.day
|
||||||
self.update_calendar(self.sel_year, self.sel_month)
|
self.update_calendar(self.sel_year, self.sel_month)
|
||||||
|
elif self.mode == "range":
|
||||||
|
ends = [end for end in (self.min_date, self.max_date) if end]
|
||||||
|
if widget_date in ends:
|
||||||
|
ends = [end for end in ends if end != widget_date]
|
||||||
|
elif len(ends) < 2:
|
||||||
|
ends.append(widget_date)
|
||||||
|
else:
|
||||||
|
start, end = min(ends), max(ends)
|
||||||
|
if abs(widget_date - start).days < abs(widget_date - end).days:
|
||||||
|
start = widget_date
|
||||||
|
else:
|
||||||
|
end = widget_date
|
||||||
|
ends = [start, end]
|
||||||
|
if len(ends) == 0:
|
||||||
|
self.min_date, self.max_date = None, None
|
||||||
|
else:
|
||||||
|
self.min_date, self.max_date = min(ends), max(ends)
|
||||||
|
self.update_calendar(self.year, self.month)
|
||||||
|
|
||||||
def set_month_day(self, day) -> None:
|
def set_month_day(self, day) -> None:
|
||||||
# This method is no longer used. The code bellow repeats the behavior
|
# This method is no longer used. The code bellow repeats the behavior
|
||||||
|
@ -1525,12 +1420,10 @@ class MDDatePicker(BaseDialogPicker):
|
||||||
)
|
)
|
||||||
weekday_label.font_name = self.font_name
|
weekday_label.font_name = self.font_name
|
||||||
self._calendar_layout.add_widget(weekday_label)
|
self._calendar_layout.add_widget(weekday_label)
|
||||||
for i, j in enumerate(range(6 * 7)): # 6 weeks, 7 days a week
|
for i in range(6 * 7): # 6 weeks, 7 days a week
|
||||||
day_selectable_item = DatePickerDaySelectableItem(
|
day_selectable_item = DatePickerDaySelectableItem(
|
||||||
index=i,
|
is_week_end=i % 7 == 6,
|
||||||
owner=self,
|
owner=self,
|
||||||
current_month=int(self.month),
|
|
||||||
current_year=int(self.year),
|
|
||||||
)
|
)
|
||||||
calendar_list.append(day_selectable_item)
|
calendar_list.append(day_selectable_item)
|
||||||
self._calendar_layout.add_widget(day_selectable_item)
|
self._calendar_layout.add_widget(day_selectable_item)
|
||||||
|
@ -1541,9 +1434,11 @@ class MDDatePicker(BaseDialogPicker):
|
||||||
Called when "chevron-left" and "chevron-right" buttons are pressed.
|
Called when "chevron-left" and "chevron-right" buttons are pressed.
|
||||||
Switches the calendar to the previous/next month.
|
Switches the calendar to the previous/next month.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
month_delta = 1 if operation == "next" else -1
|
month_delta = 1 if operation == "next" else -1
|
||||||
year = self.year + (self.month - 1 + month_delta) // 12
|
year = self.year + (self.month - 1 + month_delta) // 12
|
||||||
month = (self.month - 1 + month_delta) % 12 + 1
|
month = (self.month - 1 + month_delta) % 12 + 1
|
||||||
|
|
||||||
if year <= 0:
|
if year <= 0:
|
||||||
year, month = 1, 1
|
year, month = 1, 1
|
||||||
self.update_calendar(year, month)
|
self.update_calendar(year, month)
|
||||||
|
|
|
@ -166,7 +166,6 @@ from kivy.uix.behaviors import ButtonBehavior
|
||||||
from kivy.vector import Vector
|
from kivy.vector import Vector
|
||||||
|
|
||||||
from kivymd import uix_path
|
from kivymd import uix_path
|
||||||
from kivymd.theming import ThemableBehavior
|
|
||||||
from kivymd.uix.boxlayout import MDBoxLayout
|
from kivymd.uix.boxlayout import MDBoxLayout
|
||||||
from kivymd.uix.circularlayout import MDCircularLayout
|
from kivymd.uix.circularlayout import MDCircularLayout
|
||||||
from kivymd.uix.label import MDLabel
|
from kivymd.uix.label import MDLabel
|
||||||
|
@ -185,7 +184,7 @@ class AmPmSelectorLabel(ButtonBehavior, MDLabel):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class AmPmSelector(ThemableBehavior, MDBoxLayout):
|
class AmPmSelector(MDBoxLayout):
|
||||||
border_radius = NumericProperty()
|
border_radius = NumericProperty()
|
||||||
border_color = ColorProperty()
|
border_color = ColorProperty()
|
||||||
bg_color = ColorProperty()
|
bg_color = ColorProperty()
|
||||||
|
|
|
@ -6,7 +6,8 @@
|
||||||
self.theme_cls.divider_color \
|
self.theme_cls.divider_color \
|
||||||
if not self.back_color else \
|
if not self.back_color else \
|
||||||
self.back_color
|
self.back_color
|
||||||
Rectangle:
|
RoundedRectangle:
|
||||||
|
radius: root.radius
|
||||||
size:
|
size:
|
||||||
(self.width, self.height) \
|
(self.width, self.height) \
|
||||||
if self.orientation == "horizontal" else \
|
if self.orientation == "horizontal" else \
|
||||||
|
@ -18,7 +19,8 @@
|
||||||
Color:
|
Color:
|
||||||
rgba:
|
rgba:
|
||||||
self.theme_cls.primary_color if not self.color else self.color
|
self.theme_cls.primary_color if not self.color else self.color
|
||||||
Rectangle:
|
RoundedRectangle:
|
||||||
|
radius: root.radius
|
||||||
size:
|
size:
|
||||||
(self.width * self.value_normalized, self.height if self.height else dp(4)) \
|
(self.width * self.value_normalized, self.height if self.height else dp(4)) \
|
||||||
if self.orientation == "horizontal" else \
|
if self.orientation == "horizontal" else \
|
||||||
|
|
|
@ -145,6 +145,7 @@ from kivy.properties import (
|
||||||
NumericProperty,
|
NumericProperty,
|
||||||
OptionProperty,
|
OptionProperty,
|
||||||
StringProperty,
|
StringProperty,
|
||||||
|
VariableListProperty,
|
||||||
)
|
)
|
||||||
from kivy.uix.progressbar import ProgressBar
|
from kivy.uix.progressbar import ProgressBar
|
||||||
|
|
||||||
|
@ -158,6 +159,25 @@ with open(
|
||||||
|
|
||||||
|
|
||||||
class MDProgressBar(ThemableBehavior, ProgressBar):
|
class MDProgressBar(ThemableBehavior, ProgressBar):
|
||||||
|
"""
|
||||||
|
Progressbar class.
|
||||||
|
|
||||||
|
For more information, see in the
|
||||||
|
:class:`~kivymd.theming.ThemableBehavior` and
|
||||||
|
:class:`~kivy.uix.progressbar.ProgressBar`
|
||||||
|
classes documentation.
|
||||||
|
"""
|
||||||
|
|
||||||
|
radius = VariableListProperty([0], length=4)
|
||||||
|
"""
|
||||||
|
Progress line radius.
|
||||||
|
|
||||||
|
.. versionadded:: 1.2.0
|
||||||
|
|
||||||
|
:attr:`radius` is an :class:`~kivy.properties.VariableListProperty`
|
||||||
|
and defaults to `[0, 0, 0, 0]`.
|
||||||
|
"""
|
||||||
|
|
||||||
reversed = BooleanProperty(False)
|
reversed = BooleanProperty(False)
|
||||||
"""
|
"""
|
||||||
Reverse the direction the progressbar moves.
|
Reverse the direction the progressbar moves.
|
||||||
|
@ -179,7 +199,7 @@ class MDProgressBar(ThemableBehavior, ProgressBar):
|
||||||
|
|
||||||
color = ColorProperty(None)
|
color = ColorProperty(None)
|
||||||
"""
|
"""
|
||||||
Progress bar color in ``rgba`` format.
|
Progress bar color in (r, g, b, a) or string format.
|
||||||
|
|
||||||
:attr:`color` is an :class:`~kivy.properties.ColorProperty`
|
:attr:`color` is an :class:`~kivy.properties.ColorProperty`
|
||||||
and defaults to `None`.
|
and defaults to `None`.
|
||||||
|
@ -187,7 +207,7 @@ class MDProgressBar(ThemableBehavior, ProgressBar):
|
||||||
|
|
||||||
back_color = ColorProperty(None)
|
back_color = ColorProperty(None)
|
||||||
"""
|
"""
|
||||||
Progress bar back color in ``rgba`` format.
|
Progress bar back color in (r, g, b, a) or string format.
|
||||||
|
|
||||||
.. versionadded:: 1.0.0
|
.. versionadded:: 1.0.0
|
||||||
|
|
||||||
|
|
|
@ -85,12 +85,13 @@ Equivalent
|
||||||
|
|
||||||
from kivy.uix.recyclegridlayout import RecycleGridLayout
|
from kivy.uix.recyclegridlayout import RecycleGridLayout
|
||||||
|
|
||||||
|
from kivymd.theming import ThemableBehavior
|
||||||
from kivymd.uix import MDAdaptiveWidget
|
from kivymd.uix import MDAdaptiveWidget
|
||||||
from kivymd.uix.behaviors import DeclarativeBehavior
|
from kivymd.uix.behaviors import DeclarativeBehavior
|
||||||
|
|
||||||
|
|
||||||
class MDRecycleGridLayout(
|
class MDRecycleGridLayout(
|
||||||
DeclarativeBehavior, RecycleGridLayout, MDAdaptiveWidget
|
DeclarativeBehavior, ThemableBehavior, RecycleGridLayout, MDAdaptiveWidget
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
Recycle grid layout layout class. For more information, see in the
|
Recycle grid layout layout class. For more information, see in the
|
||||||
|
|
|
@ -34,10 +34,14 @@ __all__ = ("MDRecycleView",)
|
||||||
|
|
||||||
from kivy.uix.recycleview import RecycleView
|
from kivy.uix.recycleview import RecycleView
|
||||||
|
|
||||||
|
from kivymd.theming import ThemableBehavior
|
||||||
|
from kivymd.uix import MDAdaptiveWidget
|
||||||
from kivymd.uix.behaviors import DeclarativeBehavior
|
from kivymd.uix.behaviors import DeclarativeBehavior
|
||||||
|
|
||||||
|
|
||||||
class MDRecycleView(DeclarativeBehavior, RecycleView):
|
class MDRecycleView(
|
||||||
|
DeclarativeBehavior, ThemableBehavior, RecycleView, MDAdaptiveWidget
|
||||||
|
):
|
||||||
"""
|
"""
|
||||||
Recycle view class. For more information, see in the
|
Recycle view class. For more information, see in the
|
||||||
:class:`~kivy.uix.recycleview.RecycleView` class documentation.
|
:class:`~kivy.uix.recycleview.RecycleView` class documentation.
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
canvas:
|
canvas:
|
||||||
Clear
|
Clear
|
||||||
Color:
|
Color:
|
||||||
rgba: root.theme_cls.primary_dark
|
rgba: root.circle_color
|
||||||
Ellipse:
|
Ellipse:
|
||||||
pos: self.pos
|
pos: self.pos
|
||||||
size: self.size
|
size: self.size
|
||||||
|
@ -24,4 +24,4 @@
|
||||||
id: spinner
|
id: spinner
|
||||||
size_hint: None, None
|
size_hint: None, None
|
||||||
size: dp(30), dp(30)
|
size: dp(30), dp(30)
|
||||||
color: 1, 1, 1, 1
|
color: root.spinner_color
|
||||||
|
|
|
@ -43,6 +43,8 @@ Example
|
||||||
id: refresh_layout
|
id: refresh_layout
|
||||||
refresh_callback: app.refresh_callback
|
refresh_callback: app.refresh_callback
|
||||||
root_layout: root
|
root_layout: root
|
||||||
|
spinner_color: "brown"
|
||||||
|
circle_color: "white"
|
||||||
|
|
||||||
MDGridLayout:
|
MDGridLayout:
|
||||||
id: box
|
id: box
|
||||||
|
@ -66,6 +68,8 @@ Example
|
||||||
y = 15
|
y = 15
|
||||||
|
|
||||||
def build(self):
|
def build(self):
|
||||||
|
self.theme_cls.theme_style = "Dark"
|
||||||
|
self.theme_cls.primary_palette = "Orange"
|
||||||
self.screen = Factory.Example()
|
self.screen = Factory.Example()
|
||||||
self.set_list()
|
self.set_list()
|
||||||
|
|
||||||
|
@ -81,8 +85,10 @@ Example
|
||||||
asynckivy.start(set_list())
|
asynckivy.start(set_list())
|
||||||
|
|
||||||
def refresh_callback(self, *args):
|
def refresh_callback(self, *args):
|
||||||
'''A method that updates the state of your application
|
'''
|
||||||
while the spinner remains on the screen.'''
|
A method that updates the state of your application
|
||||||
|
while the spinner remains on the screen.
|
||||||
|
'''
|
||||||
|
|
||||||
def refresh_callback(interval):
|
def refresh_callback(interval):
|
||||||
self.screen.ids.box.clear_widgets()
|
self.screen.ids.box.clear_widgets()
|
||||||
|
@ -110,7 +116,12 @@ from kivy.core.window import Window
|
||||||
from kivy.effects.dampedscroll import DampedScrollEffect
|
from kivy.effects.dampedscroll import DampedScrollEffect
|
||||||
from kivy.lang import Builder
|
from kivy.lang import Builder
|
||||||
from kivy.metrics import dp
|
from kivy.metrics import dp
|
||||||
from kivy.properties import ColorProperty, NumericProperty, ObjectProperty
|
from kivy.properties import (
|
||||||
|
ColorProperty,
|
||||||
|
NumericProperty,
|
||||||
|
ObjectProperty,
|
||||||
|
StringProperty,
|
||||||
|
)
|
||||||
from kivy.uix.floatlayout import FloatLayout
|
from kivy.uix.floatlayout import FloatLayout
|
||||||
|
|
||||||
from kivymd import uix_path
|
from kivymd import uix_path
|
||||||
|
@ -150,7 +161,16 @@ class _RefreshScrollEffect(DampedScrollEffect):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
class MDScrollViewRefreshLayout(MDScrollView):
|
class MDScrollViewRefreshLayout(ThemableBehavior, MDScrollView):
|
||||||
|
"""
|
||||||
|
Refresh layout class.
|
||||||
|
|
||||||
|
For more information, see in the
|
||||||
|
:class:`~kivymd.theming.ThemableBehavior` and
|
||||||
|
:class:`~kivymd.uix.scrollview.MDScrollView`
|
||||||
|
class documentation.
|
||||||
|
"""
|
||||||
|
|
||||||
root_layout = ObjectProperty()
|
root_layout = ObjectProperty()
|
||||||
"""
|
"""
|
||||||
The spinner will be attached to this layout.
|
The spinner will be attached to this layout.
|
||||||
|
@ -168,8 +188,70 @@ class MDScrollViewRefreshLayout(MDScrollView):
|
||||||
and defaults to `None`.
|
and defaults to `None`.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
spinner_color = ColorProperty([1, 1, 1, 1])
|
||||||
|
"""
|
||||||
|
Color of the spinner in (r, g, b, a) or string format.
|
||||||
|
|
||||||
|
.. versionadded:: 1.2.0
|
||||||
|
|
||||||
|
:attr:`spinner_color` is a :class:`~kivy.properties.ColorProperty`
|
||||||
|
and defaults to `[1, 1, 1, 1]`.
|
||||||
|
"""
|
||||||
|
|
||||||
|
circle_color = ColorProperty(None)
|
||||||
|
"""
|
||||||
|
Color of the ellipse around the spinner in (r, g, b, a) or string format.
|
||||||
|
|
||||||
|
.. versionadded:: 1.2.0
|
||||||
|
|
||||||
|
:attr:`circle_color` is a :class:`~kivy.properties.ColorProperty`
|
||||||
|
and defaults to `None`.
|
||||||
|
"""
|
||||||
|
|
||||||
|
show_transition = StringProperty("out_elastic")
|
||||||
|
"""
|
||||||
|
Transition of the spinner's opening.
|
||||||
|
|
||||||
|
.. versionadded:: 1.2.0
|
||||||
|
|
||||||
|
:attr:`show_transition` is a :class:`~kivy.properties.StringProperty`
|
||||||
|
and defaults to `'out_elastic'`.
|
||||||
|
"""
|
||||||
|
|
||||||
|
show_duration = NumericProperty(0.8)
|
||||||
|
"""
|
||||||
|
Duration of the spinner display.
|
||||||
|
|
||||||
|
.. versionadded:: 1.2.0
|
||||||
|
|
||||||
|
:attr:`show_duration` is a :class:`~kivy.properties.NumericProperty`
|
||||||
|
and defaults to `0.8`.
|
||||||
|
"""
|
||||||
|
|
||||||
|
hide_transition = StringProperty("out_elastic")
|
||||||
|
"""
|
||||||
|
Transition of hiding the spinner.
|
||||||
|
|
||||||
|
.. versionadded:: 1.2.0
|
||||||
|
|
||||||
|
:attr:`hide_transition` is a :class:`~kivy.properties.StringProperty`
|
||||||
|
and defaults to `'out_elastic'`.
|
||||||
|
"""
|
||||||
|
|
||||||
|
hide_duration = NumericProperty(0.8)
|
||||||
|
"""
|
||||||
|
Duration of hiding the spinner.
|
||||||
|
|
||||||
|
.. versionadded:: 1.2.0
|
||||||
|
|
||||||
|
:attr:`hide_duration` is a :class:`~kivy.properties.NumericProperty`
|
||||||
|
and defaults to `0.8`.
|
||||||
|
"""
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
if not self.circle_color:
|
||||||
|
self.circle_color = self.theme_cls.primary_dark
|
||||||
self.effect_cls = _RefreshScrollEffect
|
self.effect_cls = _RefreshScrollEffect
|
||||||
self._work_spinner = False
|
self._work_spinner = False
|
||||||
self._did_overscroll = False
|
self._did_overscroll = False
|
||||||
|
@ -180,7 +262,15 @@ class MDScrollViewRefreshLayout(MDScrollView):
|
||||||
if self.refresh_callback:
|
if self.refresh_callback:
|
||||||
self.refresh_callback()
|
self.refresh_callback()
|
||||||
if not self.refresh_spinner:
|
if not self.refresh_spinner:
|
||||||
self.refresh_spinner = RefreshSpinner(_refresh_layout=self)
|
self.refresh_spinner = RefreshSpinner(
|
||||||
|
_refresh_layout=self,
|
||||||
|
spinner_color=self.spinner_color,
|
||||||
|
circle_color=self.circle_color,
|
||||||
|
show_transition=self.show_transition,
|
||||||
|
show_duration=self.show_duration,
|
||||||
|
hide_transition=self.hide_transition,
|
||||||
|
hide_duration=self.hide_duration,
|
||||||
|
)
|
||||||
self.root_layout.add_widget(self.refresh_spinner)
|
self.root_layout.add_widget(self.refresh_spinner)
|
||||||
self.refresh_spinner.start_anim_spinner()
|
self.refresh_spinner.start_anim_spinner()
|
||||||
self._work_spinner = True
|
self._work_spinner = True
|
||||||
|
@ -195,13 +285,18 @@ class MDScrollViewRefreshLayout(MDScrollView):
|
||||||
|
|
||||||
|
|
||||||
class RefreshSpinner(ThemableBehavior, FloatLayout):
|
class RefreshSpinner(ThemableBehavior, FloatLayout):
|
||||||
|
# Color of the spinner in (r, g, b, a) or string format.
|
||||||
spinner_color = ColorProperty([1, 1, 1, 1])
|
spinner_color = ColorProperty([1, 1, 1, 1])
|
||||||
"""
|
# Color of the ellipse around the spinner in (r, g, b, a) or string format.
|
||||||
Color of spinner.
|
circle_color = ColorProperty()
|
||||||
|
# Transition of the spinner's opening.
|
||||||
:attr:`spinner_color` is a :class:`~kivy.properties.ColorProperty`
|
show_transition = StringProperty()
|
||||||
and defaults to `[1, 1, 1, 1]`.
|
# The duration of the spinner display.
|
||||||
"""
|
show_duration = NumericProperty(0.8)
|
||||||
|
# Transition of hiding the spinner.
|
||||||
|
hide_transition = StringProperty()
|
||||||
|
# Duration of hiding the spinner.
|
||||||
|
hide_duration = NumericProperty(0.8)
|
||||||
|
|
||||||
# kivymd.refreshlayout.MDScrollViewRefreshLayout object
|
# kivymd.refreshlayout.MDScrollViewRefreshLayout object
|
||||||
_refresh_layout = ObjectProperty()
|
_refresh_layout = ObjectProperty()
|
||||||
|
@ -210,13 +305,15 @@ class RefreshSpinner(ThemableBehavior, FloatLayout):
|
||||||
spinner = self.ids.body_spinner
|
spinner = self.ids.body_spinner
|
||||||
Animation(
|
Animation(
|
||||||
y=spinner.y - self.theme_cls.standard_increment * 2 + dp(10),
|
y=spinner.y - self.theme_cls.standard_increment * 2 + dp(10),
|
||||||
d=0.8,
|
d=self.show_duration,
|
||||||
t="out_elastic",
|
t=self.show_transition,
|
||||||
).start(spinner)
|
).start(spinner)
|
||||||
|
|
||||||
def hide_anim_spinner(self) -> None:
|
def hide_anim_spinner(self) -> None:
|
||||||
spinner = self.ids.body_spinner
|
spinner = self.ids.body_spinner
|
||||||
anim = Animation(y=Window.height, d=0.8, t="out_elastic")
|
anim = Animation(
|
||||||
|
y=Window.height, d=self.hide_duration, t=self.hide_transition
|
||||||
|
)
|
||||||
anim.bind(on_complete=self.set_spinner)
|
anim.bind(on_complete=self.set_spinner)
|
||||||
anim.start(spinner)
|
anim.start(spinner)
|
||||||
|
|
||||||
|
|
|
@ -31,11 +31,14 @@ MDRelativeLayout
|
||||||
|
|
||||||
from kivy.uix.relativelayout import RelativeLayout
|
from kivy.uix.relativelayout import RelativeLayout
|
||||||
|
|
||||||
|
from kivymd.theming import ThemableBehavior
|
||||||
from kivymd.uix import MDAdaptiveWidget
|
from kivymd.uix import MDAdaptiveWidget
|
||||||
from kivymd.uix.behaviors import DeclarativeBehavior
|
from kivymd.uix.behaviors import DeclarativeBehavior
|
||||||
|
|
||||||
|
|
||||||
class MDRelativeLayout(DeclarativeBehavior, RelativeLayout, MDAdaptiveWidget):
|
class MDRelativeLayout(
|
||||||
|
DeclarativeBehavior, ThemableBehavior, RelativeLayout, MDAdaptiveWidget
|
||||||
|
):
|
||||||
"""
|
"""
|
||||||
Relative layout class. For more information, see in the
|
Relative layout class. For more information, see in the
|
||||||
:class:`~kivy.uix.relativelayout.RelativeLayout` class documentation.
|
:class:`~kivy.uix.relativelayout.RelativeLayout` class documentation.
|
||||||
|
|
|
@ -32,12 +32,13 @@ MDScreen
|
||||||
from kivy.properties import ListProperty, ObjectProperty
|
from kivy.properties import ListProperty, ObjectProperty
|
||||||
from kivy.uix.screenmanager import Screen
|
from kivy.uix.screenmanager import Screen
|
||||||
|
|
||||||
|
from kivymd.theming import ThemableBehavior
|
||||||
from kivymd.uix import MDAdaptiveWidget
|
from kivymd.uix import MDAdaptiveWidget
|
||||||
from kivymd.uix.behaviors import DeclarativeBehavior
|
from kivymd.uix.behaviors import DeclarativeBehavior
|
||||||
from kivymd.uix.hero import MDHeroTo
|
from kivymd.uix.hero import MDHeroTo
|
||||||
|
|
||||||
|
|
||||||
class MDScreen(DeclarativeBehavior, Screen, MDAdaptiveWidget):
|
class MDScreen(DeclarativeBehavior, ThemableBehavior, Screen, MDAdaptiveWidget):
|
||||||
"""
|
"""
|
||||||
Screen is an element intended to be used with a
|
Screen is an element intended to be used with a
|
||||||
:class:`~kivymd.uix.screenmanager.MDScreenManager`. For more information,
|
:class:`~kivymd.uix.screenmanager.MDScreenManager`. For more information,
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
from .segmentedbutton import ( # NOQA F401
|
||||||
|
MDSegmentedButton,
|
||||||
|
MDSegmentedButtonItem,
|
||||||
|
)
|
|
@ -0,0 +1,32 @@
|
||||||
|
<MDSegmentedButton>
|
||||||
|
size_hint: None, None
|
||||||
|
height: "40dp"
|
||||||
|
opacity: 0
|
||||||
|
|
||||||
|
|
||||||
|
<MDSegmentedButtonItem>
|
||||||
|
size_hint: None, None
|
||||||
|
height: self.parent.height
|
||||||
|
line_color:
|
||||||
|
self.theme_cls.disabled_hint_text_color \
|
||||||
|
if self.parent.line_color == [0, 0, 0, 0] else \
|
||||||
|
self.parent.line_color
|
||||||
|
|
||||||
|
SegmentButtonIcon:
|
||||||
|
id: scale_icon
|
||||||
|
icon: root.icon
|
||||||
|
size_hint: None, None
|
||||||
|
size: "24dp", "24dp"
|
||||||
|
pos_hint: {"center_y": .5}
|
||||||
|
scale_value_x: 1 if root.icon else 0
|
||||||
|
scale_value_y: 1 if root.icon else 0
|
||||||
|
x: label_text.x - dp(32)
|
||||||
|
|
||||||
|
MDLabel:
|
||||||
|
id: label_text
|
||||||
|
text: root.text
|
||||||
|
adaptive_size: True
|
||||||
|
pos_hint: {"center_y": .5}
|
||||||
|
x:
|
||||||
|
root.center_x - (self.texture_size[0] / 2) \
|
||||||
|
+ (dp(16) if root.icon else 0)
|
|
@ -0,0 +1,653 @@
|
||||||
|
"""
|
||||||
|
Components/SegmentedButton
|
||||||
|
==========================
|
||||||
|
|
||||||
|
.. versionadded:: 1.2.0
|
||||||
|
|
||||||
|
.. seealso::
|
||||||
|
|
||||||
|
`Material Design spec, Segmented buttons <https://m3.material.io/components/segmented-buttons/overview>`_
|
||||||
|
|
||||||
|
`Segmented control <https://kivymd.readthedocs.io/en/latest/components/segmentedcontrol/>`_
|
||||||
|
|
||||||
|
.. rubric:: Segmented buttons help people select options, switch views,
|
||||||
|
or sort elements.
|
||||||
|
|
||||||
|
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/segmented-button-preview.png
|
||||||
|
:align: center
|
||||||
|
|
||||||
|
Usage
|
||||||
|
-----
|
||||||
|
|
||||||
|
.. code-block:: kv
|
||||||
|
|
||||||
|
MDScreen:
|
||||||
|
|
||||||
|
MDSegmentedButton:
|
||||||
|
|
||||||
|
MDSegmentedButtonItem:
|
||||||
|
icon: ...
|
||||||
|
text: ...
|
||||||
|
|
||||||
|
MDSegmentedButtonItem:
|
||||||
|
icon: ...
|
||||||
|
text: ...
|
||||||
|
|
||||||
|
MDSegmentedButtonItem:
|
||||||
|
icon: ...
|
||||||
|
text: ...
|
||||||
|
|
||||||
|
Example
|
||||||
|
-------
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
from kivy.lang import Builder
|
||||||
|
|
||||||
|
from kivymd.app import MDApp
|
||||||
|
|
||||||
|
KV = '''
|
||||||
|
MDScreen:
|
||||||
|
|
||||||
|
MDSegmentedButton:
|
||||||
|
pos_hint: {"center_x": .5, "center_y": .5}
|
||||||
|
|
||||||
|
MDSegmentedButtonItem:
|
||||||
|
text: "Walking"
|
||||||
|
|
||||||
|
MDSegmentedButtonItem:
|
||||||
|
text: "Transit"
|
||||||
|
|
||||||
|
MDSegmentedButtonItem:
|
||||||
|
text: "Driving"
|
||||||
|
'''
|
||||||
|
|
||||||
|
|
||||||
|
class Example(MDApp):
|
||||||
|
def build(self):
|
||||||
|
self.theme_cls.theme_style = "Dark"
|
||||||
|
return Builder.load_string(KV)
|
||||||
|
|
||||||
|
|
||||||
|
Example().run()
|
||||||
|
|
||||||
|
By default, segmented buttons support single marking of elements:
|
||||||
|
|
||||||
|
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/segmented-button-multiselect-false.gif
|
||||||
|
:align: center
|
||||||
|
|
||||||
|
For multiple marking of elements, use the
|
||||||
|
:attr:`kivymd.uix.segmentedbutton.segmentedbutton.MDSegmentedButton.multiselect`
|
||||||
|
parameter:
|
||||||
|
|
||||||
|
.. code-block:: kv
|
||||||
|
|
||||||
|
MDSegmentedButton:
|
||||||
|
multiselect: True
|
||||||
|
|
||||||
|
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/segmented-button-multiselect-true.gif
|
||||||
|
:align: center
|
||||||
|
|
||||||
|
Control width
|
||||||
|
-------------
|
||||||
|
|
||||||
|
The width of the panel of segmented buttons will be equal to the width
|
||||||
|
of the texture of the widest button multiplied by the number of buttons:
|
||||||
|
|
||||||
|
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/segmented-button-width-by-default.png
|
||||||
|
:align: center
|
||||||
|
|
||||||
|
But you can use the `size_hint_x` parameter to specify the relative width:
|
||||||
|
|
||||||
|
.. code-block:: kv
|
||||||
|
|
||||||
|
MDSegmentedButton:
|
||||||
|
size_hint_x: .9
|
||||||
|
|
||||||
|
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/segmented-button-width-size-hint-x.png
|
||||||
|
:align: center
|
||||||
|
|
||||||
|
Customization
|
||||||
|
-------------
|
||||||
|
|
||||||
|
You can see below in the documentation from which classes the
|
||||||
|
:class:`~kivymd.uix.segmentedbutton.segmentedbutton.MDSegmentedButton` and
|
||||||
|
:class:`~kivymd.uix.segmentedbutton.segmentedbutton.MDSegmentedButtonItem`
|
||||||
|
classes are inherited and use all their attributes such as
|
||||||
|
`md_bg_color`, `md_bg_color` etc. for additional customization of segments.
|
||||||
|
|
||||||
|
Events
|
||||||
|
------
|
||||||
|
|
||||||
|
- on_marked
|
||||||
|
The method is called when a segment is marked.
|
||||||
|
|
||||||
|
- on_unmarked
|
||||||
|
The method is called when a segment is unmarked.
|
||||||
|
|
||||||
|
.. code-block:: kv
|
||||||
|
|
||||||
|
MDSegmentedButton:
|
||||||
|
on_marked: app.on_marked(*args)
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
def on_marked(
|
||||||
|
self,
|
||||||
|
segment_button: MDSegmentedButton,
|
||||||
|
segment_item: MDSegmentedButtonItem,
|
||||||
|
marked: bool,
|
||||||
|
) -> None:
|
||||||
|
print(segment_button)
|
||||||
|
print(segment_item)
|
||||||
|
print(marked)
|
||||||
|
|
||||||
|
A practical example
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
|
from faker import Faker
|
||||||
|
|
||||||
|
from kivy.clock import Clock
|
||||||
|
from kivy.lang import Builder
|
||||||
|
from kivy.properties import StringProperty
|
||||||
|
|
||||||
|
from kivymd.app import MDApp
|
||||||
|
from kivymd.uix.boxlayout import MDBoxLayout
|
||||||
|
from kivymd.uix.segmentedbutton import MDSegmentedButton, MDSegmentedButtonItem
|
||||||
|
from kivymd.utils import asynckivy
|
||||||
|
|
||||||
|
KV = '''
|
||||||
|
<UserCard>
|
||||||
|
adaptive_height: True
|
||||||
|
md_bg_color: "#343930"
|
||||||
|
radius: 16
|
||||||
|
|
||||||
|
TwoLineAvatarListItem:
|
||||||
|
id: item
|
||||||
|
divider: None
|
||||||
|
_no_ripple_effect: True
|
||||||
|
text: root.name
|
||||||
|
secondary_text: root.path_to_file
|
||||||
|
theme_text_color: "Custom"
|
||||||
|
text_color: "#8A8D79"
|
||||||
|
secondary_theme_text_color: self.theme_text_color
|
||||||
|
secondary_text_color: self.text_color
|
||||||
|
on_size:
|
||||||
|
self.ids._left_container.size = (item.height, item.height)
|
||||||
|
self.ids._left_container.x = dp(6)
|
||||||
|
self._txt_right_pad = item.height + dp(12)
|
||||||
|
|
||||||
|
ImageLeftWidget:
|
||||||
|
source: root.album
|
||||||
|
radius: root.radius
|
||||||
|
|
||||||
|
|
||||||
|
MDScreen:
|
||||||
|
md_bg_color: "#151514"
|
||||||
|
|
||||||
|
MDBoxLayout:
|
||||||
|
orientation: "vertical"
|
||||||
|
padding: "12dp"
|
||||||
|
spacing: "12dp"
|
||||||
|
|
||||||
|
MDLabel:
|
||||||
|
adaptive_height: True
|
||||||
|
text: "Your downloads"
|
||||||
|
font_style: "H5"
|
||||||
|
theme_text_color: "Custom"
|
||||||
|
text_color: "#8A8D79"
|
||||||
|
|
||||||
|
MDSegmentedButton:
|
||||||
|
size_hint_x: 1
|
||||||
|
selected_color: "#303A29"
|
||||||
|
line_color: "#343930"
|
||||||
|
on_marked: app.on_marked(*args)
|
||||||
|
|
||||||
|
MDSegmentedButtonItem:
|
||||||
|
text: "Songs"
|
||||||
|
active: True
|
||||||
|
|
||||||
|
MDSegmentedButtonItem:
|
||||||
|
text: "Albums"
|
||||||
|
|
||||||
|
MDSegmentedButtonItem:
|
||||||
|
text: "Podcasts"
|
||||||
|
|
||||||
|
RecycleView:
|
||||||
|
id: card_list
|
||||||
|
viewclass: "UserCard"
|
||||||
|
bar_width: 0
|
||||||
|
|
||||||
|
RecycleBoxLayout:
|
||||||
|
orientation: 'vertical'
|
||||||
|
spacing: "16dp"
|
||||||
|
padding: "16dp"
|
||||||
|
default_size: None, dp(72)
|
||||||
|
default_size_hint: 1, None
|
||||||
|
size_hint_y: None
|
||||||
|
height: self.minimum_height
|
||||||
|
'''
|
||||||
|
|
||||||
|
|
||||||
|
class UserCard(MDBoxLayout):
|
||||||
|
name = StringProperty()
|
||||||
|
path_to_file = StringProperty()
|
||||||
|
album = StringProperty()
|
||||||
|
|
||||||
|
|
||||||
|
class Example(MDApp):
|
||||||
|
def build(self):
|
||||||
|
self.theme_cls.theme_style = "Dark"
|
||||||
|
return Builder.load_string(KV)
|
||||||
|
|
||||||
|
def on_marked(
|
||||||
|
self,
|
||||||
|
segment_button: MDSegmentedButton,
|
||||||
|
segment_item: MDSegmentedButtonItem,
|
||||||
|
marked: bool,
|
||||||
|
) -> None:
|
||||||
|
self.generate_card()
|
||||||
|
|
||||||
|
def generate_card(self):
|
||||||
|
async def generate_card():
|
||||||
|
for i in range(10):
|
||||||
|
await asynckivy.sleep(0)
|
||||||
|
self.root.ids.card_list.data.append(
|
||||||
|
{
|
||||||
|
"name": fake.name(),
|
||||||
|
"path_to_file": f"{os.path.splitext(fake.file_path())[0]}.mp3",
|
||||||
|
"album": fake.image_url(),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
fake = Faker()
|
||||||
|
self.root.ids.card_list.data = []
|
||||||
|
Clock.schedule_once(lambda x: asynckivy.start(generate_card()))
|
||||||
|
|
||||||
|
|
||||||
|
Example().run()
|
||||||
|
|
||||||
|
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/segmented-button-practical-example.gif
|
||||||
|
:align: center
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
__all__ = ("MDSegmentedButton", "MDSegmentedButtonItem")
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
|
from kivy.animation import Animation
|
||||||
|
from kivy.clock import Clock
|
||||||
|
from kivy.lang import Builder
|
||||||
|
from kivy.metrics import dp
|
||||||
|
from kivy.properties import (
|
||||||
|
BooleanProperty,
|
||||||
|
ColorProperty,
|
||||||
|
ListProperty,
|
||||||
|
NumericProperty,
|
||||||
|
StringProperty,
|
||||||
|
VariableListProperty,
|
||||||
|
)
|
||||||
|
from kivy.uix.behaviors import ButtonBehavior
|
||||||
|
|
||||||
|
from kivymd import uix_path
|
||||||
|
from kivymd.uix.behaviors import RectangularRippleBehavior, ScaleBehavior
|
||||||
|
from kivymd.uix.boxlayout import MDBoxLayout
|
||||||
|
from kivymd.uix.floatlayout import MDFloatLayout
|
||||||
|
from kivymd.uix.label import MDIcon
|
||||||
|
|
||||||
|
with open(
|
||||||
|
os.path.join(uix_path, "segmentedbutton", "segmentedbutton.kv"),
|
||||||
|
encoding="utf-8",
|
||||||
|
) as kv_file:
|
||||||
|
Builder.load_string(kv_file.read())
|
||||||
|
|
||||||
|
|
||||||
|
class MDSegmentedButtonItem(
|
||||||
|
RectangularRippleBehavior, ButtonBehavior, MDFloatLayout
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
Segment button item.
|
||||||
|
|
||||||
|
For more information, see in the
|
||||||
|
:class:`~kivymd.uix.behaviors.RectangularRippleBehavior` and
|
||||||
|
:class:`~kivy.uix.behaviors.ButtonBehavior` and
|
||||||
|
:class:`~kivymd.uix.boxlayout.MDBoxLayout`
|
||||||
|
class documentation.
|
||||||
|
"""
|
||||||
|
|
||||||
|
icon = StringProperty()
|
||||||
|
"""
|
||||||
|
Icon segment.
|
||||||
|
|
||||||
|
:attr:`icon` is an :class:`~kivy.properties.StringProperty`
|
||||||
|
and defaults to `''`.
|
||||||
|
"""
|
||||||
|
|
||||||
|
text = StringProperty()
|
||||||
|
"""
|
||||||
|
Text segment.
|
||||||
|
|
||||||
|
:attr:`text` is an :class:`~kivy.properties.StringProperty`
|
||||||
|
and defaults to `''`.
|
||||||
|
"""
|
||||||
|
|
||||||
|
active = BooleanProperty(False)
|
||||||
|
"""
|
||||||
|
Background color of an disabled segment.
|
||||||
|
|
||||||
|
:attr:`active` is an :class:`~kivy.properties.BooleanProperty`
|
||||||
|
and defaults to `False`.
|
||||||
|
"""
|
||||||
|
|
||||||
|
disabled_color = ColorProperty(None)
|
||||||
|
"""
|
||||||
|
Is active segment.
|
||||||
|
|
||||||
|
:attr:`active` is an :class:`~kivy.properties.ColorProperty`
|
||||||
|
and defaults to `None`.
|
||||||
|
"""
|
||||||
|
|
||||||
|
_no_ripple_effect = BooleanProperty(True)
|
||||||
|
_current_icon = ""
|
||||||
|
_current_md_bg_color = None
|
||||||
|
|
||||||
|
def on_disabled(self, instance, value: bool) -> None:
|
||||||
|
def on_disabled(*args):
|
||||||
|
if value:
|
||||||
|
if not self._current_md_bg_color:
|
||||||
|
self._current_md_bg_color = self.md_bg_color
|
||||||
|
self.md_bg_color = (
|
||||||
|
self.theme_cls.disabled_hint_text_color
|
||||||
|
if not self.disabled_color
|
||||||
|
else self.disabled_color
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
if self._current_md_bg_color:
|
||||||
|
self.md_bg_color = self._current_md_bg_color
|
||||||
|
self._current_md_bg_color = None
|
||||||
|
|
||||||
|
Clock.schedule_once(on_disabled)
|
||||||
|
|
||||||
|
def on_icon(self, instance, icon_name: str):
|
||||||
|
if icon_name != "check":
|
||||||
|
self._current_icon = icon_name
|
||||||
|
|
||||||
|
|
||||||
|
# TODO:
|
||||||
|
# Add the feature to use both text and icons in segments -
|
||||||
|
# https://m3.material.io/components/segmented-buttons/guidelines#26abac1c-c6bd-44c1-a969-8c910c880b98
|
||||||
|
# Icons: optional check icon to indicate selected state -
|
||||||
|
# https://m3.material.io/components/segmented-buttons/overview#7b80f313-7d3a-4865-b26c-1f7ec98ba694
|
||||||
|
# Hovered: add a color for the hovered segment -
|
||||||
|
# https://m3.material.io/components/segmented-buttons/specs#d730b3ba-c59e-4ef8-b652-20979fe20b67
|
||||||
|
# Density: Each step down in density removes 4dp from the height -
|
||||||
|
# https://m3.material.io/components/segmented-buttons/specs#2d5cab36-1deb-40bd-9e37-bc2bb1657009
|
||||||
|
|
||||||
|
|
||||||
|
class MDSegmentedButton(MDBoxLayout):
|
||||||
|
"""
|
||||||
|
Segment button panel.
|
||||||
|
|
||||||
|
For more information, see in the
|
||||||
|
:class:`~kivymd.uix.boxlayout.MDBoxLayout` class documentation.
|
||||||
|
|
||||||
|
:Events:
|
||||||
|
`on_marked`
|
||||||
|
The method is called when a segment is marked.
|
||||||
|
`on_unmarked`
|
||||||
|
The method is called when a segment is unmarked.
|
||||||
|
"""
|
||||||
|
|
||||||
|
radius = VariableListProperty([20], length=4)
|
||||||
|
"""
|
||||||
|
Panel radius.
|
||||||
|
|
||||||
|
:attr:`radius` is an :class:`~kivy.properties.VariableListProperty`
|
||||||
|
and defaults to `[20, 20, 20, 20]`.
|
||||||
|
"""
|
||||||
|
|
||||||
|
multiselect = BooleanProperty(False)
|
||||||
|
"""
|
||||||
|
Do I allow multiple segment selection.
|
||||||
|
|
||||||
|
:attr:`multiselect` is an :class:`~kivy.properties.BooleanProperty`
|
||||||
|
and defaults to `False`.
|
||||||
|
"""
|
||||||
|
|
||||||
|
hiding_icon_transition = StringProperty("linear")
|
||||||
|
"""
|
||||||
|
Name of the transition hiding the current icon.
|
||||||
|
|
||||||
|
:attr:`hiding_icon_transition` is a :class:`~kivy.properties.StringProperty`
|
||||||
|
and defaults to `'linear'`.
|
||||||
|
"""
|
||||||
|
|
||||||
|
hiding_icon_duration = NumericProperty(0.05)
|
||||||
|
"""
|
||||||
|
Duration of hiding the current icon.
|
||||||
|
|
||||||
|
:attr:`hiding_icon_duration` is a :class:`~kivy.properties.NumericProperty`
|
||||||
|
and defaults to `0.05`.
|
||||||
|
"""
|
||||||
|
|
||||||
|
opening_icon_transition = StringProperty("linear")
|
||||||
|
"""
|
||||||
|
The name of the transition that opens a new icon of the "marked" type.
|
||||||
|
|
||||||
|
:attr:`opening_icon_transition` is a :class:`~kivy.properties.StringProperty`
|
||||||
|
and defaults to `'linear'`.
|
||||||
|
"""
|
||||||
|
|
||||||
|
opening_icon_duration = NumericProperty(0.05)
|
||||||
|
"""
|
||||||
|
The duration of opening a new icon of the "marked" type.
|
||||||
|
|
||||||
|
:attr:`opening_icon_duration` is a :class:`~kivy.properties.NumericProperty`
|
||||||
|
and defaults to `0.05`.
|
||||||
|
"""
|
||||||
|
|
||||||
|
selected_items = ListProperty()
|
||||||
|
"""
|
||||||
|
The list of :class:`~MDSegmentedButtonItem` objects that are currently
|
||||||
|
marked.
|
||||||
|
|
||||||
|
:attr:`selected_items` is a :class:`~kivy.properties.ListProperty`
|
||||||
|
and defaults to `[]`.
|
||||||
|
"""
|
||||||
|
|
||||||
|
selected_color = ColorProperty(None)
|
||||||
|
"""
|
||||||
|
Color of the marked segment.
|
||||||
|
|
||||||
|
:attr:`selected_color` is a :class:`~kivy.properties.ColorProperty`
|
||||||
|
and defaults to `None`.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
self.register_event_type("on_marked")
|
||||||
|
self.register_event_type("on_unmarked")
|
||||||
|
Clock.schedule_once(self.mark_segment)
|
||||||
|
Clock.schedule_once(self.adjust_segment_radius)
|
||||||
|
Clock.schedule_once(self.adjust_segment_panel_width, 2)
|
||||||
|
|
||||||
|
def mark_segment(self, *args) -> None:
|
||||||
|
"""Programmatically marks a segment."""
|
||||||
|
|
||||||
|
for widget in self.children:
|
||||||
|
if widget.active:
|
||||||
|
widget.active = False
|
||||||
|
widget.dispatch("on_release")
|
||||||
|
|
||||||
|
if not self.multiselect:
|
||||||
|
break
|
||||||
|
|
||||||
|
def adjust_segment_radius(self, *args) -> None:
|
||||||
|
"""Rounds off the first and last elements."""
|
||||||
|
|
||||||
|
if self.children[0].radius == [0, 0, 0, 0]:
|
||||||
|
self.children[0].radius = (0, self.height / 2, self.height / 2, 0)
|
||||||
|
if self.children[-1].radius == [0, 0, 0, 0]:
|
||||||
|
self.children[-1].radius = (self.height / 2, 0, 0, self.height / 2)
|
||||||
|
|
||||||
|
def adjust_segment_panel_width(self, *args) -> None:
|
||||||
|
"""
|
||||||
|
Sets the width of all segments and the width of the panel
|
||||||
|
by the widest segment.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if not self.size_hint_x:
|
||||||
|
width_list = [
|
||||||
|
widget.ids.label_text.texture_size[0]
|
||||||
|
+ (dp(72) if widget.icon else dp(48))
|
||||||
|
for widget in self.children
|
||||||
|
]
|
||||||
|
max_width = max(width_list)
|
||||||
|
self.width = max_width * len(width_list)
|
||||||
|
else:
|
||||||
|
max_width = self.width / len(self.children)
|
||||||
|
|
||||||
|
for widget in self.children:
|
||||||
|
widget.width = max_width
|
||||||
|
|
||||||
|
self.opacity = 1
|
||||||
|
|
||||||
|
for widget in self.children:
|
||||||
|
if widget.active:
|
||||||
|
widget.dispatch("on_release")
|
||||||
|
|
||||||
|
def shift_segment_text(self, segment_item: MDSegmentedButtonItem) -> None:
|
||||||
|
"""
|
||||||
|
Shifts the segment text to the right, thus freeing up space
|
||||||
|
for the icon (when the segment is marked).
|
||||||
|
"""
|
||||||
|
|
||||||
|
Animation(
|
||||||
|
x=(
|
||||||
|
segment_item.ids.label_text.x
|
||||||
|
+ (
|
||||||
|
dp(16)
|
||||||
|
if not segment_item.icon and not segment_item.active
|
||||||
|
else 0
|
||||||
|
)
|
||||||
|
)
|
||||||
|
if not segment_item.active
|
||||||
|
else (
|
||||||
|
segment_item.ids.label_text.x
|
||||||
|
- (
|
||||||
|
dp(16)
|
||||||
|
if not segment_item.icon and segment_item.active
|
||||||
|
else 0
|
||||||
|
)
|
||||||
|
),
|
||||||
|
d=0.2,
|
||||||
|
).start(segment_item.ids.label_text)
|
||||||
|
|
||||||
|
def show_icon_marked_segment(
|
||||||
|
self, segment_item: MDSegmentedButtonItem
|
||||||
|
) -> None:
|
||||||
|
"""
|
||||||
|
Sets the icon for the marked segment and changes the icon scale
|
||||||
|
to the normal scale.
|
||||||
|
"""
|
||||||
|
|
||||||
|
segment_item.ids.scale_icon.icon = "check"
|
||||||
|
if segment_item.ids.scale_icon.icon == "check" and segment_item.active:
|
||||||
|
segment_item.ids.scale_icon.icon = segment_item._current_icon
|
||||||
|
|
||||||
|
Animation(
|
||||||
|
scale_value_x=1,
|
||||||
|
scale_value_y=1,
|
||||||
|
d=self.opening_icon_duration,
|
||||||
|
t=self.opening_icon_transition,
|
||||||
|
).start(segment_item.ids.scale_icon)
|
||||||
|
|
||||||
|
self.shift_segment_text(segment_item)
|
||||||
|
self.set_selected_segment_list(segment_item)
|
||||||
|
self.set_bg_marked_segment(segment_item)
|
||||||
|
|
||||||
|
def hide_icon_marked_segment(
|
||||||
|
self, segment_item: MDSegmentedButtonItem
|
||||||
|
) -> None:
|
||||||
|
"""Changes the scale of the icon of the marked segment to zero."""
|
||||||
|
|
||||||
|
anim = Animation(
|
||||||
|
scale_value_x=0,
|
||||||
|
scale_value_y=0,
|
||||||
|
d=self.hiding_icon_duration,
|
||||||
|
t=self.hiding_icon_transition,
|
||||||
|
)
|
||||||
|
anim.bind(
|
||||||
|
on_complete=lambda x, y: self.show_icon_marked_segment(segment_item)
|
||||||
|
)
|
||||||
|
anim.start(segment_item.ids.scale_icon)
|
||||||
|
|
||||||
|
def restore_bg_segment(self, segment_item) -> None:
|
||||||
|
Animation(md_bg_color=self.md_bg_color, d=0.2).start(segment_item)
|
||||||
|
|
||||||
|
def set_bg_marked_segment(self, segment_item) -> None:
|
||||||
|
if segment_item.active:
|
||||||
|
Animation(
|
||||||
|
md_bg_color=self.selected_color
|
||||||
|
if self.selected_color
|
||||||
|
else self.theme_cls.primary_color,
|
||||||
|
d=0.2,
|
||||||
|
).start(segment_item)
|
||||||
|
|
||||||
|
def set_selected_segment_list(self, segment_item) -> None:
|
||||||
|
segment_item.active = not segment_item.active
|
||||||
|
|
||||||
|
if segment_item.active:
|
||||||
|
self.selected_items.append(segment_item)
|
||||||
|
self.dispatch("on_marked", segment_item, segment_item.active)
|
||||||
|
else:
|
||||||
|
if segment_item in self.selected_items:
|
||||||
|
self.selected_items.remove(segment_item)
|
||||||
|
self.dispatch("on_unmarked", segment_item, segment_item.active)
|
||||||
|
|
||||||
|
def mark_item(self, segment_item: MDSegmentedButtonItem) -> None:
|
||||||
|
if segment_item.active and not self.multiselect:
|
||||||
|
return
|
||||||
|
if not self.multiselect and self.selected_items:
|
||||||
|
self.uncheck_item()
|
||||||
|
else:
|
||||||
|
if segment_item.active:
|
||||||
|
self.restore_bg_segment(segment_item)
|
||||||
|
|
||||||
|
self.hide_icon_marked_segment(segment_item)
|
||||||
|
|
||||||
|
def uncheck_item(self) -> None:
|
||||||
|
for item in self.children:
|
||||||
|
if item.active:
|
||||||
|
self.hide_icon_marked_segment(item)
|
||||||
|
self.restore_bg_segment(item)
|
||||||
|
break
|
||||||
|
|
||||||
|
def add_widget(self, widget, *args, **kwargs):
|
||||||
|
if isinstance(widget, MDSegmentedButtonItem):
|
||||||
|
widget.bind(on_release=self.mark_item)
|
||||||
|
return super().add_widget(widget)
|
||||||
|
|
||||||
|
def on_size(self, instance_segment_button, size: list) -> None:
|
||||||
|
"""Called when the root screen is resized."""
|
||||||
|
|
||||||
|
if self.size_hint_x:
|
||||||
|
max_width = size[0] / len(self.children)
|
||||||
|
for widget in self.children:
|
||||||
|
widget.width = max_width
|
||||||
|
|
||||||
|
def on_marked(self, *args):
|
||||||
|
"""The method is called when a segment is marked."""
|
||||||
|
|
||||||
|
def on_unmarked(self, *args):
|
||||||
|
"""The method is called when a segment is unmarked."""
|
||||||
|
|
||||||
|
|
||||||
|
class SegmentButtonIcon(MDIcon, ScaleBehavior):
|
||||||
|
"""Implements an icon with scaling behavior."""
|
|
@ -1,3 +1,6 @@
|
||||||
|
#:import SEGMENT_CONTROL_SEGMENT_SWITCH_ELEVATION kivymd.material_resources.SEGMENT_CONTROL_SEGMENT_SWITCH_ELEVATION
|
||||||
|
|
||||||
|
|
||||||
<MDSegmentedControlItem>
|
<MDSegmentedControlItem>
|
||||||
adaptive_height: True
|
adaptive_height: True
|
||||||
halign: "center"
|
halign: "center"
|
||||||
|
@ -15,8 +18,9 @@
|
||||||
pos_hint: {"center_y": .5}
|
pos_hint: {"center_y": .5}
|
||||||
x: root._segment_switch_x
|
x: root._segment_switch_x
|
||||||
md_bg_color: root.segment_color
|
md_bg_color: root.segment_color
|
||||||
elevation: 2
|
elevation: SEGMENT_CONTROL_SEGMENT_SWITCH_ELEVATION
|
||||||
_radius: root.radius[0] - 4
|
_radius: root.radius[0] - 4
|
||||||
|
shadow_radius: self._radius
|
||||||
width:
|
width:
|
||||||
segment_panel.width / segment_panel.children_number \
|
segment_panel.width / segment_panel.children_number \
|
||||||
- segment_panel.spacing
|
- segment_panel.spacing
|
||||||
|
|
|
@ -121,7 +121,6 @@ from kivy.properties import (
|
||||||
)
|
)
|
||||||
|
|
||||||
from kivymd import uix_path
|
from kivymd import uix_path
|
||||||
from kivymd.theming import ThemableBehavior
|
|
||||||
from kivymd.uix.boxlayout import MDBoxLayout
|
from kivymd.uix.boxlayout import MDBoxLayout
|
||||||
from kivymd.uix.button import MDRaisedButton
|
from kivymd.uix.button import MDRaisedButton
|
||||||
from kivymd.uix.card import MDSeparator
|
from kivymd.uix.card import MDSeparator
|
||||||
|
@ -145,12 +144,12 @@ class MDSegmentedControlItem(MDLabel):
|
||||||
|
|
||||||
|
|
||||||
# TODO: Add an attribute for the color of the active segment label.
|
# TODO: Add an attribute for the color of the active segment label.
|
||||||
class MDSegmentedControl(MDRelativeLayout, ThemableBehavior):
|
class MDSegmentedControl(MDRelativeLayout):
|
||||||
"""
|
"""
|
||||||
Implements a segmented control panel.
|
Implements a segmented control panel.
|
||||||
|
|
||||||
Relative layout class. For more information, see in the
|
For more information, see in the
|
||||||
:class:`~kivy.uix.relativelayout.RelativeLayout` class documentation.
|
:class:`~kivymd.uix.relativelayout.MDRelativeLayout` class documentation.
|
||||||
|
|
||||||
:Events:
|
:Events:
|
||||||
`on_active`
|
`on_active`
|
||||||
|
@ -159,7 +158,7 @@ class MDSegmentedControl(MDRelativeLayout, ThemableBehavior):
|
||||||
|
|
||||||
md_bg_color = ColorProperty([0, 0, 0, 0])
|
md_bg_color = ColorProperty([0, 0, 0, 0])
|
||||||
"""
|
"""
|
||||||
Background color of the segment panel.
|
Background color of the segment panel in (r, g, b, a) or string format.
|
||||||
|
|
||||||
.. code-block:: kv
|
.. code-block:: kv
|
||||||
|
|
||||||
|
@ -175,7 +174,7 @@ class MDSegmentedControl(MDRelativeLayout, ThemableBehavior):
|
||||||
|
|
||||||
segment_color = ColorProperty([0, 0, 0, 0])
|
segment_color = ColorProperty([0, 0, 0, 0])
|
||||||
"""
|
"""
|
||||||
Color of the active segment.
|
Color of the active segment in (r, g, b, a) or string format.
|
||||||
|
|
||||||
.. code-block:: kv
|
.. code-block:: kv
|
||||||
|
|
||||||
|
@ -220,7 +219,8 @@ class MDSegmentedControl(MDRelativeLayout, ThemableBehavior):
|
||||||
|
|
||||||
separator_color = ColorProperty(None)
|
separator_color = ColorProperty(None)
|
||||||
"""
|
"""
|
||||||
The color of the separator between the segments.
|
The color of the separator between the segments in (r, g, b, a) or string
|
||||||
|
format.
|
||||||
|
|
||||||
.. code-block:: kv
|
.. code-block:: kv
|
||||||
|
|
||||||
|
|
|
@ -276,7 +276,6 @@ from kivy.properties import (
|
||||||
)
|
)
|
||||||
|
|
||||||
from kivymd import uix_path
|
from kivymd import uix_path
|
||||||
from kivymd.theming import ThemableBehavior
|
|
||||||
from kivymd.uix.behaviors import TouchBehavior
|
from kivymd.uix.behaviors import TouchBehavior
|
||||||
from kivymd.uix.button import MDIconButton
|
from kivymd.uix.button import MDIconButton
|
||||||
from kivymd.uix.list import MDList
|
from kivymd.uix.list import MDList
|
||||||
|
@ -295,7 +294,7 @@ class SelectionIconCheck(MDIconButton):
|
||||||
icon_check_color = ColorProperty([0, 0, 0, 1])
|
icon_check_color = ColorProperty([0, 0, 0, 1])
|
||||||
|
|
||||||
|
|
||||||
class SelectionItem(ThemableBehavior, MDRelativeLayout, TouchBehavior):
|
class SelectionItem(MDRelativeLayout, TouchBehavior):
|
||||||
selected = BooleanProperty(False)
|
selected = BooleanProperty(False)
|
||||||
"""
|
"""
|
||||||
Whether or not an item is checked.
|
Whether or not an item is checked.
|
||||||
|
@ -514,6 +513,11 @@ class SelectionItem(ThemableBehavior, MDRelativeLayout, TouchBehavior):
|
||||||
|
|
||||||
class MDSelectionList(MDList):
|
class MDSelectionList(MDList):
|
||||||
"""
|
"""
|
||||||
|
Selection list class.
|
||||||
|
|
||||||
|
For more information, see in the
|
||||||
|
:class:`~kivymd.uix.list.MDList` classes documentation.
|
||||||
|
|
||||||
:Events:
|
:Events:
|
||||||
`on_selected`
|
`on_selected`
|
||||||
Called when a list item is selected.
|
Called when a list item is selected.
|
||||||
|
@ -548,7 +552,8 @@ class MDSelectionList(MDList):
|
||||||
|
|
||||||
icon_bg_color = ColorProperty([1, 1, 1, 1])
|
icon_bg_color = ColorProperty([1, 1, 1, 1])
|
||||||
"""
|
"""
|
||||||
Background color of the icon that will mark the selected list item.
|
Background color in (r, g, b, a) or string format of the icon that will
|
||||||
|
mark the selected list item.
|
||||||
|
|
||||||
:attr:`icon_bg_color` is an :class:`~kivy.properties.ColorProperty`
|
:attr:`icon_bg_color` is an :class:`~kivy.properties.ColorProperty`
|
||||||
and defaults to `[1, 1, 1, 1]`.
|
and defaults to `[1, 1, 1, 1]`.
|
||||||
|
@ -556,7 +561,8 @@ class MDSelectionList(MDList):
|
||||||
|
|
||||||
icon_check_color = ColorProperty([0, 0, 0, 1])
|
icon_check_color = ColorProperty([0, 0, 0, 1])
|
||||||
"""
|
"""
|
||||||
Color of the icon that will mark the selected list item.
|
Color in (r, g, b, a) or string format of the icon that will mark the
|
||||||
|
selected list item.
|
||||||
|
|
||||||
:attr:`icon_check_color` is an :class:`~kivy.properties.ColorProperty`
|
:attr:`icon_check_color` is an :class:`~kivy.properties.ColorProperty`
|
||||||
and defaults to `[1, 1, 1, 1]`.
|
and defaults to `[1, 1, 1, 1]`.
|
||||||
|
@ -564,7 +570,7 @@ class MDSelectionList(MDList):
|
||||||
|
|
||||||
overlay_color = ColorProperty([0, 0, 0, 0.2])
|
overlay_color = ColorProperty([0, 0, 0, 0.2])
|
||||||
"""
|
"""
|
||||||
The overlay color of the selected list item..
|
The overlay color in (r, g, b, a) or string format of the selected list item.
|
||||||
|
|
||||||
:attr:`overlay_color` is an :class:`~kivy.properties.ColorProperty`
|
:attr:`overlay_color` is an :class:`~kivy.properties.ColorProperty`
|
||||||
and defaults to `[0, 0, 0, 0.2]]`.
|
and defaults to `[0, 0, 0, 0.2]]`.
|
||||||
|
@ -580,7 +586,8 @@ class MDSelectionList(MDList):
|
||||||
|
|
||||||
progress_round_color = ColorProperty(None)
|
progress_round_color = ColorProperty(None)
|
||||||
"""
|
"""
|
||||||
Color of the spinner for switching of `selected_mode` mode.
|
Color in (r, g, b, a) or string format of the spinner for switching of
|
||||||
|
`selected_mode` mode.
|
||||||
|
|
||||||
:attr:`progress_round_color` is an :class:`~kivy.properties.NumericProperty`
|
:attr:`progress_round_color` is an :class:`~kivy.properties.NumericProperty`
|
||||||
and defaults to `None`.
|
and defaults to `None`.
|
||||||
|
|
|
@ -4,13 +4,12 @@ Components/SelectionControls
|
||||||
|
|
||||||
.. seealso::
|
.. seealso::
|
||||||
|
|
||||||
`Material Design spec, Selection controls <https://material.io/components/selection-controls>`_
|
`Material Design spec, Checkbox <https://m3.material.io/components/checkbox/overview>`_
|
||||||
|
|
||||||
|
`Material Design spec, Switch <https://m3.material.io/components/switch/overview>`_
|
||||||
|
|
||||||
.. rubric:: Selection controls allow the user to select options.
|
.. rubric:: Selection controls allow the user to select options.
|
||||||
|
|
||||||
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/selection-controll.png
|
|
||||||
:align: center
|
|
||||||
|
|
||||||
`KivyMD` provides the following selection controls classes for use:
|
`KivyMD` provides the following selection controls classes for use:
|
||||||
|
|
||||||
- MDCheckbox_
|
- MDCheckbox_
|
||||||
|
@ -20,6 +19,12 @@ Components/SelectionControls
|
||||||
MDCheckbox
|
MDCheckbox
|
||||||
----------
|
----------
|
||||||
|
|
||||||
|
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/checkbox.png
|
||||||
|
:align: center
|
||||||
|
|
||||||
|
Usage
|
||||||
|
-----
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
from kivy.lang import Builder
|
from kivy.lang import Builder
|
||||||
|
@ -37,18 +42,20 @@ MDCheckbox
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
|
||||||
class Test(MDApp):
|
class Example(MDApp):
|
||||||
def build(self):
|
def build(self):
|
||||||
|
self.theme_cls.primary_palette = "Green"
|
||||||
|
self.theme_cls.theme_style = "Dark"
|
||||||
return Builder.load_string(KV)
|
return Builder.load_string(KV)
|
||||||
|
|
||||||
|
|
||||||
Test().run()
|
Example().run()
|
||||||
|
|
||||||
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/checkbox.gif
|
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/checkbox.gif
|
||||||
:align: center
|
:align: center
|
||||||
|
|
||||||
.. Note:: Be sure to specify the size of the checkbox. By default, it is
|
.. Note:: Be sure to specify the size of the checkbox. By default, it is
|
||||||
``(dp(48), dp(48))``, but the ripple effect takes up all the available
|
`(dp(48), dp(48))`, but the ripple effect takes up all the available
|
||||||
space.
|
space.
|
||||||
|
|
||||||
Control state
|
Control state
|
||||||
|
@ -94,20 +101,138 @@ MDCheckbox with group
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
|
||||||
class Test(MDApp):
|
class Example(MDApp):
|
||||||
def build(self):
|
def build(self):
|
||||||
|
self.theme_cls.primary_palette = "Green"
|
||||||
|
self.theme_cls.theme_style = "Dark"
|
||||||
return Builder.load_string(KV)
|
return Builder.load_string(KV)
|
||||||
|
|
||||||
|
|
||||||
Test().run()
|
Example().run()
|
||||||
|
|
||||||
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/checkbox-group.gif
|
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/checkbox-group.gif
|
||||||
:align: center
|
:align: center
|
||||||
|
|
||||||
|
Parent and child checkboxes
|
||||||
|
---------------------------
|
||||||
|
|
||||||
|
Checkboxes can have a parent-child relationship with other checkboxes. When
|
||||||
|
the parent checkbox is checked, all child checkboxes are checked. If a parent
|
||||||
|
checkbox is unchecked, all child checkboxes are unchecked. If some, but not all,
|
||||||
|
child checkboxes are checked, the parent checkbox becomes an indeterminate
|
||||||
|
checkbox.
|
||||||
|
|
||||||
|
Usage
|
||||||
|
-----
|
||||||
|
|
||||||
|
.. code-block:: kv
|
||||||
|
|
||||||
|
MDCheckbox:
|
||||||
|
group: "root" # this is a required name for the parent checkbox group
|
||||||
|
|
||||||
|
MDCheckbox:
|
||||||
|
group: "child" # this is a required name for a group of child checkboxes
|
||||||
|
|
||||||
|
MDCheckbox:
|
||||||
|
group: "child" # this is a required name for a group of child checkboxes
|
||||||
|
|
||||||
|
Example
|
||||||
|
-------
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
from kivy.lang import Builder
|
||||||
|
from kivy.properties import StringProperty
|
||||||
|
|
||||||
|
from kivymd.app import MDApp
|
||||||
|
from kivymd.uix.boxlayout import MDBoxLayout
|
||||||
|
|
||||||
|
KV = '''
|
||||||
|
<CheckItem>
|
||||||
|
adaptive_height: True
|
||||||
|
|
||||||
|
MDCheckbox:
|
||||||
|
size_hint: None, None
|
||||||
|
size: "48dp", "48dp"
|
||||||
|
group: root.group
|
||||||
|
|
||||||
|
MDLabel:
|
||||||
|
text: root.text
|
||||||
|
adaptive_height: True
|
||||||
|
theme_text_color: "Custom"
|
||||||
|
text_color: "#B2B6AE"
|
||||||
|
pos_hint: {"center_y": .5}
|
||||||
|
|
||||||
|
|
||||||
|
MDBoxLayout:
|
||||||
|
orientation: "vertical"
|
||||||
|
md_bg_color: "#141612"
|
||||||
|
|
||||||
|
MDTopAppBar:
|
||||||
|
md_bg_color: "#21271F"
|
||||||
|
specific_text_color: "#B2B6AE"
|
||||||
|
elevation: 0
|
||||||
|
title: "Meal options"
|
||||||
|
left_action_items: [["arrow-left", lambda x: x]]
|
||||||
|
anchor_title: "left"
|
||||||
|
|
||||||
|
MDBoxLayout:
|
||||||
|
orientation: "vertical"
|
||||||
|
adaptive_height: True
|
||||||
|
padding: "12dp", "36dp", 0, 0
|
||||||
|
|
||||||
|
CheckItem:
|
||||||
|
text: "Recieve emails"
|
||||||
|
group: "root"
|
||||||
|
|
||||||
|
MDBoxLayout:
|
||||||
|
orientation: "vertical"
|
||||||
|
adaptive_height: True
|
||||||
|
padding: "24dp", 0, 0, 0
|
||||||
|
|
||||||
|
CheckItem:
|
||||||
|
text: "Daily"
|
||||||
|
group: "child"
|
||||||
|
|
||||||
|
CheckItem:
|
||||||
|
text: "Weekly"
|
||||||
|
group: "child"
|
||||||
|
|
||||||
|
CheckItem:
|
||||||
|
text: "Monthly"
|
||||||
|
group: "child"
|
||||||
|
|
||||||
|
MDWidget:
|
||||||
|
'''
|
||||||
|
|
||||||
|
|
||||||
|
class CheckItem(MDBoxLayout):
|
||||||
|
text = StringProperty()
|
||||||
|
group = StringProperty()
|
||||||
|
|
||||||
|
|
||||||
|
class Example(MDApp):
|
||||||
|
def build(self):
|
||||||
|
self.theme_cls.theme_style = "Dark"
|
||||||
|
self.theme_cls.primary_palette = "Teal"
|
||||||
|
return Builder.load_string(KV)
|
||||||
|
|
||||||
|
|
||||||
|
Example().run()
|
||||||
|
|
||||||
|
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/checkbox-parent-child.gif
|
||||||
|
:align: center
|
||||||
|
|
||||||
.. MDSwitch:
|
.. MDSwitch:
|
||||||
MDSwitch
|
MDSwitch
|
||||||
--------
|
--------
|
||||||
|
|
||||||
|
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/switch.png
|
||||||
|
:align: center
|
||||||
|
|
||||||
|
Usage
|
||||||
|
-----
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
from kivy.lang import Builder
|
from kivy.lang import Builder
|
||||||
|
@ -122,58 +247,20 @@ MDSwitch
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
|
||||||
class Test(MDApp):
|
class Example(MDApp):
|
||||||
def build(self):
|
def build(self):
|
||||||
|
self.theme_cls.primary_palette = "Green"
|
||||||
|
self.theme_cls.theme_style = "Dark"
|
||||||
return Builder.load_string(KV)
|
return Builder.load_string(KV)
|
||||||
|
|
||||||
|
|
||||||
Test().run()
|
Example().run()
|
||||||
|
|
||||||
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/md-switch.gif
|
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/md-switch.gif
|
||||||
:align: center
|
:align: center
|
||||||
|
|
||||||
.. Note:: For :class:`~MDSwitch` size is not required. By default it is
|
|
||||||
``(dp(36), dp(48))``, but you can increase the width if you want.
|
|
||||||
|
|
||||||
.. code-block:: kv
|
|
||||||
|
|
||||||
MDSwitch:
|
|
||||||
width: dp(64)
|
|
||||||
|
|
||||||
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/md-switch_width.png
|
|
||||||
:align: center
|
|
||||||
|
|
||||||
.. Note:: Control state of :class:`~MDSwitch` same way as in
|
.. Note:: Control state of :class:`~MDSwitch` same way as in
|
||||||
:class:`~MDCheckbox`.
|
:class:`~MDCheckbox`.
|
||||||
|
|
||||||
MDSwitch in M3 style
|
|
||||||
--------------------
|
|
||||||
|
|
||||||
.. code-block:: python
|
|
||||||
|
|
||||||
from kivy.lang import Builder
|
|
||||||
|
|
||||||
from kivymd.app import MDApp
|
|
||||||
|
|
||||||
KV = '''
|
|
||||||
MDScreen:
|
|
||||||
|
|
||||||
MDSwitch:
|
|
||||||
pos_hint: {'center_x': .5, 'center_y': .5}
|
|
||||||
active: True
|
|
||||||
'''
|
|
||||||
|
|
||||||
|
|
||||||
class Test(MDApp):
|
|
||||||
def build(self):
|
|
||||||
self.theme_cls.material_style = "M3"
|
|
||||||
return Builder.load_string(KV)
|
|
||||||
|
|
||||||
|
|
||||||
Test().run()
|
|
||||||
|
|
||||||
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/checkbox-m3.gif
|
|
||||||
:align: center
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
__all__ = ("MDCheckbox", "MDSwitch")
|
__all__ = ("MDCheckbox", "MDSwitch")
|
||||||
|
@ -195,9 +282,14 @@ from kivy.uix.floatlayout import FloatLayout
|
||||||
|
|
||||||
from kivymd import uix_path
|
from kivymd import uix_path
|
||||||
from kivymd.theming import ThemableBehavior
|
from kivymd.theming import ThemableBehavior
|
||||||
from kivymd.uix.behaviors import CircularRippleBehavior, CommonElevationBehavior
|
from kivymd.uix.behaviors import (
|
||||||
|
CircularRippleBehavior,
|
||||||
|
CommonElevationBehavior,
|
||||||
|
ScaleBehavior,
|
||||||
|
)
|
||||||
from kivymd.uix.floatlayout import MDFloatLayout
|
from kivymd.uix.floatlayout import MDFloatLayout
|
||||||
from kivymd.uix.label import MDIcon
|
from kivymd.uix.label import MDIcon
|
||||||
|
from kivymd.utils import asynckivy
|
||||||
|
|
||||||
with open(
|
with open(
|
||||||
os.path.join(uix_path, "selectioncontrol", "selectioncontrol.kv"),
|
os.path.join(uix_path, "selectioncontrol", "selectioncontrol.kv"),
|
||||||
|
@ -206,7 +298,22 @@ with open(
|
||||||
Builder.load_string(kv_file.read())
|
Builder.load_string(kv_file.read())
|
||||||
|
|
||||||
|
|
||||||
class MDCheckbox(CircularRippleBehavior, ToggleButtonBehavior, MDIcon):
|
class MDCheckbox(
|
||||||
|
CircularRippleBehavior, ScaleBehavior, ToggleButtonBehavior, MDIcon
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
Checkbox class.
|
||||||
|
|
||||||
|
For more information, see in the
|
||||||
|
:class:`~kivymd.uix.behaviors.CircularRippleBehavior` and
|
||||||
|
:class:`~kivy.uix.behaviors.ToggleButtonBehavior` and
|
||||||
|
:class:`~kivymd.uix.label.MDIcon`
|
||||||
|
classes documentation.
|
||||||
|
"""
|
||||||
|
|
||||||
|
__allow_child_checkboxes_active = True
|
||||||
|
__allow_root_checkbox_active = True
|
||||||
|
|
||||||
active = BooleanProperty(False)
|
active = BooleanProperty(False)
|
||||||
"""
|
"""
|
||||||
Indicates if the checkbox is active or inactive.
|
Indicates if the checkbox is active or inactive.
|
||||||
|
@ -235,7 +342,7 @@ class MDCheckbox(CircularRippleBehavior, ToggleButtonBehavior, MDIcon):
|
||||||
|
|
||||||
radio_icon_normal = StringProperty("checkbox-blank-circle-outline")
|
radio_icon_normal = StringProperty("checkbox-blank-circle-outline")
|
||||||
"""
|
"""
|
||||||
Background icon (when using the ``group`` option) of the checkbox used for
|
Background icon (when using the `group` option) of the checkbox used for
|
||||||
the default graphical representation when the checkbox is not pressed.
|
the default graphical representation when the checkbox is not pressed.
|
||||||
|
|
||||||
:attr:`radio_icon_normal` is a :class:`~kivy.properties.StringProperty`
|
:attr:`radio_icon_normal` is a :class:`~kivy.properties.StringProperty`
|
||||||
|
@ -244,7 +351,7 @@ class MDCheckbox(CircularRippleBehavior, ToggleButtonBehavior, MDIcon):
|
||||||
|
|
||||||
radio_icon_down = StringProperty("checkbox-marked-circle")
|
radio_icon_down = StringProperty("checkbox-marked-circle")
|
||||||
"""
|
"""
|
||||||
Background icon (when using the ``group`` option) of the checkbox used for
|
Background icon (when using the `group` option) of the checkbox used for
|
||||||
the default graphical representation when the checkbox is pressed.
|
the default graphical representation when the checkbox is pressed.
|
||||||
|
|
||||||
:attr:`radio_icon_down` is a :class:`~kivy.properties.StringProperty`
|
:attr:`radio_icon_down` is a :class:`~kivy.properties.StringProperty`
|
||||||
|
@ -253,7 +360,7 @@ class MDCheckbox(CircularRippleBehavior, ToggleButtonBehavior, MDIcon):
|
||||||
|
|
||||||
color_active = ColorProperty(None)
|
color_active = ColorProperty(None)
|
||||||
"""
|
"""
|
||||||
Color when the checkbox is in the active state.
|
Color in (r, g, b, a) or string format when the checkbox is in the active state.
|
||||||
|
|
||||||
.. versionadded:: 1.0.0
|
.. versionadded:: 1.0.0
|
||||||
|
|
||||||
|
@ -271,7 +378,7 @@ class MDCheckbox(CircularRippleBehavior, ToggleButtonBehavior, MDIcon):
|
||||||
|
|
||||||
color_inactive = ColorProperty(None)
|
color_inactive = ColorProperty(None)
|
||||||
"""
|
"""
|
||||||
Color when the checkbox is in the inactive state.
|
Color in (r, g, b, a) or string format when the checkbox is in the inactive state.
|
||||||
|
|
||||||
.. versionadded:: 1.0.0
|
.. versionadded:: 1.0.0
|
||||||
|
|
||||||
|
@ -289,7 +396,7 @@ class MDCheckbox(CircularRippleBehavior, ToggleButtonBehavior, MDIcon):
|
||||||
|
|
||||||
disabled_color = ColorProperty(None)
|
disabled_color = ColorProperty(None)
|
||||||
"""
|
"""
|
||||||
Color when the checkbox is in the disabled state.
|
Color in (r, g, b, a) or string format when the checkbox is in the disabled state.
|
||||||
|
|
||||||
.. code-block:: kv
|
.. code-block:: kv
|
||||||
|
|
||||||
|
@ -309,7 +416,7 @@ class MDCheckbox(CircularRippleBehavior, ToggleButtonBehavior, MDIcon):
|
||||||
|
|
||||||
selected_color = ColorProperty(None, deprecated=True)
|
selected_color = ColorProperty(None, deprecated=True)
|
||||||
"""
|
"""
|
||||||
Color when the checkbox is in the active state.
|
Color in (r, g, b, a) or string format when the checkbox is in the active state.
|
||||||
|
|
||||||
.. deprecated:: 1.0.0
|
.. deprecated:: 1.0.0
|
||||||
Use :attr:`color_active` instead.
|
Use :attr:`color_active` instead.
|
||||||
|
@ -320,7 +427,7 @@ class MDCheckbox(CircularRippleBehavior, ToggleButtonBehavior, MDIcon):
|
||||||
|
|
||||||
unselected_color = ColorProperty(None, deprecated=True)
|
unselected_color = ColorProperty(None, deprecated=True)
|
||||||
"""
|
"""
|
||||||
Color when the checkbox is in the inactive state.
|
Color in (r, g, b, a) or string format when the checkbox is in the inactive state.
|
||||||
|
|
||||||
.. deprecated:: 1.0.0
|
.. deprecated:: 1.0.0
|
||||||
Use :attr:`color_inactive` instead.
|
Use :attr:`color_inactive` instead.
|
||||||
|
@ -332,9 +439,11 @@ class MDCheckbox(CircularRippleBehavior, ToggleButtonBehavior, MDIcon):
|
||||||
_current_color = ColorProperty([0.0, 0.0, 0.0, 0.0])
|
_current_color = ColorProperty([0.0, 0.0, 0.0, 0.0])
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
self.check_anim_out = Animation(font_size=0, duration=0.1, t="out_quad")
|
self.check_anim_out = Animation(
|
||||||
|
scale_value_x=0, scale_value_y=0, duration=0.1, t="out_quad"
|
||||||
|
)
|
||||||
self.check_anim_in = Animation(
|
self.check_anim_in = Animation(
|
||||||
font_size=sp(24), duration=0.1, t="out_quad"
|
scale_value_x=1, scale_value_y=1, duration=0.1, t="out_quad"
|
||||||
)
|
)
|
||||||
super().__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
self.color_active = self.theme_cls.primary_color
|
self.color_active = self.theme_cls.primary_color
|
||||||
|
@ -364,6 +473,13 @@ class MDCheckbox(CircularRippleBehavior, ToggleButtonBehavior, MDIcon):
|
||||||
self.update_color()
|
self.update_color()
|
||||||
|
|
||||||
def update_primary_color(self, instance, value) -> None:
|
def update_primary_color(self, instance, value) -> None:
|
||||||
|
"""
|
||||||
|
Called when the values of
|
||||||
|
:attr:`kivymd.theming.ThemableBehavior.theme_cls.theme_style` and
|
||||||
|
:attr:`kivymd.theming.ThemableBehavior.theme_cls.primary_color`
|
||||||
|
change.
|
||||||
|
"""
|
||||||
|
|
||||||
if value in ("Dark", "Light"):
|
if value in ("Dark", "Light"):
|
||||||
if not self.disabled:
|
if not self.disabled:
|
||||||
self.color = self.theme_cls.primary_color
|
self.color = self.theme_cls.primary_color
|
||||||
|
@ -373,18 +489,41 @@ class MDCheckbox(CircularRippleBehavior, ToggleButtonBehavior, MDIcon):
|
||||||
self.color_active = value
|
self.color_active = value
|
||||||
|
|
||||||
def update_icon(self, *args) -> None:
|
def update_icon(self, *args) -> None:
|
||||||
|
"""
|
||||||
|
Called when the values of
|
||||||
|
:attr:`checkbox_icon_normal` and
|
||||||
|
:attr:`checkbox_icon_down` and
|
||||||
|
:attr:`radio_icon_normal` and
|
||||||
|
:attr:`group`
|
||||||
|
change.
|
||||||
|
"""
|
||||||
|
|
||||||
if self.state == "down":
|
if self.state == "down":
|
||||||
self.icon = (
|
self.icon = (
|
||||||
self.radio_icon_down if self.group else self.checkbox_icon_down
|
self.radio_icon_down
|
||||||
|
if self.group and self.group not in ["root", "child"]
|
||||||
|
else self.checkbox_icon_down
|
||||||
|
if self.group != "root"
|
||||||
|
else "minus-box"
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
self.icon = (
|
self.icon = (
|
||||||
self.radio_icon_normal
|
self.radio_icon_normal
|
||||||
if self.group
|
if self.group and self.group not in ["root", "child"]
|
||||||
else self.checkbox_icon_normal
|
else self.checkbox_icon_normal
|
||||||
)
|
)
|
||||||
|
|
||||||
def update_color(self, *args) -> None:
|
def update_color(self, *args) -> None:
|
||||||
|
"""
|
||||||
|
Called when the values of
|
||||||
|
:attr:`color_active` and
|
||||||
|
:attr:`color_inactive` and
|
||||||
|
:attr:`disabled_color` and
|
||||||
|
:attr:`disabled` and
|
||||||
|
:attr:`state`
|
||||||
|
change.
|
||||||
|
"""
|
||||||
|
|
||||||
if self.disabled:
|
if self.disabled:
|
||||||
self._current_color = self.disabled_color
|
self._current_color = self.disabled_color
|
||||||
elif self.state == "down":
|
elif self.state == "down":
|
||||||
|
@ -393,6 +532,8 @@ class MDCheckbox(CircularRippleBehavior, ToggleButtonBehavior, MDIcon):
|
||||||
self._current_color = self.color_inactive
|
self._current_color = self.color_inactive
|
||||||
|
|
||||||
def on_state(self, *args) -> None:
|
def on_state(self, *args) -> None:
|
||||||
|
"""Called when the values of :attr:`state` change."""
|
||||||
|
|
||||||
if self.state == "down":
|
if self.state == "down":
|
||||||
self.check_anim_in.cancel(self)
|
self.check_anim_in.cancel(self)
|
||||||
self.check_anim_out.start(self)
|
self.check_anim_out.start(self)
|
||||||
|
@ -408,8 +549,45 @@ class MDCheckbox(CircularRippleBehavior, ToggleButtonBehavior, MDIcon):
|
||||||
self.active = False
|
self.active = False
|
||||||
|
|
||||||
def on_active(self, *args) -> None:
|
def on_active(self, *args) -> None:
|
||||||
|
"""Called when the values of :attr:`active` change."""
|
||||||
|
|
||||||
self.state = "down" if self.active else "normal"
|
self.state = "down" if self.active else "normal"
|
||||||
|
|
||||||
|
if (
|
||||||
|
self.group
|
||||||
|
and self.group == "root"
|
||||||
|
and MDCheckbox.__allow_root_checkbox_active
|
||||||
|
):
|
||||||
|
self.set_child_active(self.active)
|
||||||
|
elif self.group and self.group == "child":
|
||||||
|
if MDCheckbox.__allow_child_checkboxes_active:
|
||||||
|
self.set_root_active()
|
||||||
|
|
||||||
|
def set_root_active(self) -> None:
|
||||||
|
root_checkbox = self.get_widgets("root")
|
||||||
|
if root_checkbox:
|
||||||
|
MDCheckbox.__allow_root_checkbox_active = False
|
||||||
|
root_checkbox[0].active = True in [
|
||||||
|
child.active for child in self.get_widgets("child")
|
||||||
|
]
|
||||||
|
MDCheckbox.__allow_root_checkbox_active = True
|
||||||
|
|
||||||
|
def set_child_active(self, active: bool):
|
||||||
|
for child in self.get_widgets("child"):
|
||||||
|
child.active = active
|
||||||
|
MDCheckbox.__allow_child_checkboxes_active = True
|
||||||
|
|
||||||
|
def on_touch_down(self, touch):
|
||||||
|
if self.collide_point(touch.x, touch.y):
|
||||||
|
if self.group and self.group == "root":
|
||||||
|
MDCheckbox.__allow_child_checkboxes_active = False
|
||||||
|
return super().on_touch_down(touch)
|
||||||
|
|
||||||
|
def _release_group(self, current):
|
||||||
|
if self.group and self.group in ["root", "child"]:
|
||||||
|
return
|
||||||
|
super()._release_group(current)
|
||||||
|
|
||||||
|
|
||||||
class ThumbIcon(MDIcon):
|
class ThumbIcon(MDIcon):
|
||||||
"""
|
"""
|
||||||
|
@ -419,14 +597,8 @@ class ThumbIcon(MDIcon):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
class Thumb(
|
class Thumb(CommonElevationBehavior, CircularRippleBehavior, MDFloatLayout):
|
||||||
CommonElevationBehavior,
|
"""Implements a thumb for the :class:`~MDSwitch` widget."""
|
||||||
CircularRippleBehavior,
|
|
||||||
MDFloatLayout,
|
|
||||||
):
|
|
||||||
"""
|
|
||||||
Implements a thumb for the :class:`~MDSwitch` widget.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def _set_ellipse(self, instance, value):
|
def _set_ellipse(self, instance, value):
|
||||||
self.ellipse.size = (self._ripple_rad, self._ripple_rad)
|
self.ellipse.size = (self._ripple_rad, self._ripple_rad)
|
||||||
|
@ -443,6 +615,14 @@ class Thumb(
|
||||||
|
|
||||||
|
|
||||||
class MDSwitch(ThemableBehavior, FloatLayout):
|
class MDSwitch(ThemableBehavior, FloatLayout):
|
||||||
|
"""
|
||||||
|
Switch class.
|
||||||
|
|
||||||
|
For more information, see in the
|
||||||
|
:class:`~kivymd.theming.ThemableBehavior` and
|
||||||
|
:class:`~kivy.uix.floatlayout.FloatLayout` classes documentation.
|
||||||
|
"""
|
||||||
|
|
||||||
active = BooleanProperty(False)
|
active = BooleanProperty(False)
|
||||||
"""
|
"""
|
||||||
Indicates if the switch is active or inactive.
|
Indicates if the switch is active or inactive.
|
||||||
|
@ -490,7 +670,8 @@ class MDSwitch(ThemableBehavior, FloatLayout):
|
||||||
|
|
||||||
icon_active_color = ColorProperty(None)
|
icon_active_color = ColorProperty(None)
|
||||||
"""
|
"""
|
||||||
Thumb icon color when the switch is in the active state (only M3 style).
|
Thumb icon color in (r, g, b, a) or string format when the switch is in the
|
||||||
|
active state (only M3 style).
|
||||||
|
|
||||||
.. versionadded:: 1.0.0
|
.. versionadded:: 1.0.0
|
||||||
|
|
||||||
|
@ -510,7 +691,8 @@ class MDSwitch(ThemableBehavior, FloatLayout):
|
||||||
|
|
||||||
icon_inactive_color = ColorProperty(None)
|
icon_inactive_color = ColorProperty(None)
|
||||||
"""
|
"""
|
||||||
Thumb icon color when the switch is in an inactive state (only M3 style).
|
Thumb icon color in (r, g, b, a) or string format when the switch is in an
|
||||||
|
inactive state (only M3 style).
|
||||||
|
|
||||||
.. versionadded:: 1.0.0
|
.. versionadded:: 1.0.0
|
||||||
|
|
||||||
|
@ -529,7 +711,7 @@ class MDSwitch(ThemableBehavior, FloatLayout):
|
||||||
|
|
||||||
thumb_color_active = ColorProperty(None)
|
thumb_color_active = ColorProperty(None)
|
||||||
"""
|
"""
|
||||||
The color of the thumb when the switch is active.
|
The color in (r, g, b, a) or string format of the thumb when the switch is active.
|
||||||
|
|
||||||
.. versionadded:: 1.0.0
|
.. versionadded:: 1.0.0
|
||||||
|
|
||||||
|
@ -548,7 +730,7 @@ class MDSwitch(ThemableBehavior, FloatLayout):
|
||||||
|
|
||||||
thumb_color_inactive = ColorProperty(None)
|
thumb_color_inactive = ColorProperty(None)
|
||||||
"""
|
"""
|
||||||
The color of the thumb when the switch is inactive.
|
The color in (r, g, b, a) or string format of the thumb when the switch is inactive.
|
||||||
|
|
||||||
.. versionadded:: 1.0.0
|
.. versionadded:: 1.0.0
|
||||||
|
|
||||||
|
@ -566,7 +748,8 @@ class MDSwitch(ThemableBehavior, FloatLayout):
|
||||||
|
|
||||||
thumb_color_disabled = ColorProperty(None)
|
thumb_color_disabled = ColorProperty(None)
|
||||||
"""
|
"""
|
||||||
The color of the thumb when the switch is in the disabled state.
|
The color in (r, g, b, a) or string format of the thumb when the switch is
|
||||||
|
in the disabled state.
|
||||||
|
|
||||||
.. code-block:: kv
|
.. code-block:: kv
|
||||||
|
|
||||||
|
@ -584,7 +767,7 @@ class MDSwitch(ThemableBehavior, FloatLayout):
|
||||||
|
|
||||||
track_color_active = ColorProperty(None)
|
track_color_active = ColorProperty(None)
|
||||||
"""
|
"""
|
||||||
The color of the track when the switch is active.
|
The color in (r, g, b, a) or string format of the track when the switch is active.
|
||||||
|
|
||||||
.. code-block:: kv
|
.. code-block:: kv
|
||||||
|
|
||||||
|
@ -601,7 +784,7 @@ class MDSwitch(ThemableBehavior, FloatLayout):
|
||||||
|
|
||||||
track_color_inactive = ColorProperty(None)
|
track_color_inactive = ColorProperty(None)
|
||||||
"""
|
"""
|
||||||
The color of the track when the switch is inactive.
|
The color in (r, g, b, a) or string format of the track when the switch is inactive.
|
||||||
|
|
||||||
.. versionadded:: 1.0.0
|
.. versionadded:: 1.0.0
|
||||||
|
|
||||||
|
@ -619,7 +802,8 @@ class MDSwitch(ThemableBehavior, FloatLayout):
|
||||||
|
|
||||||
track_color_disabled = ColorProperty(None)
|
track_color_disabled = ColorProperty(None)
|
||||||
"""
|
"""
|
||||||
The color of the track when the switch is in the disabled state.
|
The color in (r, g, b, a) or string format of the track when the switch is
|
||||||
|
in the disabled state.
|
||||||
|
|
||||||
.. code-block:: kv
|
.. code-block:: kv
|
||||||
|
|
||||||
|
@ -646,6 +830,11 @@ class MDSwitch(ThemableBehavior, FloatLayout):
|
||||||
Clock.schedule_once(lambda x: self.on_active(self, self.active))
|
Clock.schedule_once(lambda x: self.on_active(self, self.active))
|
||||||
|
|
||||||
def set_icon(self, instance_switch, icon_value: str) -> None:
|
def set_icon(self, instance_switch, icon_value: str) -> None:
|
||||||
|
"""
|
||||||
|
Called when the values of
|
||||||
|
:attr:`icon_active` and :attr:`icon_inactive` change.
|
||||||
|
"""
|
||||||
|
|
||||||
def set_icon(*args):
|
def set_icon(*args):
|
||||||
icon = icon_value if icon_value else "blank"
|
icon = icon_value if icon_value else "blank"
|
||||||
self.ids.thumb.ids.icon.icon = icon
|
self.ids.thumb.ids.icon.icon = icon
|
||||||
|
@ -653,6 +842,8 @@ class MDSwitch(ThemableBehavior, FloatLayout):
|
||||||
Clock.schedule_once(set_icon, 0.2)
|
Clock.schedule_once(set_icon, 0.2)
|
||||||
|
|
||||||
def on_active(self, instance_switch, active_value: bool) -> None:
|
def on_active(self, instance_switch, active_value: bool) -> None:
|
||||||
|
"""Called when the values of :attr:`active` change."""
|
||||||
|
|
||||||
if self.theme_cls.material_style == "M3" and self.widget_style != "ios":
|
if self.theme_cls.material_style == "M3" and self.widget_style != "ios":
|
||||||
size = (
|
size = (
|
||||||
(
|
(
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue