mirror of
https://github.com/liberatedsystems/openCom-Companion.git
synced 2024-11-21 21:10:36 +01:00
Updated build system for Kivy 2.2.1
This commit is contained in:
parent
23e6b6e0c6
commit
67a8f61af8
3
.gitignore
vendored
3
.gitignore
vendored
@ -1,3 +1,4 @@
|
||||
sbapp/kivymd_working
|
||||
sbapp/.buildozer
|
||||
sbapp/requirements.txt
|
||||
sbapp/venv
|
||||
@ -29,4 +30,4 @@ build
|
||||
dist
|
||||
docs/build
|
||||
sideband*.egg-info
|
||||
sbapp*.egg-info
|
||||
sbapp*.egg-info
|
||||
|
@ -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
|
||||
|
||||
injectxml:
|
||||
# mkdir /home/markqvist/.local/lib/python3.11/site-packages/pythonforandroid/bootstraps/sdl2/build/src/main/xml
|
||||
# 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/templates
|
||||
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/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:
|
||||
buildozer android debug
|
||||
|
@ -12,9 +12,10 @@ version.regex = __version__ = ['"](.*)['"]
|
||||
version.filename = %(source.dir)s/main.py
|
||||
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
|
||||
requirements.source.kivymd = ../../Others/KivyMD-master
|
||||
|
||||
icon.filename = %(source.dir)s/assets/icon.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.api = 30
|
||||
android.minapi = 24
|
||||
android.ndk = 23b
|
||||
android.ndk = 25b
|
||||
android.skip_update = False
|
||||
android.accept_sdk_license = True
|
||||
android.release_artifact = apk
|
||||
|
@ -26,11 +26,12 @@ import os
|
||||
import kivy
|
||||
from kivy.logger import Logger
|
||||
|
||||
__version__ = "1.1.0.dev0"
|
||||
__version__ = "1.2.0.dev0"
|
||||
"""KivyMD version."""
|
||||
|
||||
release = False
|
||||
kivy.require("2.0.0")
|
||||
if "READTHEDOCS" not in os.environ:
|
||||
kivy.require("2.2.0")
|
||||
|
||||
try:
|
||||
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")
|
||||
"""Path to uix directory."""
|
||||
|
||||
glsl_path = os.path.join(path, "data", "glsl")
|
||||
"""Path to glsl directory."""
|
||||
|
||||
_log_message = (
|
||||
"KivyMD:"
|
||||
+ (" Release" if release else "")
|
||||
|
@ -54,7 +54,7 @@ from kivymd.theming import ThemeManager
|
||||
class FpsMonitoring:
|
||||
"""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."""
|
||||
|
||||
def add_monitor(*args):
|
||||
@ -62,7 +62,7 @@ class FpsMonitoring:
|
||||
|
||||
from kivymd.utils.fpsmonitor import FpsMonitor
|
||||
|
||||
monitor = FpsMonitor()
|
||||
monitor = FpsMonitor(anchor=anchor)
|
||||
monitor.start()
|
||||
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,
|
||||
),
|
||||
)
|
||||
self.update_canvas(
|
||||
self, self.size, rectangle_top, rectangle_bottom, i
|
||||
)
|
||||
|
||||
def update_canvas(
|
||||
self,
|
||||
|
@ -5,6 +5,8 @@ Register KivyMD widgets to use without import.
|
||||
from kivy.factory import Factory
|
||||
|
||||
register = Factory.register
|
||||
register("MDSegmentedButton", module="kivymd.uix.segmentedbutton")
|
||||
register("MDSegmentedButtonItem", module="kivymd.uix.segmentedbutton")
|
||||
register("MDScrollView", module="kivymd.uix.scrollview")
|
||||
register("MDRecycleView", module="kivymd.uix.recycleview")
|
||||
register("MDResponsiveLayout", module="kivymd.uix.responsivelayout")
|
||||
@ -37,6 +39,7 @@ register("FitImage", module="kivymd.uix.fitimage")
|
||||
register("MDBackdrop", module="kivymd.uix.backdrop")
|
||||
register("MDBanner", module="kivymd.uix.banner")
|
||||
register("MDTooltip", module="kivymd.uix.tooltip")
|
||||
register("MDBottomSheet", module="kivymd.uix.bottomsheet")
|
||||
register("MDBottomNavigation", module="kivymd.uix.bottomnavigation")
|
||||
register("MDBottomNavigationItem", module="kivymd.uix.bottomnavigation")
|
||||
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
|
||||
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:
|
||||
----------------------------------------------------------------------------
|
||||
@ -242,6 +242,8 @@ md_icons = {
|
||||
"account-switch-outline": "\U000F04CB",
|
||||
"account-sync": "\U000F191B",
|
||||
"account-sync-outline": "\U000F191C",
|
||||
"account-tag": "\U000F1C1B",
|
||||
"account-tag-outline": "\U000F1C1C",
|
||||
"account-tie": "\U000F0CE3",
|
||||
"account-tie-hat": "\U000F1898",
|
||||
"account-tie-hat-outline": "\U000F1899",
|
||||
@ -774,6 +776,7 @@ md_icons = {
|
||||
"audio-video": "\U000F093D",
|
||||
"audio-video-off": "\U000F11B6",
|
||||
"augmented-reality": "\U000F0850",
|
||||
"aurora": "\U000F1BB9",
|
||||
"auto-download": "\U000F137E",
|
||||
"auto-fix": "\U000F0068",
|
||||
"auto-upload": "\U000F0069",
|
||||
@ -852,6 +855,8 @@ md_icons = {
|
||||
"bandage": "\U000F0DAF",
|
||||
"bank": "\U000F0070",
|
||||
"bank-check": "\U000F1655",
|
||||
"bank-circle": "\U000F1C03",
|
||||
"bank-circle-outline": "\U000F1C04",
|
||||
"bank-minus": "\U000F0DB0",
|
||||
"bank-off": "\U000F1656",
|
||||
"bank-off-outline": "\U000F1657",
|
||||
@ -1443,6 +1448,8 @@ md_icons = {
|
||||
"camera-image": "\U000F08CC",
|
||||
"camera-iris": "\U000F0104",
|
||||
"camera-lock": "\U000F1A14",
|
||||
"camera-lock-open": "\U000F1C0D",
|
||||
"camera-lock-open-outline": "\U000F1C0E",
|
||||
"camera-lock-outline": "\U000F1A15",
|
||||
"camera-marker": "\U000F19A7",
|
||||
"camera-marker-outline": "\U000F19A8",
|
||||
@ -1707,6 +1714,7 @@ md_icons = {
|
||||
"chart-multiline": "\U000F08D4",
|
||||
"chart-multiple": "\U000F1213",
|
||||
"chart-pie": "\U000F012B",
|
||||
"chart-pie-outline": "\U000F1BDF",
|
||||
"chart-ppf": "\U000F1380",
|
||||
"chart-sankey": "\U000F11DF",
|
||||
"chart-sankey-variant": "\U000F11E0",
|
||||
@ -1978,22 +1986,53 @@ md_icons = {
|
||||
"closed-caption-outline": "\U000F0DBD",
|
||||
"cloud": "\U000F015F",
|
||||
"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-check": "\U000F0160",
|
||||
"cloud-check-outline": "\U000F12CC",
|
||||
"cloud-cancel": "\U000F1BE9",
|
||||
"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-outline": "\U000F1BED",
|
||||
"cloud-clock": "\U000F1BEE",
|
||||
"cloud-clock-outline": "\U000F1BEF",
|
||||
"cloud-cog": "\U000F1BF0",
|
||||
"cloud-cog-outline": "\U000F1BF1",
|
||||
"cloud-download": "\U000F0162",
|
||||
"cloud-download-outline": "\U000F0B7D",
|
||||
"cloud-lock": "\U000F11F1",
|
||||
"cloud-lock-open": "\U000F1BF2",
|
||||
"cloud-lock-open-outline": "\U000F1BF3",
|
||||
"cloud-lock-outline": "\U000F11F2",
|
||||
"cloud-minus": "\U000F1BF4",
|
||||
"cloud-minus-outline": "\U000F1BF5",
|
||||
"cloud-off": "\U000F1BF6",
|
||||
"cloud-off-outline": "\U000F0164",
|
||||
"cloud-outline": "\U000F0163",
|
||||
"cloud-percent": "\U000F1A35",
|
||||
"cloud-percent-outline": "\U000F1A36",
|
||||
"cloud-plus": "\U000F1BF7",
|
||||
"cloud-plus-outline": "\U000F1BF8",
|
||||
"cloud-print": "\U000F0165",
|
||||
"cloud-print-outline": "\U000F0166",
|
||||
"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-outline": "\U000F0957",
|
||||
"cloud-sync": "\U000F063F",
|
||||
@ -2311,6 +2350,7 @@ md_icons = {
|
||||
"currency-rub": "\U000F01B1",
|
||||
"currency-rupee": "\U000F1976",
|
||||
"currency-sign": "\U000F07BE",
|
||||
"currency-thb": "\U000F1C05",
|
||||
"currency-try": "\U000F01B2",
|
||||
"currency-twd": "\U000F07BF",
|
||||
"currency-uah": "\U000F1B9B",
|
||||
@ -2750,6 +2790,10 @@ md_icons = {
|
||||
"eye-check-outline": "\U000F0D05",
|
||||
"eye-circle": "\U000F0B94",
|
||||
"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-outline": "\U000F1027",
|
||||
"eye-off": "\U000F0209",
|
||||
@ -2858,6 +2902,8 @@ md_icons = {
|
||||
"file-document": "\U000F0219",
|
||||
"file-document-alert": "\U000F1A97",
|
||||
"file-document-alert-outline": "\U000F1A98",
|
||||
"file-document-arrow-right": "\U000F1C0F",
|
||||
"file-document-arrow-right-outline": "\U000F1C10",
|
||||
"file-document-check": "\U000F1A99",
|
||||
"file-document-check-outline": "\U000F1A9A",
|
||||
"file-document-edit": "\U000F0DC8",
|
||||
@ -3731,6 +3777,9 @@ md_icons = {
|
||||
"helicopter": "\U000F0AC2",
|
||||
"help": "\U000F02D6",
|
||||
"help-box": "\U000F078B",
|
||||
"help-box-multiple": "\U000F1C0A",
|
||||
"help-box-multiple-outline": "\U000F1C0B",
|
||||
"help-box-outline": "\U000F1C0C",
|
||||
"help-circle": "\U000F02D7",
|
||||
"help-circle-outline": "\U000F0625",
|
||||
"help-network": "\U000F06F5",
|
||||
@ -3907,6 +3956,7 @@ md_icons = {
|
||||
"image-filter-center-focus-strong-outline": "\U000F0F00",
|
||||
"image-filter-center-focus-weak": "\U000F02F2",
|
||||
"image-filter-drama": "\U000F02F3",
|
||||
"image-filter-drama-outline": "\U000F1BFF",
|
||||
"image-filter-frames": "\U000F02F4",
|
||||
"image-filter-hdr": "\U000F02F5",
|
||||
"image-filter-none": "\U000F02F6",
|
||||
@ -4021,6 +4071,7 @@ md_icons = {
|
||||
"keyboard-backspace": "\U000F030D",
|
||||
"keyboard-caps": "\U000F030E",
|
||||
"keyboard-close": "\U000F030F",
|
||||
"keyboard-close-outline": "\U000F1C00",
|
||||
"keyboard-esc": "\U000F12B7",
|
||||
"keyboard-f1": "\U000F12AB",
|
||||
"keyboard-f10": "\U000F12B4",
|
||||
@ -4263,6 +4314,12 @@ md_icons = {
|
||||
"lock-open-variant-outline": "\U000F0FC7",
|
||||
"lock-outline": "\U000F0341",
|
||||
"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-outline": "\U000F16B2",
|
||||
"lock-question": "\U000F08EF",
|
||||
@ -5072,6 +5129,7 @@ md_icons = {
|
||||
"pencil-remove": "\U000F0DED",
|
||||
"pencil-remove-outline": "\U000F0DEE",
|
||||
"pencil-ruler": "\U000F1353",
|
||||
"pencil-ruler-outline": "\U000F1C11",
|
||||
"penguin": "\U000F0EC0",
|
||||
"pentagon": "\U000F0701",
|
||||
"pentagon-outline": "\U000F0700",
|
||||
@ -5309,6 +5367,41 @@ md_icons = {
|
||||
"printer-off-outline": "\U000F1785",
|
||||
"printer-outline": "\U000F1786",
|
||||
"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-settings": "\U000F0707",
|
||||
"printer-wireless": "\U000F0A0B",
|
||||
@ -5497,7 +5590,10 @@ md_icons = {
|
||||
"remote-off": "\U000F0EC4",
|
||||
"remote-tv": "\U000F0EC5",
|
||||
"remote-tv-off": "\U000F0EC6",
|
||||
"rename": "\U000F1C18",
|
||||
"rename-box": "\U000F0455",
|
||||
"rename-box-outline": "\U000F1C19",
|
||||
"rename-outline": "\U000F1C1A",
|
||||
"reorder-horizontal": "\U000F0688",
|
||||
"reorder-vertical": "\U000F0689",
|
||||
"repeat": "\U000F0456",
|
||||
@ -5566,8 +5662,10 @@ md_icons = {
|
||||
"robot-outline": "\U000F167A",
|
||||
"robot-vacuum": "\U000F070D",
|
||||
"robot-vacuum-alert": "\U000F1B5D",
|
||||
"robot-vacuum-off": "\U000F1C01",
|
||||
"robot-vacuum-variant": "\U000F0908",
|
||||
"robot-vacuum-variant-alert": "\U000F1B5E",
|
||||
"robot-vacuum-variant-off": "\U000F1C02",
|
||||
"rocket": "\U000F0463",
|
||||
"rocket-launch": "\U000F14DE",
|
||||
"rocket-launch-outline": "\U000F14DF",
|
||||
@ -6605,6 +6703,8 @@ md_icons = {
|
||||
"tooltip-outline": "\U000F0526",
|
||||
"tooltip-plus": "\U000F0BD6",
|
||||
"tooltip-plus-outline": "\U000F0527",
|
||||
"tooltip-question": "\U000F1BBA",
|
||||
"tooltip-question-outline": "\U000F1BBB",
|
||||
"tooltip-remove": "\U000F1560",
|
||||
"tooltip-remove-outline": "\U000F1561",
|
||||
"tooltip-text": "\U000F0528",
|
||||
@ -7219,3 +7319,94 @@ md_icons = {
|
||||
"zodiac-virgo": "\U000F0A88",
|
||||
"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
|
||||
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)
|
||||
|
@ -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.
|
||||
"""
|
||||
|
||||
material_style = OptionProperty("M2", options=["M2", "M3"])
|
||||
material_style = OptionProperty("M3", options=["M2", "M3"])
|
||||
"""
|
||||
Material design style.
|
||||
Available options are: 'M2', 'M3'.
|
||||
|
||||
.. versionadded:: 1.0.0
|
||||
|
||||
.. versionchanged:: 1.2.0
|
||||
By default now `'M3'`.
|
||||
|
||||
.. seealso::
|
||||
|
||||
`Material Design 2 <https://material.io/>`_ and
|
||||
@ -620,7 +623,7 @@ class ThemeManager(EventDispatcher):
|
||||
|
||||
|
||||
:attr:`material_style` is an :class:`~kivy.properties.OptionProperty`
|
||||
and defaults to `'M2'`.
|
||||
and defaults to `'M3'`.
|
||||
"""
|
||||
|
||||
theme_style_switch_animation = BooleanProperty(False)
|
||||
@ -647,9 +650,8 @@ class ThemeManager(EventDispatcher):
|
||||
padding: 0, 0, 0 , "36dp"
|
||||
size_hint: .5, .5
|
||||
pos_hint: {"center_x": .5, "center_y": .5}
|
||||
elevation: 4
|
||||
shadow_radius: 6
|
||||
shadow_offset: 0, 2
|
||||
elevation: 2
|
||||
shadow_offset: 0, -2
|
||||
|
||||
MDLabel:
|
||||
text: "Theme style - {}".format(app.theme_cls.theme_style)
|
||||
@ -720,9 +722,8 @@ class ThemeManager(EventDispatcher):
|
||||
padding=(0, 0, 0, "36dp"),
|
||||
size_hint=(0.5, 0.5),
|
||||
pos_hint={"center_x": 0.5, "center_y": 0.5},
|
||||
elevation=4,
|
||||
shadow_radius=6,
|
||||
shadow_offset=(0, 2),
|
||||
elevation=2,
|
||||
shadow_offset=(0, -2),
|
||||
)
|
||||
)
|
||||
)
|
||||
@ -1665,25 +1666,60 @@ class ThemableBehavior(EventDispatcher):
|
||||
"https://github.com/kivymd/KivyMD/wiki/Modules-Material-App#exceptions"
|
||||
)
|
||||
self.theme_cls = App.get_running_app().theme_cls
|
||||
|
||||
super().__init__(**kwargs)
|
||||
|
||||
# def dec_disabled(self, *args, **kwargs) -> None:
|
||||
# callabacks = self.theme_cls.get_property_observers("theme_style")
|
||||
# Fix circular imports.
|
||||
from kivymd.uix.behaviors import CommonElevationBehavior
|
||||
from kivymd.uix.label import MDLabel
|
||||
from kivymd.uix.textfield import MDTextField
|
||||
|
||||
# for callaback in callabacks:
|
||||
# try:
|
||||
# if hasattr(callaback, "proxy") and hasattr(
|
||||
# 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
|
||||
self.common_elevation_behavior = CommonElevationBehavior
|
||||
self.md_label = MDLabel
|
||||
self.md_textfield = MDTextField
|
||||
|
||||
# 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):
|
||||
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.add_widget(self.label_toast)
|
||||
|
||||
|
@ -13,18 +13,6 @@ from pathlib import Path
|
||||
import kivymd
|
||||
|
||||
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.
|
||||
(
|
||||
kivymd.fonts_path,
|
||||
|
@ -381,13 +381,12 @@ class {name_screen}Controller:
|
||||
temp_base_screen = '''from kivy.properties import ObjectProperty
|
||||
|
||||
from kivymd.app import MDApp
|
||||
from kivymd.theming import ThemableBehavior
|
||||
from kivymd.uix.screen import MDScreen
|
||||
|
||||
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.
|
||||
The view class must be inherited from this class.
|
||||
|
@ -59,6 +59,8 @@ class MDAdaptiveWidget(SpecificBackgroundColorBehavior):
|
||||
else:
|
||||
if not isinstance(self, (FloatLayout, Screen)):
|
||||
self.bind(minimum_height=self.setter("height"))
|
||||
if not self.children:
|
||||
self.height = 0
|
||||
|
||||
def on_adaptive_width(self, md_widget, value: bool) -> None:
|
||||
self.size_hint_x = None
|
||||
@ -71,6 +73,8 @@ class MDAdaptiveWidget(SpecificBackgroundColorBehavior):
|
||||
else:
|
||||
if not isinstance(self, (FloatLayout, Screen)):
|
||||
self.bind(minimum_width=self.setter("width"))
|
||||
if not self.children:
|
||||
self.width = 0
|
||||
|
||||
def on_adaptive_size(self, md_widget, value: bool) -> None:
|
||||
self.size_hint = (None, None)
|
||||
@ -84,3 +88,5 @@ class MDAdaptiveWidget(SpecificBackgroundColorBehavior):
|
||||
else:
|
||||
if not isinstance(self, (FloatLayout, Screen)):
|
||||
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 kivymd.theming import ThemableBehavior
|
||||
from kivymd.uix import MDAdaptiveWidget
|
||||
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
|
||||
:class:`~kivy.uix.anchorlayout.AnchorLayout` class documentation.
|
||||
|
@ -201,7 +201,6 @@ from kivy.properties import (
|
||||
from kivy.uix.boxlayout import BoxLayout
|
||||
|
||||
from kivymd import uix_path
|
||||
from kivymd.theming import ThemableBehavior
|
||||
from kivymd.uix.boxlayout import MDBoxLayout
|
||||
from kivymd.uix.card import MDCard
|
||||
from kivymd.uix.floatlayout import MDFloatLayout
|
||||
@ -214,8 +213,11 @@ with open(
|
||||
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:
|
||||
:attr:`on_open`
|
||||
When the front layer drops.
|
||||
@ -277,7 +279,7 @@ class MDBackdrop(MDFloatLayout, ThemableBehavior):
|
||||
|
||||
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
|
||||
:align: center
|
||||
@ -288,7 +290,7 @@ class MDBackdrop(MDFloatLayout, ThemableBehavior):
|
||||
|
||||
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
|
||||
:align: center
|
||||
@ -512,15 +514,30 @@ class MDBackdrop(MDFloatLayout, ThemableBehavior):
|
||||
|
||||
|
||||
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):
|
||||
"""Container for front content."""
|
||||
"""
|
||||
Container for front content.
|
||||
|
||||
For more information, see in the
|
||||
:class:`~kivymd.uix.boxlayout.MDBoxLayout` classes documentation.
|
||||
"""
|
||||
|
||||
|
||||
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):
|
||||
|
@ -177,6 +177,13 @@ with open(
|
||||
|
||||
|
||||
class MDBanner(MDCard):
|
||||
"""
|
||||
Banner class.
|
||||
|
||||
For more information, see in the :class:`~kivymd.uix.card.MDCard`
|
||||
class documentation.
|
||||
"""
|
||||
|
||||
vertical_pad = NumericProperty(dp(68))
|
||||
"""
|
||||
Indent the banner at the top of the screen.
|
||||
|
@ -20,6 +20,11 @@ from .elevation import (
|
||||
RectangularElevationBehavior,
|
||||
RoundedRectangularElevationBehavior,
|
||||
)
|
||||
from .motion_behavior import (
|
||||
MotionDialogBehavior,
|
||||
MotionShackBehavior,
|
||||
MotionDropDownMenuBehavior,
|
||||
)
|
||||
from .magic_behavior import MagicBehavior
|
||||
from .ripple_behavior import CircularRippleBehavior, RectangularRippleBehavior
|
||||
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.
|
||||
"""
|
||||
|
||||
__all__ = ("BackgroundColorBehavior", "SpecificBackgroundColorBehavior")
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import List, Union
|
||||
__all__ = ("BackgroundColorBehavior", "SpecificBackgroundColorBehavior")
|
||||
|
||||
from kivy.animation import Animation
|
||||
from kivy.lang import Builder
|
||||
@ -49,6 +49,8 @@ Builder.load_string(
|
||||
source: root.background
|
||||
Color:
|
||||
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:
|
||||
width: root.line_width
|
||||
rounded_rectangle:
|
||||
@ -58,7 +60,6 @@ Builder.load_string(
|
||||
self.width, \
|
||||
self.height, \
|
||||
*self.radius, \
|
||||
100, \
|
||||
]
|
||||
PopMatrix
|
||||
""",
|
||||
@ -90,6 +91,8 @@ class BackgroundColorBehavior:
|
||||
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])
|
||||
"""
|
||||
The background color of the widget (:class:`~kivy.uix.widget.Widget`)
|
||||
@ -154,12 +157,34 @@ class BackgroundColorBehavior:
|
||||
_background_y = NumericProperty(0)
|
||||
_background_origin = ReferenceListProperty(_background_x, _background_y)
|
||||
_md_bg_color = ColorProperty([0, 0, 0, 0])
|
||||
_origin_line_color = ColorProperty(None)
|
||||
_origin_md_bg_color = ColorProperty(None)
|
||||
|
||||
def __init__(self, **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 (
|
||||
hasattr(self, "theme_cls")
|
||||
and self.theme_cls.theme_style_switch_animation
|
||||
@ -172,9 +197,12 @@ class BackgroundColorBehavior:
|
||||
else:
|
||||
self._md_bg_color = color
|
||||
|
||||
def update_background_origin(
|
||||
self, instance_md_widget, pos: List[float]
|
||||
) -> None:
|
||||
if not self.disabled:
|
||||
self._origin_md_bg_color = color
|
||||
|
||||
def update_background_origin(self, instance_md_widget, pos: list) -> None:
|
||||
"""Called when the values of :attr:`pos` change."""
|
||||
|
||||
if self.background_origin:
|
||||
self._background_origin = self.background_origin
|
||||
else:
|
||||
|
@ -41,8 +41,9 @@ For example, let's create a button with a rectangular elevation effect:
|
||||
# With elevation effect
|
||||
RectangularElevationButton:
|
||||
pos_hint: {"center_x": .5, "center_y": .6}
|
||||
elevation: 4.5
|
||||
shadow_offset: 0, 6
|
||||
elevation: 4
|
||||
shadow_offset: 0, -6
|
||||
shadow_softness: 4
|
||||
|
||||
# Without elevation effect
|
||||
RectangularElevationButton:
|
||||
@ -102,8 +103,9 @@ For example, let's create a button with a rectangular elevation effect:
|
||||
MDScreen(
|
||||
RectangularElevationButton(
|
||||
pos_hint={"center_x": .5, "center_y": .6},
|
||||
elevation=4.5,
|
||||
shadow_offset=(0, 6),
|
||||
elevation=4,
|
||||
shadow_softness=4,
|
||||
shadow_offset=(0, -6),
|
||||
),
|
||||
RectangularElevationButton(
|
||||
pos_hint={"center_x": .5, "center_y": .4},
|
||||
@ -164,6 +166,7 @@ Similarly, create a circular button:
|
||||
CircularElevationButton:
|
||||
pos_hint: {"center_x": .5, "center_y": .6}
|
||||
elevation: 4
|
||||
shadow_softness: 4
|
||||
'''
|
||||
|
||||
|
||||
@ -231,6 +234,7 @@ Similarly, create a circular button:
|
||||
CircularElevationButton(
|
||||
pos_hint={"center_x": .5, "center_y": .5},
|
||||
elevation=4,
|
||||
shadow_softness=4,
|
||||
)
|
||||
)
|
||||
)
|
||||
@ -266,7 +270,7 @@ Animating the elevation
|
||||
size_hint: None, None
|
||||
size: 100, 100
|
||||
md_bg_color: 0, 0, 1, 1
|
||||
elevation: 4
|
||||
elevation: 2
|
||||
radius: 18
|
||||
'''
|
||||
|
||||
@ -336,7 +340,7 @@ Animating the elevation
|
||||
size_hint=(None, None),
|
||||
size=(100, 100),
|
||||
md_bg_color="blue",
|
||||
elevation=4,
|
||||
elevation=2,
|
||||
radius=18,
|
||||
)
|
||||
)
|
||||
@ -360,32 +364,62 @@ __all__ = (
|
||||
"FakeCircularElevationBehavior",
|
||||
)
|
||||
|
||||
import os
|
||||
|
||||
from kivy import Logger
|
||||
from kivy.clock import Clock
|
||||
from kivy.core.window import Window
|
||||
from kivy.graphics import RenderContext, RoundedRectangle
|
||||
from kivy.lang import Builder
|
||||
from kivy.properties import (
|
||||
AliasProperty,
|
||||
BooleanProperty,
|
||||
BoundedNumericProperty,
|
||||
ColorProperty,
|
||||
ListProperty,
|
||||
NumericProperty,
|
||||
ObjectProperty,
|
||||
VariableListProperty,
|
||||
)
|
||||
from kivy.uix.widget import Widget
|
||||
|
||||
from kivymd import glsl_path
|
||||
from kivymd.app import MDApp
|
||||
Builder.load_string(
|
||||
"""
|
||||
<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):
|
||||
"""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)
|
||||
"""
|
||||
@ -418,9 +452,9 @@ class CommonElevationBehavior(Widget):
|
||||
radius: 12, 46, 12, 46
|
||||
size_hint: .5, .3
|
||||
pos_hint: {"center_x": .5, "center_y": .5}
|
||||
elevation: 4
|
||||
shadow_softness: 8
|
||||
shadow_offset: (-2, 2)
|
||||
elevation: 2
|
||||
shadow_softness: 4
|
||||
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
|
||||
: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`
|
||||
and defaults to `[0, 0, 0, 0]`.
|
||||
"""
|
||||
|
||||
shadow_softness = NumericProperty(12)
|
||||
shadow_softness = NumericProperty(0.0)
|
||||
"""
|
||||
Softness of the shadow.
|
||||
|
||||
@ -482,7 +506,9 @@ class CommonElevationBehavior(Widget):
|
||||
|
||||
|
||||
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):
|
||||
@ -499,7 +525,19 @@ class CommonElevationBehavior(Widget):
|
||||
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.
|
||||
|
||||
@ -523,14 +561,16 @@ class CommonElevationBehavior(Widget):
|
||||
RectangularElevationButton:
|
||||
pos_hint: {"center_x": .5, "center_y": .5}
|
||||
elevation: 6
|
||||
shadow_radius: 18
|
||||
shadow_softness: 24
|
||||
shadow_offset: 12, 12
|
||||
shadow_radius: 6
|
||||
shadow_softness: 12
|
||||
shadow_offset: -12, -12
|
||||
'''
|
||||
|
||||
|
||||
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):
|
||||
@ -546,7 +586,7 @@ class CommonElevationBehavior(Widget):
|
||||
.. code-block:: kv
|
||||
|
||||
RectangularElevationButton:
|
||||
shadow_offset: -12, 12
|
||||
shadow_offset: 12, -12
|
||||
|
||||
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/shadow-offset-2.png
|
||||
:align: center
|
||||
@ -554,7 +594,7 @@ class CommonElevationBehavior(Widget):
|
||||
.. code-block:: kv
|
||||
|
||||
RectangularElevationButton:
|
||||
shadow_offset: -12, -12
|
||||
shadow_offset: 12, 12
|
||||
|
||||
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/shadow-offset-3.png
|
||||
:align: center
|
||||
@ -562,13 +602,13 @@ class CommonElevationBehavior(Widget):
|
||||
.. code-block:: kv
|
||||
|
||||
RectangularElevationButton:
|
||||
shadow_offset: 12, -12
|
||||
shadow_offset: -12, 12
|
||||
|
||||
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/shadow-offset-4.png
|
||||
:align: center
|
||||
|
||||
: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])
|
||||
@ -586,252 +626,75 @@ class CommonElevationBehavior(Widget):
|
||||
:align: center
|
||||
|
||||
: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
|
||||
_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(*args):
|
||||
if hasattr(self, "context"):
|
||||
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()
|
||||
self._elevation = value
|
||||
|
||||
|
||||
class RectangularElevationBehavior(CommonElevationBehavior):
|
||||
|
@ -15,8 +15,9 @@ Usage
|
||||
from kivy.lang import Builder
|
||||
|
||||
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.behaviors.focus_behavior import FocusBehavior
|
||||
|
||||
KV = '''
|
||||
MDScreen:
|
||||
@ -72,6 +73,18 @@ from kivymd.uix.behaviors import HoverBehavior
|
||||
|
||||
|
||||
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)
|
||||
"""
|
||||
|
@ -11,13 +11,13 @@ In `KV file`:
|
||||
|
||||
.. code-block:: kv
|
||||
|
||||
<HoverItem@MDBoxLayout+ThemableBehavior+HoverBehavior>
|
||||
<HoverItem@MDBoxLayout+HoverBehavior>
|
||||
|
||||
In `python file`:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
class HoverItem(MDBoxLayout, ThemableBehavior, HoverBehavior):
|
||||
class HoverItem(MDBoxLayout, HoverBehavior):
|
||||
'''Custom item implementing hover behavior.'''
|
||||
|
||||
After creating a class, you must define two methods for it:
|
||||
@ -38,7 +38,6 @@ the widget.
|
||||
from kivymd.app import MDApp
|
||||
from kivymd.uix.behaviors import HoverBehavior
|
||||
from kivymd.uix.boxlayout import MDBoxLayout
|
||||
from kivymd.theming import ThemableBehavior
|
||||
|
||||
KV = '''
|
||||
Screen
|
||||
@ -51,7 +50,7 @@ the widget.
|
||||
'''
|
||||
|
||||
|
||||
class HoverItem(MDBoxLayout, ThemableBehavior, HoverBehavior):
|
||||
class HoverItem(MDBoxLayout, HoverBehavior):
|
||||
'''Custom item implementing hover behavior.'''
|
||||
|
||||
def on_enter(self, *args):
|
||||
|
@ -118,7 +118,6 @@ Builder.load_string(
|
||||
|
||||
|
||||
class MagicBehavior:
|
||||
|
||||
magic_speed = NumericProperty(1)
|
||||
"""
|
||||
Animation playback speed.
|
||||
|
287
sbapp/kivymd/uix/behaviors/motion_behavior.py
Normal file
287
sbapp/kivymd/uix/behaviors/motion_behavior.py
Normal file
@ -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
|
14
sbapp/kivymd/uix/behaviors/ripple_behavior.py
Executable file → Normal file
14
sbapp/kivymd/uix/behaviors/ripple_behavior.py
Executable file → Normal file
@ -413,7 +413,12 @@ class 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)
|
||||
"""
|
||||
@ -472,7 +477,12 @@ class RectangularRippleBehavior(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)
|
||||
"""
|
||||
|
@ -91,6 +91,10 @@ KivyMD
|
||||
|
||||
|
||||
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",)
|
||||
|
@ -105,12 +105,16 @@ KivyMD
|
||||
|
||||
|
||||
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",)
|
||||
|
||||
from kivy.lang import Builder
|
||||
from kivy.properties import NumericProperty
|
||||
from kivy.properties import ListProperty, NumericProperty
|
||||
|
||||
Builder.load_string(
|
||||
"""
|
||||
@ -120,8 +124,11 @@ Builder.load_string(
|
||||
Scale:
|
||||
x: self.scale_value_x
|
||||
y: self.scale_value_y
|
||||
z: self.scale_value_x
|
||||
origin: self.center
|
||||
z: self.scale_value_z
|
||||
origin:
|
||||
self.center \
|
||||
if not self.scale_value_center else \
|
||||
self.scale_value_center
|
||||
canvas.after:
|
||||
PopMatrix
|
||||
"""
|
||||
@ -154,3 +161,15 @@ class ScaleBehavior:
|
||||
: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 `[]`.
|
||||
"""
|
||||
|
@ -22,7 +22,7 @@ Usage
|
||||
from kivymd.uix.button import MDRaisedButton
|
||||
|
||||
KV = '''
|
||||
Screen:
|
||||
MDScreen:
|
||||
|
||||
MyButton:
|
||||
text: "PRESS ME"
|
||||
@ -74,9 +74,10 @@ class TouchBehavior:
|
||||
|
||||
def create_clock(self, widget, touch, *args):
|
||||
if self.collide_point(touch.x, touch.y):
|
||||
callback = partial(self.on_long_touch, touch)
|
||||
Clock.schedule_once(callback, self.duration_long_touch)
|
||||
touch.ud["event"] = callback
|
||||
if "event" not in touch.ud:
|
||||
callback = partial(self.on_long_touch, touch)
|
||||
Clock.schedule_once(callback, self.duration_long_touch)
|
||||
touch.ud["event"] = callback
|
||||
|
||||
if touch.is_double_tap:
|
||||
self.on_double_tap(touch, *args)
|
||||
@ -85,10 +86,9 @@ class TouchBehavior:
|
||||
|
||||
def delete_clock(self, widget, touch, *args):
|
||||
if self.collide_point(touch.x, touch.y):
|
||||
try:
|
||||
if "event" in touch.ud:
|
||||
Clock.unschedule(touch.ud["event"])
|
||||
except KeyError:
|
||||
pass
|
||||
del touch.ud["event"]
|
||||
|
||||
def on_long_touch(self, touch, *args):
|
||||
"""Called when the widget is pressed for a long time."""
|
||||
|
40
sbapp/kivymd/uix/bottomnavigation/bottomnavigation.py
Executable file → Normal file
40
sbapp/kivymd/uix/bottomnavigation/bottomnavigation.py
Executable file → Normal file
@ -274,12 +274,19 @@ with open(
|
||||
Builder.load_string(kv_file.read())
|
||||
|
||||
|
||||
class MDBottomNavigationHeader(
|
||||
ThemableBehavior, ButtonBehavior, MDAnchorLayout
|
||||
):
|
||||
class MDBottomNavigationHeader(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 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`
|
||||
and defaults to `[1, 1, 1, 0]`.
|
||||
@ -307,7 +314,8 @@ class MDBottomNavigationHeader(
|
||||
|
||||
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`
|
||||
and defaults to `[1, 1, 1, 1]`.
|
||||
@ -315,7 +323,7 @@ class MDBottomNavigationHeader(
|
||||
|
||||
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`
|
||||
and defaults to `[1, 1, 1, 1]`.
|
||||
@ -323,7 +331,8 @@ class MDBottomNavigationHeader(
|
||||
|
||||
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
|
||||
|
||||
@ -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
|
||||
that goes in the tab header.
|
||||
|
||||
For more information, see in the
|
||||
:class:`~kivymd.uix.screen.MDScreen` class documentation.
|
||||
"""
|
||||
|
||||
__events__ = (
|
||||
@ -524,6 +536,10 @@ class TabbedPanelBase(
|
||||
A class that contains all variables a :class:`~kivy.properties.TabPannel`
|
||||
must have. It is here so I (zingballyhoo) don't get mad about
|
||||
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)
|
||||
@ -555,6 +571,10 @@ class MDBottomNavigation(DeclarativeBehavior, TabbedPanelBase):
|
||||
A bottom navigation that is implemented by delegating all items to a
|
||||
:class:`~kivy.uix.screenmanager.ScreenManager`.
|
||||
|
||||
For more information, see in the
|
||||
:class:`~kivymd.uix.behaviors.DeclarativeBehavior` and
|
||||
:class:`~TabbedPanelBase` classes documentation.
|
||||
|
||||
:Events:
|
||||
:attr:`on_switch_tabs`
|
||||
Called when switching tabs. Returns the object of the tab to be
|
||||
@ -856,7 +876,5 @@ class MDBottomNavigation(DeclarativeBehavior, TabbedPanelBase):
|
||||
return bottom_navigation_item
|
||||
|
||||
|
||||
class MDBottomNavigationBar(
|
||||
ThemableBehavior, CommonElevationBehavior, MDFloatLayout
|
||||
):
|
||||
class MDBottomNavigationBar(CommonElevationBehavior, MDFloatLayout):
|
||||
pass
|
||||
|
@ -1,7 +1,10 @@
|
||||
# NOQA F401
|
||||
from .bottomsheet import (
|
||||
GridBottomSheetItem,
|
||||
MDBottomSheet,
|
||||
MDBottomSheetContent,
|
||||
MDBottomSheetDragHandle,
|
||||
MDBottomSheetDragHandleButton,
|
||||
MDBottomSheetDragHandleTitle,
|
||||
MDCustomBottomSheet,
|
||||
MDGridBottomSheet,
|
||||
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:
|
||||
id: box_sheet_list
|
||||
cols: 1
|
||||
adaptive_height: True
|
||||
padding: 0, 0, 0, "96dp"
|
||||
BottomSheetDragHandle:
|
||||
md_bg_color:
|
||||
app.theme_cls.disabled_hint_text_color \
|
||||
if not root.drag_handle_color else \
|
||||
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>
|
||||
md_bg_color: root.value_transparent
|
||||
_upper_padding: _upper_padding
|
||||
_gl_content: _gl_content
|
||||
_position_content: Window.height
|
||||
orientation: "vertical"
|
||||
md_bg_color: root.bg_color if root.bg_color else app.theme_cls.bg_darkest
|
||||
radius: 16, 16, 0, 0
|
||||
padding: 0, "8dp", 0, 0
|
||||
|
||||
MDBoxLayout:
|
||||
orientation: "vertical"
|
||||
padding: 0, 1, 0, 0
|
||||
id: drag_handle_container
|
||||
size_hint_y: None
|
||||
height: self.minimum_height
|
||||
|
||||
BsPadding:
|
||||
id: _upper_padding
|
||||
size_hint_y: None
|
||||
height: root.height - min(root.width * 9 / 16, root._gl_content.height)
|
||||
on_release: root.dismiss()
|
||||
|
||||
BottomSheetContent:
|
||||
id: _gl_content
|
||||
size_hint_y: None
|
||||
cols: 1
|
||||
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"
|
||||
MDBoxLayout:
|
||||
id: container
|
||||
size_hint_y: None
|
||||
height: self.minimum_height
|
1412
sbapp/kivymd/uix/bottomsheet/bottomsheet.py
Executable file → Normal file
1412
sbapp/kivymd/uix/bottomsheet/bottomsheet.py
Executable file → Normal file
File diff suppressed because it is too large
Load Diff
@ -87,11 +87,14 @@ __all__ = ("MDBoxLayout",)
|
||||
|
||||
from kivy.uix.boxlayout import BoxLayout
|
||||
|
||||
from kivymd.theming import ThemableBehavior
|
||||
from kivymd.uix import MDAdaptiveWidget
|
||||
from kivymd.uix.behaviors import DeclarativeBehavior
|
||||
|
||||
|
||||
class MDBoxLayout(DeclarativeBehavior, BoxLayout, MDAdaptiveWidget):
|
||||
class MDBoxLayout(
|
||||
DeclarativeBehavior, ThemableBehavior, BoxLayout, MDAdaptiveWidget
|
||||
):
|
||||
"""
|
||||
Box layout class.
|
||||
|
||||
|
@ -2,6 +2,7 @@
|
||||
canvas:
|
||||
Clear
|
||||
Color:
|
||||
group: "bg-color"
|
||||
rgba:
|
||||
self._md_bg_color \
|
||||
if not self.disabled else \
|
||||
@ -12,6 +13,7 @@
|
||||
source: self.source if hasattr(self, "source") else ""
|
||||
radius: [root._radius, ]
|
||||
Color:
|
||||
group: "outline-color"
|
||||
rgba:
|
||||
root._line_color \
|
||||
if not root.disabled else \
|
||||
@ -92,9 +94,11 @@
|
||||
root.theme_cls.disabled_hint_text_color \
|
||||
if not root.disabled_color else \
|
||||
root.disabled_color
|
||||
|
||||
on_icon:
|
||||
if self.icon not in md_icons.keys(): self.size_hint = (1, 1)
|
||||
# Fix https://github.com/kivymd/KivyMD/issues/1448
|
||||
# TODO: Perhaps this change may affect other widgets.
|
||||
# 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
|
||||
|
||||
|
||||
|
@ -679,6 +679,15 @@ from kivy.weakproxy import WeakProxy
|
||||
from kivymd import uix_path
|
||||
from kivymd.color_definitions import text_colors
|
||||
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.uix.behaviors import (
|
||||
CommonElevationBehavior,
|
||||
@ -704,62 +713,6 @@ theme_text_color_options = (
|
||||
"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(
|
||||
DeclarativeBehavior,
|
||||
@ -772,7 +725,12 @@ class BaseButton(
|
||||
Base class for all buttons.
|
||||
|
||||
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)])
|
||||
@ -1208,7 +1166,7 @@ class ButtonElevationBehaviour(CommonElevationBehavior):
|
||||
|
||||
_elevation_raised = NumericProperty()
|
||||
_anim_raised = ObjectProperty(None, allownone=True)
|
||||
_default_elevation = 3
|
||||
_default_elevation = 2
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
super().__init__(**kwargs)
|
||||
@ -1220,8 +1178,9 @@ class ButtonElevationBehaviour(CommonElevationBehavior):
|
||||
self.on_disabled(self, self.disabled)
|
||||
|
||||
def create_anim_raised(self, *args) -> None:
|
||||
self._elevation_raised = self.elevation + 1.2
|
||||
self._anim_raised = Animation(elevation=self.elevation + 1, d=0.15)
|
||||
if self.elevation:
|
||||
self._elevation_raised = self.elevation
|
||||
self._anim_raised = Animation(elevation=self.elevation + 1, d=0.15)
|
||||
|
||||
def on_touch_down(self, touch):
|
||||
if not self.disabled:
|
||||
@ -1231,21 +1190,21 @@ class ButtonElevationBehaviour(CommonElevationBehavior):
|
||||
return False
|
||||
if self in touch.ud:
|
||||
return False
|
||||
if self._anim_raised:
|
||||
if self._anim_raised and self.elevation:
|
||||
self._anim_raised.start(self)
|
||||
return super().on_touch_down(touch)
|
||||
|
||||
def on_touch_up(self, touch):
|
||||
if not self.disabled:
|
||||
if touch.grab_current is not self:
|
||||
if self in touch.ud:
|
||||
self.stop_elevation_anim()
|
||||
return super().on_touch_up(touch)
|
||||
self.stop_elevation_anim()
|
||||
return super().on_touch_up(touch)
|
||||
|
||||
def stop_elevation_anim(self):
|
||||
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:
|
||||
@ -1313,6 +1272,10 @@ class MDFlatButton(BaseButton, ButtonContentsText):
|
||||
"""
|
||||
A flat rectangular button with (by default) no border or background.
|
||||
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)])
|
||||
@ -1334,6 +1297,12 @@ class MDRaisedButton(BaseButton, ButtonElevationBehaviour, ButtonContentsText):
|
||||
"""
|
||||
A flat button with (by default) a primary color fill and matching
|
||||
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.
|
||||
@ -1345,15 +1314,19 @@ class MDRaisedButton(BaseButton, ButtonElevationBehaviour, ButtonContentsText):
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.shadow_softness = 8
|
||||
self.shadow_offset = (0, 2)
|
||||
self.shadow_radius = self._radius * 2
|
||||
self.shadow_softness = RAISED_BUTTON_SOFTNESS
|
||||
self.shadow_offset = RAISED_BUTTON_OFFSET
|
||||
# self.shadow_radius = self._radius * 2
|
||||
|
||||
|
||||
class MDRectangleFlatButton(BaseButton, ButtonContentsText):
|
||||
"""
|
||||
A flat button with (by default) a primary color border and primary
|
||||
color text.
|
||||
|
||||
For more information, see in the
|
||||
:class:`~BaseButton` and :class:`~ButtonContentsText`
|
||||
classes documentation.
|
||||
"""
|
||||
|
||||
_default_line_color = None
|
||||
@ -1368,6 +1341,12 @@ class MDRectangleFlatIconButton(
|
||||
"""
|
||||
A flat button with (by default) a primary color border, 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
|
||||
@ -1382,6 +1361,10 @@ class MDRoundFlatButton(BaseButton, ButtonContentsText):
|
||||
"""
|
||||
A flat button with (by default) fully rounded corners, a primary
|
||||
color border and primary color text.
|
||||
|
||||
For more information, see in the
|
||||
:class:`~BaseButton` and :class:`~ButtonContentsText`
|
||||
classes documentation.
|
||||
"""
|
||||
|
||||
_default_line_color = None
|
||||
@ -1400,6 +1383,12 @@ class MDRoundFlatIconButton(
|
||||
"""
|
||||
A flat button with (by default) rounded corners, a primary color border,
|
||||
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
|
||||
@ -1418,6 +1407,10 @@ class MDFillRoundFlatButton(BaseButton, ButtonContentsText):
|
||||
"""
|
||||
A flat button with (by default) rounded corners, a primary color fill
|
||||
and primary color text.
|
||||
|
||||
For more information, see in the
|
||||
:class:`~BaseButton` and :class:`~ButtonContentsText`
|
||||
classes documentation.
|
||||
"""
|
||||
|
||||
_default_md_bg_color = None
|
||||
@ -1436,6 +1429,12 @@ class MDFillRoundFlatIconButton(
|
||||
"""
|
||||
A flat button with (by default) rounded corners, a primary color fill,
|
||||
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
|
||||
@ -1451,7 +1450,14 @@ class MDFillRoundFlatIconButton(
|
||||
|
||||
|
||||
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")
|
||||
"""
|
||||
@ -1489,6 +1495,12 @@ class MDFloatingActionButton(
|
||||
Implementation
|
||||
`FAB <https://m3.material.io/components/floating-action-button/overview>`_
|
||||
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"])
|
||||
@ -1530,10 +1542,13 @@ class MDFloatingActionButton(
|
||||
def set__radius(self, *args) -> None:
|
||||
if self.theme_cls.material_style == "M2":
|
||||
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
|
||||
else:
|
||||
self.shadow_softness = 8
|
||||
self.shadow_offset = (0, 2)
|
||||
self.shadow_softness = FLOATING_ACTION_BUTTON_M3_SOFTNESS
|
||||
self.shadow_offset = FLOATING_ACTION_BUTTON_M3_OFFSET
|
||||
self.elevation = FLOATING_ACTION_BUTTON_M3_ELEVATION
|
||||
self.rounded_button = False
|
||||
|
||||
if self.type == "small":
|
||||
@ -1566,6 +1581,14 @@ class MDFloatingActionButton(
|
||||
|
||||
|
||||
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)
|
||||
"""
|
||||
Button color in (r, g, b, a) or string format.
|
||||
@ -1638,6 +1661,12 @@ class MDFloatingActionButtonSpeedDial(
|
||||
For more information, see in the
|
||||
: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:
|
||||
:attr:`on_open`
|
||||
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.
|
||||
|
||||
.. code-clock:: kv
|
||||
.. code-block:: kv
|
||||
|
||||
MDFloatingActionButtonSpeedDial:
|
||||
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.
|
||||
|
||||
.. code-clock:: kv
|
||||
.. code-block:: kv
|
||||
|
||||
MDFloatingActionButtonSpeedDial:
|
||||
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.
|
||||
|
||||
.. code-clock:: kv
|
||||
.. code-block:: kv
|
||||
|
||||
MDFloatingActionButtonSpeedDial:
|
||||
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.
|
||||
|
||||
.. code-clock:: kv
|
||||
.. code-block:: kv
|
||||
|
||||
MDFloatingActionButtonSpeedDial:
|
||||
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)
|
||||
or string format.
|
||||
|
||||
.. code-clock:: kv
|
||||
.. code-block:: kv
|
||||
|
||||
MDFloatingActionButtonSpeedDial:
|
||||
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,
|
||||
text=style.capitalize(),
|
||||
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),
|
||||
style=style,
|
||||
padding="4dp",
|
||||
size_hint=(None, None),
|
||||
size=("200dp", "100dp"),
|
||||
text=style.capitalize(),
|
||||
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.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.uix import MDAdaptiveWidget
|
||||
from kivymd.uix.behaviors import (
|
||||
BackgroundColorBehavior,
|
||||
CommonElevationBehavior,
|
||||
@ -716,12 +721,17 @@ with open(
|
||||
Builder.load_string(kv_file.read())
|
||||
|
||||
|
||||
class MDSeparator(ThemableBehavior, MDBoxLayout):
|
||||
"""A separator line."""
|
||||
class MDSeparator(MDBoxLayout):
|
||||
"""
|
||||
A separator line.
|
||||
|
||||
For more information, see in the
|
||||
:class:`~kivymd.uix.boxlayout.MDBoxLayout` class documentation.
|
||||
"""
|
||||
|
||||
color = ColorProperty(None)
|
||||
"""
|
||||
Separator color.
|
||||
Separator color in (r, g, b, a) or string format.
|
||||
|
||||
:attr:`color` is a :class:`~kivy.properties.ColorProperty`
|
||||
and defaults to `None`.
|
||||
@ -743,6 +753,7 @@ class MDSeparator(ThemableBehavior, MDBoxLayout):
|
||||
|
||||
class MDCard(
|
||||
DeclarativeBehavior,
|
||||
MDAdaptiveWidget,
|
||||
ThemableBehavior,
|
||||
BackgroundColorBehavior,
|
||||
RectangularRippleBehavior,
|
||||
@ -750,6 +761,21 @@ class MDCard(
|
||||
FocusBehavior,
|
||||
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)
|
||||
"""
|
||||
Using focus when hovering over a card.
|
||||
@ -824,9 +850,9 @@ class MDCard(
|
||||
def set_elevation(self) -> None:
|
||||
if self.theme_cls.material_style == "M3":
|
||||
if self.style == "outlined" or self.style == "filled":
|
||||
self.elevation = 0
|
||||
self.elevation = CARD_STYLE_OUTLINED_FILLED_M3_ELEVATION
|
||||
elif self.style == "elevated":
|
||||
self.elevation = 2
|
||||
self.elevation = CARD_STYLE_ELEVATED_M3_ELEVATION
|
||||
|
||||
def set_radius(self) -> None:
|
||||
if (
|
||||
@ -848,6 +874,11 @@ class MDCard(
|
||||
|
||||
class MDCardSwipe(MDRelativeLayout):
|
||||
"""
|
||||
Card swipe class.
|
||||
|
||||
For more information, see in the
|
||||
:class:`~kivymd.uix.relativelayout.MDRelativeLayout` class documentation.
|
||||
|
||||
:Events:
|
||||
:attr:`on_swipe_complete`
|
||||
Called when a swipe of card is completed.
|
||||
@ -1065,7 +1096,11 @@ class MDCardSwipe(MDRelativeLayout):
|
||||
|
||||
|
||||
class MDCardSwipeFrontBox(MDCard):
|
||||
pass
|
||||
"""
|
||||
Card swipe front box.
|
||||
|
||||
For more information, see in the :class:`~MDCard` class documentation.
|
||||
"""
|
||||
|
||||
|
||||
class MDCardSwipeLayerBox(MDBoxLayout):
|
||||
|
@ -57,10 +57,11 @@ MDCarousel
|
||||
from kivy.animation import Animation
|
||||
from kivy.uix.carousel import Carousel
|
||||
|
||||
from kivymd.theming import ThemableBehavior
|
||||
from kivymd.uix.behaviors import DeclarativeBehavior
|
||||
|
||||
|
||||
class MDCarousel(DeclarativeBehavior, Carousel):
|
||||
class MDCarousel(DeclarativeBehavior, ThemableBehavior, 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>
|
||||
size_hint_y: None
|
||||
height: "32dp"
|
||||
spacing: "8dp"
|
||||
adaptive_width: True
|
||||
radius: 16 if self.radius == [0, 0, 0, 0] else self.radius
|
||||
padding:
|
||||
"12dp" if not self.icon_left else "4dp", \
|
||||
0, \
|
||||
"12dp" if not self.icon_right else "8dp", \
|
||||
0
|
||||
radius:
|
||||
16 \
|
||||
if self.radius == [0, 0, 0, 0] else \
|
||||
(max(self.radius) if max(self.radius) < self.height / 2 else 16)
|
||||
md_bg_color:
|
||||
( \
|
||||
( \
|
||||
app.theme_cls.bg_darkest \
|
||||
if app.theme_cls.theme_style == "Light" else \
|
||||
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
|
||||
) \
|
||||
if not self.disabled else app.theme_cls.disabled_primary_color
|
||||
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 \
|
||||
)
|
||||
|
||||
canvas.before:
|
||||
Color:
|
||||
rgba:
|
||||
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 \
|
||||
)
|
||||
LeadingIconContainer:
|
||||
id: leading_icon_container
|
||||
adaptive_width: True
|
||||
|
||||
MDRelativeLayout:
|
||||
id: relative_box
|
||||
size_hint: None, None
|
||||
size: ("24dp", "24dp") if root.icon_left else (0, 0)
|
||||
pos_hint: {"center_y": .5}
|
||||
radius: [int(self.height / 2),]
|
||||
LabelTextContainer:
|
||||
id: label_container
|
||||
adaptive_width: True
|
||||
|
||||
MDIcon:
|
||||
id: icon_left
|
||||
icon: root.icon_left
|
||||
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:
|
||||
id: icon_left_box
|
||||
size_hint: None, None
|
||||
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
|
||||
TrailingIconContainer:
|
||||
id: trailing_icon_container
|
||||
adaptive_width: True
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -53,7 +53,6 @@ from kivymd.uix.floatlayout import MDFloatLayout
|
||||
|
||||
|
||||
class MDCircularLayout(MDFloatLayout):
|
||||
|
||||
degree_spacing = NumericProperty(30)
|
||||
"""
|
||||
The space between children in degree.
|
||||
|
@ -231,4 +231,11 @@
|
||||
id: container
|
||||
orientation: "vertical"
|
||||
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"
|
||||
md_bg_color: app.theme_cls.bg_normal
|
||||
|
@ -37,6 +37,7 @@ from kivy.lang import Builder
|
||||
from kivy.metrics import dp
|
||||
from kivy.properties import (
|
||||
BooleanProperty,
|
||||
BoundedNumericProperty,
|
||||
ColorProperty,
|
||||
DictProperty,
|
||||
ListProperty,
|
||||
@ -44,6 +45,7 @@ from kivy.properties import (
|
||||
ObjectProperty,
|
||||
OptionProperty,
|
||||
StringProperty,
|
||||
VariableListProperty,
|
||||
)
|
||||
from kivy.uix.anchorlayout import AnchorLayout
|
||||
from kivy.uix.behaviors import ButtonBehavior, FocusBehavior
|
||||
@ -56,6 +58,11 @@ from kivy.uix.scrollview import ScrollView
|
||||
|
||||
from kivymd import uix_path
|
||||
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.uix.behaviors import HoverBehavior
|
||||
from kivymd.uix.boxlayout import MDBoxLayout
|
||||
@ -758,7 +765,7 @@ class TableData(RecycleView):
|
||||
# instance_pagination.ids.button_forward.disabled = True
|
||||
|
||||
|
||||
class TablePagination(ThemableBehavior, MDBoxLayout):
|
||||
class TablePagination(MDBoxLayout):
|
||||
"""Pagination Container."""
|
||||
|
||||
table_data = ObjectProperty()
|
||||
@ -772,8 +779,11 @@ class TablePagination(ThemableBehavior, MDBoxLayout):
|
||||
|
||||
class MDDataTable(ThemableBehavior, AnchorLayout):
|
||||
"""
|
||||
See :class:`~kivy.uix.anchorlayout.AnchorLayout` class documentation for
|
||||
more information.
|
||||
Datatable class.
|
||||
|
||||
For more information, see in the
|
||||
:class:`~kivymd.theming.ThemableBehavior` and
|
||||
:class:`~kivy.uix.anchorlayout.AnchorLayout` classes documentation.
|
||||
|
||||
:Events:
|
||||
:attr:`on_row_press`
|
||||
@ -1297,14 +1307,70 @@ class MDDataTable(ThemableBehavior, AnchorLayout):
|
||||
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`
|
||||
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)
|
||||
"""
|
||||
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
|
||||
: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
|
||||
"""
|
||||
|
||||
@ -1760,7 +1897,6 @@ class MDDataTable(ThemableBehavior, AnchorLayout):
|
||||
|
||||
|
||||
class CellRow(
|
||||
ThemableBehavior,
|
||||
RecycleDataViewBehavior,
|
||||
HoverBehavior,
|
||||
ButtonBehavior,
|
||||
|
@ -32,7 +32,7 @@
|
||||
orientation: "vertical"
|
||||
size_hint_y: None
|
||||
height: self.minimum_height
|
||||
padding: "24dp", "24dp", "16dp", "8dp"
|
||||
padding: "24dp", "24dp", "8dp", "8dp"
|
||||
radius: root.radius
|
||||
md_bg_color:
|
||||
root.theme_cls.bg_dark \
|
||||
|
@ -89,7 +89,7 @@ from kivy.uix.modalview import ModalView
|
||||
from kivymd import uix_path
|
||||
from kivymd.material_resources import DEVICE_TYPE
|
||||
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.card import MDSeparator
|
||||
from kivymd.uix.list import BaseListItem
|
||||
@ -100,7 +100,9 @@ with open(
|
||||
Builder.load_string(kv_file.read())
|
||||
|
||||
|
||||
class BaseDialog(ThemableBehavior, ModalView, CommonElevationBehavior):
|
||||
class BaseDialog(
|
||||
ThemableBehavior, MotionDialogBehavior, ModalView, CommonElevationBehavior
|
||||
):
|
||||
elevation = NumericProperty(3)
|
||||
"""
|
||||
See :attr:`kivymd.uix.behaviors.elevation.CommonElevationBehavior.elevation`
|
||||
@ -159,6 +161,16 @@ class BaseDialog(ThemableBehavior, ModalView, CommonElevationBehavior):
|
||||
|
||||
|
||||
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 dialog.
|
||||
@ -286,22 +298,22 @@ class MDDialog(BaseDialog):
|
||||
class Example(MDApp):
|
||||
dialog = None
|
||||
|
||||
def build(self):
|
||||
self.theme_cls.theme_style = "Dark"
|
||||
self.theme_cls.primary_palette = "Orange"
|
||||
return Builder.load_string(KV)
|
||||
def build(self):
|
||||
self.theme_cls.theme_style = "Dark"
|
||||
self.theme_cls.primary_palette = "Orange"
|
||||
return Builder.load_string(KV)
|
||||
|
||||
def show_simple_dialog(self):
|
||||
if not self.dialog:
|
||||
self.dialog = MDDialog(
|
||||
title="Set backup account",
|
||||
type="simple",
|
||||
items=[
|
||||
Item(text="user01@gmail.com", source="kivymd/images/logo/kivymd-icon-128.png"),
|
||||
Item(text="user02@gmail.com", source="data/logo/kivy-icon-128.png"),
|
||||
],
|
||||
)
|
||||
self.dialog.open()
|
||||
def show_simple_dialog(self):
|
||||
if not self.dialog:
|
||||
self.dialog = MDDialog(
|
||||
title="Set backup account",
|
||||
type="simple",
|
||||
items=[
|
||||
Item(text="user01@gmail.com", source="kivymd/images/logo/kivymd-icon-128.png"),
|
||||
Item(text="user02@gmail.com", source="data/logo/kivy-icon-128.png"),
|
||||
],
|
||||
)
|
||||
self.dialog.open()
|
||||
|
||||
|
||||
Example().run()
|
||||
@ -422,7 +434,8 @@ class MDDialog(BaseDialog):
|
||||
|
||||
content_cls = ObjectProperty()
|
||||
"""
|
||||
Custom content class.
|
||||
Custom content class. This attribute is only available when :attr:`type` is
|
||||
set to `'custom'`.
|
||||
|
||||
.. tabs::
|
||||
|
||||
@ -637,6 +650,7 @@ class MDDialog(BaseDialog):
|
||||
def on_open(self) -> None:
|
||||
# TODO: Add scrolling text.
|
||||
self.height = self.ids.container.height
|
||||
super().on_open()
|
||||
|
||||
def get_normal_height(self) -> float:
|
||||
return (
|
||||
|
@ -68,6 +68,17 @@ class _Triangle(Widget):
|
||||
class MDDropDownItem(
|
||||
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 item.
|
||||
|
@ -185,21 +185,39 @@ class MDExpansionChevronRight(IRightBodyTouch, MDIconButton):
|
||||
|
||||
|
||||
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):
|
||||
"""Two-line panel."""
|
||||
"""
|
||||
Two-line panel.
|
||||
|
||||
For more information, see in the
|
||||
:class:`~kivymd.uix.list.TwoLineAvatarIconListItem` class documentation.
|
||||
"""
|
||||
|
||||
|
||||
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):
|
||||
"""
|
||||
Label panel.
|
||||
|
||||
For more information, see in the
|
||||
:class:`~kivymd.uix.list.TwoLineListItem` class documentation.
|
||||
|
||||
..warning:: This class is created for use in the
|
||||
:class:`~kivymd.uix.stepper.MDStepperVertical` and
|
||||
:class:`~kivymd.uix.stepper.MDStepper` classes, and has not
|
||||
@ -217,6 +235,11 @@ class MDExpansionPanelLabel(TwoLineListItem):
|
||||
|
||||
class MDExpansionPanel(RelativeLayout):
|
||||
"""
|
||||
Expansion panel class.
|
||||
|
||||
For more information, see in the
|
||||
:class:`~kivy.uix.relativelayout.RelativeLayout` classes documentation.
|
||||
|
||||
:Events:
|
||||
:attr:`on_open`
|
||||
Called when a panel is opened.
|
||||
|
@ -1,4 +1,6 @@
|
||||
#:import os os
|
||||
#:import FILE_MANAGER_TOP_APP_BAR_ELEVATION kivymd.material_resources.FILE_MANAGER_TOP_APP_BAR_ELEVATION
|
||||
|
||||
|
||||
<BodyManager>
|
||||
icon: "folder"
|
||||
@ -74,7 +76,7 @@
|
||||
title: root.current_path
|
||||
right_action_items: [["close-box", lambda x: root.exit_manager(1)]]
|
||||
left_action_items: [["chevron-left", lambda x: root.back()]]
|
||||
elevation: 3
|
||||
elevation: FILE_MANAGER_TOP_APP_BAR_ELEVATION
|
||||
md_bg_color:
|
||||
app.theme_cls.primary_color \
|
||||
if not root.background_color_toolbar else \
|
||||
|
@ -158,7 +158,6 @@ from kivy.uix.behaviors import ButtonBehavior
|
||||
from kivy.uix.modalview import ModalView
|
||||
|
||||
from kivymd import images_path, uix_path
|
||||
from kivymd.theming import ThemableBehavior
|
||||
from kivymd.uix.behaviors import CircularRippleBehavior
|
||||
from kivymd.uix.boxlayout import MDBoxLayout
|
||||
from kivymd.uix.button import MDFloatingActionButton
|
||||
@ -197,7 +196,7 @@ class ModifiedOneLineIconListItem(BaseListItem):
|
||||
self.height = dp(48)
|
||||
|
||||
|
||||
class MDFileManager(MDRelativeLayout, ThemableBehavior):
|
||||
class MDFileManager(MDRelativeLayout):
|
||||
"""
|
||||
Implements a modal dialog with a file manager.
|
||||
|
||||
@ -248,7 +247,8 @@ class MDFileManager(MDRelativeLayout, ThemableBehavior):
|
||||
|
||||
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
|
||||
|
||||
@ -268,7 +268,7 @@ class MDFileManager(MDRelativeLayout, ThemableBehavior):
|
||||
|
||||
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
|
||||
|
||||
@ -307,7 +307,8 @@ class MDFileManager(MDRelativeLayout, ThemableBehavior):
|
||||
|
||||
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
|
||||
|
||||
|
@ -137,6 +137,14 @@ from kivymd.uix.boxlayout import MDBoxLayout
|
||||
|
||||
|
||||
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()
|
||||
"""
|
||||
Filename/source of your image.
|
||||
|
@ -35,11 +35,14 @@ MDFloatLayout
|
||||
|
||||
from kivy.uix.floatlayout import FloatLayout
|
||||
|
||||
from kivymd.theming import ThemableBehavior
|
||||
from kivymd.uix import MDAdaptiveWidget
|
||||
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
|
||||
:class:`~kivy.uix.floatlayout.FloatLayout` class documentation.
|
||||
|
@ -85,11 +85,14 @@ Equivalent
|
||||
|
||||
from kivy.uix.gridlayout import GridLayout
|
||||
|
||||
from kivymd.theming import ThemableBehavior
|
||||
from kivymd.uix import MDAdaptiveWidget
|
||||
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
|
||||
:class:`~kivy.uix.gridlayout.GridLayout` class documentation.
|
||||
|
@ -13,6 +13,7 @@
|
||||
(0, 0)
|
||||
on_release: root.dispatch("on_release")
|
||||
on_press: root.dispatch("on_press")
|
||||
_no_ripple_effect: root._no_ripple_effect
|
||||
|
||||
SmartTileOverlayBox:
|
||||
id: box
|
||||
|
14
sbapp/kivymd/uix/imagelist/imagelist.py
Executable file → Normal file
14
sbapp/kivymd/uix/imagelist/imagelist.py
Executable file → Normal file
@ -71,6 +71,7 @@ __all__ = [
|
||||
|
||||
import os
|
||||
|
||||
from kivy.clock import Clock
|
||||
from kivy.lang import Builder
|
||||
from kivy.properties import (
|
||||
BooleanProperty,
|
||||
@ -82,7 +83,6 @@ from kivy.properties import (
|
||||
from kivy.uix.behaviors import ButtonBehavior
|
||||
|
||||
from kivymd import uix_path
|
||||
from kivymd.theming import ThemableBehavior
|
||||
from kivymd.uix.behaviors import RectangularRippleBehavior
|
||||
from kivymd.uix.boxlayout import MDBoxLayout
|
||||
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."""
|
||||
|
||||
|
||||
class MDSmartTile(MDRelativeLayout, ThemableBehavior):
|
||||
class MDSmartTile(MDRelativeLayout):
|
||||
"""
|
||||
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
|
||||
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))
|
||||
"""
|
||||
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
|
||||
|
||||
@ -249,6 +253,8 @@ class MDSmartTile(MDRelativeLayout, ThemableBehavior):
|
||||
and defaults to `False`.
|
||||
"""
|
||||
|
||||
_no_ripple_effect = BooleanProperty(False)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.register_event_type("on_release")
|
||||
@ -270,4 +276,4 @@ class MDSmartTile(MDRelativeLayout, ThemableBehavior):
|
||||
if isinstance(widget, MDLabel):
|
||||
widget.shorten = True
|
||||
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>
|
||||
disabled_color: self.theme_cls.disabled_hint_text_color
|
||||
# FIXME: Overriding the values of this property greatly affects application
|
||||
# performance. Especially when the application window is resized and a
|
||||
# custom font is used. Performance is especially slow when you are using
|
||||
# `PIL` as your text processing provider - os.environ ['KIVY_TEXT'] = 'pil'.
|
||||
# Priority - CRITICAL.
|
||||
text_size: self.width, None
|
||||
text_size:
|
||||
(self.width if not self.adaptive_width else None) \
|
||||
if not self.adaptive_size else None, \
|
||||
None
|
||||
|
||||
|
||||
<MDIcon>:
|
||||
@ -16,6 +14,7 @@
|
||||
Color:
|
||||
rgba: (1, 1, 1, 1) if self.source else (0, 0, 0, 0)
|
||||
Rectangle:
|
||||
group: "rectangle"
|
||||
source: self.source if self.source else None
|
||||
pos:
|
||||
self.pos \
|
||||
@ -32,6 +31,7 @@
|
||||
|
||||
# Badge icon.
|
||||
MDLabel:
|
||||
id: badge
|
||||
font_style: "Icon"
|
||||
adaptive_size: True
|
||||
opposite_icon_color: True
|
||||
@ -62,6 +62,7 @@
|
||||
if root.badge_icon else \
|
||||
(0, 0, 0, 0)
|
||||
RoundedRectangle:
|
||||
group: "badge"
|
||||
radius: [self.width / 2,]
|
||||
pos: self.pos
|
||||
size: self.size
|
||||
|
@ -18,32 +18,58 @@ Class :class:`MDLabel` inherited from the :class:`~kivy.uix.label.Label` class
|
||||
but for :class:`MDLabel` the ``text_size`` parameter is ``(self.width, None)``
|
||||
and default is positioned on the left:
|
||||
|
||||
.. code-block:: python
|
||||
.. tabs::
|
||||
|
||||
from kivy.lang import Builder
|
||||
.. tab:: Declarative KV style
|
||||
|
||||
from kivymd.app import MDApp
|
||||
.. code-block:: python
|
||||
|
||||
KV = '''
|
||||
MDScreen:
|
||||
from kivy.lang import Builder
|
||||
|
||||
MDBoxLayout:
|
||||
orientation: "vertical"
|
||||
from kivymd.app import MDApp
|
||||
|
||||
MDTopAppBar:
|
||||
title: "MDLabel"
|
||||
KV = '''
|
||||
MDScreen:
|
||||
|
||||
MDLabel:
|
||||
text: "MDLabel"
|
||||
'''
|
||||
MDLabel:
|
||||
text: "MDLabel"
|
||||
'''
|
||||
|
||||
|
||||
class Test(MDApp):
|
||||
def build(self):
|
||||
return Builder.load_string(KV)
|
||||
class Test(MDApp):
|
||||
def build(self):
|
||||
self.theme_cls.theme_style = "Dark"
|
||||
self.theme_cls.primary_palette = "Orange"
|
||||
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
|
||||
:align: center
|
||||
@ -74,20 +100,16 @@ and default is positioned on the left:
|
||||
from kivymd.uix.label import MDLabel
|
||||
|
||||
KV = '''
|
||||
MDScreen:
|
||||
|
||||
MDBoxLayout:
|
||||
id: box
|
||||
orientation: "vertical"
|
||||
|
||||
MDTopAppBar:
|
||||
title: "MDLabel"
|
||||
MDBoxLayout:
|
||||
orientation: "vertical"
|
||||
'''
|
||||
|
||||
|
||||
class Test(MDApp):
|
||||
def build(self):
|
||||
self.theme_cls.theme_style = "Dark"
|
||||
screen = Builder.load_string(KV)
|
||||
|
||||
# Names of standard color themes.
|
||||
for name_theme in [
|
||||
"Primary",
|
||||
@ -96,7 +118,7 @@ and default is positioned on the left:
|
||||
"Error",
|
||||
"ContrastParentBackground",
|
||||
]:
|
||||
screen.ids.box.add_widget(
|
||||
screen.add_widget(
|
||||
MDLabel(
|
||||
text=name_theme,
|
||||
halign="center",
|
||||
@ -121,7 +143,7 @@ in the ``text_color`` parameter:
|
||||
text: "Custom color"
|
||||
halign: "center"
|
||||
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
|
||||
:align: center
|
||||
@ -138,26 +160,20 @@ parameter:
|
||||
from kivymd.uix.label import MDLabel
|
||||
from kivymd.font_definitions import theme_font_styles
|
||||
|
||||
|
||||
KV = '''
|
||||
MDScreen:
|
||||
MDScrollView:
|
||||
|
||||
MDBoxLayout:
|
||||
orientation: "vertical"
|
||||
|
||||
MDTopAppBar:
|
||||
title: "MDLabel"
|
||||
|
||||
ScrollView:
|
||||
|
||||
MDList:
|
||||
id: box
|
||||
MDList:
|
||||
id: box
|
||||
spacing: "8dp"
|
||||
'''
|
||||
|
||||
|
||||
class Test(MDApp):
|
||||
def build(self):
|
||||
self.theme_cls.theme_style = "Dark"
|
||||
screen = Builder.load_string(KV)
|
||||
|
||||
# Names of standard font styles.
|
||||
for name_style in theme_font_styles[:-1]:
|
||||
screen.ids.box.add_widget(
|
||||
@ -165,6 +181,7 @@ parameter:
|
||||
text=f"{name_style} style",
|
||||
halign="center",
|
||||
font_style=name_style,
|
||||
adaptive_height=True,
|
||||
)
|
||||
)
|
||||
return screen
|
||||
@ -172,7 +189,273 @@ parameter:
|
||||
|
||||
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
|
||||
|
||||
.. MDIcon:
|
||||
@ -217,6 +500,8 @@ MDIcon with badge icon
|
||||
:align: center
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
__all__ = ("MDLabel", "MDIcon")
|
||||
|
||||
import os
|
||||
@ -224,6 +509,8 @@ from typing import Union
|
||||
|
||||
from kivy.animation import Animation
|
||||
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.lang import Builder
|
||||
from kivy.metrics import sp
|
||||
@ -243,7 +530,7 @@ from kivymd import uix_path
|
||||
from kivymd.theming import ThemableBehavior
|
||||
from kivymd.theming_dynamic_text import get_contrast_text_color
|
||||
from kivymd.uix import MDAdaptiveWidget
|
||||
from kivymd.uix.behaviors import DeclarativeBehavior
|
||||
from kivymd.uix.behaviors import DeclarativeBehavior, TouchBehavior
|
||||
from kivymd.uix.floatlayout import MDFloatLayout
|
||||
|
||||
__MDLabel_colors__ = {
|
||||
@ -264,7 +551,36 @@ with open(
|
||||
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")
|
||||
"""
|
||||
Label font style.
|
||||
@ -316,29 +632,84 @@ class MDLabel(DeclarativeBehavior, ThemableBehavior, Label, MDAdaptiveWidget):
|
||||
|
||||
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`
|
||||
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()
|
||||
|
||||
parent_background = ColorProperty(None)
|
||||
can_capitalize = BooleanProperty(True)
|
||||
canvas_bg = ObjectProperty()
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
super().__init__(**kwargs)
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.bind(
|
||||
font_style=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.update_font_style(None, "")
|
||||
self.on_opposite_colors(None, self.opposite_colors)
|
||||
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:
|
||||
if self.font_style not in list(self.theme_cls.font_styles.keys()):
|
||||
@ -353,7 +724,8 @@ class MDLabel(DeclarativeBehavior, ThemableBehavior, Label, MDAdaptiveWidget):
|
||||
if self.check_font_styles() is True:
|
||||
font_info = self.theme_cls.font_styles[self.font_style]
|
||||
self.font_name = font_info[0]
|
||||
self.font_size = sp(font_info[1])
|
||||
if self.font_style in list(self.theme_cls.font_styles.keys())[0:14]:
|
||||
self.font_size = sp(font_info[1])
|
||||
|
||||
if font_info[2] and self.can_capitalize:
|
||||
self._capitalizing = True
|
||||
@ -363,6 +735,64 @@ class MDLabel(DeclarativeBehavior, ThemableBehavior, Label, MDAdaptiveWidget):
|
||||
# TODO: Add letter spacing change
|
||||
# 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(
|
||||
self, instance_label, theme_text_color: str
|
||||
) -> None:
|
||||
@ -414,6 +844,7 @@ class MDLabel(DeclarativeBehavior, ThemableBehavior, Label, MDAdaptiveWidget):
|
||||
|
||||
def on_md_bg_color(self, instance_label, color: Union[list, str]) -> None:
|
||||
self.canvas.remove_group("Background_instruction")
|
||||
self.canvas.before.clear()
|
||||
with self.canvas.before:
|
||||
Color(rgba=color)
|
||||
self.canvas_bg = Rectangle(pos=self.pos, size=self.size)
|
||||
@ -445,6 +876,13 @@ class MDLabel(DeclarativeBehavior, ThemableBehavior, Label, MDAdaptiveWidget):
|
||||
|
||||
|
||||
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")
|
||||
"""
|
||||
Label icon name.
|
||||
@ -465,7 +903,7 @@ class MDIcon(MDFloatLayout, MDLabel):
|
||||
|
||||
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
|
||||
|
||||
@ -475,7 +913,7 @@ class MDIcon(MDFloatLayout, MDLabel):
|
||||
|
||||
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
|
||||
|
||||
|
@ -984,6 +984,9 @@ class MDList(MDGridLayout):
|
||||
|
||||
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.
|
||||
|
||||
For more information, see in the
|
||||
:class:`~kivymd.uix.gridlayout.MDGridLayout` classes documentation.
|
||||
"""
|
||||
|
||||
_list_vertical_padding = NumericProperty("8dp")
|
||||
@ -1002,6 +1005,13 @@ class BaseListItem(
|
||||
):
|
||||
"""
|
||||
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()
|
||||
@ -1242,7 +1252,12 @@ class IRightBodyTouch:
|
||||
|
||||
|
||||
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_bot_pad = NumericProperty("15dp")
|
||||
@ -1255,7 +1270,12 @@ class OneLineListItem(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_bot_pad = NumericProperty("15dp")
|
||||
@ -1267,7 +1287,12 @@ class TwoLineListItem(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_bot_pad = NumericProperty("15dp")
|
||||
@ -1280,6 +1305,13 @@ class ThreeLineListItem(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_top_pad = NumericProperty("20dp")
|
||||
_txt_bot_pad = NumericProperty("19dp")
|
||||
@ -1292,6 +1324,13 @@ class OneLineAvatarListItem(BaseListItem):
|
||||
|
||||
|
||||
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_bot_pad = NumericProperty("15dp")
|
||||
_height = NumericProperty()
|
||||
@ -1303,6 +1342,13 @@ class TwoLineAvatarListItem(OneLineAvatarListItem):
|
||||
|
||||
|
||||
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")
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
@ -1310,10 +1356,24 @@ class ThreeLineAvatarListItem(ThreeLineListItem):
|
||||
|
||||
|
||||
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")
|
||||
|
||||
|
||||
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_bot_pad = NumericProperty("15dp")
|
||||
_height = NumericProperty()
|
||||
@ -1325,10 +1385,24 @@ class TwoLineIconListItem(OneLineIconListItem):
|
||||
|
||||
|
||||
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")
|
||||
|
||||
|
||||
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")
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
@ -1337,6 +1411,13 @@ class OneLineRightIconListItem(OneLineListItem):
|
||||
|
||||
|
||||
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_bot_pad = NumericProperty("15dp")
|
||||
_height = NumericProperty()
|
||||
@ -1348,6 +1429,13 @@ class TwoLineRightIconListItem(OneLineRightIconListItem):
|
||||
|
||||
|
||||
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")
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
@ -1356,6 +1444,13 @@ class ThreeLineRightIconListItem(ThreeLineListItem):
|
||||
|
||||
|
||||
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")
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
@ -1364,6 +1459,13 @@ class OneLineAvatarIconListItem(OneLineAvatarListItem):
|
||||
|
||||
|
||||
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")
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
@ -1372,6 +1474,13 @@ class TwoLineAvatarIconListItem(TwoLineAvatarListItem):
|
||||
|
||||
|
||||
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")
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
@ -1388,13 +1497,31 @@ class TouchBehavior:
|
||||
class ImageLeftWidget(
|
||||
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(
|
||||
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
|
||||
"""
|
||||
|
||||
@ -1404,13 +1531,31 @@ class ImageLeftWidgetWithoutTouch(
|
||||
class ImageRightWidget(
|
||||
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(
|
||||
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
|
||||
"""
|
||||
|
||||
@ -1418,11 +1563,29 @@ class ImageRightWidgetWithoutTouch(
|
||||
|
||||
|
||||
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}
|
||||
|
||||
|
||||
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
|
||||
"""
|
||||
|
||||
@ -1431,11 +1594,29 @@ class IconRightWidgetWithoutTouch(TouchBehavior, IRightBody, 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}
|
||||
|
||||
|
||||
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
|
||||
"""
|
||||
|
||||
@ -1444,4 +1625,11 @@ class IconLeftWidgetWithoutTouch(TouchBehavior, ILeftBody, MDIconButton):
|
||||
|
||||
|
||||
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>
|
||||
size_hint: None, None
|
||||
width: root.width_mult * STANDARD_INCREMENT
|
||||
bar_width: 0
|
||||
key_viewclass: "viewclass"
|
||||
key_size: "height"
|
||||
|
||||
RecycleBoxLayout:
|
||||
padding: 0, "4dp", 0, "4dp"
|
||||
default_size: None, dp(48)
|
||||
default_size_hint: 1, None
|
||||
size_hint_y: None
|
||||
@ -28,32 +11,478 @@
|
||||
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>
|
||||
orientation: "vertical"
|
||||
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
|
||||
radius: root.radius
|
||||
size_hint: None, None
|
||||
|
||||
MenuContainer:
|
||||
id: card
|
||||
orientation: "vertical"
|
||||
elevation: root.elevation
|
||||
size_hint: None, None
|
||||
size: md_menu.size[0], md_menu.size[1] + content_header.height
|
||||
pos: md_menu.pos
|
||||
opacity: md_menu.opacity
|
||||
radius: root.radius
|
||||
md_bg_color:
|
||||
root.background_color \
|
||||
if root.background_color else root.theme_cls.bg_dark
|
||||
MDBoxLayout:
|
||||
id: content_header
|
||||
adaptive_size: True
|
||||
|
||||
MDBoxLayout:
|
||||
id: content_header
|
||||
adaptive_size: True
|
||||
|
||||
MDMenu:
|
||||
id: md_menu
|
||||
drop_cls: root
|
||||
width_mult: root.width_mult
|
||||
size_hint: None, None
|
||||
size: 0, 0
|
||||
opacity: 0
|
||||
MDMenu:
|
||||
id: md_menu
|
||||
drop_cls: root
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -575,8 +575,8 @@ class NavigationDrawerContentError(Exception):
|
||||
|
||||
class MDNavigationLayout(MDFloatLayout):
|
||||
"""
|
||||
For more information, see in the :class:`~kivymd.uix.floatlayout.MDFloatLayout`
|
||||
class documentation.
|
||||
For more information, see in the
|
||||
:class:`~kivymd.uix.floatlayout.MDFloatLayout` class documentation.
|
||||
"""
|
||||
|
||||
_scrim_color = ObjectProperty(None)
|
||||
@ -737,7 +737,7 @@ class MDNavigationDrawerDivider(MDBoxLayout):
|
||||
|
||||
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`
|
||||
and defaults to `None`.
|
||||
@ -811,7 +811,7 @@ class MDNavigationDrawerHeader(MDBoxLayout):
|
||||
|
||||
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`
|
||||
and defaults to `None`.
|
||||
@ -851,7 +851,7 @@ class MDNavigationDrawerHeader(MDBoxLayout):
|
||||
|
||||
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`
|
||||
and defaults to `None`.
|
||||
@ -893,7 +893,9 @@ class MDNavigationDrawerItem(OneLineAvatarIconListItem, FocusBehavior):
|
||||
Implements an item for the :class:`~MDNavigationDrawer` menu list.
|
||||
|
||||
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
|
||||
|
||||
@ -936,7 +938,7 @@ class MDNavigationDrawerItem(OneLineAvatarIconListItem, FocusBehavior):
|
||||
|
||||
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`
|
||||
and defaults to `None`.
|
||||
@ -944,7 +946,8 @@ class MDNavigationDrawerItem(OneLineAvatarIconListItem, FocusBehavior):
|
||||
|
||||
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`
|
||||
and defaults to `[0, 0, 0, 1]`.
|
||||
@ -960,7 +963,7 @@ class MDNavigationDrawerItem(OneLineAvatarIconListItem, FocusBehavior):
|
||||
|
||||
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`
|
||||
and defaults to `None`.
|
||||
@ -1095,9 +1098,9 @@ class MDNavigationDrawer(MDCard):
|
||||
# FIXME: Doesn't work in Kivy v2.1.0.
|
||||
scrim_color = ColorProperty([0, 0, 0, 0.5])
|
||||
"""
|
||||
Color for scrim. Alpha channel will be multiplied with
|
||||
:attr:`_scrim_alpha`. Set fourth channel to 0 if you want to disable
|
||||
scrim.
|
||||
Color for scrim in (r, g, b, a) or string format. Alpha channel will be
|
||||
multiplied with :attr:`_scrim_alpha`. Set fourth channel to 0 if you want
|
||||
to disable scrim.
|
||||
|
||||
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/navigation-drawer-scrim-color.png
|
||||
:align: center
|
||||
|
@ -8,26 +8,14 @@ Components/NavigationRail
|
||||
|
||||
`Material Design spec, Navigation rail <https://m3.material.io/components/navigation-rail/specs>`_
|
||||
|
||||
.. rubric::
|
||||
|
||||
Navigation rails provide access to primary destinations in apps when using
|
||||
tablet and desktop screens.
|
||||
.. rubric:: 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
|
||||
:align: center
|
||||
|
||||
Usage
|
||||
=====
|
||||
|
||||
.. code-block:: kv
|
||||
|
||||
MDNavigationRail:
|
||||
|
||||
MDNavigationRailItem:
|
||||
|
||||
MDNavigationRailItem:
|
||||
|
||||
MDNavigationRailItem:
|
||||
-----
|
||||
|
||||
.. tabs::
|
||||
|
||||
@ -113,6 +101,21 @@ Usage
|
||||
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/navigation-rail-usage.png
|
||||
: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
|
||||
=======
|
||||
|
||||
@ -137,9 +140,8 @@ Example
|
||||
|
||||
|
||||
<ExtendedButton>
|
||||
elevation: 3.5
|
||||
elevation: 1
|
||||
shadow_radius: 12
|
||||
shadow_softness: 4
|
||||
-height: "56dp"
|
||||
|
||||
|
||||
@ -207,9 +209,9 @@ Example
|
||||
|
||||
MDNavigationDrawer:
|
||||
id: nav_drawer
|
||||
radius: (0, 16, 16, 0)
|
||||
radius: 0, 16, 16, 0
|
||||
md_bg_color: "#fffcf4"
|
||||
elevation: 4
|
||||
elevation: 2
|
||||
width: "240dp"
|
||||
|
||||
MDNavigationDrawerMenu:
|
||||
@ -218,14 +220,18 @@ Example
|
||||
orientation: "vertical"
|
||||
adaptive_height: True
|
||||
spacing: "12dp"
|
||||
padding: "3dp", 0, 0, "12dp"
|
||||
padding: 0, 0, 0, "12dp"
|
||||
|
||||
MDIconButton:
|
||||
icon: "menu"
|
||||
|
||||
ExtendedButton:
|
||||
text: "Compose"
|
||||
icon: "pencil"
|
||||
MDBoxLayout:
|
||||
adaptive_height: True
|
||||
padding: "12dp", 0, 0, 0
|
||||
|
||||
ExtendedButton:
|
||||
text: "Compose"
|
||||
icon: "pencil"
|
||||
|
||||
DrawerClickableItem:
|
||||
text: "Python"
|
||||
@ -269,7 +275,9 @@ Example
|
||||
|
||||
def set_radius(self, *args):
|
||||
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):
|
||||
@ -357,9 +365,8 @@ Example
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.padding = "16dp"
|
||||
self.elevation = 3.5
|
||||
self.elevation = 1
|
||||
self.shadow_radius = 12
|
||||
self.shadow_softness = 4
|
||||
self.height = dp(56)
|
||||
Clock.schedule_once(self.set_spacing)
|
||||
|
||||
@ -439,9 +446,13 @@ Example
|
||||
MDIconButton(
|
||||
icon="menu",
|
||||
),
|
||||
ExtendedButton(
|
||||
text="Compose",
|
||||
icon="pencil",
|
||||
MDBoxLayout(
|
||||
ExtendedButton(
|
||||
text="Compose",
|
||||
icon="pencil",
|
||||
),
|
||||
adaptive_height=True,
|
||||
padding=["12dp", 0, 0, 0],
|
||||
),
|
||||
orientation="vertical",
|
||||
adaptive_height=True,
|
||||
@ -540,6 +551,7 @@ from typing import Union
|
||||
|
||||
from kivy.animation import Animation
|
||||
from kivy.clock import Clock
|
||||
from kivy.core.window import Window
|
||||
from kivy.lang import Builder
|
||||
from kivy.logger import Logger
|
||||
from kivy.metrics import dp
|
||||
@ -556,7 +568,6 @@ from kivy.properties import (
|
||||
from kivy.uix.behaviors import ButtonBehavior
|
||||
|
||||
from kivymd import uix_path
|
||||
from kivymd.theming import ThemableBehavior
|
||||
from kivymd.uix.behaviors import ScaleBehavior
|
||||
from kivymd.uix.boxlayout import MDBoxLayout
|
||||
from kivymd.uix.button import MDFloatingActionButton, MDIconButton
|
||||
@ -591,7 +602,12 @@ class RippleWidget(MDWidget, ScaleBehavior):
|
||||
|
||||
|
||||
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")
|
||||
"""
|
||||
@ -613,7 +629,12 @@ class MDNavigationRailFabButton(MDFloatingActionButton):
|
||||
|
||||
|
||||
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")
|
||||
"""
|
||||
@ -634,8 +655,15 @@ class MDNavigationRailMenuButton(MDIconButton):
|
||||
"""
|
||||
|
||||
|
||||
class MDNavigationRailItem(ThemableBehavior, ButtonBehavior, MDBoxLayout):
|
||||
"""Implements a menu item with an icon and text."""
|
||||
class MDNavigationRailItem(ButtonBehavior, MDBoxLayout):
|
||||
"""
|
||||
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()
|
||||
"""
|
||||
@ -814,6 +842,11 @@ class MDNavigationRailItem(ThemableBehavior, ButtonBehavior, MDBoxLayout):
|
||||
|
||||
class MDNavigationRail(MDCard):
|
||||
"""
|
||||
Navigation rail class.
|
||||
|
||||
For more information, see in the
|
||||
:class:`~kivymd.uix.card.MDCard` class documentation.
|
||||
|
||||
:Events:
|
||||
:attr:`on_item_press`
|
||||
Called on the `on_press` event of menu item -
|
||||
@ -941,7 +974,8 @@ class MDNavigationRail(MDCard):
|
||||
|
||||
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
|
||||
|
||||
@ -960,7 +994,8 @@ class MDNavigationRail(MDCard):
|
||||
|
||||
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
|
||||
|
||||
@ -979,7 +1014,8 @@ class MDNavigationRail(MDCard):
|
||||
|
||||
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
|
||||
|
||||
@ -998,7 +1034,8 @@ class MDNavigationRail(MDCard):
|
||||
|
||||
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
|
||||
|
||||
@ -1110,6 +1147,9 @@ class MDNavigationRail(MDCard):
|
||||
self.register_event_type("on_item_press")
|
||||
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:
|
||||
"""
|
||||
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_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
|
||||
:class:`~MDNavigationRailMenuButton` buttons on the panel.
|
||||
|
@ -100,7 +100,6 @@ from PIL import ImageDraw
|
||||
from kivymd import uix_path
|
||||
from kivymd.color_definitions import colors as _colors
|
||||
from kivymd.color_definitions import text_colors
|
||||
from kivymd.theming import ThemableBehavior
|
||||
from kivymd.uix.behaviors import RectangularRippleBehavior
|
||||
from kivymd.uix.behaviors.toggle_behavior import MDToggleButton
|
||||
from kivymd.uix.boxlayout import MDBoxLayout
|
||||
@ -123,9 +122,11 @@ class TypeColorButton(MDRaisedButton, MDToggleButton):
|
||||
'RGBA', 'HEX', 'RGB'.
|
||||
"""
|
||||
|
||||
theme_text_color = "Custom"
|
||||
text_color = (0, 0, 0, 1)
|
||||
elevation = 0
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.theme_text_color = "Custom"
|
||||
self.text_color = (0, 0, 0, 1)
|
||||
self.elevation = 0
|
||||
|
||||
|
||||
class SelectAlphaChannelWidget(MDBoxLayout):
|
||||
@ -186,7 +187,7 @@ class SliderTab(MDBoxLayout):
|
||||
"""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
|
||||
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 value The set color value will be used when you open the
|
||||
dialog.
|
||||
Default color value in (r, g, b, a) or string format. The set color value
|
||||
will be used when you open the dialog.
|
||||
|
||||
:attr:`default_color` is an :class:`~kivy.properties.ColorProperty`
|
||||
and defaults to `None`.
|
||||
@ -416,7 +417,8 @@ class MDColorPicker(BaseDialog):
|
||||
|
||||
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
|
||||
:align: center
|
||||
|
@ -31,9 +31,7 @@
|
||||
|
||||
canvas:
|
||||
Color:
|
||||
rgb:
|
||||
app.theme_cls.primary_color \
|
||||
if not root.primary_color else root.primary_color
|
||||
rgb: root.primary_color or app.theme_cls.primary_color
|
||||
RoundedRectangle:
|
||||
size:
|
||||
(dp(328), dp(120)) \
|
||||
@ -48,9 +46,7 @@
|
||||
if root.theme_cls.device_orientation == "portrait" \
|
||||
else (root.radius[0], dp(0), dp(0), root.radius[3])
|
||||
Color:
|
||||
rgba:
|
||||
app.theme_cls.bg_normal \
|
||||
if not root.accent_color else root.accent_color
|
||||
rgba: root.accent_color or app.theme_cls.bg_normal
|
||||
RoundedRectangle:
|
||||
size:
|
||||
(dp(328), dp(512) - dp(120) - root._shift_dialog_height) \
|
||||
@ -79,9 +75,7 @@
|
||||
(dp(24), root.height - self.height - dp(18)) \
|
||||
if root.theme_cls.device_orientation == "portrait" \
|
||||
else (dp(24), root.height - self.height - dp(24))
|
||||
text_color:
|
||||
root.specific_text_color \
|
||||
if not root.text_toolbar_color else root.text_toolbar_color
|
||||
text_color: root.text_toolbar_color or root.specific_text_color
|
||||
|
||||
MDLabel:
|
||||
id: label_full_date
|
||||
@ -100,28 +94,13 @@
|
||||
dp(24) if not root._input_date_dialog_open else dp(168) + dp(24), \
|
||||
root.height - self.height - dp(96) \
|
||||
)
|
||||
text:
|
||||
root.set_text_full_date(root.sel_year, root.sel_month, root.sel_day, \
|
||||
root.theme_cls.device_orientation)
|
||||
text: root._date_label_text
|
||||
text_color:
|
||||
( \
|
||||
root.specific_text_color \
|
||||
if not root.text_toolbar_color else root.text_toolbar_color \
|
||||
) \
|
||||
if root.theme_cls.device_orientation == "portrait" \
|
||||
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 \
|
||||
) \
|
||||
)
|
||||
root.text_toolbar_color or root.specific_text_color \
|
||||
if root.theme_cls.device_orientation == "portrait" else \
|
||||
root.primary_color or self.theme_cls.primary_color \
|
||||
if root._input_date_dialog_open else \
|
||||
root.text_toolbar_color or root.specific_text_color
|
||||
|
||||
RecycleView:
|
||||
id: _year_layout
|
||||
@ -164,9 +143,7 @@
|
||||
(root.height - dp(120) + dp(12)) \
|
||||
if root.theme_cls.device_orientation == "portrait" \
|
||||
else dp(12)
|
||||
text_color:
|
||||
root.specific_text_color \
|
||||
if not root.text_toolbar_color else root.text_toolbar_color
|
||||
text_color: root.text_toolbar_color or root.specific_text_color
|
||||
|
||||
MDLabel:
|
||||
id: label_month_selector
|
||||
@ -180,9 +157,7 @@
|
||||
(dp(24), root.height - dp(120) - self.height - dp(20)) \
|
||||
if root.theme_cls.device_orientation == "portrait" \
|
||||
else (dp(168) + dp(24), label_title.y)
|
||||
text_color:
|
||||
app.theme_cls.text_color \
|
||||
if not root.text_color else root.text_color
|
||||
text_color: root.text_color or app.theme_cls.text_color
|
||||
|
||||
DatePickerIconTooltipButton:
|
||||
id: triangle
|
||||
@ -199,9 +174,7 @@
|
||||
(label_month_selector.width + dp(14), root.height - dp(123) - self.height) \
|
||||
if root.theme_cls.device_orientation == "portrait" \
|
||||
else (dp(180) + label_month_selector.width, label_title.y - dp(14))
|
||||
text_color:
|
||||
app.theme_cls.text_color \
|
||||
if not root.text_color else root.text_color
|
||||
text_color: root.text_color or app.theme_cls.text_color
|
||||
md_bg_color_disabled: 0, 0, 0, 0
|
||||
|
||||
DatePickerIconTooltipButton:
|
||||
@ -218,9 +191,7 @@
|
||||
root.height - dp(120) - self.height / 2 - dp(30) \
|
||||
if root.theme_cls.device_orientation == "portrait" \
|
||||
else dp(272)
|
||||
text_color:
|
||||
app.theme_cls.text_color \
|
||||
if not root.text_color else root.text_color
|
||||
text_color: root.text_color or app.theme_cls.text_color
|
||||
|
||||
DatePickerIconTooltipButton:
|
||||
id: chevron_right
|
||||
@ -236,9 +207,7 @@
|
||||
root.height - dp(120) - self.height / 2 - dp(30) \
|
||||
if root.theme_cls.device_orientation == "portrait" \
|
||||
else dp(272)
|
||||
text_color:
|
||||
app.theme_cls.text_color \
|
||||
if not root.text_color else root.text_color
|
||||
text_color: root.text_color or app.theme_cls.text_color
|
||||
|
||||
# TODO: Replace the GridLayout with a RecycleView
|
||||
# if it improves performance.
|
||||
@ -280,10 +249,7 @@
|
||||
text: "OK"
|
||||
theme_text_color: "Custom"
|
||||
font_name: root.font_name
|
||||
text_color:
|
||||
root.theme_cls.primary_color \
|
||||
if not root.text_button_color else \
|
||||
root.text_button_color
|
||||
text_color: root.text_button_color or root.theme_cls.primary_color
|
||||
on_release: root.on_ok_button_pressed()
|
||||
|
||||
MDFlatButton:
|
||||
@ -293,10 +259,7 @@
|
||||
theme_text_color: "Custom"
|
||||
pos: root.width - self.width - ok_button.width - dp(10), dp(10)
|
||||
font_name: root.font_name
|
||||
text_color:
|
||||
root.theme_cls.primary_color \
|
||||
if not root.text_button_color else \
|
||||
root.text_button_color
|
||||
text_color: root.text_button_color or root.theme_cls.primary_color
|
||||
|
||||
|
||||
<DatePickerDaySelectableItem>
|
||||
@ -312,14 +275,8 @@
|
||||
canvas.before:
|
||||
Color:
|
||||
rgba:
|
||||
(\
|
||||
self.owner.selector_color[:-1] + [.3] \
|
||||
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)) \
|
||||
(self.owner.selector_color or self.theme_cls.primary_color)[:-1] + [.3] \
|
||||
if self.is_in_range \
|
||||
else (0, 0, 0, 0)
|
||||
RoundedRectangle:
|
||||
size:
|
||||
@ -327,55 +284,24 @@
|
||||
if root.theme_cls.device_orientation == "portrait" \
|
||||
else \
|
||||
(dp(32), dp(28)) \
|
||||
if self.index in [6, 13, 20, 27, 34] or self.owner._date_range \
|
||||
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] \
|
||||
if self.is_range_end or self.is_week_end or self.is_month_end \
|
||||
else (dp(46), dp(28))
|
||||
pos:
|
||||
(self.x - dp(1.5), self.y + dp(5)) \
|
||||
if root.theme_cls.device_orientation == "portrait" else \
|
||||
(self.x, self.y + 1)
|
||||
radius:
|
||||
[0, 0, 0, 0] if not self.owner._date_range else \
|
||||
( \
|
||||
[self.width / 2, 0, 0, self.width / 2] \
|
||||
if self.text and self.owner._date_range[0] == date( \
|
||||
self.current_year, \
|
||||
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]) \
|
||||
)
|
||||
[
|
||||
self.width / 2 if self.is_range_start else 0,
|
||||
self.width / 2 if self.is_range_end else 0,
|
||||
self.width / 2 if self.is_range_end else 0,
|
||||
self.width / 2 if self.is_range_start else 0,
|
||||
]
|
||||
|
||||
# Selection circle.
|
||||
Color:
|
||||
rgba:
|
||||
( \
|
||||
self.theme_cls.primary_color if not root.owner.selector_color \
|
||||
else root.owner.selector_color \
|
||||
) \
|
||||
root.owner.selector_color or self.theme_cls.primary_color \
|
||||
if root.is_selected and not self.disabled \
|
||||
else (0, 0, 0, 0)
|
||||
Ellipse:
|
||||
@ -393,25 +319,11 @@
|
||||
font_name: root.owner.font_name
|
||||
theme_text_color: "Custom"
|
||||
text_color:
|
||||
( \
|
||||
root.theme_cls.primary_color \
|
||||
if not root.owner.text_current_color \
|
||||
else root.owner.text_current_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)) \
|
||||
)
|
||||
|
||||
root.owner.accent_color or root.theme_cls.bg_normal \
|
||||
if root.is_selected else \
|
||||
root.owner.text_current_color or root.theme_cls.primary_color \
|
||||
if root.is_today else \
|
||||
root.owner.text_color or root.theme_cls.text_color
|
||||
|
||||
<DatePickerWeekdayLabel>
|
||||
font_style: "Caption"
|
||||
@ -425,9 +337,7 @@
|
||||
size:
|
||||
(dp(40), dp(40)) if root.theme_cls.device_orientation == "portrait" \
|
||||
else (dp(32), dp(32))
|
||||
text_color:
|
||||
app.theme_cls.disabled_hint_text_color \
|
||||
if not root.owner.text_weekday_color else root.owner.text_weekday_color
|
||||
text_color: root.owner.text_weekday_color or app.theme_cls.disabled_hint_text_color
|
||||
|
||||
|
||||
<DatePickerYearSelectableItem>
|
||||
@ -436,13 +346,21 @@
|
||||
valign: "middle"
|
||||
halign: "center"
|
||||
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
|
||||
|
||||
canvas.before:
|
||||
Color:
|
||||
rgba:
|
||||
root.selected_color if root.selected_color \
|
||||
else self.theme_cls.primary_color
|
||||
self.owner.selector_color or self.theme_cls.primary_color \
|
||||
if self.selected else \
|
||||
(0, 0, 0, 0)
|
||||
RoundedRectangle:
|
||||
pos: self.x + dp(12), self.y
|
||||
size: self.width - dp(24), self.height
|
||||
@ -453,6 +371,7 @@
|
||||
adaptive_height: True
|
||||
size_hint_x: None
|
||||
spacing: dp(8)
|
||||
opacity: 0
|
||||
width:
|
||||
self.owner.width - dp(48) \
|
||||
if root.owner.theme_cls.device_orientation == "portrait" \
|
||||
@ -468,10 +387,6 @@
|
||||
|
||||
<DatePickerInputField>
|
||||
mode: "fill"
|
||||
opacity: 0
|
||||
hint_text: "dd/mm/yyyy"
|
||||
input_filter: root.input_filter
|
||||
fill_color:
|
||||
(0, 0, 0, .15) \
|
||||
if not self.owner.input_field_background_color \
|
||||
else root.owner.input_field_background_color
|
||||
fill_color: root.owner.input_field_background_color or (0, 0, 0, .15)
|
||||
|
@ -203,7 +203,6 @@ from datetime import date
|
||||
from itertools import zip_longest
|
||||
from typing import Union
|
||||
|
||||
from kivy import Logger
|
||||
from kivy.animation import Animation
|
||||
from kivy.lang import Builder
|
||||
from kivy.metrics import dp
|
||||
@ -253,6 +252,12 @@ class BaseDialogPicker(
|
||||
Base class for :class:`~kivymd.uix.picker.MDDatePicker` and
|
||||
: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:
|
||||
`on_save`
|
||||
Events called when the "OK" dialog box button is clicked.
|
||||
@ -644,11 +649,30 @@ class DatePickerTypeDateError(Exception):
|
||||
|
||||
|
||||
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")
|
||||
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):
|
||||
"""Sets a text field to an error state."""
|
||||
|
||||
@ -699,54 +723,14 @@ class DatePickerDaySelectableItem(
|
||||
owner = ObjectProperty()
|
||||
is_today = BooleanProperty(False)
|
||||
is_selected = BooleanProperty(False)
|
||||
current_month = NumericProperty()
|
||||
current_year = NumericProperty()
|
||||
index = NumericProperty(0)
|
||||
|
||||
def check_date(self, year: int, month: int, day: int):
|
||||
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
|
||||
is_in_range = BooleanProperty(False)
|
||||
is_range_start = BooleanProperty(False)
|
||||
is_range_end = BooleanProperty(False)
|
||||
is_month_end = BooleanProperty(False)
|
||||
is_week_end = BooleanProperty(False)
|
||||
|
||||
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):
|
||||
# If year_layout is active don't dispatch on_touch_down events,
|
||||
@ -760,7 +744,7 @@ class DatePickerYearSelectableItem(RecycleDataViewBehavior, MDLabel):
|
||||
"""Implements an item for a pick list of the year."""
|
||||
|
||||
index = None
|
||||
selected_color = ColorProperty([0, 0, 0, 0])
|
||||
selected = BooleanProperty(False)
|
||||
owner = ObjectProperty()
|
||||
|
||||
def refresh_view_attrs(self, rv, index, data):
|
||||
@ -772,32 +756,10 @@ class DatePickerYearSelectableItem(RecycleDataViewBehavior, MDLabel):
|
||||
return True
|
||||
if self.collide_point(*touch.pos):
|
||||
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)
|
||||
|
||||
def apply_selection(self, table_data, index, is_selected):
|
||||
if 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)
|
||||
self.selected = is_selected
|
||||
|
||||
|
||||
# TODO: Add the feature to embed the `MDDatePicker` class in other layouts
|
||||
@ -889,7 +851,7 @@ class MDDatePicker(BaseDialogPicker):
|
||||
and defaults to `picker`.
|
||||
"""
|
||||
|
||||
min_date = ObjectProperty()
|
||||
min_date = ObjectProperty(allownone=True)
|
||||
"""
|
||||
The minimum value of the date range for the `'mode`' parameter.
|
||||
Must be an object <class 'datetime.date'>.
|
||||
@ -900,7 +862,7 @@ class MDDatePicker(BaseDialogPicker):
|
||||
and defaults to `None`.
|
||||
"""
|
||||
|
||||
max_date = ObjectProperty()
|
||||
max_date = ObjectProperty(allownone=True)
|
||||
"""
|
||||
The minimum value of the date range for the `'mode`' parameter.
|
||||
Must be an object <class 'datetime.date'>.
|
||||
@ -955,17 +917,13 @@ class MDDatePicker(BaseDialogPicker):
|
||||
|
||||
_calendar_layout = ObjectProperty()
|
||||
_calendar_list = None
|
||||
_enter_data_field = None
|
||||
_enter_data_field_two = None
|
||||
_enter_data_field_container = None
|
||||
_date_range = []
|
||||
_fields_container = None
|
||||
_scale_calendar_layout = NumericProperty(1)
|
||||
_scale_year_layout = NumericProperty(0)
|
||||
_shift_dialog_height = NumericProperty(0)
|
||||
_input_date_dialog_open = BooleanProperty(False)
|
||||
_select_year_dialog_open = False
|
||||
_start_range_date = 0
|
||||
_end_range_date = 0
|
||||
_date_label_text = StringProperty()
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
@ -996,7 +954,6 @@ class MDDatePicker(BaseDialogPicker):
|
||||
"'max_date' must be of class <class 'datetime.date'>"
|
||||
)
|
||||
self.compare_date_range()
|
||||
self._date_range = self.get_date_range()
|
||||
|
||||
self.generate_list_widgets_days()
|
||||
self.update_calendar(self.sel_year, self.sel_month)
|
||||
@ -1006,6 +963,8 @@ class MDDatePicker(BaseDialogPicker):
|
||||
) -> None:
|
||||
"""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 orientation_value == "portrait":
|
||||
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.
|
||||
"""
|
||||
|
||||
if self._enter_data_field and not self.is_date_valaid(
|
||||
self._enter_data_field.text
|
||||
):
|
||||
self._enter_data_field.set_error()
|
||||
if self._input_date_dialog_open and not self._try_apply_input():
|
||||
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(
|
||||
"on_save",
|
||||
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:
|
||||
@ -1085,56 +1035,25 @@ class MDDatePicker(BaseDialogPicker):
|
||||
self.ids._year_layout.children[0].clear_selection()
|
||||
|
||||
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
|
||||
if self._select_year_dialog_open:
|
||||
self.transformation_from_dialog_select_year()
|
||||
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.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(
|
||||
_shift_dialog_height=dp(250)
|
||||
if self.theme_cls.device_orientation == "portrait"
|
||||
@ -1152,28 +1071,22 @@ class MDDatePicker(BaseDialogPicker):
|
||||
).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.triangle)
|
||||
Animation(opacity=1, d=0.15).start(self._enter_data_field)
|
||||
if self._enter_data_field_two:
|
||||
Animation(opacity=1, d=0.15).start(self._enter_data_field_two)
|
||||
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,
|
||||
)
|
||||
Animation(opacity=1, d=0.15).start(self._fields_container)
|
||||
# The label text separator in landscape orientation depends on the
|
||||
# open dialog.
|
||||
self._update_date_label_text()
|
||||
|
||||
def transformation_from_dialog_input_date(
|
||||
self, interval: Union[int, float]
|
||||
) -> None:
|
||||
if not self._try_apply_input():
|
||||
return
|
||||
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.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(
|
||||
_shift_dialog_height=dp(0), _scale_calendar_layout=1, d=0.15
|
||||
).start(self)
|
||||
@ -1187,41 +1100,67 @@ class MDDatePicker(BaseDialogPicker):
|
||||
).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.triangle)
|
||||
Animation(opacity=0, d=0.15).start(self._enter_data_field)
|
||||
self.ids.edit_icon.icon = "pencil"
|
||||
self.ids.label_title.text = self.title
|
||||
# The label text separator in landscape orientation depends on the
|
||||
# open dialog.
|
||||
self._update_date_label_text()
|
||||
|
||||
if not self.min_date and not self.max_date:
|
||||
list_date = self._enter_data_field.get_list_date()
|
||||
if len(list_date) == 3 and len(list_date[2]) == 4:
|
||||
self.sel_day = int(list_date[0])
|
||||
self.sel_month = int(list_date[1])
|
||||
self.sel_year = int(list_date[2])
|
||||
self.update_calendar(self.sel_year, self.sel_month)
|
||||
elif self.min_date and self.max_date:
|
||||
list_min_date = self._enter_data_field.get_list_date()
|
||||
list_max_date = self._enter_data_field_two.get_list_date()
|
||||
def _get_dates_from_fields(self):
|
||||
"""
|
||||
Return a list of dates entered by the user in the input fields.
|
||||
|
||||
if len(list_min_date) == 3 and len(list_min_date[2]) == 4:
|
||||
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]),
|
||||
)
|
||||
If there is an error in the field or the field is empty, None will be
|
||||
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.
|
||||
"""
|
||||
|
||||
self.update_calendar_for_date_range()
|
||||
self.ids.label_full_date.text = self.set_text_full_date(
|
||||
int(list_max_date[2]),
|
||||
int(list_max_date[1]),
|
||||
int(list_max_date[0]),
|
||||
self.theme_cls.device_orientation,
|
||||
)
|
||||
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)
|
||||
elif self.mode == "range":
|
||||
date1, date2 = dates[0] or self.min_date, dates[1] or self.max_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)
|
||||
|
||||
return True
|
||||
|
||||
def _on_date_field_text_changes(self, *args):
|
||||
self._update_date_label_text()
|
||||
|
||||
def compare_date_range(self) -> None:
|
||||
# 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:
|
||||
# self.compare_date_range()
|
||||
self._date_range = self.get_date_range()
|
||||
# This method is no longer used, use update_calendar instead.
|
||||
self.update_calendar(self.year, self.month)
|
||||
|
||||
def update_text_full_date(self, list_date) -> None:
|
||||
@ -1243,27 +1181,13 @@ class MDDatePicker(BaseDialogPicker):
|
||||
in an open date input dialog.
|
||||
"""
|
||||
|
||||
if len(list_date) == 1 and len(list_date[0]) == 2:
|
||||
self.ids.label_full_date.text = self.set_text_full_date(
|
||||
self.sel_year,
|
||||
self.sel_month,
|
||||
list_date[0],
|
||||
self.theme_cls.device_orientation,
|
||||
)
|
||||
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,
|
||||
)
|
||||
# This method no longer used, use update_calendar instead.
|
||||
year = int(list_date[2]) if len(list_date) > 2 else self.sel_year
|
||||
month = int(list_date[1]) if len(list_date) > 1 else self.sel_month
|
||||
day = int(list_date[0]) if len(list_date) > 0 else self.sel_day
|
||||
day = min(day, calendar.monthrange(year, month)[1])
|
||||
self.sel_year, self.sel_month, self.sel_day = year, month, day
|
||||
self.update_calendar(year, month)
|
||||
|
||||
def update_calendar(self, year, month) -> None:
|
||||
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_dates = {selected_date}
|
||||
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)
|
||||
for widget, widget_date in zip_longest(self._calendar_list, dates):
|
||||
# Only widgets whose dates are in the displayed month are visible.
|
||||
@ -1281,21 +1208,31 @@ class MDDatePicker(BaseDialogPicker):
|
||||
and widget_date.year == year
|
||||
)
|
||||
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_selected = visible and widget_date in selected_dates
|
||||
# I don't understand why, but this line is important. Without this
|
||||
# line, some widgets that we are trying to disable remain enabled.
|
||||
widget.disabled = False
|
||||
widget.disabled = (
|
||||
not visible
|
||||
or self.mode == "range"
|
||||
and self._date_range
|
||||
and widget_date not in self._date_range
|
||||
widget.disabled = not visible
|
||||
widget.is_in_range = (
|
||||
visible
|
||||
and self.min_date is not None
|
||||
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."""
|
||||
|
||||
if issubclass(self.input_field_cls, MDTextField):
|
||||
@ -1322,6 +1259,7 @@ class MDDatePicker(BaseDialogPicker):
|
||||
|
||||
field = self.input_field_cls(
|
||||
owner=self,
|
||||
text=date.strftime("%d/%m/%Y") if date else "",
|
||||
helper_text=self.helper_text,
|
||||
fill_color_normal=fill_color_normal,
|
||||
fill_color_focus=fill_color_focus,
|
||||
@ -1340,6 +1278,8 @@ class MDDatePicker(BaseDialogPicker):
|
||||
)
|
||||
|
||||
def get_date_range(self) -> list:
|
||||
if not self.min_date or not self.max_date:
|
||||
return []
|
||||
date_range = [
|
||||
self.min_date + datetime.timedelta(days=x)
|
||||
for x in range((self.max_date - self.min_date).days + 1)
|
||||
@ -1353,118 +1293,73 @@ class MDDatePicker(BaseDialogPicker):
|
||||
a date range.
|
||||
"""
|
||||
|
||||
if 12 < int(month) < 0:
|
||||
raise ValueError(
|
||||
"set_text_full_date:\n\t" f"Month [{month}] out of range."
|
||||
)
|
||||
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 " "
|
||||
# In portrait orientation, the label is stretched in width, so we
|
||||
# should not insert line breaks. When the input dialog is open, the
|
||||
# label moves to the right and also stretches in width.
|
||||
horizontal = orientation == "portrait" or self._input_date_dialog_open
|
||||
|
||||
def date_repr(date):
|
||||
return date.strftime("%b").capitalize() + " " + str(date.day)
|
||||
|
||||
input_dates = self._get_dates_from_fields()
|
||||
if self.mode == "picker":
|
||||
selected_date = date(self.sel_year, self.sel_month, self.sel_day)
|
||||
if input_dates:
|
||||
selected_date = input_dates[0] or selected_date
|
||||
weekday_repr = selected_date.strftime("%a").capitalize()
|
||||
separator = ", " if horizontal else ",\n"
|
||||
return weekday_repr + separator + date_repr(selected_date)
|
||||
elif self.mode == "range":
|
||||
start, end = self.min_date, self.max_date
|
||||
if input_dates:
|
||||
start, end = input_dates[0] or start, input_dates[1] or end
|
||||
ends = [end for end in (start, end) if end]
|
||||
if len(ends) == 0:
|
||||
start_repr, end_repr = "Start", "End"
|
||||
else:
|
||||
start, end = min(ends), max(ends)
|
||||
start_repr, end_repr = date_repr(start), date_repr(end)
|
||||
separator = " — " if horizontal else ",\n"
|
||||
return start_repr + separator + end_repr
|
||||
|
||||
def _update_date_label_text(self):
|
||||
self._date_label_text = self.set_text_full_date(
|
||||
self.sel_year,
|
||||
self.sel_month,
|
||||
self.sel_day,
|
||||
self.theme_cls.device_orientation,
|
||||
)
|
||||
|
||||
if self.mode == "picker":
|
||||
if not self.min_date and not self.max_date:
|
||||
return (
|
||||
date.strftime("%a,").capitalize()
|
||||
+ separator
|
||||
+ date.strftime("%b ").capitalize()
|
||||
+ str(day).lstrip("0")
|
||||
)
|
||||
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":
|
||||
if self._start_range_date and self._end_range_date:
|
||||
if (
|
||||
orientation == "landscape"
|
||||
and "-" in self.ids.label_full_date.text
|
||||
):
|
||||
return (
|
||||
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:
|
||||
if (
|
||||
orientation == "landscape"
|
||||
and "," in self.ids.label_full_date.text
|
||||
):
|
||||
return (
|
||||
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")
|
||||
)
|
||||
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:
|
||||
self.sel_year = self.year
|
||||
self.sel_month = self.month
|
||||
self.sel_day = int(widget.text)
|
||||
self.update_calendar(self.sel_year, self.sel_month)
|
||||
if self._select_year_dialog_open or self._input_date_dialog_open:
|
||||
return
|
||||
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)
|
||||
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:
|
||||
# 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
|
||||
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(
|
||||
index=i,
|
||||
is_week_end=i % 7 == 6,
|
||||
owner=self,
|
||||
current_month=int(self.month),
|
||||
current_year=int(self.year),
|
||||
)
|
||||
calendar_list.append(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.
|
||||
Switches the calendar to the previous/next month.
|
||||
"""
|
||||
|
||||
month_delta = 1 if operation == "next" else -1
|
||||
year = self.year + (self.month - 1 + month_delta) // 12
|
||||
month = (self.month - 1 + month_delta) % 12 + 1
|
||||
|
||||
if year <= 0:
|
||||
year, month = 1, 1
|
||||
self.update_calendar(year, month)
|
||||
|
@ -166,7 +166,6 @@ from kivy.uix.behaviors import ButtonBehavior
|
||||
from kivy.vector import Vector
|
||||
|
||||
from kivymd import uix_path
|
||||
from kivymd.theming import ThemableBehavior
|
||||
from kivymd.uix.boxlayout import MDBoxLayout
|
||||
from kivymd.uix.circularlayout import MDCircularLayout
|
||||
from kivymd.uix.label import MDLabel
|
||||
@ -185,7 +184,7 @@ class AmPmSelectorLabel(ButtonBehavior, MDLabel):
|
||||
pass
|
||||
|
||||
|
||||
class AmPmSelector(ThemableBehavior, MDBoxLayout):
|
||||
class AmPmSelector(MDBoxLayout):
|
||||
border_radius = NumericProperty()
|
||||
border_color = ColorProperty()
|
||||
bg_color = ColorProperty()
|
||||
|
@ -6,7 +6,8 @@
|
||||
self.theme_cls.divider_color \
|
||||
if not self.back_color else \
|
||||
self.back_color
|
||||
Rectangle:
|
||||
RoundedRectangle:
|
||||
radius: root.radius
|
||||
size:
|
||||
(self.width, self.height) \
|
||||
if self.orientation == "horizontal" else \
|
||||
@ -18,7 +19,8 @@
|
||||
Color:
|
||||
rgba:
|
||||
self.theme_cls.primary_color if not self.color else self.color
|
||||
Rectangle:
|
||||
RoundedRectangle:
|
||||
radius: root.radius
|
||||
size:
|
||||
(self.width * self.value_normalized, self.height if self.height else dp(4)) \
|
||||
if self.orientation == "horizontal" else \
|
||||
|
@ -145,6 +145,7 @@ from kivy.properties import (
|
||||
NumericProperty,
|
||||
OptionProperty,
|
||||
StringProperty,
|
||||
VariableListProperty,
|
||||
)
|
||||
from kivy.uix.progressbar import ProgressBar
|
||||
|
||||
@ -158,6 +159,25 @@ with open(
|
||||
|
||||
|
||||
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)
|
||||
"""
|
||||
Reverse the direction the progressbar moves.
|
||||
@ -179,7 +199,7 @@ class MDProgressBar(ThemableBehavior, ProgressBar):
|
||||
|
||||
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`
|
||||
and defaults to `None`.
|
||||
@ -187,7 +207,7 @@ class MDProgressBar(ThemableBehavior, ProgressBar):
|
||||
|
||||
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
|
||||
|
||||
|
@ -85,12 +85,13 @@ Equivalent
|
||||
|
||||
from kivy.uix.recyclegridlayout import RecycleGridLayout
|
||||
|
||||
from kivymd.theming import ThemableBehavior
|
||||
from kivymd.uix import MDAdaptiveWidget
|
||||
from kivymd.uix.behaviors import DeclarativeBehavior
|
||||
|
||||
|
||||
class MDRecycleGridLayout(
|
||||
DeclarativeBehavior, RecycleGridLayout, MDAdaptiveWidget
|
||||
DeclarativeBehavior, ThemableBehavior, RecycleGridLayout, MDAdaptiveWidget
|
||||
):
|
||||
"""
|
||||
Recycle grid layout layout class. For more information, see in the
|
||||
|
@ -34,10 +34,14 @@ __all__ = ("MDRecycleView",)
|
||||
|
||||
from kivy.uix.recycleview import RecycleView
|
||||
|
||||
from kivymd.theming import ThemableBehavior
|
||||
from kivymd.uix import MDAdaptiveWidget
|
||||
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
|
||||
:class:`~kivy.uix.recycleview.RecycleView` class documentation.
|
||||
|
@ -15,7 +15,7 @@
|
||||
canvas:
|
||||
Clear
|
||||
Color:
|
||||
rgba: root.theme_cls.primary_dark
|
||||
rgba: root.circle_color
|
||||
Ellipse:
|
||||
pos: self.pos
|
||||
size: self.size
|
||||
@ -24,4 +24,4 @@
|
||||
id: spinner
|
||||
size_hint: None, None
|
||||
size: dp(30), dp(30)
|
||||
color: 1, 1, 1, 1
|
||||
color: root.spinner_color
|
||||
|
@ -43,6 +43,8 @@ Example
|
||||
id: refresh_layout
|
||||
refresh_callback: app.refresh_callback
|
||||
root_layout: root
|
||||
spinner_color: "brown"
|
||||
circle_color: "white"
|
||||
|
||||
MDGridLayout:
|
||||
id: box
|
||||
@ -66,6 +68,8 @@ Example
|
||||
y = 15
|
||||
|
||||
def build(self):
|
||||
self.theme_cls.theme_style = "Dark"
|
||||
self.theme_cls.primary_palette = "Orange"
|
||||
self.screen = Factory.Example()
|
||||
self.set_list()
|
||||
|
||||
@ -81,8 +85,10 @@ Example
|
||||
asynckivy.start(set_list())
|
||||
|
||||
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):
|
||||
self.screen.ids.box.clear_widgets()
|
||||
@ -110,7 +116,12 @@ from kivy.core.window import Window
|
||||
from kivy.effects.dampedscroll import DampedScrollEffect
|
||||
from kivy.lang import Builder
|
||||
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 kivymd import uix_path
|
||||
@ -150,7 +161,16 @@ class _RefreshScrollEffect(DampedScrollEffect):
|
||||
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()
|
||||
"""
|
||||
The spinner will be attached to this layout.
|
||||
@ -168,8 +188,70 @@ class MDScrollViewRefreshLayout(MDScrollView):
|
||||
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):
|
||||
super().__init__(*args, **kwargs)
|
||||
if not self.circle_color:
|
||||
self.circle_color = self.theme_cls.primary_dark
|
||||
self.effect_cls = _RefreshScrollEffect
|
||||
self._work_spinner = False
|
||||
self._did_overscroll = False
|
||||
@ -180,7 +262,15 @@ class MDScrollViewRefreshLayout(MDScrollView):
|
||||
if self.refresh_callback:
|
||||
self.refresh_callback()
|
||||
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.refresh_spinner.start_anim_spinner()
|
||||
self._work_spinner = True
|
||||
@ -195,13 +285,18 @@ class MDScrollViewRefreshLayout(MDScrollView):
|
||||
|
||||
|
||||
class RefreshSpinner(ThemableBehavior, FloatLayout):
|
||||
# Color of the spinner in (r, g, b, a) or string format.
|
||||
spinner_color = ColorProperty([1, 1, 1, 1])
|
||||
"""
|
||||
Color of spinner.
|
||||
|
||||
:attr:`spinner_color` is a :class:`~kivy.properties.ColorProperty`
|
||||
and defaults to `[1, 1, 1, 1]`.
|
||||
"""
|
||||
# Color of the ellipse around the spinner in (r, g, b, a) or string format.
|
||||
circle_color = ColorProperty()
|
||||
# Transition of the spinner's opening.
|
||||
show_transition = StringProperty()
|
||||
# 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
|
||||
_refresh_layout = ObjectProperty()
|
||||
@ -210,13 +305,15 @@ class RefreshSpinner(ThemableBehavior, FloatLayout):
|
||||
spinner = self.ids.body_spinner
|
||||
Animation(
|
||||
y=spinner.y - self.theme_cls.standard_increment * 2 + dp(10),
|
||||
d=0.8,
|
||||
t="out_elastic",
|
||||
d=self.show_duration,
|
||||
t=self.show_transition,
|
||||
).start(spinner)
|
||||
|
||||
def hide_anim_spinner(self) -> None:
|
||||
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.start(spinner)
|
||||
|
||||
|
@ -31,11 +31,14 @@ MDRelativeLayout
|
||||
|
||||
from kivy.uix.relativelayout import RelativeLayout
|
||||
|
||||
from kivymd.theming import ThemableBehavior
|
||||
from kivymd.uix import MDAdaptiveWidget
|
||||
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
|
||||
:class:`~kivy.uix.relativelayout.RelativeLayout` class documentation.
|
||||
|
@ -32,12 +32,13 @@ MDScreen
|
||||
from kivy.properties import ListProperty, ObjectProperty
|
||||
from kivy.uix.screenmanager import Screen
|
||||
|
||||
from kivymd.theming import ThemableBehavior
|
||||
from kivymd.uix import MDAdaptiveWidget
|
||||
from kivymd.uix.behaviors import DeclarativeBehavior
|
||||
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
|
||||
:class:`~kivymd.uix.screenmanager.MDScreenManager`. For more information,
|
||||
|
4
sbapp/kivymd/uix/segmentedbutton/__init__.py
Normal file
4
sbapp/kivymd/uix/segmentedbutton/__init__.py
Normal file
@ -0,0 +1,4 @@
|
||||
from .segmentedbutton import ( # NOQA F401
|
||||
MDSegmentedButton,
|
||||
MDSegmentedButtonItem,
|
||||
)
|
32
sbapp/kivymd/uix/segmentedbutton/segmentedbutton.kv
Normal file
32
sbapp/kivymd/uix/segmentedbutton/segmentedbutton.kv
Normal file
@ -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)
|
653
sbapp/kivymd/uix/segmentedbutton/segmentedbutton.py
Normal file
653
sbapp/kivymd/uix/segmentedbutton/segmentedbutton.py
Normal file
@ -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>
|
||||
adaptive_height: True
|
||||
halign: "center"
|
||||
@ -15,8 +18,9 @@
|
||||
pos_hint: {"center_y": .5}
|
||||
x: root._segment_switch_x
|
||||
md_bg_color: root.segment_color
|
||||
elevation: 2
|
||||
elevation: SEGMENT_CONTROL_SEGMENT_SWITCH_ELEVATION
|
||||
_radius: root.radius[0] - 4
|
||||
shadow_radius: self._radius
|
||||
width:
|
||||
segment_panel.width / segment_panel.children_number \
|
||||
- segment_panel.spacing
|
||||
|
@ -121,7 +121,6 @@ from kivy.properties import (
|
||||
)
|
||||
|
||||
from kivymd import uix_path
|
||||
from kivymd.theming import ThemableBehavior
|
||||
from kivymd.uix.boxlayout import MDBoxLayout
|
||||
from kivymd.uix.button import MDRaisedButton
|
||||
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.
|
||||
class MDSegmentedControl(MDRelativeLayout, ThemableBehavior):
|
||||
class MDSegmentedControl(MDRelativeLayout):
|
||||
"""
|
||||
Implements a segmented control panel.
|
||||
|
||||
Relative layout class. For more information, see in the
|
||||
:class:`~kivy.uix.relativelayout.RelativeLayout` class documentation.
|
||||
For more information, see in the
|
||||
:class:`~kivymd.uix.relativelayout.MDRelativeLayout` class documentation.
|
||||
|
||||
:Events:
|
||||
`on_active`
|
||||
@ -159,7 +158,7 @@ class MDSegmentedControl(MDRelativeLayout, ThemableBehavior):
|
||||
|
||||
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
|
||||
|
||||
@ -175,7 +174,7 @@ class MDSegmentedControl(MDRelativeLayout, ThemableBehavior):
|
||||
|
||||
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
|
||||
|
||||
@ -220,7 +219,8 @@ class MDSegmentedControl(MDRelativeLayout, ThemableBehavior):
|
||||
|
||||
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
|
||||
|
||||
|
@ -276,7 +276,6 @@ from kivy.properties import (
|
||||
)
|
||||
|
||||
from kivymd import uix_path
|
||||
from kivymd.theming import ThemableBehavior
|
||||
from kivymd.uix.behaviors import TouchBehavior
|
||||
from kivymd.uix.button import MDIconButton
|
||||
from kivymd.uix.list import MDList
|
||||
@ -295,7 +294,7 @@ class SelectionIconCheck(MDIconButton):
|
||||
icon_check_color = ColorProperty([0, 0, 0, 1])
|
||||
|
||||
|
||||
class SelectionItem(ThemableBehavior, MDRelativeLayout, TouchBehavior):
|
||||
class SelectionItem(MDRelativeLayout, TouchBehavior):
|
||||
selected = BooleanProperty(False)
|
||||
"""
|
||||
Whether or not an item is checked.
|
||||
@ -514,6 +513,11 @@ class SelectionItem(ThemableBehavior, MDRelativeLayout, TouchBehavior):
|
||||
|
||||
class MDSelectionList(MDList):
|
||||
"""
|
||||
Selection list class.
|
||||
|
||||
For more information, see in the
|
||||
:class:`~kivymd.uix.list.MDList` classes documentation.
|
||||
|
||||
:Events:
|
||||
`on_selected`
|
||||
Called when a list item is selected.
|
||||
@ -548,7 +552,8 @@ class MDSelectionList(MDList):
|
||||
|
||||
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`
|
||||
and defaults to `[1, 1, 1, 1]`.
|
||||
@ -556,7 +561,8 @@ class MDSelectionList(MDList):
|
||||
|
||||
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`
|
||||
and defaults to `[1, 1, 1, 1]`.
|
||||
@ -564,7 +570,7 @@ class MDSelectionList(MDList):
|
||||
|
||||
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`
|
||||
and defaults to `[0, 0, 0, 0.2]]`.
|
||||
@ -580,7 +586,8 @@ class MDSelectionList(MDList):
|
||||
|
||||
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`
|
||||
and defaults to `None`.
|
||||
|
@ -4,13 +4,12 @@ Components/SelectionControls
|
||||
|
||||
.. 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.
|
||||
|
||||
.. 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:
|
||||
|
||||
- MDCheckbox_
|
||||
@ -20,6 +19,12 @@ Components/SelectionControls
|
||||
MDCheckbox
|
||||
----------
|
||||
|
||||
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/checkbox.png
|
||||
:align: center
|
||||
|
||||
Usage
|
||||
-----
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from kivy.lang import Builder
|
||||
@ -37,18 +42,20 @@ MDCheckbox
|
||||
'''
|
||||
|
||||
|
||||
class Test(MDApp):
|
||||
class Example(MDApp):
|
||||
def build(self):
|
||||
self.theme_cls.primary_palette = "Green"
|
||||
self.theme_cls.theme_style = "Dark"
|
||||
return Builder.load_string(KV)
|
||||
|
||||
|
||||
Test().run()
|
||||
Example().run()
|
||||
|
||||
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/checkbox.gif
|
||||
:align: center
|
||||
|
||||
.. 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.
|
||||
|
||||
Control state
|
||||
@ -94,20 +101,138 @@ MDCheckbox with group
|
||||
'''
|
||||
|
||||
|
||||
class Test(MDApp):
|
||||
class Example(MDApp):
|
||||
def build(self):
|
||||
self.theme_cls.primary_palette = "Green"
|
||||
self.theme_cls.theme_style = "Dark"
|
||||
return Builder.load_string(KV)
|
||||
|
||||
|
||||
Test().run()
|
||||
Example().run()
|
||||
|
||||
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/checkbox-group.gif
|
||||
: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
|
||||
--------
|
||||
|
||||
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/switch.png
|
||||
:align: center
|
||||
|
||||
Usage
|
||||
-----
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from kivy.lang import Builder
|
||||
@ -122,58 +247,20 @@ MDSwitch
|
||||
'''
|
||||
|
||||
|
||||
class Test(MDApp):
|
||||
class Example(MDApp):
|
||||
def build(self):
|
||||
self.theme_cls.primary_palette = "Green"
|
||||
self.theme_cls.theme_style = "Dark"
|
||||
return Builder.load_string(KV)
|
||||
|
||||
|
||||
Test().run()
|
||||
Example().run()
|
||||
|
||||
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/md-switch.gif
|
||||
: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
|
||||
: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")
|
||||
@ -195,9 +282,14 @@ from kivy.uix.floatlayout import FloatLayout
|
||||
|
||||
from kivymd import uix_path
|
||||
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.label import MDIcon
|
||||
from kivymd.utils import asynckivy
|
||||
|
||||
with open(
|
||||
os.path.join(uix_path, "selectioncontrol", "selectioncontrol.kv"),
|
||||
@ -206,7 +298,22 @@ with open(
|
||||
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)
|
||||
"""
|
||||
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")
|
||||
"""
|
||||
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.
|
||||
|
||||
: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")
|
||||
"""
|
||||
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.
|
||||
|
||||
:attr:`radio_icon_down` is a :class:`~kivy.properties.StringProperty`
|
||||
@ -253,7 +360,7 @@ class MDCheckbox(CircularRippleBehavior, ToggleButtonBehavior, MDIcon):
|
||||
|
||||
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
|
||||
|
||||
@ -271,7 +378,7 @@ class MDCheckbox(CircularRippleBehavior, ToggleButtonBehavior, MDIcon):
|
||||
|
||||
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
|
||||
|
||||
@ -289,7 +396,7 @@ class MDCheckbox(CircularRippleBehavior, ToggleButtonBehavior, MDIcon):
|
||||
|
||||
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
|
||||
|
||||
@ -309,7 +416,7 @@ class MDCheckbox(CircularRippleBehavior, ToggleButtonBehavior, MDIcon):
|
||||
|
||||
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
|
||||
Use :attr:`color_active` instead.
|
||||
@ -320,7 +427,7 @@ class MDCheckbox(CircularRippleBehavior, ToggleButtonBehavior, MDIcon):
|
||||
|
||||
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
|
||||
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])
|
||||
|
||||
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(
|
||||
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)
|
||||
self.color_active = self.theme_cls.primary_color
|
||||
@ -364,6 +473,13 @@ class MDCheckbox(CircularRippleBehavior, ToggleButtonBehavior, MDIcon):
|
||||
self.update_color()
|
||||
|
||||
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 not self.disabled:
|
||||
self.color = self.theme_cls.primary_color
|
||||
@ -373,18 +489,41 @@ class MDCheckbox(CircularRippleBehavior, ToggleButtonBehavior, MDIcon):
|
||||
self.color_active = value
|
||||
|
||||
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":
|
||||
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:
|
||||
self.icon = (
|
||||
self.radio_icon_normal
|
||||
if self.group
|
||||
if self.group and self.group not in ["root", "child"]
|
||||
else self.checkbox_icon_normal
|
||||
)
|
||||
|
||||
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:
|
||||
self._current_color = self.disabled_color
|
||||
elif self.state == "down":
|
||||
@ -393,6 +532,8 @@ class MDCheckbox(CircularRippleBehavior, ToggleButtonBehavior, MDIcon):
|
||||
self._current_color = self.color_inactive
|
||||
|
||||
def on_state(self, *args) -> None:
|
||||
"""Called when the values of :attr:`state` change."""
|
||||
|
||||
if self.state == "down":
|
||||
self.check_anim_in.cancel(self)
|
||||
self.check_anim_out.start(self)
|
||||
@ -408,8 +549,45 @@ class MDCheckbox(CircularRippleBehavior, ToggleButtonBehavior, MDIcon):
|
||||
self.active = False
|
||||
|
||||
def on_active(self, *args) -> None:
|
||||
"""Called when the values of :attr:`active` change."""
|
||||
|
||||
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):
|
||||
"""
|
||||
@ -419,14 +597,8 @@ class ThumbIcon(MDIcon):
|
||||
"""
|
||||
|
||||
|
||||
class Thumb(
|
||||
CommonElevationBehavior,
|
||||
CircularRippleBehavior,
|
||||
MDFloatLayout,
|
||||
):
|
||||
"""
|
||||
Implements a thumb for the :class:`~MDSwitch` widget.
|
||||
"""
|
||||
class Thumb(CommonElevationBehavior, CircularRippleBehavior, MDFloatLayout):
|
||||
"""Implements a thumb for the :class:`~MDSwitch` widget."""
|
||||
|
||||
def _set_ellipse(self, instance, value):
|
||||
self.ellipse.size = (self._ripple_rad, self._ripple_rad)
|
||||
@ -443,6 +615,14 @@ class Thumb(
|
||||
|
||||
|
||||
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)
|
||||
"""
|
||||
Indicates if the switch is active or inactive.
|
||||
@ -490,7 +670,8 @@ class MDSwitch(ThemableBehavior, FloatLayout):
|
||||
|
||||
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
|
||||
|
||||
@ -510,7 +691,8 @@ class MDSwitch(ThemableBehavior, FloatLayout):
|
||||
|
||||
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
|
||||
|
||||
@ -529,7 +711,7 @@ class MDSwitch(ThemableBehavior, FloatLayout):
|
||||
|
||||
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
|
||||
|
||||
@ -548,7 +730,7 @@ class MDSwitch(ThemableBehavior, FloatLayout):
|
||||
|
||||
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
|
||||
|
||||
@ -566,7 +748,8 @@ class MDSwitch(ThemableBehavior, FloatLayout):
|
||||
|
||||
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
|
||||
|
||||
@ -584,7 +767,7 @@ class MDSwitch(ThemableBehavior, FloatLayout):
|
||||
|
||||
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
|
||||
|
||||
@ -601,7 +784,7 @@ class MDSwitch(ThemableBehavior, FloatLayout):
|
||||
|
||||
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
|
||||
|
||||
@ -619,7 +802,8 @@ class MDSwitch(ThemableBehavior, FloatLayout):
|
||||
|
||||
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
|
||||
|
||||
@ -646,6 +830,11 @@ class MDSwitch(ThemableBehavior, FloatLayout):
|
||||
Clock.schedule_once(lambda x: self.on_active(self, self.active))
|
||||
|
||||
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):
|
||||
icon = icon_value if icon_value else "blank"
|
||||
self.ids.thumb.ids.icon.icon = icon
|
||||
@ -653,6 +842,8 @@ class MDSwitch(ThemableBehavior, FloatLayout):
|
||||
Clock.schedule_once(set_icon, 0.2)
|
||||
|
||||
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":
|
||||
size = (
|
||||
(
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user