├── .github
└── workflows
│ └── main.yml
├── .gitignore
├── README.md
├── assets
├── lottie.json
└── pass.png
├── buildozer.spec
├── kivymd
├── __init__.py
├── app.py
├── color_definitions.py
├── effects
│ ├── __init__.py
│ ├── roulettescroll
│ │ ├── LICENSE
│ │ ├── README.md
│ │ └── __init__.py
│ └── stiffscroll
│ │ ├── LICENSE
│ │ ├── README.md
│ │ └── __init__.py
├── factory_registers.py
├── font_definitions.py
├── fonts
│ ├── DejavuSans.ttf
│ ├── DejavuSansBold.ttf
│ ├── Marvel-Bold.ttf
│ ├── Poppins-Bold.ttf
│ ├── Poppins-Regular.ttf
│ ├── Roboto-Black.ttf
│ ├── Roboto-BlackItalic.ttf
│ ├── Roboto-Bold.ttf
│ ├── Roboto-BoldItalic.ttf
│ ├── Roboto-Italic.ttf
│ ├── Roboto-Light.ttf
│ ├── Roboto-LightItalic.ttf
│ ├── Roboto-Medium.ttf
│ ├── Roboto-MediumItalic.ttf
│ ├── Roboto-Regular.ttf
│ ├── Roboto-Thin.ttf
│ ├── Roboto-ThinItalic.ttf
│ └── materialdesignicons-webfont.ttf
├── icon_definitions.py
├── images
│ ├── folder.png
│ ├── kivy-logo-white-512.png
│ ├── kivymd_512.png
│ ├── kivymd_alpha.png
│ ├── kivymd_logo.png
│ ├── quad_shadow-0.png
│ ├── quad_shadow-1.png
│ ├── quad_shadow-2.png
│ ├── quad_shadow.atlas
│ ├── rec_shadow-0.png
│ ├── rec_shadow-1.png
│ ├── rec_shadow.atlas
│ ├── rec_st_shadow-0.png
│ ├── rec_st_shadow-1.png
│ ├── rec_st_shadow-2.png
│ ├── rec_st_shadow.atlas
│ ├── round_shadow-0.png
│ ├── round_shadow-1.png
│ ├── round_shadow-2.png
│ ├── round_shadow.atlas
│ └── transparent.png
├── material_resources.py
├── stiffscroll
│ ├── LICENSE
│ ├── README.md
│ └── __init__.py
├── tests
│ ├── pyinstaller
│ │ └── test_pyinstaller_packaging.py
│ ├── test_app.py
│ ├── test_font_definitions.py
│ └── test_icon_definitions.py
├── theming.py
├── theming_dynamic_text.py
├── toast
│ ├── LICENSE
│ ├── README.md
│ ├── __init__.py
│ ├── androidtoast
│ │ ├── __init__.py
│ │ └── androidtoast.py
│ └── kivytoast
│ │ ├── __init__.py
│ │ └── kivytoast.py
├── tools
│ ├── __init__.py
│ ├── packaging
│ │ ├── __init__.py
│ │ └── pyinstaller
│ │ │ ├── __init__.py
│ │ │ └── hook-kivymd.py
│ ├── release
│ │ ├── __init__.py
│ │ ├── argument_parser.py
│ │ ├── git_commands.py
│ │ ├── make_release.py
│ │ └── update_icons.py
│ └── update_icons.py
├── uix
│ ├── __init__.py
│ ├── backdrop.py
│ ├── banner.py
│ ├── behaviors
│ │ ├── __init__.py
│ │ ├── backgroundcolor_behavior.py
│ │ ├── backgroundcolorbehavior.py
│ │ ├── elevation.py
│ │ ├── focus_behavior.py
│ │ ├── hover_behavior.py
│ │ ├── magic_behavior.py
│ │ ├── ripple_behavior.py
│ │ ├── ripplebehavior.py
│ │ ├── toggle_behavior.py
│ │ └── touch_behavior.py
│ ├── bottomnavigation.py
│ ├── bottomsheet.py
│ ├── boxlayout.py
│ ├── button.py
│ ├── card.py
│ ├── carousel.py
│ ├── chip.py
│ ├── circularlayout.py
│ ├── context_menu.py
│ ├── datatables.py
│ ├── dialog.py
│ ├── dropdownitem.py
│ ├── expansionpanel.py
│ ├── filemanager.py
│ ├── floatlayout.py
│ ├── gridlayout.py
│ ├── imagelist.py
│ ├── label.py
│ ├── list.py
│ ├── menu.py
│ ├── navigationdrawer.py
│ ├── navigationrail.py
│ ├── picker.py
│ ├── progressbar.py
│ ├── progressloader.py
│ ├── refreshlayout.py
│ ├── relativelayout.py
│ ├── screen.py
│ ├── selection.py
│ ├── selectioncontrol.py
│ ├── slider.py
│ ├── snackbar.py
│ ├── spinner.py
│ ├── stacklayout.py
│ ├── swiper.py
│ ├── tab.py
│ ├── taptargetview.py
│ ├── textfield.py
│ ├── toolbar.py
│ ├── tooltip.py
│ └── useranimationcard.py
├── utils
│ ├── __init__.py
│ ├── asynckivy.py
│ ├── cropimage.py
│ ├── fitimage.py
│ ├── fpsmonitor.py
│ └── hot_reload_viewer.py
└── vendor
│ ├── __init__.py
│ ├── circleLayout
│ ├── LICENSE
│ ├── README.md
│ └── __init__.py
│ └── circularTimePicker
│ ├── LICENSE
│ ├── README.md
│ └── __init__.py
├── libs
├── encryption.py
├── firebase.py
├── modules
│ ├── AndroidAPI.py
│ ├── CardTextField.py
│ ├── List.py
│ ├── Toolbar.py
│ ├── dialogs.py
│ ├── picker.py
│ └── spinners.py
├── save_config.py
├── screens
│ ├── HomeScreen
│ │ ├── HomeScreen.kv
│ │ └── HomeScreen.py
│ ├── LoginScreen
│ │ ├── LoginScreen.kv
│ │ └── LoginScreen.py
│ ├── SettingsScreen
│ │ ├── SettingsScreen.kv
│ │ └── SettingsScreen.py
│ ├── SignupScreen
│ │ ├── SignupScreen.kv
│ │ └── SignupScreen.py
│ ├── classes.py
│ └── root.py
└── utils.py
├── main.py
├── requirements.txt
└── screenshots
├── 1.png
├── 2.png
├── 3.png
├── 4.png
├── 5.png
└── 6.png
/.github/workflows/main.yml:
--------------------------------------------------------------------------------
1 | name: Build APK
2 | on:
3 | push:
4 | tags:
5 | - 'v*.*.*'
6 |
7 | jobs:
8 | build-android:
9 | name: Build for Android
10 | runs-on: ubuntu-latest
11 | steps:
12 | - name: Checkout
13 | uses: actions/checkout@v2
14 | # Setting environment variables for app.
15 | - name: Create env file
16 | run: |
17 | cd ${{ github.workspace }}
18 | echo "import os
19 | os.environ['WEB_API_KEY'] = '${{ secrets.WEB_API_KEY }}'
20 | os.environ['DATABASE_URL'] = '${{ vars.DATABASE_URL }}'" > libs/firebase_config.py
21 | cat libs/firebase_config.py
22 | - name: Build with Buildozer
23 | run: |
24 | pip3 install --user --upgrade buildozer
25 | sudo apt update
26 | sudo apt install -y git zip unzip openjdk-17-jdk python3-pip autoconf libtool pkg-config zlib1g-dev libncurses5-dev libncursesw5-dev libtinfo5 cmake libffi-dev libssl-dev
27 | pip3 install --user --upgrade Cython==0.29.33 virtualenv
28 | export PATH=$PATH:~/.local/bin/
29 | export APP_ANDROID_ACCEPT_SDK_LICENSE=1
30 | export BUILDOZER_WARN_ON_ROOT=0
31 | buildozer android debug
32 | - name: Upload artifacts
33 | uses: actions/upload-artifact@v2
34 | with:
35 | name: package
36 | path: bin/*.apk
37 |
38 | - name: Apk Release
39 | uses: softprops/action-gh-release@v1
40 | if: startsWith(github.ref, 'refs/tags/')
41 | env:
42 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
43 | with:
44 | files: bin/*.apk
45 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | bin
2 | .buildozer
3 | test.py
4 | OldPasslock
5 | PassLOCK_old
6 | OtherStuff
7 | kivymd1
8 | kivymd2
9 | kivymd_orginal
10 | .idea
11 | screenshot_original
12 | __pycache__
13 | .vscode
14 | data
15 | test.txt
16 | libs/firebase_config.py
17 | backup_design.kv
18 | myapp.profile
19 | .env
20 | api_key.txt
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Passlock for android
7 |
8 |
9 |
14 |
15 |
16 | A Password Manager for android.
17 |
18 | Also Check Out Passlock for desktop 💻 [here.](https://github.com/AM-ash-OR-AM-I/PasslockDesktop)
19 |
20 | ## Features ✨
21 |
22 | * Backup and sync passwords across devices 💻📱.
23 | * Encrypted passwords using AES 128bit for maximum security.
24 | * Mimics Material v3 Monet engine with 🌙 Dark Mode, to use different 🎨 color themes. (Self-made)
25 | * Make strong passwords 🔑 through built in password generator.
26 | * Advanced 🔍 finding algorithm to search for passwords easily.
27 |
28 | ## Releases 📥️
29 |
30 | #### Check latest apk links for [📱 Android here.](https://github.com/AM-ash-OR-AM-I/Passlock/releases)
31 |
32 | ## Build 📦️
33 |
34 | * Get `WEB_API_KEY` from firebase project and add it to github secrets.
35 | * Add `DATABASE_URL` to repository variables.
36 | * Now you are all set build app 🎉, make some changes push it then tag a release 🚀
37 | * e.g. `git tag v0.0.1 && git push --tags`
38 | * Workflow will be triggered and will automatically create a release for apk.
39 |
40 | ## Screenshots 📱
41 |
42 |  |  |  |
43 | ------------------------- | ------------------------- | ------------------------- |
44 |  |  |  |
45 |
--------------------------------------------------------------------------------
/assets/pass.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AM-ash-OR-AM-I/Passlock/0232719654997770309bcdd8c84be8ff1dcce500/assets/pass.png
--------------------------------------------------------------------------------
/kivymd/__init__.py:
--------------------------------------------------------------------------------
1 | """
2 | KivyMD
3 | ======
4 |
5 | .. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/previous.png
6 |
7 | Is a collection of Material Design compliant widgets for use with,
8 | `Kivy cross-platform graphical framework `_
9 | a framework for cross-platform, touch-enabled graphical applications.
10 | The project's goal is to approximate Google's `Material Design spec
11 | `_ as close as possible without
12 | sacrificing ease of use or application performance.
13 |
14 | This library is a fork of the `KivyMD project
15 | `_ the author of which stopped supporting
16 | this project three years ago. We found the strength and brought this project
17 | to a new level. Currently we're in **beta** status, so things are changing
18 | all the time and we cannot promise any kind of API stability.
19 | However it is safe to vendor now and make use of what's currently available.
20 |
21 | Join the project! Just fork the project, branch out and submit a pull request
22 | when your patch is ready. If any changes are necessary, we'll guide you
23 | through the steps that need to be done via PR comments or access to your for
24 | may be requested to outright submit them. If you wish to become a project
25 | developer (permission to create branches on the project without forking for
26 | easier collaboration), have at least one PR approved and ask for it.
27 | If you contribute regularly to the project the role may be offered to you
28 | without asking too.
29 | """
30 |
31 | import os
32 |
33 | import kivy
34 | from kivy.logger import Logger
35 |
36 | __version__ = "0.104.2.dev0"
37 | """KivyMD version."""
38 |
39 | release = False
40 | kivy.require("2.0.0")
41 |
42 | try:
43 | from kivymd._version import __date__, __hash__, __short_hash__
44 | except ImportError:
45 | __hash__ = __short_hash__ = __date__ = ""
46 |
47 | path = os.path.dirname(__file__)
48 | """Path to KivyMD package directory."""
49 |
50 | fonts_path = os.path.join(path, f"fonts{os.sep}")
51 | """Path to fonts directory."""
52 |
53 | images_path = os.path.join(path, f"images{os.sep}")
54 | """Path to images directory."""
55 |
56 | _log_message = (
57 | "KivyMD:"
58 | + (" Release" if release else "")
59 | + f" {__version__}"
60 | + (f", git-{__short_hash__}" if __short_hash__ else "")
61 | + (f", {__date__}" if __date__ else "")
62 | + f' (installed at "{__file__}")'
63 | )
64 | Logger.info(_log_message)
65 |
66 | import kivymd.factory_registers # NOQA
67 | import kivymd.font_definitions # NOQA
68 | from kivymd.tools.packaging.pyinstaller import hooks_path # NOQA
69 |
--------------------------------------------------------------------------------
/kivymd/app.py:
--------------------------------------------------------------------------------
1 | """
2 | Themes/Material App
3 | ===================
4 |
5 | This module contains :class:`MDApp` class that is inherited from
6 | :class:`~kivy.app.App`. :class:`MDApp` has some properties needed for ``KivyMD``
7 | library (like :attr:`~MDApp.theme_cls`).
8 |
9 | You can turn on the monitor displaying the current ``FPS`` value in your application:
10 |
11 | .. code-block:: python
12 |
13 | KV = '''
14 | Screen:
15 |
16 | MDLabel:
17 | text: "Hello, World!"
18 | halign: "center"
19 | '''
20 |
21 | from kivy.lang import Builder
22 |
23 | from kivymd.app import MDApp
24 |
25 |
26 | class MainApp(MDApp):
27 | def build(self):
28 | return Builder.load_string(KV)
29 |
30 | def on_start(self):
31 | self.fps_monitor_start()
32 |
33 |
34 | MainApp().run()
35 |
36 | .. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/fps-monitor.png
37 | :width: 350 px
38 | :align: center
39 |
40 | """
41 |
42 | __all__ = ("MDApp",)
43 |
44 | from kivy.app import App
45 | from kivy.properties import ObjectProperty
46 |
47 | from kivymd.theming import ThemeManager
48 |
49 |
50 | class FpsMonitoring:
51 | """Adds a monitor to display the current FPS in the toolbar."""
52 |
53 | def fps_monitor_start(self):
54 | from kivy.core.window import Window
55 |
56 | from kivymd.utils.fpsmonitor import FpsMonitor
57 |
58 | monitor = FpsMonitor()
59 | monitor.start()
60 | Window.add_widget(monitor)
61 |
62 |
63 | class MDApp(App, FpsMonitoring):
64 | theme_cls = ObjectProperty()
65 | """
66 | Instance of :class:`~ThemeManager` class.
67 |
68 | .. Warning:: The :attr:`~theme_cls` attribute is already available
69 | in a class that is inherited from the :class:`~MDApp` class.
70 | The following code will result in an error!
71 |
72 | .. code-block:: python
73 |
74 | class MainApp(MDApp):
75 | theme_cls = ThemeManager()
76 | theme_cls.primary_palette = "Teal"
77 |
78 | .. Note:: Correctly do as shown below!
79 |
80 | .. code-block:: python
81 |
82 | class MainApp(MDApp):
83 | def build(self):
84 | self.theme_cls.primary_palette = "Teal"
85 |
86 | :attr:`theme_cls` is an :class:`~kivy.properties.ObjectProperty`.
87 | """
88 |
89 | def __init__(self, **kwargs):
90 | super().__init__(**kwargs)
91 | self.theme_cls = ThemeManager()
92 |
--------------------------------------------------------------------------------
/kivymd/effects/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AM-ash-OR-AM-I/Passlock/0232719654997770309bcdd8c84be8ff1dcce500/kivymd/effects/__init__.py
--------------------------------------------------------------------------------
/kivymd/effects/roulettescroll/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2010-2021 Kivy Team and other contributors
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy
4 | of this software and associated documentation files (the "Software"), to deal
5 | in the Software without restriction, including without limitation the rights
6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | copies of the Software, and to permit persons to whom the Software is
8 | furnished to do so, subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice shall be included in
11 | all copies or substantial portions of the Software.
12 |
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | THE SOFTWARE.
20 |
--------------------------------------------------------------------------------
/kivymd/effects/roulettescroll/README.md:
--------------------------------------------------------------------------------
1 | RouletteScrollEffect
2 | ===================
3 |
4 | This is a subclass of `kivy.effects.ScrollEffect` that simulates the
5 | motion of a roulette, or a notched wheel (think Wheel of Fortune). It is
6 | primarily designed for emulating the effect of the iOS and android date pickers.
7 |
8 | Usage
9 | -----
10 |
11 | Here's an example of using `RouletteScrollEffect` for a `kivy.uix.scrollview.ScrollView`:
12 |
13 | ```python
14 | from kivy.uix.gridlayout import GridLayout
15 | from kivy.uix.button import Button
16 | from kivy.uix.scrollview import ScrollView
17 |
18 | # Preparing a `GridLayout` inside a `ScrollView`.
19 | layout = GridLayout(cols=1, padding=10, size_hint=(None, None), width=500)
20 | layout.bind(minimum_height=layout.setter('height'))
21 |
22 | for i in range(30):
23 | btn = Button(text=str(i), size=(480, 40), size_hint=(None, None))
24 | layout.add_widget(btn)
25 |
26 | root = ScrollView(
27 | size_hint=(None, None),
28 | size=(500, 320),
29 | pos_hint={'center_x': .5, 'center_y': .5},
30 | do_scroll_x=False,
31 | )
32 | root.add_widget(layout)
33 |
34 | # Preparation complete. Now add the new scroll effect.
35 | root.effect_y = RouletteScrollEffect(anchor=20, interval=40)
36 | runTouchApp(root)
37 | ```
38 |
39 | Here the `ScrollView` scrolls through a series of buttons with height `40`. We then attached a `RouletteScrollEffect` with interval 40,
40 | corresponding to the button heights. This allows the scrolling to stop at
41 | the same offset no matter where it stops. The `RouletteScrollEffect.anchor`
42 | adjusts this offset.
43 |
44 | Customizations
45 | --------------
46 |
47 | Other settings that can be played with include:
48 |
49 | - `RouletteScrollEffect.pull_duration`
50 | - `RouletteScrollEffect.coasting_alpha`
51 | - `RouletteScrollEffect.pull_back_velocity`
52 | - `RouletteScrollEffect.terminal_velocity`
53 |
54 | See their module documentations for details.
55 |
56 | `RouletteScrollEffect` has one event ``on_coasted_to_stop`` that
57 | is fired when the roulette stops, "making a selection". It can be listened to
58 | for handling or cleaning up choice making.
--------------------------------------------------------------------------------
/kivymd/effects/stiffscroll/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2014 LogicalDash
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of
6 | this software and associated documentation files (the "Software"), to deal in
7 | the Software without restriction, including without limitation the rights to
8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9 | the Software, and to permit persons to whom the Software is furnished to do so,
10 | subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 |
--------------------------------------------------------------------------------
/kivymd/effects/stiffscroll/README.md:
--------------------------------------------------------------------------------
1 | stiffscroll
2 | ===========
3 |
4 | A ScrollEffect for use with a Kivy ScrollView. It makes scrolling more
5 | laborious as you reach the edge of the scrollable area.
6 |
7 | A ScrollView constructed with StiffScrollEffect,
8 | eg. ScrollView(effect_cls=StiffScrollEffect), will get harder to
9 | scroll as you get nearer to its edges. You can scroll all the way to
10 | the edge if you want to, but it will take more finger-movement than
11 | usual.
12 |
13 | Unlike DampedScrollEffect, it is impossible to overscroll with
14 | StiffScrollEffect. That means you cannot push the contents of the
15 | ScrollView far enough to see what's beneath them. This is appropriate
16 | if the ScrollView contains, eg., a background image, like a desktop
17 | wallpaper. Overscrolling may give the impression that there is some
18 | reason to overscroll, even if just to take a peek beneath, and that
19 | impression may be misleading.
20 |
21 | StiffScrollEffect was written by Zachary Spector. His other stuff is at:
22 | https://github.com/LogicalDash/
23 | He can be reached, and possibly hired, at:
24 | zacharyspector@gmail.com
--------------------------------------------------------------------------------
/kivymd/factory_registers.py:
--------------------------------------------------------------------------------
1 | """
2 | Register KivyMD widgets to use without import
3 | """
4 |
5 | from kivy.factory import Factory
6 |
7 | r = Factory.register
8 | r("MDNavigationRail", module="kivymd.uix.navigationrail")
9 | r("MDSwiper", module="kivymd.uix.swiper")
10 | r("MDCarousel", module="kivymd.uix.carousel")
11 | r("MDFloatLayout", module="kivymd.uix.floatlayout")
12 | r("MDScreen", module="kivymd.uix.screen")
13 | r("MDBoxLayout", module="kivymd.uix.boxlayout")
14 | r("MDRelativeLayout", module="kivymd.uix.relativelayout")
15 | r("MDGridLayout", module="kivymd.uix.gridlayout")
16 | r("MDStackLayout", module="kivymd.uix.stacklayout")
17 | r("MDExpansionPanel", module="kivymd.uix.expansionpanel")
18 | r("MDExpansionPanelOneLine", module="kivymd.uix.expansionpanel")
19 | r("MDExpansionPanelTwoLine", module="kivymd.uix.expansionpanel")
20 | r("MDExpansionPanelThreeLine", module="kivymd.uix.expansionpanel")
21 | r("FitImage", module="kivymd.utils.fitimage")
22 | r("MDBackdrop", module="kivymd.uix.backdrop")
23 | r("MDBanner", module="kivymd.uix.banner")
24 | r("MDTooltip", module="kivymd.uix.tooltip")
25 | r("MDBottomNavigation", module="kivymd.uix.bottomnavigation")
26 | r("MDBottomNavigationItem", module="kivymd.uix.bottomnavigation")
27 | r("MDToggleButton", module="kivymd.uix.behaviors.toggle_behavior")
28 | r("MDFloatingActionButtonSpeedDial", module="kivymd.uix.button")
29 | r("MDIconButton", module="kivymd.uix.button")
30 | r("MDRoundImageButton", module="kivymd.uix.button")
31 | r("MDFlatButton", module="kivymd.uix.button")
32 | r("MDRaisedButton", module="kivymd.uix.button")
33 | r("MDFloatingActionButton", module="kivymd.uix.button")
34 | r("MDRectangleFlatButton", module="kivymd.uix.button")
35 | r("MDTextButton", module="kivymd.uix.button")
36 | r("MDCustomRoundIconButton", module="kivymd.uix.button")
37 | r("MDRoundFlatButton", module="kivymd.uix.button")
38 | r("MDFillRoundFlatButton", module="kivymd.uix.button")
39 | r("MDRectangleFlatIconButton", module="kivymd.uix.button")
40 | r("MDRoundFlatIconButton", module="kivymd.uix.button")
41 | r("MDFillRoundFlatIconButton", module="kivymd.uix.button")
42 | r("MDCard", module="kivymd.uix.card")
43 | r("MDSeparator", module="kivymd.uix.card")
44 | r("MDSelectionList", module="kivymd.uix.selection")
45 | r("MDChip", module="kivymd.uix.chip")
46 | r("MDChooseChip", module="kivymd.uix.chip")
47 | r("SmartTile", module="kivymd.uix.imagelist")
48 | r("SmartTileWithLabel", module="kivymd.uix.imagelist")
49 | r("SmartTileWithStar", module="kivymd.uix.imagelist")
50 | r("MDLabel", module="kivymd.uix.label")
51 | r("MDIcon", module="kivymd.uix.label")
52 | r("MDList", module="kivymd.uix.list")
53 | r("ILeftBody", module="kivymd.uix.list")
54 | r("ILeftBodyTouch", module="kivymd.uix.list")
55 | r("IRightBody", module="kivymd.uix.list")
56 | r("IRightBodyTouch", module="kivymd.uix.list")
57 | r("ContainerSupport", module="kivymd.uix.list")
58 | r("OneLineListItem", module="kivymd.uix.list")
59 | r("TwoLineListItem", module="kivymd.uix.list")
60 | r("ThreeLineListItem", module="kivymd.uix.list")
61 | r("OneLineAvatarListItem", module="kivymd.uix.list")
62 | r("TwoLineAvatarListItem", module="kivymd.uix.list")
63 | r("ThreeLineAvatarListItem", module="kivymd.uix.list")
64 | r("OneLineIconListItem", module="kivymd.uix.list")
65 | r("TwoLineIconListItem", module="kivymd.uix.list")
66 | r("ThreeLineIconListItem", module="kivymd.uix.list")
67 | r("OneLineRightIconListItem", module="kivymd.uix.list")
68 | r("TwoLineRightIconListItem", module="kivymd.uix.list")
69 | r("ThreeLineRightIconListItem", module="kivymd.uix.list")
70 | r("OneLineAvatarIconListItem", module="kivymd.uix.list")
71 | r("TwoLineAvatarIconListItem", module="kivymd.uix.list")
72 | r("ThreeLineAvatarIconListItem", module="kivymd.uix.list")
73 | r("HoverBehavior", module="kivymd.uix.behaviors.hover_behavior")
74 | r("FocusBehavior", module="kivymd.uix.behaviors.focus_behavior")
75 | r("MagicBehavior", module="kivymd.uix.behaviors.magic_behavior")
76 | r("MDNavigationDrawer", module="kivymd.uix.navigationdrawer")
77 | r("MDNavigationLayout", module="kivymd.uix.navigationdrawer")
78 | r("MDProgressBar", module="kivymd.uix.progressbar")
79 | r("MDScrollViewRefreshLayout", module="kivymd.uix.refreshlayout")
80 | r("MDCheckbox", module="kivymd.uix.selectioncontrol")
81 | r("MDSwitch", module="kivymd.uix.selectioncontrol")
82 | r("MDSlider", module="kivymd.uix.slider")
83 | r("MDSpinner", module="kivymd.uix.spinner")
84 | r("MDTabs", module="kivymd.uix.tab")
85 | r("MDTextField", module="kivymd.uix.textfield")
86 | r("MDTextFieldRound", module="kivymd.uix.textfield")
87 | r("MDTextFieldRect", module="kivymd.uix.textfield")
88 | r("MDToolbar", module="kivymd.uix.toolbar")
89 | r("MDBottomAppBar", module="kivymd.uix.toolbar")
90 | r("MDDropDownItem", module="kivymd.uix.dropdownitem")
91 | r("MDCircularLayout", module="kivymd.uix.circularlayout")
92 |
--------------------------------------------------------------------------------
/kivymd/font_definitions.py:
--------------------------------------------------------------------------------
1 | """
2 | Themes/Font Definitions
3 | =======================
4 |
5 | .. seealso::
6 |
7 | `Material Design spec, The type system `_
8 | """
9 |
10 | from kivy.core.text import LabelBase
11 |
12 | from kivymd import fonts_path
13 |
14 | fonts = [
15 | {
16 | "name": "Roboto",
17 | "fn_regular": fonts_path + "Roboto-Regular.ttf",
18 | "fn_bold": fonts_path + "Roboto-Bold.ttf",
19 | "fn_italic": fonts_path + "Roboto-Italic.ttf",
20 | "fn_bolditalic": fonts_path + "Roboto-BoldItalic.ttf",
21 | },
22 | {
23 | "name": "RobotoThin",
24 | "fn_regular": fonts_path + "Roboto-Thin.ttf",
25 | "fn_italic": fonts_path + "Roboto-ThinItalic.ttf",
26 | },
27 | {
28 | "name": "RobotoLight",
29 | "fn_regular": fonts_path + "Roboto-Light.ttf",
30 | "fn_italic": fonts_path + "Roboto-LightItalic.ttf",
31 | },
32 | {
33 | "name": "RobotoMedium",
34 | "fn_regular": fonts_path + "Roboto-Medium.ttf",
35 | "fn_italic": fonts_path + "Roboto-MediumItalic.ttf",
36 | },
37 | {
38 | "name": "RobotoBlack",
39 | "fn_regular": fonts_path + "Roboto-Black.ttf",
40 | "fn_italic": fonts_path + "Roboto-BlackItalic.ttf",
41 | },
42 | {
43 | "name": "Marvel-Bold",
44 | "fn_regular": fonts_path + "Marvel-Bold.ttf",
45 | "fn_italic": fonts_path + "Marvel-Bold.ttf",
46 | },
47 | {
48 | "name": "Poppins",
49 | "fn_regular": fonts_path + "Poppins-Regular.ttf",
50 | "fn_bold": fonts_path + "Poppins-Bold.ttf",
51 | },
52 | {
53 | "name": "BigCircleFont",
54 | "fn_regular": fonts_path + "DejavuSans.ttf",
55 | },
56 | {
57 | "name": "Icons",
58 | "fn_regular": fonts_path + "materialdesignicons-webfont.ttf",
59 | },
60 | ]
61 |
62 | for font in fonts:
63 | LabelBase.register(**font)
64 |
65 | theme_font_styles = [
66 | "H1",
67 | "H2",
68 | "H3",
69 | "H4",
70 | "H5",
71 | "H6",
72 | "Subtitle1",
73 | "Subtitle2",
74 | "Body1",
75 | "Body2",
76 | "Button",
77 | "Caption",
78 | "Overline",
79 | "Icon",
80 | ]
81 | """
82 | .. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/font-styles-2.png
83 | """
84 |
85 |
--------------------------------------------------------------------------------
/kivymd/fonts/DejavuSans.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AM-ash-OR-AM-I/Passlock/0232719654997770309bcdd8c84be8ff1dcce500/kivymd/fonts/DejavuSans.ttf
--------------------------------------------------------------------------------
/kivymd/fonts/DejavuSansBold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AM-ash-OR-AM-I/Passlock/0232719654997770309bcdd8c84be8ff1dcce500/kivymd/fonts/DejavuSansBold.ttf
--------------------------------------------------------------------------------
/kivymd/fonts/Marvel-Bold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AM-ash-OR-AM-I/Passlock/0232719654997770309bcdd8c84be8ff1dcce500/kivymd/fonts/Marvel-Bold.ttf
--------------------------------------------------------------------------------
/kivymd/fonts/Poppins-Bold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AM-ash-OR-AM-I/Passlock/0232719654997770309bcdd8c84be8ff1dcce500/kivymd/fonts/Poppins-Bold.ttf
--------------------------------------------------------------------------------
/kivymd/fonts/Poppins-Regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AM-ash-OR-AM-I/Passlock/0232719654997770309bcdd8c84be8ff1dcce500/kivymd/fonts/Poppins-Regular.ttf
--------------------------------------------------------------------------------
/kivymd/fonts/Roboto-Black.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AM-ash-OR-AM-I/Passlock/0232719654997770309bcdd8c84be8ff1dcce500/kivymd/fonts/Roboto-Black.ttf
--------------------------------------------------------------------------------
/kivymd/fonts/Roboto-BlackItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AM-ash-OR-AM-I/Passlock/0232719654997770309bcdd8c84be8ff1dcce500/kivymd/fonts/Roboto-BlackItalic.ttf
--------------------------------------------------------------------------------
/kivymd/fonts/Roboto-Bold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AM-ash-OR-AM-I/Passlock/0232719654997770309bcdd8c84be8ff1dcce500/kivymd/fonts/Roboto-Bold.ttf
--------------------------------------------------------------------------------
/kivymd/fonts/Roboto-BoldItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AM-ash-OR-AM-I/Passlock/0232719654997770309bcdd8c84be8ff1dcce500/kivymd/fonts/Roboto-BoldItalic.ttf
--------------------------------------------------------------------------------
/kivymd/fonts/Roboto-Italic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AM-ash-OR-AM-I/Passlock/0232719654997770309bcdd8c84be8ff1dcce500/kivymd/fonts/Roboto-Italic.ttf
--------------------------------------------------------------------------------
/kivymd/fonts/Roboto-Light.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AM-ash-OR-AM-I/Passlock/0232719654997770309bcdd8c84be8ff1dcce500/kivymd/fonts/Roboto-Light.ttf
--------------------------------------------------------------------------------
/kivymd/fonts/Roboto-LightItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AM-ash-OR-AM-I/Passlock/0232719654997770309bcdd8c84be8ff1dcce500/kivymd/fonts/Roboto-LightItalic.ttf
--------------------------------------------------------------------------------
/kivymd/fonts/Roboto-Medium.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AM-ash-OR-AM-I/Passlock/0232719654997770309bcdd8c84be8ff1dcce500/kivymd/fonts/Roboto-Medium.ttf
--------------------------------------------------------------------------------
/kivymd/fonts/Roboto-MediumItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AM-ash-OR-AM-I/Passlock/0232719654997770309bcdd8c84be8ff1dcce500/kivymd/fonts/Roboto-MediumItalic.ttf
--------------------------------------------------------------------------------
/kivymd/fonts/Roboto-Regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AM-ash-OR-AM-I/Passlock/0232719654997770309bcdd8c84be8ff1dcce500/kivymd/fonts/Roboto-Regular.ttf
--------------------------------------------------------------------------------
/kivymd/fonts/Roboto-Thin.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AM-ash-OR-AM-I/Passlock/0232719654997770309bcdd8c84be8ff1dcce500/kivymd/fonts/Roboto-Thin.ttf
--------------------------------------------------------------------------------
/kivymd/fonts/Roboto-ThinItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AM-ash-OR-AM-I/Passlock/0232719654997770309bcdd8c84be8ff1dcce500/kivymd/fonts/Roboto-ThinItalic.ttf
--------------------------------------------------------------------------------
/kivymd/fonts/materialdesignicons-webfont.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AM-ash-OR-AM-I/Passlock/0232719654997770309bcdd8c84be8ff1dcce500/kivymd/fonts/materialdesignicons-webfont.ttf
--------------------------------------------------------------------------------
/kivymd/images/folder.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AM-ash-OR-AM-I/Passlock/0232719654997770309bcdd8c84be8ff1dcce500/kivymd/images/folder.png
--------------------------------------------------------------------------------
/kivymd/images/kivy-logo-white-512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AM-ash-OR-AM-I/Passlock/0232719654997770309bcdd8c84be8ff1dcce500/kivymd/images/kivy-logo-white-512.png
--------------------------------------------------------------------------------
/kivymd/images/kivymd_512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AM-ash-OR-AM-I/Passlock/0232719654997770309bcdd8c84be8ff1dcce500/kivymd/images/kivymd_512.png
--------------------------------------------------------------------------------
/kivymd/images/kivymd_alpha.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AM-ash-OR-AM-I/Passlock/0232719654997770309bcdd8c84be8ff1dcce500/kivymd/images/kivymd_alpha.png
--------------------------------------------------------------------------------
/kivymd/images/kivymd_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AM-ash-OR-AM-I/Passlock/0232719654997770309bcdd8c84be8ff1dcce500/kivymd/images/kivymd_logo.png
--------------------------------------------------------------------------------
/kivymd/images/quad_shadow-0.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AM-ash-OR-AM-I/Passlock/0232719654997770309bcdd8c84be8ff1dcce500/kivymd/images/quad_shadow-0.png
--------------------------------------------------------------------------------
/kivymd/images/quad_shadow-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AM-ash-OR-AM-I/Passlock/0232719654997770309bcdd8c84be8ff1dcce500/kivymd/images/quad_shadow-1.png
--------------------------------------------------------------------------------
/kivymd/images/quad_shadow-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AM-ash-OR-AM-I/Passlock/0232719654997770309bcdd8c84be8ff1dcce500/kivymd/images/quad_shadow-2.png
--------------------------------------------------------------------------------
/kivymd/images/quad_shadow.atlas:
--------------------------------------------------------------------------------
1 | {"quad_shadow-1.png": {"20": [2, 136, 128, 128], "21": [132, 136, 128, 128], "22": [262, 136, 128, 128], "23": [2, 6, 128, 128], "19": [132, 266, 128, 128], "18": [2, 266, 128, 128], "1": [262, 266, 128, 128], "3": [262, 6, 128, 128], "2": [132, 6, 128, 128]}, "quad_shadow-0.png": {"11": [262, 266, 128, 128], "10": [132, 266, 128, 128], "13": [132, 136, 128, 128], "12": [2, 136, 128, 128], "15": [2, 6, 128, 128], "14": [262, 136, 128, 128], "17": [262, 6, 128, 128], "16": [132, 6, 128, 128], "0": [2, 266, 128, 128]}, "quad_shadow-2.png": {"5": [132, 266, 128, 128], "4": [2, 266, 128, 128], "7": [2, 136, 128, 128], "6": [262, 266, 128, 128], "9": [262, 136, 128, 128], "8": [132, 136, 128, 128]}}
--------------------------------------------------------------------------------
/kivymd/images/rec_shadow-0.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AM-ash-OR-AM-I/Passlock/0232719654997770309bcdd8c84be8ff1dcce500/kivymd/images/rec_shadow-0.png
--------------------------------------------------------------------------------
/kivymd/images/rec_shadow-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AM-ash-OR-AM-I/Passlock/0232719654997770309bcdd8c84be8ff1dcce500/kivymd/images/rec_shadow-1.png
--------------------------------------------------------------------------------
/kivymd/images/rec_shadow.atlas:
--------------------------------------------------------------------------------
1 | {"rec_shadow-1.png": {"20": [2, 266, 256, 128], "21": [260, 266, 256, 128], "22": [518, 266, 256, 128], "23": [776, 266, 256, 128], "3": [260, 136, 256, 128], "2": [2, 136, 256, 128], "5": [776, 136, 256, 128], "4": [518, 136, 256, 128], "7": [260, 6, 256, 128], "6": [2, 6, 256, 128], "9": [776, 6, 256, 128], "8": [518, 6, 256, 128]}, "rec_shadow-0.png": {"11": [518, 266, 256, 128], "10": [260, 266, 256, 128], "13": [2, 136, 256, 128], "12": [776, 266, 256, 128], "15": [518, 136, 256, 128], "14": [260, 136, 256, 128], "17": [2, 6, 256, 128], "16": [776, 136, 256, 128], "19": [518, 6, 256, 128], "18": [260, 6, 256, 128], "1": [776, 6, 256, 128], "0": [2, 266, 256, 128]}}
--------------------------------------------------------------------------------
/kivymd/images/rec_st_shadow-0.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AM-ash-OR-AM-I/Passlock/0232719654997770309bcdd8c84be8ff1dcce500/kivymd/images/rec_st_shadow-0.png
--------------------------------------------------------------------------------
/kivymd/images/rec_st_shadow-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AM-ash-OR-AM-I/Passlock/0232719654997770309bcdd8c84be8ff1dcce500/kivymd/images/rec_st_shadow-1.png
--------------------------------------------------------------------------------
/kivymd/images/rec_st_shadow-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AM-ash-OR-AM-I/Passlock/0232719654997770309bcdd8c84be8ff1dcce500/kivymd/images/rec_st_shadow-2.png
--------------------------------------------------------------------------------
/kivymd/images/rec_st_shadow.atlas:
--------------------------------------------------------------------------------
1 | {"rec_st_shadow-0.png": {"11": [262, 138, 128, 256], "10": [132, 138, 128, 256], "13": [522, 138, 128, 256], "12": [392, 138, 128, 256], "15": [782, 138, 128, 256], "14": [652, 138, 128, 256], "16": [912, 138, 128, 256], "0": [2, 138, 128, 256]}, "rec_st_shadow-1.png": {"20": [522, 138, 128, 256], "21": [652, 138, 128, 256], "17": [2, 138, 128, 256], "23": [912, 138, 128, 256], "19": [262, 138, 128, 256], "18": [132, 138, 128, 256], "22": [782, 138, 128, 256], "1": [392, 138, 128, 256]}, "rec_st_shadow-2.png": {"3": [132, 138, 128, 256], "2": [2, 138, 128, 256], "5": [392, 138, 128, 256], "4": [262, 138, 128, 256], "7": [652, 138, 128, 256], "6": [522, 138, 128, 256], "9": [912, 138, 128, 256], "8": [782, 138, 128, 256]}}
--------------------------------------------------------------------------------
/kivymd/images/round_shadow-0.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AM-ash-OR-AM-I/Passlock/0232719654997770309bcdd8c84be8ff1dcce500/kivymd/images/round_shadow-0.png
--------------------------------------------------------------------------------
/kivymd/images/round_shadow-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AM-ash-OR-AM-I/Passlock/0232719654997770309bcdd8c84be8ff1dcce500/kivymd/images/round_shadow-1.png
--------------------------------------------------------------------------------
/kivymd/images/round_shadow-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AM-ash-OR-AM-I/Passlock/0232719654997770309bcdd8c84be8ff1dcce500/kivymd/images/round_shadow-2.png
--------------------------------------------------------------------------------
/kivymd/images/round_shadow.atlas:
--------------------------------------------------------------------------------
1 | {"round_shadow-1.png": {"20": [2, 136, 128, 128], "21": [132, 136, 128, 128], "22": [262, 136, 128, 128], "23": [2, 6, 128, 128], "19": [132, 266, 128, 128], "18": [2, 266, 128, 128], "1": [262, 266, 128, 128], "3": [262, 6, 128, 128], "2": [132, 6, 128, 128]}, "round_shadow-0.png": {"11": [262, 266, 128, 128], "10": [132, 266, 128, 128], "13": [132, 136, 128, 128], "12": [2, 136, 128, 128], "15": [2, 6, 128, 128], "14": [262, 136, 128, 128], "17": [262, 6, 128, 128], "16": [132, 6, 128, 128], "0": [2, 266, 128, 128]}, "round_shadow-2.png": {"5": [132, 266, 128, 128], "4": [2, 266, 128, 128], "7": [2, 136, 128, 128], "6": [262, 266, 128, 128], "9": [262, 136, 128, 128], "8": [132, 136, 128, 128]}}
--------------------------------------------------------------------------------
/kivymd/images/transparent.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AM-ash-OR-AM-I/Passlock/0232719654997770309bcdd8c84be8ff1dcce500/kivymd/images/transparent.png
--------------------------------------------------------------------------------
/kivymd/material_resources.py:
--------------------------------------------------------------------------------
1 | """
2 | Material Resources
3 | ==================
4 | """
5 |
6 | import os
7 |
8 | from kivy.core.window import Window
9 | from kivy.metrics import dp
10 | from kivy.utils import platform
11 |
12 | if "KIVY_DOC_INCLUDE" in os.environ:
13 | dp = lambda x: x # NOQA: F811
14 |
15 | # Feel free to override this const if you're designing for a device such as
16 | # a GNU/Linux tablet.
17 | DEVICE_IOS = platform == "ios" or platform == "macosx"
18 | if platform != "android" and platform != "ios":
19 | DEVICE_TYPE = "desktop"
20 | elif Window.width >= dp(600) and Window.height >= dp(600):
21 | DEVICE_TYPE = "tablet"
22 | else:
23 | DEVICE_TYPE = "mobile"
24 |
25 | if DEVICE_TYPE == "mobile":
26 | MAX_NAV_DRAWER_WIDTH = dp(300)
27 | HORIZ_MARGINS = dp(16)
28 | STANDARD_INCREMENT = dp(56)
29 | PORTRAIT_TOOLBAR_HEIGHT = STANDARD_INCREMENT
30 | LANDSCAPE_TOOLBAR_HEIGHT = STANDARD_INCREMENT - dp(8)
31 | else:
32 | MAX_NAV_DRAWER_WIDTH = dp(400)
33 | HORIZ_MARGINS = dp(24)
34 | STANDARD_INCREMENT = dp(64)
35 | PORTRAIT_TOOLBAR_HEIGHT = STANDARD_INCREMENT
36 | LANDSCAPE_TOOLBAR_HEIGHT = STANDARD_INCREMENT
37 |
38 | TOUCH_TARGET_HEIGHT = dp(48)
39 |
--------------------------------------------------------------------------------
/kivymd/stiffscroll/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2014 LogicalDash
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of
6 | this software and associated documentation files (the "Software"), to deal in
7 | the Software without restriction, including without limitation the rights to
8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9 | the Software, and to permit persons to whom the Software is furnished to do so,
10 | subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 |
--------------------------------------------------------------------------------
/kivymd/stiffscroll/README.md:
--------------------------------------------------------------------------------
1 | stiffscroll
2 | ===========
3 |
4 | A ScrollEffect for use with a Kivy ScrollView. It makes scrolling more
5 | laborious as you reach the edge of the scrollable area.
6 |
7 | A ScrollView constructed with StiffScrollEffect,
8 | eg. ScrollView(effect_cls=StiffScrollEffect), will get harder to
9 | scroll as you get nearer to its edges. You can scroll all the way to
10 | the edge if you want to, but it will take more finger-movement than
11 | usual.
12 |
13 | Unlike DampedScrollEffect, it is impossible to overscroll with
14 | StiffScrollEffect. That means you cannot push the contents of the
15 | ScrollView far enough to see what's beneath them. This is appropriate
16 | if the ScrollView contains, eg., a background image, like a desktop
17 | wallpaper. Overscrolling may give the impression that there is some
18 | reason to overscroll, even if just to take a peek beneath, and that
19 | impression may be misleading.
20 |
21 | StiffScrollEffect was written by Zachary Spector. His other stuff is at:
22 | https://github.com/LogicalDash/
23 | He can be reached, and possibly hired, at:
24 | zacharyspector@gmail.com
--------------------------------------------------------------------------------
/kivymd/tests/pyinstaller/test_pyinstaller_packaging.py:
--------------------------------------------------------------------------------
1 | """
2 | PyInstaller freezing test
3 | =========================
4 |
5 | PyInstaller must package KivyMD apps correctly.
6 | """
7 |
8 | import subprocess
9 |
10 | from PyInstaller import __main__ as pyi_main
11 |
12 |
13 | def test_datas(tmp_path):
14 | """Test fonts and images."""
15 | app_name = "userapp"
16 | workpath = tmp_path / "build"
17 | distpath = tmp_path / "dist"
18 | app = tmp_path / (app_name + ".py")
19 | app.write_text(
20 | """
21 | import os
22 | import kivymd
23 | from kivy.core.text import LabelBase
24 |
25 | fonts = os.listdir(kivymd.fonts_path)
26 | print(fonts)
27 | assert "Roboto-Regular.ttf" in fonts
28 | assert "materialdesignicons-webfont.ttf" in fonts
29 | print(LabelBase._fonts.keys())
30 | assert "Roboto" in LabelBase._fonts.keys() # NOQA
31 | assert "Icons" in LabelBase._fonts.keys() # NOQA
32 |
33 | images = os.listdir(kivymd.images_path)
34 | print(images)
35 | assert "folder.png" in images
36 | assert "rec_shadow.atlas" in images
37 | """
38 | )
39 | pyi_main.run(
40 | [
41 | "--workpath",
42 | str(workpath),
43 | "--distpath",
44 | str(distpath),
45 | "--specpath",
46 | str(tmp_path),
47 | str(app),
48 | ]
49 | )
50 | subprocess.run([str(distpath / app_name / app_name)], check=True)
51 |
52 |
53 | def test_widgets(tmp_path):
54 | """Test that all widgets are accesible."""
55 | app_name = "userapp"
56 | workpath = tmp_path / "build"
57 | distpath = tmp_path / "dist"
58 | app = tmp_path / (app_name + ".py")
59 | app.write_text(
60 | """
61 | import os
62 | import kivymd # NOQA
63 | __import__("kivymd.uix.label")
64 | __import__("kivymd.uix.button")
65 | __import__("kivymd.uix.list")
66 | __import__("kivymd.uix.navigationdrawer")
67 |
68 | print(os.listdir(os.path.dirname(kivymd.uix.__path__[0])))
69 | """
70 | )
71 | pyi_main.run(
72 | [
73 | "--workpath",
74 | str(workpath),
75 | "--distpath",
76 | str(distpath),
77 | "--specpath",
78 | str(tmp_path),
79 | str(app),
80 | ]
81 | )
82 | subprocess.run([str(distpath / app_name / app_name)], check=True)
83 |
--------------------------------------------------------------------------------
/kivymd/tests/test_app.py:
--------------------------------------------------------------------------------
1 | from kivy import lang
2 | from kivy.clock import Clock
3 | from kivy.tests.common import GraphicUnitTest
4 |
5 | from kivymd.app import MDApp
6 | from kivymd.theming import ThemeManager
7 |
8 |
9 | class AppTest(GraphicUnitTest):
10 | def test_start_raw_app(self):
11 | lang._delayed_start = None
12 | a = MDApp()
13 | Clock.schedule_once(a.stop, 0.1)
14 | a.run()
15 |
16 | def test_theme_manager_existance(self):
17 | lang._delayed_start = None
18 | a = MDApp()
19 | Clock.schedule_once(a.stop, 0.1)
20 | a.run()
21 | assert isinstance(a.theme_cls, ThemeManager)
22 |
--------------------------------------------------------------------------------
/kivymd/tests/test_font_definitions.py:
--------------------------------------------------------------------------------
1 | def test_fonts_registration():
2 | # This should register fonts:
3 | from kivy.core.text import LabelBase
4 |
5 | import kivymd # NOQA
6 |
7 | fonts = [
8 | "Roboto",
9 | "RobotoThin",
10 | "RobotoLight",
11 | "RobotoMedium",
12 | "RobotoBlack",
13 | "Icons",
14 | ]
15 | for font in fonts:
16 | assert font in LabelBase._fonts.keys()
17 |
--------------------------------------------------------------------------------
/kivymd/tests/test_icon_definitions.py:
--------------------------------------------------------------------------------
1 | def test_icons_have_size():
2 | from kivy.core.text import Label
3 |
4 | from kivymd.icon_definitions import md_icons
5 |
6 | lbl = Label(font_name="Icons")
7 | for icon_name, icon_value in md_icons.items():
8 | assert len(icon_value) == 1
9 | lbl.refresh()
10 | assert lbl.get_extents(icon_value) is not None
11 |
--------------------------------------------------------------------------------
/kivymd/theming_dynamic_text.py:
--------------------------------------------------------------------------------
1 | """
2 | Theming Dynamic Text
3 | ====================
4 |
5 | Two implementations. The first is based on color brightness obtained from-
6 | https://www.w3.org/TR/AERT#color-contrast
7 | The second is based on relative luminance calculation for sRGB obtained from-
8 | https://www.w3.org/TR/2008/REC-WCAG20-20081211/#relativeluminancedef
9 | and contrast ratio calculation obtained from-
10 | https://www.w3.org/TR/2008/REC-WCAG20-20081211/#contrast-ratiodef
11 |
12 | Preliminary testing suggests color brightness more closely matches the
13 | `Material Design spec` suggested text colors, but the alternative implementation
14 | is both newer and the current 'correct' recommendation, so is included here
15 | as an option.
16 | """
17 |
18 |
19 | def _color_brightness(color):
20 | # Implementation of color brightness method
21 | brightness = color[0] * 299 + color[1] * 587 + color[2] * 114
22 | brightness = brightness
23 | return brightness
24 |
25 |
26 | def _black_or_white_by_color_brightness(color):
27 | if _color_brightness(color) >= 500:
28 | return "black"
29 | else:
30 | return "white"
31 |
32 |
33 | def _normalized_channel(color):
34 | # Implementation of contrast ratio and relative luminance method
35 | if color <= 0.03928:
36 | return color / 12.92
37 | else:
38 | return ((color + 0.055) / 1.055) ** 2.4
39 |
40 |
41 | def _luminance(color):
42 | rg = _normalized_channel(color[0])
43 | gg = _normalized_channel(color[1])
44 | bg = _normalized_channel(color[2])
45 | return 0.2126 * rg + 0.7152 * gg + 0.0722 * bg
46 |
47 |
48 | def _black_or_white_by_contrast_ratio(color):
49 | l_color = _luminance(color)
50 | l_black = 0.0
51 | l_white = 1.0
52 | b_contrast = (l_color + 0.05) / (l_black + 0.05)
53 | w_contrast = (l_white + 0.05) / (l_color + 0.05)
54 | return "white" if w_contrast >= b_contrast else "black"
55 |
56 |
57 | def get_contrast_text_color(color, use_color_brightness=True):
58 | if use_color_brightness:
59 | contrast_color = _black_or_white_by_color_brightness(color)
60 | else:
61 | contrast_color = _black_or_white_by_contrast_ratio(color)
62 | if contrast_color == "white":
63 | return 1, 1, 1, 1
64 | else:
65 | return 0, 0, 0, 1
66 |
67 |
68 | if __name__ == "__main__":
69 | from kivy.utils import get_color_from_hex
70 |
71 | from kivymd.color_definitions import colors, text_colors
72 |
73 | for c in colors.items():
74 | if c[0] in ["Light", "Dark"]:
75 | continue
76 | color = c[0]
77 | print(f"For the {color} color palette:")
78 | for name, hex_color in c[1].items():
79 | if hex_color:
80 | col = get_color_from_hex(hex_color)
81 | col_bri = get_contrast_text_color(col)
82 | con_rat = get_contrast_text_color(
83 | col, use_color_brightness=False
84 | )
85 | text_color = text_colors[c[0]][name]
86 | print(
87 | f" The {name} hue gives {col_bri} using color "
88 | f"brightness, {con_rat} using contrast ratio, and "
89 | f"{text_color} from the MD spec"
90 | )
91 |
--------------------------------------------------------------------------------
/kivymd/toast/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2013 Brian - androidtoast library
4 | Copyright (c) 2019 Ivanov Yuri - kivytoast library
5 |
6 | Permission is hereby granted, free of charge, to any person obtaining a copy of
7 | this software and associated documentation files (the "Software"), to deal in
8 | the Software without restriction, including without limitation the rights to
9 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
10 | the Software, and to permit persons to whom the Software is furnished to do so,
11 | subject to the following conditions:
12 |
13 | The above copyright notice and this permission notice shall be included in all
14 | copies or substantial portions of the Software.
15 |
16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
18 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
19 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
20 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
21 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
--------------------------------------------------------------------------------
/kivymd/toast/README.md:
--------------------------------------------------------------------------------
1 | KivyToast
2 | ========
3 |
4 | A package for working with messages like Toast on Android. It is intended for use in applications written using the Kivy framework.
5 |
6 | This package is an improved version of the package https://github.com/knappador/kivy-toaster in which human toasts are written, written on Kivy.
7 |
8 |
9 |
10 | The package modules are written using the framework for cross-platform development of .
11 | Information about the framework is available at http://kivy.org.
12 |
13 | An example of usage (note that with this import the native implementation of toasts will be used for the Android platform and implementation on Kivy for others:
14 |
15 | ```python
16 | from toast import toast
17 |
18 | ...
19 |
20 | # And then in the code, toasts are available
21 | # by calling the toast function:
22 | toast ('Your message')
23 | ```
24 |
25 | To force the Kivy implementation on the Android platform, use the import of the form:
26 |
27 | ```python
28 | from toast.kivytoast import toast
29 | ```
30 |
31 | PROGRAMMING LANGUAGE
32 | --------------------
33 | Python 2.7 +
34 |
35 | DEPENDENCE
36 | ----------
37 | The [Kivy] framework (http://kivy.org/docs/installation/installation.html)
38 |
39 | LICENSE
40 | -------
41 | MIT
--------------------------------------------------------------------------------
/kivymd/toast/__init__.py:
--------------------------------------------------------------------------------
1 | __all__ = ("toast",)
2 |
3 | from kivy.utils import platform
4 |
5 | if platform == "android":
6 | try:
7 | from .androidtoast import toast
8 | except BaseException:
9 | from .kivytoast import toast
10 | else:
11 | from .kivytoast import toast
12 |
--------------------------------------------------------------------------------
/kivymd/toast/androidtoast/__init__.py:
--------------------------------------------------------------------------------
1 | """
2 | Toast for Android device
3 | ========================
4 |
5 | .. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/toast.png
6 | :align: center
7 |
8 | """
9 |
10 | __all__ = ("toast",)
11 |
12 | from .androidtoast import toast
13 |
--------------------------------------------------------------------------------
/kivymd/toast/androidtoast/androidtoast.py:
--------------------------------------------------------------------------------
1 | """
2 | AndroidToast
3 | ============
4 |
5 | .. rubric:: Native implementation of toast for Android devices.
6 |
7 | .. code-block:: python
8 |
9 | # Will be automatically used native implementation of the toast
10 | # if your application is running on an Android device.
11 | # Otherwise, will be used toast implementation
12 | # from the kivymd/toast/kivytoast package.
13 |
14 | from kivy.lang import Builder
15 | from kivy.uix.screenmanager import ScreenManager
16 |
17 | from kivymd.toast import toast
18 | from kivymd.app import MDApp
19 |
20 | KV = '''
21 | MDScreen:
22 |
23 | MDFlatButton:
24 | text: "My Toast"
25 | pos_hint:{"center_x": .5, "center_y": .5}
26 | on_press: app.show_toast()
27 | '''
28 |
29 |
30 | class Test(MDApp):
31 | def build(self):
32 | return Builder.load_string(KV)
33 |
34 | def show_toast(self):
35 | toast("Hello World", True, 80, 200, 0)
36 |
37 |
38 | Test().run()
39 | """
40 | __all__ = ("toast",)
41 |
42 | from android.runnable import run_on_ui_thread
43 | from jnius import autoclass
44 |
45 | activity = autoclass("org.kivy.android.PythonActivity").mActivity
46 | Toast = autoclass("android.widget.Toast")
47 | String = autoclass("java.lang.String")
48 |
49 |
50 | @run_on_ui_thread
51 | def toast(text, length_long=False, gravity=80, y=150, x=0):
52 | """
53 | Displays a toast.
54 |
55 | :param length_long: the amount of time (in seconds) that the toast is
56 | visible on the screen.
57 | :param text: text to be displayed in the toast;
58 | :param gravity: refers to the toast position, if it is 80the toast will
59 | be shown below, if it is 40 the toast will be displayed above;
60 | :param y: refers to the vertical position of the toast;
61 | :param x: refers to the horizontal position of the toast;
62 |
63 | Important: if only the text value is specified and the value of
64 | the `gravity`, `y`, `x` parameters is not specified, their values will
65 | be 0 which means that the toast will be shown in the center.
66 | """
67 |
68 | duration = Toast.LENGTH_LONG if length_long else Toast.LENGTH_SHORT
69 | t = Toast.makeText(activity, String(text), duration)
70 | t.setGravity(gravity, x, y)
71 | t.show()
72 |
--------------------------------------------------------------------------------
/kivymd/toast/kivytoast/__init__.py:
--------------------------------------------------------------------------------
1 | __all__ = ("toast",)
2 |
3 | from .kivytoast import toast
4 |
--------------------------------------------------------------------------------
/kivymd/toast/kivytoast/kivytoast.py:
--------------------------------------------------------------------------------
1 | """
2 | KivyToast
3 | =========
4 |
5 | .. rubric:: Implementation of toasts for desktop.
6 |
7 | .. code-block:: python
8 |
9 | from kivy.lang import Builder
10 |
11 | from kivymd.app import MDApp
12 | from kivymd.toast import toast
13 |
14 | KV = '''
15 | MDScreen:
16 |
17 | MDToolbar:
18 | title: 'Test Toast'
19 | pos_hint: {'top': 1}
20 | left_action_items: [['menu', lambda x: x]]
21 |
22 | MDRaisedButton:
23 | text: 'TEST KIVY TOAST'
24 | pos_hint: {'center_x': .5, 'center_y': .5}
25 | on_release: app.show_toast()
26 | '''
27 |
28 |
29 | class Test(MDApp):
30 | def show_toast(self):
31 | '''Displays a toast on the screen.'''
32 |
33 | toast('Test Kivy Toast')
34 |
35 | def build(self):
36 | return Builder.load_string(KV)
37 |
38 | Test().run()
39 | """
40 |
41 | from kivy.animation import Animation
42 | from kivy.clock import Clock
43 | from kivy.core.window import Window
44 | from kivy.lang import Builder
45 | from kivy.metrics import dp
46 | from kivy.properties import ListProperty, NumericProperty
47 | from kivy.uix.label import Label
48 |
49 | from kivymd.uix.dialog import BaseDialog
50 |
51 | Builder.load_string(
52 | """
53 | :
54 | size_hint: (None, None)
55 | pos_hint: {"center_x": 0.5, "center_y": 0.1}
56 | opacity: 0
57 | auto_dismiss: True
58 | overlay_color: [0, 0, 0, 0]
59 | canvas:
60 | Color:
61 | rgba: root._md_bg_color
62 | RoundedRectangle:
63 | pos: self.pos
64 | size: self.size
65 | radius: root.radius
66 | """
67 | )
68 |
69 |
70 | class Toast(BaseDialog):
71 | duration = NumericProperty(2.5)
72 | """
73 | The amount of time (in seconds) that the toast is visible on the screen.
74 |
75 | :attr:`duration` is an :class:`~kivy.properties.NumericProperty`
76 | and defaults to `2.5`.
77 | """
78 |
79 | _md_bg_color = ListProperty()
80 |
81 | def __init__(self, **kwargs):
82 | super().__init__(**kwargs)
83 | self.label_toast = Label(size_hint=(None, None), opacity=0)
84 | self.label_toast.bind(texture_size=self.label_check_texture_size)
85 | self.add_widget(self.label_toast)
86 |
87 | def label_check_texture_size(self, instance, texture_size):
88 | texture_width, texture_height = texture_size
89 | if texture_width > Window.width:
90 | instance.text_size = (Window.width - dp(10), None)
91 | instance.texture_update()
92 | texture_width, texture_height = instance.texture_size
93 | self.size = (texture_width + 25, texture_height + 25)
94 |
95 | def toast(self, text_toast):
96 | self.label_toast.text = text_toast
97 | self.open()
98 |
99 | def on_open(self):
100 | self.fade_in()
101 | Clock.schedule_once(self.fade_out, self.duration)
102 |
103 | def fade_in(self):
104 | anim = Animation(opacity=1, duration=0.4)
105 | anim.start(self.label_toast)
106 | anim.start(self)
107 |
108 | def fade_out(self, *args):
109 | anim = Animation(opacity=0, duration=0.4)
110 | anim.bind(on_complete=lambda *x: self.dismiss())
111 | anim.start(self.label_toast)
112 | anim.start(self)
113 |
114 | def on_touch_down(self, touch):
115 | if not self.collide_point(*touch.pos):
116 | if self.auto_dismiss:
117 | self.fade_out()
118 | return False
119 | super().on_touch_down(touch)
120 | return True
121 |
122 |
123 | def toast(text="", background=[0.2, 0.2, 0.2, 1], duration=2.5):
124 | """Displays a toast.
125 |
126 | :attr duration: the amount of time (in seconds) that the toast is visible on the screen
127 | :type duration: float
128 |
129 | :attr background: color ``rgba`` in Kivy format
130 | :type background: list
131 | """
132 |
133 | Toast(duration=duration, _md_bg_color=background).toast(text)
134 |
--------------------------------------------------------------------------------
/kivymd/tools/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AM-ash-OR-AM-I/Passlock/0232719654997770309bcdd8c84be8ff1dcce500/kivymd/tools/__init__.py
--------------------------------------------------------------------------------
/kivymd/tools/packaging/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AM-ash-OR-AM-I/Passlock/0232719654997770309bcdd8c84be8ff1dcce500/kivymd/tools/packaging/__init__.py
--------------------------------------------------------------------------------
/kivymd/tools/packaging/pyinstaller/__init__.py:
--------------------------------------------------------------------------------
1 | """
2 | PyInstaller hooks
3 | =================
4 |
5 | Add ``hookspath=[kivymd.hooks_path]`` to your .spec file.
6 |
7 | Example of .spec file
8 | =====================
9 |
10 | .. code-block:: python
11 |
12 | # -*- mode: python ; coding: utf-8 -*-
13 |
14 | import sys
15 | import os
16 |
17 | from kivy_deps import sdl2, glew
18 |
19 | from kivymd import hooks_path as kivymd_hooks_path
20 |
21 | path = os.path.abspath(".")
22 |
23 | a = Analysis(
24 | ["main.py"],
25 | pathex=[path],
26 | hookspath=[kivymd_hooks_path],
27 | win_no_prefer_redirects=False,
28 | win_private_assemblies=False,
29 | cipher=None,
30 | noarchive=False,
31 | )
32 | pyz = PYZ(a.pure, a.zipped_data, cipher=None)
33 |
34 | exe = EXE(
35 | pyz,
36 | a.scripts,
37 | a.binaries,
38 | a.zipfiles,
39 | a.datas,
40 | *[Tree(p) for p in (sdl2.dep_bins + glew.dep_bins)],
41 | debug=False,
42 | strip=False,
43 | upx=True,
44 | name="app_name",
45 | console=True,
46 | )
47 | """
48 |
49 | __all__ = ("hooks_path", "get_hook_dirs", "get_pyinstaller_tests")
50 |
51 | import os
52 | from pathlib import Path
53 |
54 | import kivymd
55 |
56 | hooks_path = str(Path(__file__).absolute().parent)
57 | """Path to hook directory to use with PyInstaller.
58 | See :mod:`kivymd.tools.packaging.pyinstaller` for more information."""
59 |
60 |
61 | def get_hook_dirs():
62 | return [hooks_path]
63 |
64 |
65 | def get_pyinstaller_tests():
66 | return [os.path.join(kivymd.path, "tests", "pyinstaller")]
67 |
68 |
69 | if __name__ == "__main__":
70 | print(hooks_path)
71 | print(get_hook_dirs())
72 | print(get_pyinstaller_tests())
73 |
--------------------------------------------------------------------------------
/kivymd/tools/packaging/pyinstaller/hook-kivymd.py:
--------------------------------------------------------------------------------
1 | """
2 | PyInstaller hook for KivyMD
3 | ===========================
4 |
5 | Adds fonts and images to package.
6 |
7 | All modules from uix directory are added by Kivy hook.
8 | """
9 |
10 | from pathlib import Path
11 |
12 | import kivymd
13 |
14 | datas = [
15 | (
16 | kivymd.fonts_path,
17 | str(Path("kivymd").joinpath(Path(kivymd.fonts_path).name)),
18 | ),
19 | (
20 | kivymd.images_path,
21 | str(Path("kivymd").joinpath(Path(kivymd.images_path).name)),
22 | ),
23 | ]
24 |
--------------------------------------------------------------------------------
/kivymd/tools/release/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AM-ash-OR-AM-I/Passlock/0232719654997770309bcdd8c84be8ff1dcce500/kivymd/tools/release/__init__.py
--------------------------------------------------------------------------------
/kivymd/tools/release/argument_parser.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2019-2020 Artem Bulgakov
2 | #
3 | # This file is distributed under the terms of the same license,
4 | # as the Kivy framework.
5 |
6 | import argparse
7 | import sys
8 |
9 |
10 | class ArgumentParserWithHelp(argparse.ArgumentParser):
11 | def parse_args(self, args=None, namespace=None):
12 | # Add help when no arguments specified
13 | if not args and not len(sys.argv) > 1:
14 | self.print_help()
15 | self.exit(1)
16 | return super().parse_args(args, namespace)
17 |
18 | def error(self, message):
19 | # Add full help on error
20 | self.print_help()
21 | self.exit(2, f"\nError: {message}\n")
22 |
23 | def format_help(self):
24 | # Add subparsers usage and help to full help text
25 | formatter = self._get_formatter()
26 |
27 | # Get subparsers
28 | subparsers_actions = [
29 | action
30 | for action in self._actions
31 | if isinstance(action, argparse._SubParsersAction)
32 | ]
33 |
34 | # Description
35 | formatter.add_text(self.description)
36 |
37 | # Usage
38 | formatter.add_usage(
39 | self.usage,
40 | self._actions,
41 | self._mutually_exclusive_groups,
42 | prefix="Usage:\n",
43 | )
44 |
45 | # Subparsers usage
46 | for subparsers_action in subparsers_actions:
47 | for choice, subparser in subparsers_action.choices.items():
48 | formatter.add_usage(
49 | subparser.usage,
50 | subparser._actions,
51 | subparser._mutually_exclusive_groups,
52 | prefix="",
53 | )
54 |
55 | # Positionals, optionals and user-defined groups
56 | for action_group in self._action_groups:
57 | if not any(
58 | [
59 | action in subparsers_actions
60 | for action in action_group._group_actions
61 | ]
62 | ):
63 | formatter.start_section(action_group.title)
64 | formatter.add_text(action_group.description)
65 | formatter.add_arguments(action_group._group_actions)
66 | formatter.end_section()
67 | else:
68 | # Process subparsers differently
69 | # Just show list of choices
70 | formatter.start_section(action_group.title)
71 | # formatter.add_text(action_group.description)
72 | for action in action_group._group_actions:
73 | for choice in action.choices:
74 | formatter.add_text(choice)
75 | formatter.end_section()
76 |
77 | # Subparsers help
78 | for subparsers_action in subparsers_actions:
79 | for choice, subparser in subparsers_action.choices.items():
80 | formatter.start_section(choice)
81 | for action_group in subparser._action_groups:
82 | formatter.start_section(action_group.title)
83 | formatter.add_text(action_group.description)
84 | formatter.add_arguments(action_group._group_actions)
85 | formatter.end_section()
86 | formatter.end_section()
87 |
88 | # Epilog
89 | formatter.add_text(self.epilog)
90 |
91 | # Determine help from format above
92 | return formatter.format_help()
93 |
--------------------------------------------------------------------------------
/kivymd/tools/release/git_commands.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2019-2020 Artem Bulgakov
2 | #
3 | # This file is distributed under the terms of the same license,
4 | # as the Kivy framework.
5 |
6 | import subprocess
7 |
8 |
9 | def command(cmd: list, capture_output: bool = False) -> str:
10 | """Run system command."""
11 | print(f"Command: {subprocess.list2cmdline(cmd)}")
12 | if capture_output:
13 | out = subprocess.check_output(cmd)
14 | out = out.decode("utf-8")
15 | print(out.strip())
16 | return out
17 | else:
18 | subprocess.check_call(cmd)
19 | return ""
20 |
21 |
22 | def get_previous_version() -> str:
23 | """Returns latest tag in git."""
24 | command(["git", "checkout", "master"])
25 | old_version = command(
26 | ["git", "describe", "--abbrev=0", "--tags"], capture_output=True
27 | )
28 | old_version = old_version[:-1] # Remove \n
29 | return old_version
30 |
31 |
32 | def git_clean(ask: bool = True):
33 | """Clean git repository from untracked and changed files."""
34 | # Check what files will be removed
35 | files_to_clean = command(
36 | ["git", "clean", "-dx", "--force", "--dry-run"], capture_output=True
37 | ).strip()
38 | # Ask before removing
39 | if ask and files_to_clean:
40 | while True:
41 | ans = input("Do you want to remove these files? (yes/no)").lower()
42 | if ans == "y" or ans == "yes":
43 | break
44 | elif ans == "n" or ans == "no":
45 | print("git clean is required. Exit")
46 | exit(0)
47 |
48 | # Remove all untracked files
49 | command(["git", "clean", "-dx", "--force"])
50 | command(["git", "reset", "--hard"])
51 |
52 |
53 | def git_commit(message: str, allow_error: bool = False, add_files: list = None):
54 | """Make commit."""
55 | add_files = add_files if add_files else ["-A"]
56 | command(["git", "add", *add_files])
57 | try:
58 | command(["git", "commit", "--all", "-m", message])
59 | except subprocess.CalledProcessError as e:
60 | if not allow_error:
61 | raise e
62 |
63 |
64 | def git_tag(name: str):
65 | """Create tag."""
66 | command(["git", "tag", name])
67 |
68 |
69 | def git_push(branches_to_push: list, ask: bool = True, push: bool = False):
70 | """Push all changes."""
71 | if ask:
72 | push = input("Do you want to push changes? (y)") in (
73 | "",
74 | "y",
75 | "yes",
76 | )
77 |
78 | cmd = ["git", "push", "--tags", "origin", "master", *branches_to_push]
79 | if push:
80 | command(cmd)
81 | else:
82 | print(
83 | f"Changes are not pushed. Command for manual pushing: {subprocess.list2cmdline(cmd)}"
84 | )
85 |
86 |
87 | if __name__ == "__main__":
88 | git_clean(ask=True)
89 |
--------------------------------------------------------------------------------
/kivymd/tools/release/update_icons.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2019-2020 Artem Bulgakov
2 | #
3 | # This file is distributed under the terms of the same license,
4 | # as the Kivy framework.
5 |
6 | """
7 | Tool for updating Iconic font
8 | =============================
9 |
10 | Downloads archive from https://github.com/Templarian/MaterialDesign-Webfont and
11 | updates font file with icon_definitions.
12 | """
13 |
14 | import json
15 | import os
16 | import re
17 | import shutil
18 | import sys
19 | import zipfile
20 |
21 | import requests
22 |
23 | from kivymd.tools.release.git_commands import git_commit
24 |
25 | # Paths to files in kivymd repository
26 | kivymd_path = os.path.dirname(os.path.dirname(os.path.dirname(__file__)))
27 | font_path = os.path.join(
28 | kivymd_path, "fonts", "materialdesignicons-webfont.ttf"
29 | )
30 | icon_definitions_path = os.path.join(kivymd_path, "icon_definitions.py")
31 |
32 | font_version = "master"
33 | # URL to download new archive (set None if already downloaded)
34 | url = (
35 | f"https://github.com/Templarian/MaterialDesign-Webfont"
36 | f"/archive/{font_version}.zip"
37 | )
38 | # url = None
39 |
40 | # Paths to files in loaded archive
41 | temp_path = os.path.join(os.path.dirname(__file__), "temp")
42 | temp_repo_path = os.path.join(
43 | temp_path, f"MaterialDesign-Webfont-{font_version}"
44 | )
45 | temp_font_path = os.path.join(
46 | temp_repo_path, "fonts", "materialdesignicons-webfont.ttf"
47 | )
48 | temp_preview_path = os.path.join(temp_repo_path, "preview.html")
49 |
50 | # Regex
51 | re_icons_json = re.compile(r"(?<=var icons = )[\S ]+(?=;)")
52 | re_additional_icons = re.compile(r"(?<=icons\.push\()[\S ]+(?=\);)")
53 | re_version = re.compile(r"(?<=)[\d.]+(?=)")
54 | re_quote_keys = re.compile(r"([{\s,])(\w+)(:)")
55 | re_icon_definitions = re.compile(r"md_icons = {\n([ ]{4}[\s\S]*,\n)*}")
56 | re_version_in_file = re.compile(r"(?<=LAST UPDATED: Version )[\d.]+(?=\n)")
57 |
58 |
59 | def download_file(url, path):
60 | response = requests.get(url, stream=True)
61 | if response.status_code != 200:
62 | return False
63 | with open(path, "wb") as f:
64 | shutil.copyfileobj(response.raw, f)
65 | return True
66 |
67 |
68 | def unzip_archive(archive_path, dir_path):
69 | with zipfile.ZipFile(archive_path, "r") as zip_ref:
70 | zip_ref.extractall(dir_path)
71 |
72 |
73 | def get_icons_list():
74 | # There is js array with icons in file preview.html
75 | with open(temp_preview_path, "r") as f:
76 | preview_file = f.read()
77 | # Find version
78 | version = re_version.findall(preview_file)[0]
79 | # Load icons
80 | jsons_icons = re_icons_json.findall(preview_file)[0]
81 | json_icons = re_quote_keys.sub(r'\1"\2"\3', jsons_icons)
82 | icons = json.loads(json_icons)
83 | # Find additional icons (like a blank icon)
84 | # jsons_additional_icons = re_additional_icons.findall(preview_file)
85 | # for j in jsons_additional_icons:
86 | # json_additional_icons = re_quote_keys.sub(r'\1"\2"\3', j)
87 | # icons.append(json.loads(json_additional_icons))
88 | return icons, version
89 |
90 |
91 | def make_icon_definitions(icons):
92 | # Make python dict ("name": hex)
93 | icon_definitions = "md_icons = {\n"
94 | for i in icons:
95 | icon_definitions += " " * 4
96 | if len(i["hex"]) != 4:
97 | # Some icons has 5-digit unicode
98 | i["hex"] = "0" * (8 - len(i["hex"])) + i["hex"]
99 | icon_definitions += f'"{i["name"]}": "\\U{i["hex"].upper()}",\n'
100 | else:
101 | icon_definitions += f'"{i["name"]}": "\\u{i["hex"].upper()}",\n'
102 | icon_definitions += " " * 4 + '"blank": " ",\n' # Add blank icon (space)
103 | icon_definitions += "}"
104 | return icon_definitions
105 |
106 |
107 | def export_icon_definitions(icon_definitions, version):
108 | with open(icon_definitions_path, "r") as f:
109 | icon_definitions_file = f.read()
110 | # Change md_icons list
111 | new_icon_definitions = re_icon_definitions.sub(
112 | icon_definitions.replace("\\", "\\\\"), icon_definitions_file, 1
113 | )
114 | # Change version
115 | new_icon_definitions = re_version_in_file.sub(
116 | version, new_icon_definitions, 1
117 | )
118 | with open(icon_definitions_path, "w") as f:
119 | f.write(new_icon_definitions)
120 |
121 |
122 | def update_icons(make_commit: bool = False):
123 | if url is not None:
124 | print(f"Downloading Material Design Icons from {url}")
125 | if download_file(url, "iconic-font.zip"):
126 | print("Archive downloaded")
127 | else:
128 | print("Error: Could not download archive", file=sys.stderr)
129 | else:
130 | print("URL is None. Do not download archive")
131 | if os.path.exists("iconic-font.zip"):
132 | unzip_archive("iconic-font.zip", temp_path)
133 | print("Unzip successful")
134 | os.remove("iconic-font.zip")
135 | if os.path.exists(temp_repo_path):
136 | shutil.copy2(temp_font_path, font_path)
137 | print("Font copied")
138 | icons, version = get_icons_list()
139 | print(f"Version {version}. {len(icons)} icons loaded")
140 | icon_definitions = make_icon_definitions(icons)
141 | export_icon_definitions(icon_definitions, version)
142 | print("File icon_definitions.py updated")
143 | shutil.rmtree(temp_path, ignore_errors=True)
144 |
145 | if make_commit:
146 | git_commit(
147 | f"Update Iconic font (v{version})",
148 | allow_error=True,
149 | add_files=[
150 | "kivymd/icon_definitions.py",
151 | "kivymd/fonts/materialdesignicons-webfont.ttf",
152 | ],
153 | )
154 | print("\nSuccessful. You can now push changes")
155 | else:
156 | print(
157 | f'\nSuccessful. Commit message: "Update Iconic font (v{version})"'
158 | )
159 | else:
160 | print(f"Error: {temp_repo_path} not exists", file=sys.stderr)
161 | exit(1)
162 |
163 |
164 | def main():
165 | make_commit = "--commit" in sys.argv
166 | if "--commit" in sys.argv:
167 | sys.argv.remove("--commit")
168 | update_icons(make_commit=make_commit)
169 |
170 |
171 | if __name__ == "__main__":
172 | main()
173 |
--------------------------------------------------------------------------------
/kivymd/tools/update_icons.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2019 Artem S. Bulgakov
2 | #
3 | # This file is distributed under the terms of the same license,
4 | # as the Kivy framework.
5 |
6 | """
7 | Tool for updating Iconic font
8 | =============================
9 |
10 | Downloads archive from https://github.com/Templarian/MaterialDesign-Webfont and
11 | updates font file with icon_definitions.
12 | """
13 |
14 | import os
15 | import requests
16 | import zipfile
17 | import shutil
18 | import re
19 | import json
20 |
21 | os.chdir(os.path.dirname(__file__))
22 | # Paths to files in kivymd repository
23 | font_path = "../fonts/materialdesignicons-webfont.ttf"
24 | icon_definitions_path = "../icon_definitions.py"
25 |
26 | font_version = "master"
27 | # URL to download new archive (set None if already downloaded)
28 | url = (
29 | f"https://github.com/Templarian/MaterialDesign-Webfont"
30 | f"/archive/{font_version}.zip"
31 | )
32 | # url = None
33 |
34 | # Paths to files in loaded archive
35 | temp_path = os.path.abspath("temp")
36 | temp_repo_path = f"{temp_path}/MaterialDesign-Webfont-{font_version}"
37 | temp_font_path = f"{temp_repo_path}/fonts/materialdesignicons-webfont.ttf"
38 | temp_preview_path = f"{temp_repo_path}/preview.html"
39 |
40 | # Regex
41 | re_icons_json = re.compile(r"(?<=var icons = )[\S ]+(?=;)")
42 | re_additional_icons = re.compile(r"(?<=icons\.push\()[\S ]+(?=\);)")
43 | re_version = re.compile(r"(?<=)[\d.]+(?=)")
44 | re_quote_keys = re.compile(r"([{\s,])(\w+)(:)")
45 | re_icon_definitions = re.compile(r"md_icons = {\n([ ]{4}[\s\S]*,\n)*}")
46 | re_version_in_file = re.compile(r"(?<=LAST UPDATED: Version )[\d.]+(?=\n)")
47 |
48 |
49 | def download_file(url, path):
50 | response = requests.get(url, stream=True)
51 | if response.status_code != 200:
52 | return False
53 | with open(path, "wb") as f:
54 | shutil.copyfileobj(response.raw, f)
55 | return True
56 |
57 |
58 | def unzip_archive(archive_path, dir_path):
59 | with zipfile.ZipFile(archive_path, "r") as zip_ref:
60 | zip_ref.extractall(dir_path)
61 |
62 |
63 | def get_icons_list():
64 | # There is js array with icons in file preview.html
65 | with open(temp_preview_path, "r") as f:
66 | preview_file = f.read()
67 | # Find version
68 | version = re_version.findall(preview_file)[0]
69 | # Load icons
70 | jsons_icons = re_icons_json.findall(preview_file)[0]
71 | json_icons = re_quote_keys.sub(r'\1"\2"\3', jsons_icons)
72 | icons = json.loads(json_icons)
73 | # Find additional icons (like a blank icon)
74 | # jsons_additional_icons = re_additional_icons.findall(preview_file)
75 | # for j in jsons_additional_icons:
76 | # json_additional_icons = re_quote_keys.sub(r'\1"\2"\3', j)
77 | # icons.append(json.loads(json_additional_icons))
78 | return icons, version
79 |
80 |
81 | def make_icon_definitions(icons):
82 | # Make python dict ("name": hex)
83 | icon_definitions = "md_icons = {\n"
84 | for i in icons:
85 | icon_definitions += " " * 4
86 | if len(i["hex"]) != 4:
87 | # Some icons has 5-digit unicode
88 | i["hex"] = "0" * (8 - len(i["hex"])) + i["hex"]
89 | icon_definitions += f'"{i["name"]}": "\\U{i["hex"].upper()}",\n'
90 | else:
91 | icon_definitions += f'"{i["name"]}": "\\u{i["hex"].upper()}",\n'
92 | icon_definitions += " " * 4 + f'"blank": " ",\n' # Add blank icon (space)
93 | icon_definitions += "}"
94 | return icon_definitions
95 |
96 |
97 | def export_icon_definitions(icon_definitions, version):
98 | with open(icon_definitions_path, "r") as f:
99 | icon_definitions_file = f.read()
100 | # Change md_icons list
101 | new_icon_definitions = re_icon_definitions.sub(
102 | icon_definitions.replace("\\", "\\\\"), icon_definitions_file, 1
103 | )
104 | # Change version
105 | new_icon_definitions = re_version_in_file.sub(
106 | version, new_icon_definitions, 1
107 | )
108 | with open(icon_definitions_path, "w") as f:
109 | f.write(new_icon_definitions)
110 |
111 |
112 | def main():
113 | if url is not None:
114 | print(f"Downloading Material Design Icons from {url}")
115 | if download_file(url, "iconic-font.zip"):
116 | print("Archive downloaded")
117 | else:
118 | print("Could not download archive")
119 | else:
120 | print("URL is None. Do not download archive")
121 | if os.path.exists("iconic-font.zip"):
122 | unzip_archive("iconic-font.zip", temp_path)
123 | print("Unzip successful")
124 | os.remove("iconic-font.zip")
125 | if os.path.exists(temp_repo_path):
126 | shutil.copy2(temp_font_path, font_path)
127 | print("Font copied")
128 | icons, version = get_icons_list()
129 | print(f"Version {version}. {len(icons)} icons loaded")
130 | icon_definitions = make_icon_definitions(icons)
131 | export_icon_definitions(icon_definitions, version)
132 | print("File icon_definitions.py updated")
133 | shutil.rmtree(temp_path, ignore_errors=True)
134 | print(
135 | f'\nSuccessful. Commit message: "Update Iconic font (v{version})"'
136 | )
137 | else:
138 | print(f"{temp_repo_path} not exists")
139 |
140 |
141 | if __name__ == "__main__":
142 | main()
143 |
--------------------------------------------------------------------------------
/kivymd/uix/__init__.py:
--------------------------------------------------------------------------------
1 | from kivy.properties import BooleanProperty
2 | from kivy.uix.floatlayout import FloatLayout
3 | from kivy.uix.label import Label
4 | from kivy.uix.screenmanager import Screen
5 |
6 | from kivymd.uix.behaviors import SpecificBackgroundColorBehavior
7 |
8 |
9 | class MDAdaptiveWidget(SpecificBackgroundColorBehavior):
10 | adaptive_height = BooleanProperty(False)
11 | """
12 | If `True`, the following properties will be applied to the widget:
13 |
14 | .. code-block:: kv
15 |
16 | size_hint_y: None
17 | height: self.minimum_height
18 |
19 | :attr:`adaptive_height` is an :class:`~kivy.properties.BooleanProperty`
20 | and defaults to `False`.
21 | """
22 |
23 | adaptive_width = BooleanProperty(False)
24 | """
25 | If `True`, the following properties will be applied to the widget:
26 |
27 | .. code-block:: kv
28 |
29 | size_hint_x: None
30 | width: self.minimum_width
31 |
32 | :attr:`adaptive_width` is an :class:`~kivy.properties.BooleanProperty`
33 | and defaults to `False`.
34 | """
35 |
36 | adaptive_size = BooleanProperty(False)
37 | """
38 | If `True`, the following properties will be applied to the widget:
39 |
40 | .. code-block:: kv
41 |
42 | size_hint: None, None
43 | size: self.minimum_size
44 |
45 | :attr:`adaptive_size` is an :class:`~kivy.properties.BooleanProperty`
46 | and defaults to `False`.
47 | """
48 |
49 | def on_adaptive_height(self, instance, value):
50 | self.size_hint_y = None
51 | if issubclass(self.__class__, Label):
52 | self.bind(
53 | texture_size=lambda *x: self.setter("height")(
54 | self, self.texture_size[1]
55 | )
56 | )
57 | else:
58 | if not isinstance(self, (FloatLayout, Screen)):
59 | self.bind(minimum_height=self.setter("height"))
60 |
61 | def on_adaptive_width(self, instance, value):
62 | self.size_hint_x = None
63 | if issubclass(self.__class__, Label):
64 | self.bind(
65 | texture_size=lambda *x: self.setter("width")(
66 | self, self.texture_size[0]
67 | )
68 | )
69 | else:
70 | if not isinstance(self, (FloatLayout, Screen)):
71 | self.bind(minimum_width=self.setter("width"))
72 |
73 | def on_adaptive_size(self, instance, value):
74 | self.size_hint = (None, None)
75 | if issubclass(self.__class__, Label):
76 | self.text_size = (None, None)
77 | self.bind(
78 | texture_size=lambda *x: self.setter("size")(
79 | self, self.texture_size
80 | )
81 | )
82 | else:
83 | if not isinstance(self, (FloatLayout, Screen)):
84 | self.bind(minimum_size=self.setter("size"))
85 |
--------------------------------------------------------------------------------
/kivymd/uix/behaviors/__init__.py:
--------------------------------------------------------------------------------
1 | """
2 | Behaviors
3 | =========
4 |
5 | Modules and classes implementing various behaviors for buttons etc.
6 | """
7 |
8 | # flake8: NOQA
9 | from .hover_behavior import HoverBehavior # isort:skip
10 | from .backgroundcolor_behavior import (
11 | BackgroundColorBehavior,
12 | SpecificBackgroundColorBehavior,
13 | )
14 | from .elevation import (
15 | CircularElevationBehavior,
16 | CommonElevationBehavior,
17 | FakeCircularElevationBehavior,
18 | FakeRectangularElevationBehavior,
19 | ObservableShadow,
20 | RectangularElevationBehavior,
21 | RoundedRectangularElevationBehavior,
22 | )
23 | from .focus_behavior import FocusBehavior
24 | from .magic_behavior import MagicBehavior
25 | from .ripple_behavior import CircularRippleBehavior, RectangularRippleBehavior
26 | from .touch_behavior import TouchBehavior
27 |
--------------------------------------------------------------------------------
/kivymd/uix/behaviors/backgroundcolorbehavior.py:
--------------------------------------------------------------------------------
1 | """
2 | Behaviors/Background Color
3 | ==========================
4 |
5 | .. note:: The following classes are intended for in-house use of the library.
6 | """
7 |
8 | from kivy.lang import Builder
9 | from kivy.properties import BoundedNumericProperty, ReferenceListProperty
10 | from kivy.properties import OptionProperty, ListProperty
11 | from kivy.uix.widget import Widget
12 | from kivy.utils import get_color_from_hex
13 |
14 | from kivymd.color_definitions import palette, hue, text_colors
15 |
16 | Builder.load_string(
17 | """
18 |
19 | canvas:
20 | Color:
21 | rgba: self.md_bg_color
22 | RoundedRectangle:
23 | size: self.size
24 | pos: self.pos
25 | radius: root.radius
26 | """
27 | )
28 |
29 |
30 | class BackgroundColorBehavior(Widget):
31 | r = BoundedNumericProperty(1.0, min=0.0, max=1.0)
32 | """The value of ``red`` in the ``rgba`` palette.
33 |
34 | :attr:`r` is an :class:`~kivy.properties.BoundedNumericProperty`
35 | and defaults to `1.0`.
36 | """
37 |
38 | g = BoundedNumericProperty(1.0, min=0.0, max=1.0)
39 | """The value of ``green`` in the ``rgba`` palette.
40 |
41 | :attr:`g` is an :class:`~kivy.properties.BoundedNumericProperty`
42 | and defaults to `1.0`.
43 | """
44 |
45 | b = BoundedNumericProperty(1.0, min=0.0, max=1.0)
46 | """The value of ``blue`` in the ``rgba`` palette.
47 |
48 | :attr:`b` is an :class:`~kivy.properties.BoundedNumericProperty`
49 | and defaults to `1.0`.
50 | """
51 |
52 | a = BoundedNumericProperty(0.0, min=0.0, max=1.0)
53 | """The value of ``alpha channel`` in the ``rgba`` palette.
54 |
55 | :attr:`a` is an :class:`~kivy.properties.BoundedNumericProperty`
56 | and defaults to `0.0`.
57 | """
58 |
59 | radius = ListProperty([0, 0, 0, 0])
60 | """Canvas radius.
61 |
62 | .. code-block:: python
63 |
64 | # Top left corner slice.
65 | MDBoxLayout:
66 | md_bg_color: app.theme_cls.primary_color
67 | radius: [25, 0, 0, 0]
68 |
69 | :attr:`radius` is an :class:`~kivy.properties.ListProperty`
70 | and defaults to `[0, 0, 0, 0]`.
71 | """
72 |
73 | md_bg_color = ReferenceListProperty(r, g, b, a)
74 | """The background color of the widget (:class:`~kivy.uix.widget.Widget`)
75 | that will be inherited from the :attr:`BackgroundColorBehavior` class.
76 |
77 | For example:
78 |
79 | .. code-block:: kv
80 |
81 | Widget:
82 | canvas:
83 | Color:
84 | rgba: 0, 1, 1, 1
85 | Rectangle:
86 | size: self.size
87 | pos: self.pos
88 |
89 | similar to code:
90 |
91 | .. code-block:: kv
92 |
93 |
94 | md_bg_color: 0, 1, 1, 1
95 |
96 | :attr:`md_bg_color` is an :class:`~kivy.properties.ReferenceListProperty`
97 | and defaults to :attr:`r`, :attr:`g`, :attr:`b`, :attr:`a`.
98 | """
99 |
100 |
101 | class SpecificBackgroundColorBehavior(BackgroundColorBehavior):
102 | background_palette = OptionProperty(
103 | "Primary", options=["Primary", "Accent", *palette]
104 | )
105 | """See :attr:`kivymd.color_definitions.palette`.
106 |
107 | :attr:`background_palette` is an :class:`~kivy.properties.OptionProperty`
108 | and defaults to `'Primary'`.
109 | """
110 |
111 | background_hue = OptionProperty("500", options=hue)
112 | """See :attr:`kivymd.color_definitions.hue`.
113 |
114 | :attr:`background_hue` is an :class:`~kivy.properties.OptionProperty`
115 | and defaults to `'500'`.
116 | """
117 |
118 | specific_text_color = ListProperty([0, 0, 0, 0.87])
119 | """:attr:`specific_text_color` is an :class:`~kivy.properties.ListProperty`
120 | and defaults to `[0, 0, 0, 0.87]`.
121 | """
122 |
123 | specific_secondary_text_color = ListProperty([0, 0, 0, 0.87])
124 | """:attr:`specific_secondary_text_color`is an :class:`~kivy.properties.ListProperty`
125 | and defaults to `[0, 0, 0, 0.87]`.
126 | """
127 |
128 | def _update_specific_text_color(self, instance, value):
129 | if hasattr(self, "theme_cls"):
130 | palette = {
131 | "Primary": self.theme_cls.primary_palette,
132 | "Accent": self.theme_cls.accent_palette,
133 | }.get(self.background_palette, self.background_palette)
134 | else:
135 | palette = {"Primary": "Blue", "Accent": "Amber"}.get(
136 | self.background_palette, self.background_palette
137 | )
138 | color = get_color_from_hex(text_colors[palette][self.background_hue])
139 | secondary_color = color[:]
140 | # Check for black text (need to adjust opacity)
141 | if (color[0] + color[1] + color[2]) == 0:
142 | color[3] = 0.87
143 | secondary_color[3] = 0.54
144 | else:
145 | secondary_color[3] = 0.7
146 | self.specific_text_color = color
147 | self.specific_secondary_text_color = secondary_color
148 |
149 | def __init__(self, **kwargs):
150 | super().__init__(**kwargs)
151 | if hasattr(self, "theme_cls"):
152 | self.theme_cls.bind(
153 | primary_palette=self._update_specific_text_color
154 | )
155 | self.theme_cls.bind(accent_palette=self._update_specific_text_color)
156 | self.theme_cls.bind(theme_style=self._update_specific_text_color)
157 | self.bind(background_hue=self._update_specific_text_color)
158 | self.bind(background_palette=self._update_specific_text_color)
159 | self._update_specific_text_color(None, None)
160 |
--------------------------------------------------------------------------------
/kivymd/uix/behaviors/focus_behavior.py:
--------------------------------------------------------------------------------
1 | """
2 | Behaviors/Focus
3 | ===============
4 |
5 | .. rubric:: Changing the background color when the mouse is on the widget.
6 |
7 | To apply focus behavior, you must create a new class that is inherited from the
8 | widget to which you apply the behavior and from the :class:`FocusBehavior` class.
9 |
10 | Usage
11 | -----
12 |
13 | .. code-block:: python
14 |
15 | from kivy.lang import Builder
16 |
17 | from kivymd.app import MDApp
18 | from kivymd.uix.behaviors import RectangularElevationBehavior, FocusBehavior
19 | from kivymd.uix.boxlayout import MDBoxLayout
20 |
21 | KV = '''
22 | MDScreen:
23 | md_bg_color: 1, 1, 1, 1
24 |
25 | FocusWidget:
26 | size_hint: .5, .3
27 | pos_hint: {"center_x": .5, "center_y": .5}
28 | md_bg_color: app.theme_cls.bg_light
29 |
30 | MDLabel:
31 | text: "Label"
32 | theme_text_color: "Primary"
33 | pos_hint: {"center_y": .5}
34 | halign: "center"
35 | '''
36 |
37 |
38 | class FocusWidget(MDBoxLayout, RectangularElevationBehavior, FocusBehavior):
39 | pass
40 |
41 |
42 | class Test(MDApp):
43 | def build(self):
44 | self.theme_cls.theme_style = "Dark"
45 | return Builder.load_string(KV)
46 |
47 |
48 | Test().run()
49 |
50 | .. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/focus-widget.gif
51 | :align: center
52 |
53 | Color change at focus/defocus
54 |
55 | .. code-block:: kv
56 |
57 | FocusWidget:
58 | focus_color: 1, 0, 1, 1
59 | unfocus_color: 0, 0, 1, 1
60 |
61 | .. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/focus-defocus-color.gif
62 | :align: center
63 | """
64 |
65 | __all__ = ("FocusBehavior",)
66 |
67 | from kivy.app import App
68 | from kivy.properties import BooleanProperty, ColorProperty
69 | from kivy.uix.behaviors import ButtonBehavior
70 |
71 | from kivymd.uix.behaviors import HoverBehavior
72 |
73 |
74 | class FocusBehavior(HoverBehavior, ButtonBehavior):
75 |
76 | focus_behavior = BooleanProperty(True)
77 | """
78 | Using focus when hovering over a widget.
79 |
80 | :attr:`focus_behavior` is a :class:`~kivy.properties.BooleanProperty`
81 | and defaults to `False`.
82 | """
83 |
84 | focus_color = ColorProperty(None)
85 | """
86 | The color of the widget when the mouse enters the bbox of the widget.
87 |
88 | :attr:`focus_color` is a :class:`~kivy.properties.ColorProperty`
89 | and defaults to `None`.
90 | """
91 |
92 | unfocus_color = ColorProperty(None)
93 | """
94 | The color of the widget when the mouse exits the bbox widget.
95 |
96 | :attr:`unfocus_color` is a :class:`~kivy.properties.ColorProperty`
97 | and defaults to `None`.
98 | """
99 |
100 | def on_enter(self):
101 | """Called when mouse enter the bbox of the widget."""
102 |
103 | if hasattr(self, "md_bg_color") and self.focus_behavior:
104 | if hasattr(self, "theme_cls") and not self.focus_color:
105 | self.md_bg_color = self.theme_cls.bg_normal
106 | else:
107 | if not self.focus_color:
108 | self.md_bg_color = App.get_running_app().theme_cls.bg_normal
109 | else:
110 | self.md_bg_color = self.focus_color
111 |
112 | def on_leave(self):
113 | """Called when the mouse exit the widget."""
114 |
115 | if hasattr(self, "md_bg_color") and self.focus_behavior:
116 | if hasattr(self, "theme_cls") and not self.unfocus_color:
117 | self.md_bg_color = self.theme_cls.bg_light
118 | else:
119 | if not self.unfocus_color:
120 | self.md_bg_color = App.get_running_app().theme_cls.bg_light
121 | else:
122 | self.md_bg_color = self.unfocus_color
123 |
--------------------------------------------------------------------------------
/kivymd/uix/behaviors/magic_behavior.py:
--------------------------------------------------------------------------------
1 | """
2 | Behaviors/Magic
3 | ===============
4 |
5 | .. rubric:: Magical effects for buttons.
6 |
7 | .. warning:: Magic effects do not work correctly with `KivyMD` buttons!
8 |
9 | To apply magic effects, you must create a new class that is inherited from the
10 | widget to which you apply the effect and from the :attr:`MagicBehavior` class.
11 |
12 | In `KV file`:
13 |
14 | .. code-block:: kv
15 |
16 |
17 |
18 | In `python file`:
19 |
20 | .. code-block:: python
21 |
22 | class MagicButton(MagicBehavior, MDRectangleFlatButton):
23 | pass
24 |
25 | .. rubric:: The :attr:`MagicBehavior` class provides five effects:
26 |
27 | - :attr:`MagicBehavior.wobble`
28 | - :attr:`MagicBehavior.grow`
29 | - :attr:`MagicBehavior.shake`
30 | - :attr:`MagicBehavior.twist`
31 | - :attr:`MagicBehavior.shrink`
32 |
33 | Example:
34 |
35 | .. code-block:: python
36 |
37 | from kivymd.app import MDApp
38 | from kivy.lang import Builder
39 |
40 | KV = '''
41 | #:import MagicBehavior kivymd.uix.behaviors.MagicBehavior
42 |
43 |
44 |
45 |
46 |
47 | FloatLayout:
48 |
49 | MagicButton:
50 | text: "WOBBLE EFFECT"
51 | on_release: self.wobble()
52 | pos_hint: {"center_x": .5, "center_y": .3}
53 |
54 | MagicButton:
55 | text: "GROW EFFECT"
56 | on_release: self.grow()
57 | pos_hint: {"center_x": .5, "center_y": .4}
58 |
59 | MagicButton:
60 | text: "SHAKE EFFECT"
61 | on_release: self.shake()
62 | pos_hint: {"center_x": .5, "center_y": .5}
63 |
64 | MagicButton:
65 | text: "TWIST EFFECT"
66 | on_release: self.twist()
67 | pos_hint: {"center_x": .5, "center_y": .6}
68 |
69 | MagicButton:
70 | text: "SHRINK EFFECT"
71 | on_release: self.shrink()
72 | pos_hint: {"center_x": .5, "center_y": .7}
73 | '''
74 |
75 |
76 | class Example(MDApp):
77 | def build(self):
78 | return Builder.load_string(KV)
79 |
80 |
81 | Example().run()
82 |
83 |
84 | .. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/magic-button.gif
85 | :width: 250 px
86 | :align: center
87 | """
88 |
89 | __all__ = ("MagicBehavior",)
90 |
91 | from kivy.animation import Animation
92 | from kivy.lang import Builder
93 | from kivy.properties import NumericProperty
94 |
95 | Builder.load_string(
96 | """
97 |
98 | translate_x: 0
99 | translate_y: 0
100 | scale_x: 1
101 | scale_y: 1
102 | rotate: 0
103 |
104 | canvas.before:
105 | PushMatrix
106 | Translate:
107 | x: self.translate_x or 0
108 | y: self.translate_y or 0
109 | Rotate:
110 | origin: self.center
111 | angle: self.rotate or 0
112 | Scale:
113 | origin: self.center
114 | x: self.scale_x or 1
115 | y: self.scale_y or 1
116 | canvas.after:
117 | PopMatrix
118 | """
119 | )
120 |
121 |
122 | class MagicBehavior:
123 |
124 | magic_speed = NumericProperty(1)
125 | """
126 | Animation playback speed.
127 |
128 | :attr:`magic_speed` is a :class:`~kivy.properties.NumericProperty`
129 | and defaults to `1`.
130 | """
131 |
132 | def grow(self):
133 | """Grow effect animation."""
134 |
135 | (
136 | Animation(
137 | scale_x=1.2,
138 | scale_y=1.2,
139 | t="out_quad",
140 | d=0.03 / self.magic_speed,
141 | )
142 | + Animation(
143 | scale_x=1, scale_y=1, t="out_elastic", d=0.4 / self.magic_speed
144 | )
145 | ).start(self)
146 |
147 | def shake(self):
148 | """Shake effect animation."""
149 |
150 | (
151 | Animation(translate_x=50, t="out_quad", d=0.02 / self.magic_speed)
152 | + Animation(
153 | translate_x=0, t="out_elastic", d=0.5 / self.magic_speed
154 | )
155 | ).start(self)
156 |
157 | def wobble(self):
158 | """Wobble effect animation."""
159 |
160 | (
161 | (
162 | Animation(scale_y=0.7, t="out_quad", d=0.03 / self.magic_speed)
163 | & Animation(
164 | scale_x=1.4, t="out_quad", d=0.03 / self.magic_speed
165 | )
166 | )
167 | + (
168 | Animation(scale_y=1, t="out_elastic", d=0.5 / self.magic_speed)
169 | & Animation(
170 | scale_x=1, t="out_elastic", d=0.4 / self.magic_speed
171 | )
172 | )
173 | ).start(self)
174 |
175 | def twist(self):
176 | """Twist effect animation."""
177 |
178 | (
179 | Animation(rotate=25, t="out_quad", d=0.05 / self.magic_speed)
180 | + Animation(rotate=0, t="out_elastic", d=0.5 / self.magic_speed)
181 | ).start(self)
182 |
183 | def shrink(self):
184 | """Shrink effect animation."""
185 |
186 | Animation(
187 | scale_x=0.95, scale_y=0.95, t="out_quad", d=0.1 / self.magic_speed
188 | ).start(self)
189 |
190 | def on_touch_up(self, *args):
191 | Animation.stop_all(self)
192 | return super().on_touch_up(*args)
193 |
--------------------------------------------------------------------------------
/kivymd/uix/behaviors/toggle_behavior.py:
--------------------------------------------------------------------------------
1 | """
2 | Behaviors/ToggleButton
3 | ======================
4 |
5 | This behavior must always be inherited after the button's Widget class since it
6 | works with the inherited properties of the button class.
7 |
8 | example:
9 |
10 | .. code-block:: python
11 |
12 | class MyToggleButtonWidget(MDFlatButton, MDToggleButton):
13 | # [...]
14 | pass
15 |
16 |
17 | .. code-block:: python
18 |
19 | from kivy.lang import Builder
20 |
21 | from kivymd.app import MDApp
22 | from kivymd.uix.behaviors.toggle_behavior import MDToggleButton
23 | from kivymd.uix.button import MDRectangleFlatButton
24 |
25 | KV = '''
26 | Screen:
27 |
28 | MDBoxLayout:
29 | adaptive_size: True
30 | pos_hint: {"center_x": .5, "center_y": .5}
31 |
32 | MyToggleButton:
33 | text: "Show ads"
34 | group: "x"
35 |
36 | MyToggleButton:
37 | text: "Do not show ads"
38 | group: "x"
39 |
40 | MyToggleButton:
41 | text: "Does not matter"
42 | group: "x"
43 | '''
44 |
45 |
46 | class MyToggleButton(MDRectangleFlatButton, MDToggleButton):
47 | def __init__(self, **kwargs):
48 | super().__init__(**kwargs)
49 | self.background_down = self.theme_cls.primary_light
50 |
51 |
52 | class Test(MDApp):
53 | def build(self):
54 | return Builder.load_string(KV)
55 |
56 |
57 | Test().run()
58 |
59 | .. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/toggle-button-1.gif
60 | :align: center
61 |
62 | .. code-block:: python
63 |
64 | class MyToggleButton(MDFillRoundFlatButton, MDToggleButton):
65 | def __init__(self, **kwargs):
66 | self.background_down = MDApp.get_running_app().theme_cls.primary_dark
67 | super().__init__(**kwargs)
68 |
69 | .. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/toggle-button-2.gif
70 | :align: center
71 |
72 | You can inherit the ``MyToggleButton`` class only from the following classes
73 | ----------------------------------------------------------------------------
74 |
75 | - :class:`~kivymd.uix.button.MDRaisedButton`
76 | - :class:`~kivymd.uix.button.MDFlatButton`
77 | - :class:`~kivymd.uix.button.MDRectangleFlatButton`
78 | - :class:`~kivymd.uix.button.MDRectangleFlatIconButton`
79 | - :class:`~kivymd.uix.button.MDRoundFlatButton`
80 | - :class:`~kivymd.uix.button.MDRoundFlatIconButton`
81 | - :class:`~kivymd.uix.button.MDFillRoundFlatButton`
82 | - :class:`~kivymd.uix.button.MDFillRoundFlatIconButton`
83 | """
84 |
85 | __all__ = ("MDToggleButton",)
86 |
87 | from kivy.properties import BooleanProperty, ColorProperty
88 | from kivy.uix.behaviors import ToggleButtonBehavior
89 |
90 | from kivymd.uix.button import (
91 | MDFillRoundFlatButton,
92 | MDFillRoundFlatIconButton,
93 | MDFlatButton,
94 | MDRaisedButton,
95 | MDRectangleFlatButton,
96 | MDRectangleFlatIconButton,
97 | MDRoundFlatButton,
98 | MDRoundFlatIconButton,
99 | )
100 |
101 |
102 | class MDToggleButton(ToggleButtonBehavior):
103 | background_normal = ColorProperty(None)
104 | """
105 | Color of the button in ``rgba`` format for the 'normal' state.
106 |
107 | :attr:`background_normal` is a :class:`~kivy.properties.ColorProperty`
108 | and is defaults to `None`.
109 | """
110 |
111 | background_down = ColorProperty(None)
112 | """
113 | Color of the button in ``rgba`` format for the 'down' state.
114 |
115 | :attr:`background_down` is a :class:`~kivy.properties.ColorProperty`
116 | and is defaults to `None`.
117 | """
118 |
119 | font_color_normal = ColorProperty(None)
120 | """
121 | Color of the font's button in ``rgba`` format for the 'normal' state.
122 |
123 | :attr:`font_color_normal` is a :class:`~kivy.properties.ColorProperty`
124 | and is defaults to `None`.
125 | """
126 |
127 | font_color_down = ColorProperty([1, 1, 1, 1])
128 | """
129 | Color of the font's button in ``rgba`` format for the 'down' state.
130 |
131 | :attr:`font_color_down` is a :class:`~kivy.properties.ColorProperty`
132 | and is defaults to `[1, 1, 1, 1]`.
133 | """
134 |
135 | __is_filled = BooleanProperty(False)
136 |
137 | def __init__(self, **kwargs):
138 | super().__init__(**kwargs)
139 | classinfo = (
140 | MDRaisedButton,
141 | MDFlatButton,
142 | MDRectangleFlatButton,
143 | MDRectangleFlatIconButton,
144 | MDRoundFlatButton,
145 | MDRoundFlatIconButton,
146 | MDFillRoundFlatButton,
147 | MDFillRoundFlatIconButton,
148 | )
149 | # Do the object inherited from the "supported" buttons?
150 | if not issubclass(self.__class__, classinfo):
151 | raise ValueError(
152 | f"Class {self.__class__} must be inherited from one of the classes in the list {classinfo}"
153 | )
154 | if (
155 | not self.background_normal
156 | ): # This means that if the value == [] or None will return True.
157 | # If the object inherits from buttons with background:
158 | if isinstance(
159 | self,
160 | (
161 | MDRaisedButton,
162 | MDFillRoundFlatButton,
163 | MDFillRoundFlatIconButton,
164 | ),
165 | ):
166 | self.__is_filled = True
167 | self.background_normal = self.theme_cls.primary_color
168 | # If not the background_normal must be the same as the inherited one:
169 | else:
170 | self.background_normal = self.md_bg_color[:]
171 | # If no background_down is setted:
172 | if (
173 | not self.background_down
174 | ): # This means that if the value == [] or None will return True.
175 | self.background_down = (
176 | self.theme_cls.primary_dark
177 | ) # get the primary_color dark from theme_cls
178 | if not self.font_color_normal:
179 | self.font_color_normal = self.theme_cls.primary_color
180 | # Alternative to bind the function to the property.
181 | # self.bind(state=self._update_bg)
182 | self.fbind("state", self._update_bg)
183 |
184 | def _update_bg(self, ins, val):
185 | """Updates the color of the background."""
186 |
187 | if val == "down":
188 | self.md_bg_color = self.background_down
189 | if (
190 | self.__is_filled is False
191 | ): # If the background is transparent, and the button it toggled,
192 | # the font color must be withe [1, 1, 1, 1].
193 | self.text_color = self.font_color_down
194 | if self.group:
195 | self._release_group(self)
196 | else:
197 | self.md_bg_color = self.background_normal
198 | if (
199 | self.__is_filled is False
200 | ): # If the background is transparent, the font color must be the
201 | # primary color.
202 | self.text_color = self.font_color_normal
203 |
--------------------------------------------------------------------------------
/kivymd/uix/behaviors/touch_behavior.py:
--------------------------------------------------------------------------------
1 | """
2 | Behaviors/Touch
3 | ===============
4 |
5 | .. rubric:: Provides easy access to events.
6 |
7 | The following events are available:
8 |
9 | - on_long_touch
10 | - on_double_tap
11 | - on_triple_tap
12 |
13 | Usage
14 | -----
15 |
16 | .. code-block:: python
17 |
18 | from kivy.lang import Builder
19 |
20 | from kivymd.app import MDApp
21 | from kivymd.uix.behaviors import TouchBehavior
22 | from kivymd.uix.button import MDRaisedButton
23 |
24 | KV = '''
25 | Screen:
26 |
27 | MyButton:
28 | text: "PRESS ME"
29 | pos_hint: {"center_x": .5, "center_y": .5}
30 | '''
31 |
32 |
33 | class MyButton(MDRaisedButton, TouchBehavior):
34 | def on_long_touch(self, *args):
35 | print(" event")
36 |
37 | def on_double_tap(self, *args):
38 | print(" event")
39 |
40 | def on_triple_tap(self, *args):
41 | print(" event")
42 |
43 |
44 | class MainApp(MDApp):
45 | def build(self):
46 | return Builder.load_string(KV)
47 |
48 |
49 | MainApp().run()
50 | """
51 |
52 | __all__ = ("TouchBehavior",)
53 |
54 | from functools import partial
55 |
56 | from kivy.clock import Clock
57 | from kivy.properties import NumericProperty
58 |
59 |
60 | class TouchBehavior:
61 | duration_long_touch = NumericProperty(0.4)
62 | """
63 | Time for a long touch.
64 |
65 | :attr:`duration_long_touch` is an :class:`~kivy.properties.NumericProperty`
66 | and defaults to `0.4`.
67 | """
68 |
69 | def __init__(self, **kwargs):
70 | super().__init__(**kwargs)
71 | self.bind(
72 | on_touch_down=self.create_clock, on_touch_up=self.delete_clock
73 | )
74 |
75 | def create_clock(self, widget, touch, *args):
76 | if self.collide_point(touch.x, touch.y):
77 | callback = partial(self.on_long_touch, touch)
78 | Clock.schedule_once(callback, self.duration_long_touch)
79 | touch.ud["event"] = callback
80 |
81 | def delete_clock(self, widget, touch, *args):
82 | if self.collide_point(touch.x, touch.y):
83 | try:
84 | Clock.unschedule(touch.ud["event"])
85 | except KeyError:
86 | pass
87 |
88 | if touch.is_double_tap:
89 | self.on_double_tap(touch, *args)
90 | if touch.is_triple_tap:
91 | self.on_triple_tap(touch, *args)
92 |
93 | def on_long_touch(self, touch, *args):
94 | """Called when the widget is pressed for a long time."""
95 |
96 | def on_double_tap(self, touch, *args):
97 | """Called by double clicking on the widget."""
98 |
99 | def on_triple_tap(self, touch, *args):
100 | """Called by triple clicking on the widget."""
101 |
--------------------------------------------------------------------------------
/kivymd/uix/boxlayout.py:
--------------------------------------------------------------------------------
1 | """
2 | Components/Box Layout
3 | =====================
4 |
5 | :class:`~kivy.uix.boxlayout.BoxLayout` class equivalent. Simplifies working
6 | with some widget properties. For example:
7 |
8 | BoxLayout
9 | ---------
10 |
11 | .. code-block::
12 |
13 | BoxLayout:
14 | size_hint_y: None
15 | height: self.minimum_height
16 |
17 | canvas:
18 | Color:
19 | rgba: app.theme_cls.primary_color
20 | Rectangle:
21 | pos: self.pos
22 | size: self.size
23 |
24 | MDBoxLayout
25 | -----------
26 |
27 | .. code-block::
28 |
29 | MDBoxLayout:
30 | adaptive_height: True
31 | md_bg_color: app.theme_cls.primary_color
32 |
33 | Available options are:
34 | ----------------------
35 |
36 | - adaptive_height_
37 | - adaptive_width_
38 | - adaptive_size_
39 |
40 | .. adaptive_height:
41 | adaptive_height
42 | ---------------
43 |
44 | .. code-block:: kv
45 |
46 | adaptive_height: True
47 |
48 | Equivalent
49 |
50 | .. code-block:: kv
51 |
52 | size_hint_y: None
53 | height: self.minimum_height
54 |
55 | .. adaptive_width:
56 | adaptive_width
57 | --------------
58 |
59 | .. code-block:: kv
60 |
61 | adaptive_width: True
62 |
63 | Equivalent
64 |
65 | .. code-block:: kv
66 |
67 | size_hint_x: None
68 | height: self.minimum_width
69 |
70 | .. adaptive_size:
71 | adaptive_size
72 | -------------
73 |
74 | .. code-block:: kv
75 |
76 | adaptive_size: True
77 |
78 | Equivalent
79 |
80 | .. code-block:: kv
81 |
82 | size_hint: None, None
83 | size: self.minimum_size
84 | """
85 |
86 | from kivy.uix.boxlayout import BoxLayout
87 |
88 | from kivymd.uix import MDAdaptiveWidget
89 |
90 |
91 | class MDBoxLayout(BoxLayout, MDAdaptiveWidget):
92 | pass
93 |
--------------------------------------------------------------------------------
/kivymd/uix/circularlayout.py:
--------------------------------------------------------------------------------
1 | """
2 | Components/CircularLayout
3 | =========================
4 |
5 | CircularLayout is a special layout that places widgets around a circle.
6 |
7 | MDCircularLayout
8 | ----------------
9 |
10 | .. rubric:: Usage
11 |
12 | .. code-block::
13 |
14 | from kivy.lang.builder import Builder
15 | from kivy.uix.label import Label
16 |
17 | from kivymd.app import MDApp
18 |
19 | kv = '''
20 | Screen:
21 | MDCircularLayout:
22 | id: container
23 | pos_hint: {"center_x": .5, "center_y": .5}
24 | row_spacing: min(self.size)*0.1
25 | '''
26 |
27 |
28 | class Main(MDApp):
29 | def build(self):
30 | return Builder.load_string(kv)
31 |
32 | def on_start(self):
33 | for x in range(1, 49):
34 | self.root.ids.container.add_widget(
35 | Label(text=f"{x}", color=[0, 0, 0, 1])
36 | )
37 |
38 |
39 | Main().run()
40 |
41 | """
42 |
43 | __all__ = ("MDCircularLayout",)
44 |
45 | from math import atan2, cos, degrees, radians, sin
46 |
47 | from kivy.properties import BooleanProperty, NumericProperty
48 |
49 | from kivymd.uix.floatlayout import MDFloatLayout
50 |
51 |
52 | class MDCircularLayout(MDFloatLayout):
53 |
54 | degree_spacing = NumericProperty(30)
55 | """
56 | The space between children in degree.
57 |
58 | :attr:`degree_spacing` is an :class:`~kivy.properties.NumericProperty`
59 | and defaults to `30`.
60 | """
61 |
62 | circular_radius = NumericProperty(None, allownone=True)
63 | """
64 | Radius of circle. Radius will be the greatest value in the layout if `circular_radius` if not specified.
65 |
66 | :attr:`circular_radius` is an :class:`~kivy.properties.NumericProperty`
67 | and defaults to `None`.
68 | """
69 |
70 | start_from = NumericProperty(60)
71 | """
72 | The positon of first child in degree.
73 |
74 | :attr:`start_from` is an :class:`~kivy.properties.NumericProperty`
75 | and defaults to `60`.
76 | """
77 |
78 | max_degree = NumericProperty(360)
79 | """
80 | Maximum range in degree allowed for each row of widgets before jumping to the next row.
81 |
82 | :attr:`max_degree` is an :class:`~kivy.properties.NumericProperty`
83 | and defaults to `360`.
84 | """
85 |
86 | circular_padding = NumericProperty("25dp")
87 | """
88 | Padding between outer widgets and the edge of the biggest circle.
89 |
90 | :attr:`circular_padding` is an :class:`~kivy.properties.NumericProperty`
91 | and defaults to `25dp`.
92 | """
93 |
94 | row_spacing = NumericProperty("50dp")
95 | """
96 | Space between each row of widget.
97 |
98 | :attr:`row_spacing` is an :class:`~kivy.properties.NumericProperty`
99 | and defaults to `50dp`.
100 | """
101 |
102 | clockwise = BooleanProperty(True)
103 | """
104 | Direction of widgets in circular direction.
105 |
106 | :attr:`clockwise` is an :class:`~kivy.properties.BooleanProperty`
107 | and defaults to `True`.
108 | """
109 |
110 | def __init__(self, **kwargs):
111 | super().__init__(**kwargs)
112 | self.bind(
113 | row_spacing=self._update_layout,
114 | )
115 |
116 | def _update_layout(self, *args):
117 | for index, child in enumerate(reversed(self.children)):
118 | pos = self._point_on_circle(
119 | self._calculate_radius(index),
120 | self._calculate_degree(index),
121 | )
122 | child.center = pos
123 |
124 | def do_layout(self, *largs, **kwargs):
125 | self._update_layout()
126 | return super().do_layout(*largs, **kwargs)
127 |
128 | def _max_per_row(self):
129 | return int(self.max_degree / self.degree_spacing)
130 |
131 | def _calculate_radius(self, index):
132 | """
133 | calculates the radius for given index
134 |
135 | """
136 |
137 | idx = int(index / self._max_per_row())
138 |
139 | if not self.circular_radius:
140 | init_radius = (
141 | min([self.width / 2, self.height / 2]) - self.circular_padding
142 | )
143 | else:
144 | init_radius = self.circular_radius
145 |
146 | if idx != 0:
147 | space = self.row_spacing * idx
148 | init_radius -= space
149 |
150 | return init_radius
151 |
152 | def _calculate_degree(self, index):
153 | """ calculates the angle for given index"""
154 | if self.clockwise:
155 | degree = self.start_from - index * self.degree_spacing
156 | else:
157 | degree = self.start_from + index * self.degree_spacing
158 |
159 | return degree
160 |
161 | def remove_widget(self, widget, **kwargs):
162 | super().remove_widget(widget, **kwargs)
163 | self._update_layout()
164 |
165 | def _point_on_circle(self, radius, degree):
166 | angle = radians(degree)
167 | center = [self.pos[0] + self.width / 2, self.pos[1] + self.height / 2]
168 | x = center[0] + (radius * cos(angle))
169 | y = center[1] + (radius * sin(angle))
170 | return [x, y]
171 |
172 | def get_angle(self, pos):
173 | """
174 | Returns the angle of given pos
175 | """
176 |
177 | center = [self.pos[0] + self.width / 2, self.pos[1] + self.height / 2]
178 | (dx, dy) = (center[0] - pos[0], center[1] - pos[1])
179 | angle = degrees(atan2(float(dy), float(dx)))
180 | angle += 180
181 | return angle
182 |
183 |
184 | if __name__ == "__main__":
185 | from kivy.lang.builder import Builder
186 | from kivy.uix.label import Label
187 |
188 | from kivymd.app import MDApp
189 |
190 | kv = """
191 | Screen:
192 | MDCircularLayout:
193 | id: container
194 | pos_hint: {"center_x": .5, "center_y": .5}
195 | row_spacing: min(self.size)*0.1
196 | """
197 |
198 | class Main(MDApp):
199 | def build(self):
200 | return Builder.load_string(kv)
201 |
202 | def on_start(self):
203 | for x in range(1, 49):
204 | self.root.ids.container.add_widget(
205 | Label(text=f"{x}", color=[0, 0, 0, 1])
206 | )
207 |
208 | Main().run()
209 |
--------------------------------------------------------------------------------
/kivymd/uix/dropdownitem.py:
--------------------------------------------------------------------------------
1 | """
2 | Components/Dropdown Item
3 | ========================
4 |
5 | .. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/dropdown-item.png
6 | :align: center
7 |
8 | Usage
9 | -----
10 |
11 | .. code-block:: python
12 |
13 | from kivy.lang import Builder
14 |
15 | from kivymd.app import MDApp
16 |
17 | KV = '''
18 | Screen
19 |
20 | MDDropDownItem:
21 | id: drop_item
22 | pos_hint: {'center_x': .5, 'center_y': .5}
23 | text: 'Item'
24 | on_release: self.set_item("New Item")
25 | '''
26 |
27 |
28 | class Test(MDApp):
29 | def __init__(self, **kwargs):
30 | super().__init__(**kwargs)
31 | self.screen = Builder.load_string(KV)
32 |
33 | def build(self):
34 | return self.screen
35 |
36 |
37 | Test().run()
38 |
39 | .. seealso::
40 |
41 | `Work with the class MDDropdownMenu see here `_
42 | """
43 |
44 | __all__ = ("MDDropDownItem",)
45 |
46 | from kivy.lang import Builder
47 | from kivy.properties import NumericProperty, StringProperty
48 | from kivy.uix.behaviors import ButtonBehavior
49 | from kivy.uix.widget import Widget
50 |
51 | from kivymd.theming import ThemableBehavior
52 | from kivymd.uix.behaviors import FakeRectangularElevationBehavior
53 | from kivymd.uix.boxlayout import MDBoxLayout
54 |
55 | Builder.load_string(
56 | """
57 | <_Triangle>:
58 | canvas:
59 | Color:
60 | rgba: root.theme_cls.text_color
61 | Triangle:
62 | points:
63 | [ \
64 | self.right-dp(14), self.y+dp(7), \
65 | self.right-dp(7), self.y+dp(7), \
66 | self.right-dp(7), self.y+dp(14) \
67 | ]
68 |
69 |
70 |
71 | orientation: "vertical"
72 | adaptive_size: True
73 | spacing: "5dp"
74 | padding: "5dp", "5dp", "5dp", 0
75 |
76 | MDBoxLayout:
77 | adaptive_size: True
78 | spacing: "10dp"
79 |
80 | Label:
81 | id: label_item
82 | size_hint: None, None
83 | size: self.texture_size
84 | color: root.theme_cls.text_color
85 | font_size: root.font_size
86 |
87 |
88 | _Triangle:
89 | size_hint: None, None
90 | size: "20dp", "20dp"
91 |
92 | MDSeparator:
93 | """
94 | )
95 |
96 |
97 | class _Triangle(ThemableBehavior, Widget):
98 | pass
99 |
100 |
101 | class MDDropDownItem(
102 | ThemableBehavior,
103 | FakeRectangularElevationBehavior,
104 | ButtonBehavior,
105 | MDBoxLayout,
106 | ):
107 | text = StringProperty()
108 | """
109 | Text item.
110 |
111 | :attr:`text` is a :class:`~kivy.properties.StringProperty`
112 | and defaults to `''`.
113 | """
114 |
115 | current_item = StringProperty()
116 | """
117 | Current name item.
118 |
119 | :attr:`current_item` is a :class:`~kivy.properties.StringProperty`
120 | and defaults to `''`.
121 | """
122 |
123 | font_size = NumericProperty("16sp")
124 | """
125 | Item font size.
126 |
127 | :attr:`font_size` is a :class:`~kivy.properties.NumericProperty`
128 | and defaults to `'16sp'`.
129 | """
130 |
131 | def on_text(self, instance, value):
132 | self.ids.label_item.text = value
133 |
134 | def set_item(self, name_item):
135 | """Sets new text for an item."""
136 |
137 | self.ids.label_item.text = name_item
138 | self.current_item = name_item
139 |
--------------------------------------------------------------------------------
/kivymd/uix/floatlayout.py:
--------------------------------------------------------------------------------
1 | """
2 | Components/Float Layout
3 | =======================
4 |
5 | :class:`~kivy.uix.floatlayout.FloatLayout` class equivalent. Simplifies working
6 | with some widget properties. For example:
7 |
8 | FloatLayout
9 | -----------
10 |
11 | .. code-block::
12 |
13 | FloatLayout:
14 | canvas:
15 | Color:
16 | rgba: app.theme_cls.primary_color
17 | RoundedRectangle:
18 | pos: self.pos
19 | size: self.size
20 | radius: [25, 0, 0, 0]
21 |
22 | MDFloatLayout
23 | -------------
24 |
25 | .. code-block::
26 |
27 | MDFloatLayout:
28 | radius: [25, 0, 0, 0]
29 | md_bg_color: app.theme_cls.primary_color
30 |
31 | .. Warning:: For a :class:`~kivy.uix.floatlayout.FloatLayout`, the
32 | ``minimum_size`` attributes are always 0, so you cannot use
33 | ``adaptive_size`` and related options.
34 | """
35 |
36 | from kivy.uix.floatlayout import FloatLayout
37 |
38 | from kivymd.uix import MDAdaptiveWidget
39 |
40 |
41 | class MDFloatLayout(FloatLayout, MDAdaptiveWidget):
42 | pass
43 |
--------------------------------------------------------------------------------
/kivymd/uix/gridlayout.py:
--------------------------------------------------------------------------------
1 | """
2 | Components/Grid Layout
3 | ======================
4 |
5 | :class:`~kivy.uix.gridlayout.GridLayout` class equivalent. Simplifies working
6 | with some widget properties. For example:
7 |
8 | GridLayout
9 | ----------
10 |
11 | .. code-block::
12 |
13 | GridLayout:
14 | size_hint_y: None
15 | height: self.minimum_height
16 |
17 | canvas:
18 | Color:
19 | rgba: app.theme_cls.primary_color
20 | Rectangle:
21 | pos: self.pos
22 | size: self.size
23 |
24 | MDGridLayout
25 | ------------
26 |
27 | .. code-block::
28 |
29 | MDGridLayout:
30 | adaptive_height: True
31 | md_bg_color: app.theme_cls.primary_color
32 |
33 | Available options are:
34 | ----------------------
35 |
36 | - adaptive_height_
37 | - adaptive_width_
38 | - adaptive_size_
39 |
40 | .. adaptive_height:
41 | adaptive_height
42 | ---------------
43 |
44 | .. code-block:: kv
45 |
46 | adaptive_height: True
47 |
48 | Equivalent
49 |
50 | .. code-block:: kv
51 |
52 | size_hint_y: None
53 | height: self.minimum_height
54 |
55 | .. adaptive_width:
56 | adaptive_width
57 | --------------
58 |
59 | .. code-block:: kv
60 |
61 | adaptive_width: True
62 |
63 | Equivalent
64 |
65 | .. code-block:: kv
66 |
67 | size_hint_x: None
68 | height: self.minimum_width
69 |
70 | .. adaptive_size:
71 | adaptive_size
72 | -------------
73 |
74 | .. code-block:: kv
75 |
76 | adaptive_size: True
77 |
78 | Equivalent
79 |
80 | .. code-block:: kv
81 |
82 | size_hint: None, None
83 | size: self.minimum_size
84 | """
85 |
86 | from kivy.uix.gridlayout import GridLayout
87 |
88 | from kivymd.uix import MDAdaptiveWidget
89 |
90 |
91 | class MDGridLayout(GridLayout, MDAdaptiveWidget):
92 | pass
93 |
--------------------------------------------------------------------------------
/kivymd/uix/label.py:
--------------------------------------------------------------------------------
1 | __all__ = ("MDLabel", "MDIcon")
2 |
3 | from kivy.clock import Clock
4 | from kivy.lang import Builder
5 | from kivy.metrics import sp
6 | from kivy.properties import (
7 | AliasProperty,
8 | BooleanProperty,
9 | ColorProperty,
10 | OptionProperty,
11 | StringProperty,
12 | )
13 | from kivy.uix.label import Label
14 |
15 | from kivymd.theming import ThemableBehavior
16 | from kivymd.theming_dynamic_text import get_contrast_text_color
17 | from kivymd.uix import MDAdaptiveWidget
18 |
19 | __MDLabel_colors__ = {
20 | "Primary": "text_color",
21 | "Secondary": "secondary_text_color",
22 | "Hint": "disabled_hint_text_color",
23 | "Error": "error_color",
24 | "OP": {
25 | "primary": "opposite_text_color",
26 | "Secondary": "opposite_secondary_text_color",
27 | "Hint": "opposite_disabled_hint_text_color",
28 | },
29 | }
30 |
31 | Builder.load_string(
32 | """
33 | #:import md_icons kivymd.icon_definitions.md_icons
34 |
35 |
36 |
37 | disabled_color: self.theme_cls.disabled_hint_text_color
38 | text_size: self.width, None
39 |
40 |
41 | :
42 | font_style: "Icon"
43 | text: u"{}".format(md_icons[self.icon]) if self.icon in md_icons else ""
44 | source: None if self.icon in md_icons else self.icon
45 | canvas:
46 | Color:
47 | rgba: (1, 1, 1, 1) if self.source else (0, 0, 0, 0)
48 | Rectangle:
49 | source: self.source if self.source else None
50 | pos: self.pos
51 | size: self.size
52 | """
53 | )
54 |
55 |
56 | class MDLabel(ThemableBehavior, Label, MDAdaptiveWidget):
57 | font_style = StringProperty("Body1")
58 | """
59 | Label font style.
60 |
61 | Available vanilla font_style are: `'H1'`, `'H2'`, `'H3'`, `'H4'`, `'H5'`, `'H6'`,
62 | `'Subtitle1'`, `'Subtitle2'`, `'Body1'`, `'Body2'`, `'Button'`,
63 | `'Caption'`, `'Overline'`, `'Icon'`.
64 |
65 | :attr:`font_style` is an :class:`~kivy.properties.StringProperty`
66 | and defaults to `'Body1'`.
67 | """
68 |
69 | _capitalizing = BooleanProperty(False)
70 |
71 | def _get_text(self):
72 | if self._capitalizing:
73 | return self._text.upper()
74 | return self._text
75 |
76 | def _set_text(self, value):
77 | self._text = value
78 |
79 | _text = StringProperty()
80 |
81 | text = AliasProperty(_get_text, _set_text, bind=["_text", "_capitalizing"])
82 | """Text of the label."""
83 |
84 | theme_text_color = OptionProperty(
85 | "Primary",
86 | allownone=True,
87 | options=[
88 | "Primary",
89 | "Secondary",
90 | "Hint",
91 | "Error",
92 | "Custom",
93 | "ContrastParentBackground",
94 | ],
95 | )
96 | """
97 | Label color scheme name.
98 |
99 | Available options are: `'Primary'`, `'Secondary'`, `'Hint'`, `'Error'`,
100 | `'Custom'`, `'ContrastParentBackground'`.
101 |
102 | :attr:`theme_text_color` is an :class:`~kivy.properties.OptionProperty`
103 | and defaults to `None`.
104 | """
105 |
106 | text_color = ColorProperty(None)
107 | """Label text color in ``rgba`` format.
108 |
109 | :attr:`text_color` is an :class:`~kivy.properties.ColorProperty`
110 | and defaults to `None`.
111 | """
112 | _text_color_str = StringProperty()
113 |
114 | parent_background = ColorProperty(None)
115 | can_capitalize = BooleanProperty(True)
116 |
117 | def __init__(self, **kwargs):
118 | super().__init__(**kwargs)
119 | self.bind(
120 | font_style=self.update_font_style,
121 | can_capitalize=self.update_font_style,
122 | )
123 | self.on_theme_text_color(None, self.theme_text_color)
124 | self.update_font_style()
125 | self.on_opposite_colors(None, self.opposite_colors)
126 | Clock.schedule_once(self.check_font_styles)
127 | self.theme_cls.bind(theme_style=self._do_update_theme_color)
128 |
129 | def check_font_styles(self, *dt):
130 | if self.font_style not in list(self.theme_cls.font_styles.keys()):
131 | raise ValueError(
132 | f"MDLabel.font_style is set to an invalid option '{self.font_style}'."
133 | f"Must be one of: {list(self.theme_cls.font_styles)}"
134 | )
135 | else:
136 | return True
137 |
138 | def update_font_style(self, *args):
139 | if self.check_font_styles() is True:
140 | font_info = self.theme_cls.font_styles[self.font_style]
141 | self.font_name = font_info[0]
142 | self.font_size = sp(font_info[1])
143 | if font_info[2] and self.can_capitalize:
144 | self._capitalizing = True
145 | else:
146 | self._capitalizing = False
147 |
148 | # TODO: Add letter spacing change
149 | # self.letter_spacing = font_info[3]
150 |
151 | def on_theme_text_color(self, instance, value):
152 | op = self.opposite_colors
153 | if op:
154 | self._text_color_str = __MDLabel_colors__.get("OP", "").get(
155 | value, ""
156 | )
157 | else:
158 | self._text_color_str = __MDLabel_colors__.get(value, "")
159 | if self._text_color_str:
160 | self._do_update_theme_color()
161 | else:
162 | # 'Custom' and 'ContrastParentBackground' lead here, as well as the
163 | # generic None value it's not yet been set
164 | self._text_color_str = ""
165 | if value == "Custom" and self.text_color:
166 | self.color = self.text_color
167 | elif value == "ContrastParentBackground" and self.parent_background:
168 | self.color = get_contrast_text_color(self.parent_background)
169 | else:
170 | self.color = [0, 0, 0, 1]
171 |
172 | def _do_update_theme_color(self, *arguments):
173 | if self._text_color_str:
174 | self.color = getattr(self.theme_cls, self._text_color_str)
175 |
176 | def on_text_color(self, *args):
177 | if self.theme_text_color == "Custom":
178 | self.color = self.text_color
179 |
180 | def on_opposite_colors(self, instance, value):
181 | self.on_theme_text_color(self, self.theme_text_color)
182 |
183 |
184 | class MDIcon(MDLabel):
185 | icon = StringProperty("android")
186 | """
187 | Label icon name.
188 |
189 | :attr:`icon` is an :class:`~kivy.properties.StringProperty`
190 | and defaults to `'android'`.
191 | """
192 |
193 | source = StringProperty(None, allownone=True)
194 | """
195 | Path to icon.
196 |
197 | :attr:`source` is an :class:`~kivy.properties.StringProperty`
198 | and defaults to `None`.
199 | """
200 |
--------------------------------------------------------------------------------
/kivymd/uix/relativelayout.py:
--------------------------------------------------------------------------------
1 | """
2 | Components/Relative Layout
3 | ==========================
4 |
5 | :class:`~kivy.uix.relativelayout.RelativeLayout` class equivalent. Simplifies working
6 | with some widget properties. For example:
7 |
8 | RelativeLayout
9 | --------------
10 |
11 | .. code-block::
12 |
13 | RelativeLayout:
14 | canvas:
15 | Color:
16 | rgba: app.theme_cls.primary_color
17 | RoundedRectangle:
18 | pos: (0, 0)
19 | size: self.size
20 | radius: [25, ]
21 |
22 | MDRelativeLayout
23 | ----------------
24 |
25 | .. code-block::
26 |
27 | MDRelativeLayout:
28 | radius: [25, ]
29 | md_bg_color: app.theme_cls.primary_color
30 | """
31 |
32 | from kivy.uix.relativelayout import RelativeLayout
33 |
34 | from kivymd.uix import MDAdaptiveWidget
35 |
36 |
37 | class MDRelativeLayout(RelativeLayout, MDAdaptiveWidget):
38 | pass
39 |
--------------------------------------------------------------------------------
/kivymd/uix/screen.py:
--------------------------------------------------------------------------------
1 | """
2 | Components/Screen
3 | =================
4 |
5 | :class:`~kivy.uix.screenmanager.Screen` class equivalent. Simplifies working
6 | with some widget properties. For example:
7 |
8 | Screen
9 | ------
10 |
11 | .. code-block::
12 |
13 | Screen:
14 | canvas:
15 | Color:
16 | rgba: app.theme_cls.primary_color
17 | RoundedRectangle:
18 | pos: self.pos
19 | size: self.size
20 | radius: [25, 0, 0, 0]
21 |
22 | MDScreen
23 | --------
24 |
25 | .. code-block::
26 |
27 | MDScreen:
28 | radius: [25, 0, 0, 0]
29 | md_bg_color: app.theme_cls.primary_color
30 | """
31 |
32 | from kivy.uix.screenmanager import Screen
33 |
34 | from kivymd.uix import MDAdaptiveWidget
35 |
36 |
37 | class MDScreen(Screen, MDAdaptiveWidget):
38 | pass
39 |
--------------------------------------------------------------------------------
/kivymd/uix/stacklayout.py:
--------------------------------------------------------------------------------
1 | """
2 | Components/StackLayout
3 | ======================
4 |
5 | :class:`~kivy.uix.stacklayout.StackLayout` class equivalent. Simplifies working
6 | with some widget properties. For example:
7 |
8 | StackLayout
9 | -----------
10 |
11 | .. code-block::
12 |
13 | StackLayout:
14 | size_hint_y: None
15 | height: self.minimum_height
16 |
17 | canvas:
18 | Color:
19 | rgba: app.theme_cls.primary_color
20 | Rectangle:
21 | pos: self.pos
22 | size: self.size
23 |
24 | MDStackLayout
25 | -------------
26 |
27 | .. code-block::
28 |
29 | MDStackLayout:
30 | adaptive_height: True
31 | md_bg_color: app.theme_cls.primary_color
32 |
33 | Available options are:
34 | ----------------------
35 |
36 | - adaptive_height_
37 | - adaptive_width_
38 | - adaptive_size_
39 |
40 | .. adaptive_height:
41 | adaptive_height
42 | ---------------
43 |
44 | .. code-block:: kv
45 |
46 | adaptive_height: True
47 |
48 | Equivalent
49 |
50 | .. code-block:: kv
51 |
52 | size_hint_y: None
53 | height: self.minimum_height
54 |
55 | .. adaptive_width:
56 | adaptive_width
57 | --------------
58 |
59 | .. code-block:: kv
60 |
61 | adaptive_width: True
62 |
63 | Equivalent
64 |
65 | .. code-block:: kv
66 |
67 | size_hint_x: None
68 | width: self.minimum_width
69 |
70 | .. adaptive_size:
71 | adaptive_size
72 | -------------
73 |
74 | .. code-block:: kv
75 |
76 | adaptive_size: True
77 |
78 | Equivalent
79 |
80 | .. code-block:: kv
81 |
82 | size_hint: None, None
83 | size: self.minimum_size
84 | """
85 |
86 | from kivy.uix.stacklayout import StackLayout
87 |
88 | from kivymd.uix import MDAdaptiveWidget
89 |
90 |
91 | class MDStackLayout(StackLayout, MDAdaptiveWidget):
92 | pass
93 |
--------------------------------------------------------------------------------
/kivymd/utils/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AM-ash-OR-AM-I/Passlock/0232719654997770309bcdd8c84be8ff1dcce500/kivymd/utils/__init__.py
--------------------------------------------------------------------------------
/kivymd/utils/asynckivy.py:
--------------------------------------------------------------------------------
1 | """
2 | asynckivy
3 | =========
4 |
5 | Copyright (c) 2019 Nattōsai Mitō
6 |
7 | GitHub -
8 | https://github.com/gottadiveintopython
9 | GitHub Gist -
10 | https://gist.github.com/gottadiveintopython/5f4a775849f9277081c396de65dc57c1
11 |
12 | """
13 |
14 | __all__ = ("start", "sleep", "event")
15 |
16 | import types
17 | from collections import namedtuple
18 | from functools import partial
19 |
20 | from kivy.clock import Clock
21 |
22 | CallbackParameter = namedtuple("CallbackParameter", ("args", "kwargs"))
23 |
24 |
25 | def start(coro):
26 | def step(*args, **kwargs):
27 | try:
28 | coro.send(CallbackParameter(args, kwargs))(step)
29 | except StopIteration:
30 | pass
31 |
32 | try:
33 | coro.send(None)(step)
34 | except StopIteration:
35 | pass
36 |
37 |
38 | @types.coroutine
39 | def sleep(duration):
40 | # The partial() here looks meaningless. But this is needed in order
41 | # to avoid weak reference.
42 | param = yield lambda step_coro: Clock.schedule_once(
43 | partial(step_coro), duration
44 | )
45 | return param.args[0]
46 |
47 |
48 | class event:
49 | def __init__(self, ed, name):
50 | self.bind_id = None
51 | self.ed = ed
52 | self.name = name
53 |
54 | def bind(self, step_coro):
55 | self.bind_id = bind_id = self.ed.fbind(self.name, self.callback)
56 | assert bind_id > 0 # check if binding succeeded
57 | self.step_coro = step_coro
58 |
59 | def callback(self, *args, **kwargs):
60 | self.parameter = CallbackParameter(args, kwargs)
61 | ed = self.ed
62 | ed.unbind_uid(self.name, self.bind_id)
63 | self.step_coro()
64 |
65 | def __await__(self):
66 | yield self.bind
67 | return self.parameter
68 |
--------------------------------------------------------------------------------
/kivymd/utils/cropimage.py:
--------------------------------------------------------------------------------
1 | """
2 | Crop Image
3 | ==========
4 | """
5 |
6 |
7 | def crop_image(
8 | cutting_size,
9 | path_to_image,
10 | path_to_save_crop_image,
11 | corner=0,
12 | blur=0,
13 | corner_mode="all",
14 | ):
15 | """Call functions of cropping/blurring/rounding image.
16 |
17 | cutting_size: size to which the image will be cropped;
18 | path_to_image: path to origin image;
19 | path_to_save_crop_image: path to new image;
20 | corner: value of rounding corners;
21 | blur: blur value;
22 | corner_mode: 'all'/'top'/'bottom' - indicates which corners to round out;
23 |
24 | """
25 |
26 | im = _crop_image(cutting_size, path_to_image, path_to_save_crop_image)
27 | if corner:
28 | im = add_corners(im, corner, corner_mode)
29 | if blur:
30 | im = add_blur(im, blur)
31 | try:
32 | im.save(path_to_save_crop_image)
33 | except IOError:
34 | im.save(path_to_save_crop_image, "JPEG")
35 |
36 |
37 | def add_blur(im, mode):
38 | from PIL import ImageFilter
39 |
40 | im = im.filter(ImageFilter.GaussianBlur(mode))
41 |
42 | return im
43 |
44 |
45 | def _crop_image(cutting_size, path_to_image, path_to_save_crop_image):
46 | from PIL import Image, ImageOps
47 |
48 | image = Image.open(path_to_image)
49 | image = ImageOps.fit(image, cutting_size)
50 | image.save(path_to_save_crop_image)
51 |
52 | return image
53 |
54 |
55 | def add_corners(im, corner, corner_mode):
56 | def add_top_corners():
57 | alpha.paste(circle.crop((0, 0, corner, corner)), (0, 0))
58 | alpha.paste(
59 | circle.crop((corner, 0, corner * 2, corner)), (w - corner, 0)
60 | )
61 |
62 | def add_bottom_corners():
63 | alpha.paste(
64 | circle.crop((0, corner, corner, corner * 2)), (0, h - corner)
65 | )
66 | alpha.paste(
67 | circle.crop((corner, corner, corner * 2, corner * 2)),
68 | (w - corner, h - corner),
69 | )
70 |
71 | from PIL import Image, ImageDraw
72 |
73 | circle = Image.new("L", (corner * 2, corner * 2), 0)
74 | draw = ImageDraw.Draw(circle)
75 | draw.ellipse((0, 0, corner * 2, corner * 2), fill=255)
76 | alpha = Image.new("L", im.size, 255)
77 | w, h = im.size
78 |
79 | if corner_mode == "all":
80 | add_top_corners()
81 | add_bottom_corners()
82 | elif corner_mode == "top":
83 | add_top_corners()
84 | if corner_mode == "bottom":
85 | add_bottom_corners()
86 | im.putalpha(alpha)
87 |
88 | return im
89 |
90 |
91 | def prepare_mask(size, antialias=2):
92 | from PIL import Image, ImageDraw
93 |
94 | mask = Image.new("L", (size[0] * antialias, size[1] * antialias), 0)
95 | ImageDraw.Draw(mask).ellipse((0, 0) + mask.size, fill=255)
96 | return mask.resize(size, Image.ANTIALIAS)
97 |
98 |
99 | def _crop_round_image(im, s):
100 | from PIL import Image
101 |
102 | w, h = im.size
103 | k = w // s[0] - h // s[1]
104 | if k > 0:
105 | im = im.crop(((w - h) // 2, 0, (w + h) // 2, h))
106 | elif k < 0:
107 | im = im.crop((0, (h - w) // 2, w, (h + w) // 2))
108 | return im.resize(s, Image.ANTIALIAS)
109 |
110 |
111 | def crop_round_image(cutting_size, path_to_image, path_to_new_image):
112 | from PIL import Image
113 |
114 | im = Image.open(path_to_image)
115 | im = _crop_round_image(im, cutting_size)
116 | im.putalpha(prepare_mask(cutting_size, 4))
117 | im.save(path_to_new_image)
118 |
--------------------------------------------------------------------------------
/kivymd/utils/fitimage.py:
--------------------------------------------------------------------------------
1 | """
2 | Fit Image
3 | =========
4 |
5 | Feature to automatically crop a `Kivy` image to fit your layout
6 | Write by Benedikt Zwölfer
7 |
8 | Referene - https://gist.github.com/benni12er/95a45eb168fc33a4fcd2d545af692dad
9 |
10 |
11 | Example:
12 | ========
13 |
14 | .. code-block:: kv
15 |
16 | BoxLayout:
17 | size_hint_y: None
18 | height: "200dp"
19 | orientation: 'vertical'
20 |
21 | FitImage:
22 | size_hint_y: 3
23 | source: 'images/img1.jpg'
24 |
25 | FitImage:
26 | size_hint_y: 1
27 | source: 'images/img2.jpg'
28 |
29 | Example with round corners:
30 | ===========================
31 |
32 | .. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/fitimage-round-corners.png
33 | :align: center
34 |
35 | .. code-block:: python
36 |
37 | from kivy.uix.modalview import ModalView
38 | from kivy.lang import Builder
39 |
40 | from kivymd import images_path
41 | from kivymd.app import MDApp
42 | from kivymd.uix.card import MDCard
43 |
44 | Builder.load_string(
45 | '''
46 | :
47 | elevation: 10
48 | radius: [36, ]
49 |
50 | FitImage:
51 | id: bg_image
52 | source: "images/bg.png"
53 | size_hint_y: .35
54 | pos_hint: {"top": 1}
55 | radius: [36, 36, 0, 0, ]
56 | ''')
57 |
58 |
59 | class Card(MDCard):
60 | pass
61 |
62 |
63 | class Example(MDApp):
64 | def build(self):
65 | modal = ModalView(
66 | size_hint=(0.4, 0.8),
67 | background=f"{images_path}/transparent.png",
68 | overlay_color=(0, 0, 0, 0),
69 | )
70 | modal.add_widget(Card())
71 | modal.open()
72 |
73 |
74 | Example().run()
75 | """
76 |
77 | __all__ = ("FitImage",)
78 |
79 | from kivy.clock import Clock
80 | from kivy.graphics.context_instructions import Color
81 | from kivy.graphics.vertex_instructions import Rectangle
82 | from kivy.lang import Builder
83 | from kivy.properties import BooleanProperty, ListProperty, ObjectProperty
84 | from kivy.uix.boxlayout import BoxLayout
85 | from kivy.uix.image import AsyncImage
86 | from kivy.uix.widget import Widget
87 |
88 | Builder.load_string(
89 | """
90 |
91 | canvas.before:
92 | StencilPush
93 | RoundedRectangle:
94 | size: self.size
95 | pos: self.pos
96 | radius: root.radius
97 | StencilUse
98 |
99 | canvas.after:
100 | StencilUnUse
101 | RoundedRectangle:
102 | size: self.size
103 | pos: self.pos
104 | radius: root.radius
105 | StencilPop
106 | """
107 | )
108 |
109 |
110 | class FitImage(BoxLayout):
111 | source = ObjectProperty()
112 | container = ObjectProperty()
113 | radius = ListProperty([0, 0, 0, 0])
114 | mipmap = BooleanProperty(False)
115 |
116 | def __init__(self, **kwargs):
117 | super().__init__(**kwargs)
118 | Clock.schedule_once(self._late_init)
119 |
120 | def _late_init(self, *args):
121 | self.container = Container(self.source, self.mipmap)
122 | self.bind(source=self.container.setter("source"))
123 | self.add_widget(self.container)
124 |
125 | def reload(self):
126 | self.container.image.reload()
127 |
128 |
129 | class Container(Widget):
130 | source = ObjectProperty()
131 | image = ObjectProperty()
132 |
133 | def __init__(self, source, mipmap, **kwargs):
134 | super().__init__(**kwargs)
135 | self.image = AsyncImage(mipmap=mipmap)
136 | self.image.bind(on_load=self.adjust_size)
137 | self.source = source
138 | self.bind(size=self.adjust_size, pos=self.adjust_size)
139 |
140 | def on_source(self, instance, value):
141 | if isinstance(value, str):
142 | self.image.source = value
143 | else:
144 | self.image.texture = value
145 | self.adjust_size()
146 |
147 | def adjust_size(self, *args):
148 | if not self.parent or not self.image.texture:
149 | return
150 |
151 | (par_x, par_y) = self.parent.size
152 |
153 | if par_x == 0 or par_y == 0:
154 | with self.canvas:
155 | self.canvas.clear()
156 | return
157 |
158 | par_scale = par_x / par_y
159 | (img_x, img_y) = self.image.texture.size
160 | img_scale = img_x / img_y
161 |
162 | if par_scale > img_scale:
163 | (img_x_new, img_y_new) = (img_x, img_x / par_scale)
164 | else:
165 | (img_x_new, img_y_new) = (img_y * par_scale, img_y)
166 |
167 | crop_pos_x = (img_x - img_x_new) / 2
168 | crop_pos_y = (img_y - img_y_new) / 2
169 |
170 | subtexture = self.image.texture.get_region(
171 | crop_pos_x, crop_pos_y, img_x_new, img_y_new
172 | )
173 |
174 | with self.canvas:
175 | self.canvas.clear()
176 | Color(1, 1, 1)
177 | Rectangle(texture=subtexture, pos=self.pos, size=(par_x, par_y))
178 |
--------------------------------------------------------------------------------
/kivymd/utils/fpsmonitor.py:
--------------------------------------------------------------------------------
1 | """
2 | Monitor module
3 | ==============
4 |
5 | The Monitor module is a toolbar that shows the activity of your current
6 | application :
7 |
8 | * FPS
9 |
10 | """
11 |
12 | from kivy.clock import Clock
13 | from kivy.lang import Builder
14 | from kivy.properties import NumericProperty, StringProperty
15 | from kivy.uix.label import Label
16 |
17 | Builder.load_string(
18 | """
19 | :
20 | size_hint_y: None
21 | height: self.texture_size[1]
22 | text: root._fsp_value
23 | pos_hint: {"top": 1}
24 |
25 | canvas.before:
26 | Color:
27 | rgba: [0,0,0,0]
28 | Rectangle:
29 | pos: self.pos
30 | size: self.size
31 | """
32 | )
33 |
34 |
35 | class FpsMonitor(Label):
36 | updated_interval = NumericProperty(0.5)
37 | """FPS refresh rate."""
38 |
39 | _fsp_value = StringProperty()
40 |
41 | def start(self):
42 | Clock.schedule_interval(self.update_fps, self.updated_interval)
43 |
44 | def update_fps(self, *args):
45 | self._fsp_value = "FPS: %f" % Clock.get_fps()
46 |
--------------------------------------------------------------------------------
/kivymd/utils/hot_reload_viewer.py:
--------------------------------------------------------------------------------
1 | """
2 | HotReloadViewer
3 | ===============
4 |
5 | .. Note:: The :class:`~HotReloadViewer` class is based on
6 | the `KvViewerApp `_ class
7 |
8 | :class:`~HotReloadViewer`, for KV-Viewer, is a simple tool allowing you to
9 | dynamically display a KV file, taking its changes into account
10 | (thanks to watchdog). The idea is to facilitate design using the KV language.
11 |
12 | Usage
13 | -----
14 |
15 | .. code-block:: python
16 |
17 | from kivy.lang import Builder
18 |
19 | from kivymd.app import MDApp
20 |
21 | KV = '''
22 | #:import KivyLexer kivy.extras.highlight.KivyLexer
23 | #:import HotReloadViewer kivymd.utils.hot_reload_viewer.HotReloadViewer
24 |
25 |
26 | BoxLayout:
27 |
28 | CodeInput:
29 | lexer: KivyLexer()
30 | style_name: "native"
31 | on_text: app.update_kv_file(self.text)
32 | size_hint_x: .7
33 |
34 | HotReloadViewer:
35 | size_hint_x: .3
36 | path: app.path_to_kv_file
37 | errors: True
38 | errors_text_color: 1, 1, 0, 1
39 | errors_background_color: app.theme_cls.bg_dark
40 | '''
41 |
42 |
43 | class Example(MDApp):
44 | path_to_kv_file = "kv_file.kv"
45 |
46 | def build(self):
47 | self.theme_cls.theme_style = "Dark"
48 | return Builder.load_string(KV)
49 |
50 | def update_kv_file(self, text):
51 | with open(self.path_to_kv_file, "w") as kv_file:
52 | kv_file.write(text)
53 |
54 |
55 | Example().run()
56 |
57 | This will display the test.kv and automatically update the display when the
58 | file changes.
59 |
60 | .. raw:: html
61 |
62 |
63 |
64 |
65 |
66 |
67 | .. rubric:: This scripts uses watchdog to listen for file changes. To install
68 | watchdog.
69 |
70 | .. code-block:: bash
71 |
72 | pip install watchdog
73 | """
74 |
75 | import os
76 |
77 | from kivy.clock import Clock, mainthread
78 | from kivy.lang import Builder
79 | from kivy.properties import BooleanProperty, ColorProperty, StringProperty
80 | from kivy.uix.scrollview import ScrollView
81 | from watchdog.events import FileSystemEventHandler
82 | from watchdog.observers import Observer
83 |
84 | from kivymd.theming import ThemableBehavior
85 | from kivymd.uix.boxlayout import MDBoxLayout
86 |
87 | Builder.load_string(
88 | """
89 |
90 |
91 | MDLabel:
92 | size_hint_y: None
93 | height: self.texture_size[1]
94 | theme_text_color: "Custom"
95 | text_color:
96 | root.errors_text_color if root.errors_text_color \
97 | else root.theme_cls.text_color
98 | text: root.text
99 | """
100 | )
101 |
102 |
103 | class HotReloadErrorText(ThemableBehavior, ScrollView):
104 | text = StringProperty()
105 | """Text errors.
106 |
107 | :attr:`text` is an :class:`~kivy.properties.StringProperty`
108 | and defaults to `''`.
109 | """
110 |
111 | errors_text_color = ColorProperty(None)
112 | """
113 | Error text color.
114 |
115 | :attr:`errors_text_color` is an :class:`~kivy.properties.ColorProperty`
116 | and defaults to `None`.
117 | """
118 |
119 |
120 | class HotReloadHandler(FileSystemEventHandler):
121 | def __init__(self, callback, target, **kwargs):
122 | super().__init__(**kwargs)
123 | self.callback = callback
124 | self.target = target
125 |
126 | def on_any_event(self, event):
127 | self.callback()
128 |
129 |
130 | class HotReloadViewer(ThemableBehavior, MDBoxLayout):
131 | """
132 | :Events:
133 | :attr:`on_error`
134 | Called when an error occurs in the KV-file that the user is editing.
135 | """
136 |
137 | path = StringProperty()
138 | """Path to KV file.
139 |
140 | :attr:`path` is an :class:`~kivy.properties.StringProperty`
141 | and defaults to `''`.
142 | """
143 |
144 | errors = BooleanProperty(False)
145 | """
146 | Show errors while editing KV-file.
147 |
148 | :attr:`errors` is an :class:`~kivy.properties.BooleanProperty`
149 | and defaults to `False`.
150 | """
151 |
152 | errors_background_color = ColorProperty(None)
153 | """
154 | Error background color.
155 |
156 | :attr:`errors_background_color` is an :class:`~kivy.properties.ColorProperty`
157 | and defaults to `None`.
158 | """
159 |
160 | errors_text_color = ColorProperty(None)
161 | """
162 | Error text color.
163 |
164 | :attr:`errors_text_color` is an :class:`~kivy.properties.ColorProperty`
165 | and defaults to `None`.
166 | """
167 |
168 | _temp_widget = None
169 |
170 | def __init__(self, **kwargs):
171 | self.observer = Observer()
172 | self.error_text = HotReloadErrorText()
173 | super().__init__(**kwargs)
174 | self.register_event_type("on_error")
175 |
176 | @mainthread
177 | def update(self, *args):
178 | """Updates and displays the KV-file that the user edits."""
179 |
180 | Builder.unload_file(self.path)
181 | self.clear_widgets()
182 | try:
183 | self.padding = (0, 0, 0, 0)
184 | self.md_bg_color = (0, 0, 0, 0)
185 | self._temp_widget = Builder.load_file(self.path)
186 | self.add_widget(self._temp_widget)
187 | except Exception as error:
188 | self.show_error(error)
189 | self.dispatch("on_error", error)
190 |
191 | def show_error(self, error):
192 | """Displays text with a current error."""
193 |
194 | if self._temp_widget and not self.errors:
195 | self.add_widget(self._temp_widget)
196 | return
197 | else:
198 | if self.errors_background_color:
199 | self.md_bg_color = self.errors_background_color
200 | self.padding = ("4dp", "4dp", "4dp", "4dp")
201 | self.error_text.text = (
202 | error.message
203 | if getattr(error, r"message", None)
204 | else str(error)
205 | )
206 | self.add_widget(self.error_text)
207 |
208 | def on_error(self, *args):
209 | """
210 | Called when an error occurs in the KV-file that the user is editing.
211 | """
212 |
213 | def on_errors_text_color(self, instance, value):
214 | self.error_text.errors_text_color = value
215 |
216 | def on_path(self, instance, value):
217 | value = os.path.abspath(value)
218 | self.observer.schedule(
219 | HotReloadHandler(self.update, value), os.path.dirname(value)
220 | )
221 | self.observer.start()
222 | Clock.schedule_once(self.update, 1)
223 |
--------------------------------------------------------------------------------
/kivymd/vendor/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AM-ash-OR-AM-I/Passlock/0232719654997770309bcdd8c84be8ff1dcce500/kivymd/vendor/__init__.py
--------------------------------------------------------------------------------
/kivymd/vendor/circleLayout/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 Davide Depau
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
23 |
--------------------------------------------------------------------------------
/kivymd/vendor/circleLayout/README.md:
--------------------------------------------------------------------------------
1 | CircularLayout
2 | ==============
3 |
4 | CircularLayout is a special layout that places widgets around a circle.
5 |
6 | See the widget's documentation and the example for more information.
7 |
8 | 
9 |
10 | size_hint
11 | ---------
12 |
13 | size_hint_x is used as an angle-quota hint (widget with higher
14 | size_hint_x will be farther from each other, and viceversa), while
15 | size_hint_y is used as a widget size hint (widgets with a higher size
16 | hint will be bigger).size_hint_x cannot be None.
17 |
18 | Widgets are all squares, unless you set size_hint_y to None (in that
19 | case you'll be able to specify your own size), and their size is the
20 | difference between the outer and the inner circle's radii. To make the
21 | widgets bigger you can just decrease inner_radius_hint.
--------------------------------------------------------------------------------
/kivymd/vendor/circularTimePicker/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 Davide Depau
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
23 |
--------------------------------------------------------------------------------
/kivymd/vendor/circularTimePicker/README.md:
--------------------------------------------------------------------------------
1 | Circular Date & Time Picker for Kivy
2 | ====================================
3 |
4 | (currently only time, date coming soon)
5 |
6 | Based on [CircularLayout](https://github.com/kivy-garden/garden.circularlayout).
7 | The main aim is to provide a date and time selector similar to the
8 | one found in Android KitKat+.
9 |
10 | 
11 |
12 | Simple usage
13 | ------------
14 |
15 | Import the widget with
16 |
17 | ```python
18 | from kivy.garden.circulardatetimepicker import CircularTimePicker
19 | ```
20 |
21 | then use it! That's it!
22 |
23 | ```python
24 | c = CircularTimePicker()
25 | c.bind(time=self.set_time)
26 | root.add_widget(c)
27 | ```
28 |
29 | in Kv language:
30 |
31 | ```
32 | :
33 | BoxLayout:
34 | orientation: "vertical"
35 |
36 | CircularTimePicker
37 |
38 | Button:
39 | text: "Dismiss"
40 | size_hint_y: None
41 | height: "40dp"
42 | on_release: root.dismiss()
43 | ```
--------------------------------------------------------------------------------
/libs/firebase.py:
--------------------------------------------------------------------------------
1 | from kivy.network.urlrequest import UrlRequest
2 | from kivy.logger import Logger
3 | from libs.utils import *
4 | import json
5 |
6 | # Store web api key in github actions secrets, create api_key.txt file using actions
7 | # then use WEB_API_KEY to store the key.
8 | WEB_API_KEY = os.environ["WEB_API_KEY"]
9 | DATABASE_URL = os.environ["DATABASE_URL"]
10 |
11 |
12 | class Firebase:
13 | BASE_URL = "https://www.googleapis.com/identitytoolkit/v3/relyingparty"
14 | SIGNUP_URL = f"{BASE_URL}/signupNewUser?key="
15 | LOGIN_URL = f"{BASE_URL}/verifyPassword?key="
16 | DELETE_URL = f"{BASE_URL}/deleteAccount?key="
17 |
18 | def signup_success(self, req, result):
19 | """
20 | Implement this method to handle the result of the successful signup request.
21 | """
22 | Logger.warn("Firebase: Signup success not implemented")
23 |
24 | def signup_failure(self, req, result):
25 | """
26 | Implement this method to handle the result of the signup failure request.
27 | """
28 | Logger.warn("Firebase: Signup failure not implemented")
29 |
30 | def signup(self, name, password):
31 | """
32 | Used to signup the user.
33 | """
34 | url = self.SIGNUP_URL + WEB_API_KEY
35 | data = json.dumps(
36 | {"email": name, "password": password, "returnSecureToken": True}
37 | )
38 | UrlRequest(
39 | url,
40 | req_body=data,
41 | on_success=self.signup_success,
42 | on_failure=self.signup_failure,
43 | timeout=15,
44 | )
45 |
46 | def login_success(self, req, result):
47 | """
48 | Implement this method to handle the result of the successful login request.
49 | """
50 | Logger.warn("Firebase: Login success not implemented")
51 |
52 | def login_failure(self, req, result):
53 | """
54 | Implement this method to handle the result of the login failure request.
55 | """
56 | Logger.warn("Firebase: Login failure not implemented")
57 |
58 | def login(self, name, password):
59 | """
60 | Used to login the user.
61 | """
62 |
63 | url = self.LOGIN_URL + WEB_API_KEY
64 | data = json.dumps(
65 | {"email": name, "password": password, "returnSecureToken": True}
66 | )
67 | UrlRequest(
68 | url,
69 | req_body=data,
70 | on_success=self.login_success,
71 | on_failure=self.login_failure,
72 | timeout=10,
73 | )
74 |
75 | def backup_success(self, req, result):
76 | """
77 | Override this method to handle the result of the successful backup request.
78 | """
79 | Logger.warn(f"Firebase: Backup success not implemented, {result}")
80 |
81 | def backup_failure(self, req, result):
82 | """
83 | Override this method to handle the result of the failure in backup request.
84 | """
85 | Logger.warn(f"Firebase: Backup failure not implemented, {result}")
86 |
87 | def backup(self):
88 | """
89 | Used to backup the user's passwords.
90 | """
91 | result = load_passwords()
92 | _json = {}
93 | _json[get_uid()] = result
94 | UrlRequest(
95 | DATABASE_URL + "/.json",
96 | req_body=json.dumps(_json),
97 | req_headers={"Content-type": "application/json", "Accept": "text/plain"},
98 | on_success=self.backup_success,
99 | on_failure=self.backup_failure,
100 | on_error=self.backup_failure,
101 | method="PATCH",
102 | )
103 |
104 | def restore_success(self, req, result):
105 | """
106 | Override this method to handle the result of the successful restore request.
107 | """
108 | Logger.warn(f"Firebase: Restore success not implemented, {result}")
109 |
110 | def restore_failure(self, req, result):
111 | """
112 | Override this method to handle the result of the failure in restore request.
113 | """
114 | Logger.warn(f"Firebase: Restore failure not implemented, {result}")
115 |
116 | def restore(self, user_id=None):
117 | """
118 | Used to restore the user's passwords.
119 | """
120 | if user_id is None:
121 | user_id = get_uid()
122 |
123 | UrlRequest(
124 | f"{DATABASE_URL}/{user_id}.json",
125 | on_success=self.restore_success,
126 | on_failure=self.restore_failure,
127 | on_error=self.restore_failure,
128 | )
129 |
--------------------------------------------------------------------------------
/libs/modules/AndroidAPI.py:
--------------------------------------------------------------------------------
1 | from kivy.utils import platform
2 |
3 | if platform == "android":
4 | from android.runnable import run_on_ui_thread
5 | from jnius import autoclass
6 | from jnius import JavaException
7 |
8 | Color = autoclass("android.graphics.Color")
9 | WindowManager = autoclass("android.view.WindowManager$LayoutParams")
10 | activity = autoclass("org.kivy.android.PythonActivity").mActivity
11 | View = autoclass("android.view.View")
12 | Configuration = autoclass("android.content.res.Configuration")
13 |
14 | def _class_call(cls, args: tuple, instantiate: bool):
15 | if not args:
16 | return cls() if instantiate else cls
17 | else:
18 | return cls(*args)
19 |
20 | def Rect(*args, instantiate: bool = False):
21 | return _class_call(autoclass("android.graphics.Rect"), args, instantiate)
22 |
23 | @run_on_ui_thread
24 | def fix_back_button():
25 | activity.onWindowFocusChanged(False)
26 | activity.onWindowFocusChanged(True)
27 |
28 | def keyboard_height():
29 | try:
30 | decor_view = activity.getWindow().getDecorView()
31 | height = activity.getWindowManager().getDefaultDisplay().getHeight()
32 | decor_view.getWindowVisibleDisplayFrame(Rect)
33 | return height - Rect().bottom
34 |
35 | except JavaException:
36 | return 0
37 |
38 | @run_on_ui_thread
39 | def statusbar(
40 | theme="Custom",
41 | status_color="121212",
42 | nav_color=None,
43 | white_text=None,
44 | full=False,
45 | ):
46 | window = activity.getWindow()
47 | print("Setting StatusBar Color") # Updates everytime color is changed
48 | window.clearFlags(WindowManager.FLAG_TRANSLUCENT_STATUS)
49 | try:
50 | window.addFlags(WindowManager.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS)
51 | if theme == "black":
52 | window.setNavigationBarColor(Color.parseColor("#" + status_color))
53 | window.setStatusBarColor(Color.parseColor("#" + status_color))
54 | window.getDecorView().setSystemUiVisibility(0)
55 | elif theme == "white":
56 | window.setNavigationBarColor(Color.parseColor("#FAFAFA"))
57 | window.setStatusBarColor(Color.parseColor("#FAFAFA"))
58 | window.getDecorView().setSystemUiVisibility(
59 | View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR
60 | | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
61 | )
62 | elif theme == "Custom":
63 | if nav_color is None:
64 | nav_color = status_color
65 | window.setNavigationBarColor(Color.parseColor("#" + nav_color))
66 | window.setStatusBarColor(Color.parseColor("#" + status_color))
67 | if white_text is True:
68 | window.getDecorView().setSystemUiVisibility(
69 | View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR
70 | | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
71 | )
72 | elif white_text is False:
73 | window.getDecorView().setSystemUiVisibility(0)
74 |
75 | except Exception:
76 | pass
77 |
78 | def android_dark_mode():
79 | night_mode_flags = (
80 | activity.getContext().getResources().getConfiguration().uiMode
81 | & Configuration.UI_MODE_NIGHT_MASK
82 | )
83 | if night_mode_flags == Configuration.UI_MODE_NIGHT_YES:
84 | return True
85 | elif night_mode_flags in [
86 | Configuration.UI_MODE_NIGHT_NO,
87 | Configuration.UI_MODE_NIGHT_UNDEFINED,
88 | ]:
89 | return False
90 |
91 | def orientation():
92 | config = activity.getResources().getConfiguration()
93 | if config.orientation == 1:
94 | return "Portrait"
95 | else:
96 | return "Landscape"
97 |
--------------------------------------------------------------------------------
/libs/modules/List.py:
--------------------------------------------------------------------------------
1 | from kivy.animation import Animation
2 | from kivy.lang import Builder
3 | from kivy.properties import BooleanProperty, StringProperty, ColorProperty, DictProperty
4 | from kivy.uix.behaviors import ButtonBehavior
5 | from kivy.uix.recycleview.views import RecycleDataViewBehavior
6 |
7 | from kivymd.app import MDApp
8 | from kivymd.material_resources import dp
9 | from kivymd.theming import ThemableBehavior
10 | from kivymd.uix.behaviors import RectangularRippleBehavior
11 | from kivymd.uix.boxlayout import MDBoxLayout
12 |
13 | app = MDApp.get_running_app()
14 |
15 | KV = """
16 |
17 | orientation:'vertical'
18 | size_hint_y:None
19 | padding: dp(28),dp(0),dp(10),dp(15)
20 | size_hint_x:1
21 | spacing:'6dp'
22 | height:'90dp' if self.selected else "56dp"
23 | elevation:0
24 | radius:'28dp'
25 | disable: True
26 | md_bg_color:self.list_color_active if self.selected else app.theme_cls.primary_light[:-1] + [0]
27 | MDLabel:
28 | adaptive_height: True
29 | text:root.name
30 | theme_text_color:"Custom"
31 | text_color: app.text_color
32 | MDBoxLayout:
33 | id: sec_box
34 | size_hint_y:None
35 | height:root.selected*dp(30)
36 | opacity: root.selected*1
37 | MDLabel:
38 | adaptive_height:True
39 | text:root.password
40 | theme_text_color:"Secondary"
41 | MDIconButton:
42 | id: copy_button
43 | disabled: not root.selected
44 | md_bg_color_disabled:[0,0,0,0]
45 | theme_text_color:"Custom"
46 | text_color: app.text_color
47 | icon:'content-copy'
48 | pos_hint:{'center_y':.5}
49 | MDIconButton:
50 | id: update_button
51 | disabled: not root.selected
52 | md_bg_color_disabled:[0,0,0,0]
53 | icon:'pencil-outline'
54 | theme_text_color:"Custom"
55 | text_color: app.text_color
56 | pos_hint:{'center_y':.5}
57 | # text_color:[.3,.3,.3,1]
58 | MDIconButton:
59 | id: delete_button
60 | disabled: not root.selected
61 | md_bg_color_disabled:[0,0,0,0]
62 | icon:'trash-can-outline'
63 | theme_text_color:"Custom"
64 | text_color: app.text_color
65 | pos_hint:{'center_y':.5}
66 |
67 | """
68 |
69 |
70 | class List(
71 | RectangularRippleBehavior,
72 | RecycleDataViewBehavior,
73 | ThemableBehavior,
74 | ButtonBehavior,
75 | MDBoxLayout,
76 | ):
77 | Builder.load_string(KV)
78 |
79 | name = StringProperty("Google")
80 | password = StringProperty("12345678910111213")
81 | selected = BooleanProperty()
82 | list_color_active = ColorProperty()
83 | button_actions = DictProperty()
84 |
85 | _no_ripple_effect = True
86 | ripple_alpha = 0.1
87 |
88 | def on_button_actions(self, *args):
89 | if len(self.button_actions) == 3:
90 | self.ids.copy_button.on_release = self.button_actions["copy"]
91 | self.ids.update_button.on_release = self.button_actions["update"]
92 | self.ids.delete_button.on_release = self.button_actions["delete"]
93 |
94 | def on_release(self):
95 | if self.selected:
96 | if self.right - self.last_touch.x >= dp(170):
97 | self.parent.clear_selection()
98 | else:
99 | recycle_list = self.parent.children
100 | snack = app.root.HomeScreen.ids.find.snackbar
101 |
102 | if (
103 | not (snack and snack.is_open)
104 | or self not in recycle_list[:2]
105 | or len(recycle_list) <= 13
106 | ):
107 | self.parent.select_with_touch(self.index, None)
108 |
109 | def refresh_view_attrs(self, rv, index, data):
110 | self.index = index
111 | return super().refresh_view_attrs(rv, index, data)
112 |
113 | def apply_selection(self, rv, index, is_selected):
114 | try:
115 | self.selected = is_selected
116 | rv.data[index]["selected"] = is_selected
117 | except IndexError:
118 | print(index, self.selected, "Index error")
119 |
120 | def on_selected(self, instance, selected):
121 | if selected:
122 | self.list_color_active = self.theme_cls.primary_light[:-1] + [0]
123 | Animation(
124 | list_color_active=self.theme_cls.primary_light[:-1] + [0.3], d=0.1
125 | ).start(self)
126 |
--------------------------------------------------------------------------------
/libs/modules/Toolbar.py:
--------------------------------------------------------------------------------
1 | from kivy.lang import Builder
2 | from kivy.properties import StringProperty, NumericProperty, ListProperty, ColorProperty
3 |
4 | from kivymd.app import MDApp
5 | from kivymd.theming import ThemableBehavior
6 | from kivymd.uix import SpecificBackgroundColorBehavior
7 | from kivymd.uix.behaviors import FakeRectangularElevationBehavior
8 | from kivymd.uix.boxlayout import MDBoxLayout
9 | from kivymd.uix.button import MDIconButton
10 |
11 | Builder.load_string("""
12 |
13 | id: toolbox
14 | set_opacity: root.set_opacity
15 | size_hint_y: None
16 | elevation: root.elevation
17 | md_bg_color: root.md_bg_color if root.md_bg_color !=[0,0,0,0] else [0,0,0,0]
18 | pos_hint:{'top':1}
19 | spacing: '10dp'
20 | font_style:None
21 | height: root.theme_cls.standard_increment + root.increase_height
22 |
23 | MDBoxLayout:
24 | id: left_action_box
25 | adaptive_size: True
26 | pos_hint: {'center_y': .5}
27 |
28 | MDBoxLayout:
29 | adaptive_height: True
30 | pos_hint: {'center_y': .5}
31 | MDLabel:
32 | id:setting_label
33 | text: root.title
34 | theme_text_color:'Custom'
35 | # font_size: root.text_height if root.text_height else 0
36 | font_style: "H6" if not root.font_style else root.font_style
37 | font_name:'Poppins'
38 | text_color:app.theme_cls.text_color if root.icon_color is None else root.icon_color
39 | shorten: True
40 | shorten_from: "right"
41 | height: root.height-root.padding[0]/2
42 |
43 | MDBoxLayout:
44 | id: right_action_box
45 | adaptive_size: True
46 | spacing:'5dp'
47 | pos_hint: {'center_y': .5}
48 |
49 |
50 |
51 |
52 | """
53 | )
54 |
55 |
56 | class Toolbar(
57 | MDBoxLayout,
58 | ThemableBehavior,
59 | FakeRectangularElevationBehavior,
60 | SpecificBackgroundColorBehavior,
61 | ):
62 | elevation = NumericProperty(0)
63 | adaptive_height = True
64 | md_bg_color = ColorProperty()
65 | title = StringProperty("")
66 | increase_height = NumericProperty(0)
67 | left_action_items = ListProperty()
68 | right_action_items = ListProperty()
69 | icon_color = ColorProperty(None)
70 | text_height = NumericProperty(0)
71 |
72 | def __init__(self, **kwargs):
73 | super().__init__(**kwargs)
74 |
75 | def on_left_action_items(self, instance, value):
76 | self.ids.left_action_box.clear_widgets()
77 | for icon_left_action in self.left_action_items:
78 | if len(icon_left_action) == 1:
79 | self.icon_left = MDIconButton(
80 | icon=icon_left_action[0],
81 | theme_text_color="Custom",
82 | text_color=self.icon_color,
83 | user_font_size=self.text_height,
84 | pos_hint={"center_y": 0.5},
85 | )
86 |
87 | else:
88 | self.icon_left = MDIconButton(
89 | icon=icon_left_action[0],
90 | theme_text_color="Custom",
91 | text_color=self.icon_color,
92 | user_font_size=self.text_height,
93 | pos_hint={"center_y": 0.5},
94 | on_release=icon_left_action[1],
95 | )
96 |
97 | self.ids.left_action_box.add_widget(self.icon_left)
98 |
99 | def on_right_action_items(self, instance, value):
100 | self.ids.right_action_box.clear_widgets()
101 | for icon_right_action in self.right_action_items:
102 | if len(icon_right_action) == 1:
103 | self.icon_right = MDIconButton(
104 | icon=icon_right_action[0],
105 | theme_text_color="Custom",
106 | text_color=self.icon_color,
107 | user_font_size=self.text_height,
108 | pos_hint={"center_y": 0.5},
109 | )
110 |
111 | else:
112 | self.icon_right = MDIconButton(
113 | icon=icon_right_action[0],
114 | theme_text_color="Custom",
115 | text_color=self.icon_color,
116 | user_font_size=self.text_height,
117 | pos_hint={"center_y": 0.5},
118 | on_release=icon_right_action[1],
119 | )
120 | self.ids.right_action_box.add_widget(self.icon_right)
121 |
122 | def on_icon_color(self, instance, color):
123 | for icon in self.ids.left_action_box.children:
124 | icon.text_color = color
125 | for icon in self.ids.right_action_box.children:
126 | icon.text_color = color
127 |
128 |
129 | if __name__ == "__main__":
130 |
131 | class ToolbarApp(MDApp):
132 | def build(self):
133 | return Builder.load_string(
134 | """
135 | Screen:
136 | Toolbar:
137 | title:'Test'
138 | icon_color: [1,0,.4,1]
139 | right_action_items:[['cog',lambda x: print('Call any function you want like this.')]]
140 | """
141 | )
142 |
143 | ToolbarApp().run()
144 |
--------------------------------------------------------------------------------
/libs/modules/dialogs.py:
--------------------------------------------------------------------------------
1 | from kivy.animation import Animation
2 | from kivy.clock import Clock
3 | from kivy.core.window import Window
4 | from kivy.lang.builder import Builder
5 | from kivy.properties import (
6 | ListProperty,
7 | NumericProperty,
8 | ObjectProperty,
9 | OptionProperty,
10 | StringProperty,
11 | )
12 | from kivy.uix.boxlayout import BoxLayout
13 | from kivymd.uix.behaviors import FakeRectangularElevationBehavior
14 | from kivymd.uix.dialog import BaseDialog
15 | from kivymd.material_resources import dp
16 |
17 | Builder.load_string("""
18 | #: import md_icons kivymd.icon_definitions.md_icons
19 | #: import Window kivy.core.window.Window
20 |
21 | :
22 | background_color: [0,0,0,0]
23 | overlay_color: [0,0,0,0.25]
24 | size_hint: alert.width / Window.width, alert.height / Window.height
25 | MainAlertBox:
26 | id: alert
27 | size_hint: None,None
28 | elevation: root.elevation
29 | size: root.size_portrait if root._orientation == "portrait" \
30 | else root.size_landscape
31 | orientation: "vertical" if root._orientation == "portrait" \
32 | else "horizontal"
33 |
34 | canvas.before:
35 | Color:
36 | rgba: root.bg_color if root.bg_color else root.theme_cls.bg_light
37 | RoundedRectangle:
38 | pos: self.pos
39 | size: self.size
40 | radius: [root.dialog_radius, ]
41 |
42 | canvas.after:
43 | Color:
44 | rgba: root.progress_color if root.progress_color else root.theme_cls.primary_dark
45 | RoundedRectangle:
46 | pos: self.pos[0] + root.dialog_radius, self.pos[1] + root.height - root.progress_width
47 | size: root._progress_value, root.progress_width
48 | radius: [root.progress_width / 2, ]
49 |
50 | BoxLayout:
51 | size_hint_y: None if root._orientation == "portrait" \
52 | else 1
53 |
54 | size_hint_x: None if root._orientation == "landscape" \
55 | else 1
56 |
57 | size: (root.width, root.header_height_portrait) if root._orientation == "portrait" \
58 | else (root.header_width_landscape, root.height)
59 |
60 | canvas.before:
61 | Color:
62 | rgba: root.header_bg if root.header_bg else root.theme_cls.primary_color
63 | RoundedRectangle:
64 | pos: self.pos
65 | size: self.size
66 | radius: [root.dialog_radius, root.dialog_radius, 0, 0] if root._orientation == "portrait" \
67 | else [root.dialog_radius, 0, 0, root.dialog_radius]
68 |
69 | MDLabel:
70 | font_style: "Icon" if root.header_text_type == "icon" else "Body1"
71 | bold: True
72 | text: u"{}".format(md_icons[root.header_icon]) if root.header_text_type == "icon" else root.header_text
73 | theme_text_color: "Custom"
74 | text_color: root.header_color if root.header_color else [1, 1, 1, 1]
75 | valign: root.header_v_pos
76 | halign: root.header_h_pos
77 | font_size: root.header_font_size
78 |
79 | BoxLayout:
80 | id: content
81 | """
82 | )
83 |
84 |
85 | class MainAlertBox(FakeRectangularElevationBehavior, BoxLayout):
86 | pass
87 |
88 |
89 | class AKAlertDialog(BaseDialog):
90 |
91 | dialog_radius = NumericProperty(dp(30))
92 | radius = [dp(30)] * 4
93 | bg_color = ListProperty()
94 | auto_dismiss = True
95 | size_portrait = ListProperty(["250dp", "350dp"])
96 | size_landscape = ListProperty(["400dp", "250dp"])
97 | header_width_landscape = NumericProperty("110dp")
98 | header_height_portrait = NumericProperty("110dp")
99 | fixed_orientation = OptionProperty(None, options=["portrait", "landscape"])
100 | header_bg = ListProperty()
101 | header_text_type = OptionProperty("icon", options=["icon", "text"])
102 | header_text = StringProperty()
103 | header_icon = StringProperty("android")
104 | header_color = ListProperty()
105 | header_h_pos = StringProperty("center")
106 | header_v_pos = StringProperty("center")
107 | header_font_size = NumericProperty("55dp")
108 | progress_interval = NumericProperty(None)
109 | progress_width = NumericProperty("2dp")
110 | progress_color = ListProperty()
111 | elevation = NumericProperty(5)
112 | content_cls = ObjectProperty()
113 | _anim_duration = 0.25
114 | _orientation = StringProperty()
115 | _progress_value = NumericProperty()
116 |
117 | def __init__(self, **kwargs):
118 | super().__init__(**kwargs)
119 | Window.bind(on_resize=self._get_orientation)
120 | self.register_event_type("on_progress_finish")
121 | Clock.schedule_once(self._update)
122 |
123 | def _update(self, *args):
124 | self._get_orientation()
125 |
126 | def _get_orientation(self, *args):
127 | if self.fixed_orientation:
128 | self._orientation = self.fixed_orientation
129 | elif self.theme_cls.device_orientation == "portrait":
130 | self._orientation = "portrait"
131 | else:
132 | self._orientation = "landscape"
133 |
134 | def on_content_cls(self, *args):
135 | if not self.content_cls:
136 | return
137 |
138 | self.ids.content.clear_widgets()
139 | self.ids.content.add_widget(self.content_cls)
140 |
141 | def on_open(self):
142 | self._start_progress()
143 | return super().on_open()
144 |
145 | def on_pre_open(self):
146 | self._opening_animation()
147 | return super().on_pre_open()
148 |
149 | def on_dismiss(self):
150 | self._dismiss_animation()
151 | return super().on_dismiss()
152 |
153 | def _opening_animation(self):
154 | self.opacity = 0
155 | anim = Animation(opacity=1, duration=self._anim_duration, t="out_quad")
156 | anim.start(self)
157 |
158 | def _dismiss_animation(self):
159 | anim = Animation(opacity=0, duration=self._anim_duration - .05, t="out_quad")
160 | anim.start(self)
161 |
162 | def _start_progress(self):
163 | if not self.progress_interval:
164 | return
165 | max_width = self.size[0] - self.dialog_radius * 2
166 | anim = Animation(_progress_value=max_width, duration=self.progress_interval)
167 | anim.bind(on_complete=lambda x, y: self.dispatch("on_progress_finish"))
168 | anim.start(self)
169 |
170 | def on_progress_finish(self, *args):
171 | pass
172 |
--------------------------------------------------------------------------------
/libs/modules/picker.py:
--------------------------------------------------------------------------------
1 | from kivy.factory import Factory
2 | from kivy.lang import Builder
3 | from kivy.properties import OptionProperty, get_color_from_hex
4 | from kivymd.app import MDApp
5 | from libs.modules.dialogs import AKAlertDialog
6 | from kivymd.color_definitions import colors
7 | from kivymd.uix.button import MDIconButton
8 |
9 | MY_PALETTE = ["DeepOrange", "Blue", "Purple"]
10 |
11 | KV = """
12 | #: import colors kivymd.color_definitions.colors
13 |
14 | canvas:
15 | Color:
16 | rgba: root.rgb_hex(root.color_name)
17 | Ellipse:
18 | size: self.size
19 | pos: self.pos
20 | theme_text_color:'Custom'
21 | text_color: [0,0,0,0]
22 |
23 |
24 | on_release:
25 | app.primary_palette = root.color_name
26 | app.set_theme_style()
27 |
28 |
29 | cols: 5
30 | rows: 4
31 | adaptive_size: True
32 | spacing: "8dp"
33 | padding: dp(20)
34 | pos_hint: {"center_x": .5, "top": 1}
35 |
36 | """
37 | Builder.load_string(KV)
38 |
39 |
40 | class ColorSelector(MDIconButton):
41 | color_name = OptionProperty("Indigo", options=MY_PALETTE)
42 |
43 | def rgb_hex(self, col):
44 | return get_color_from_hex(colors[col][self.theme_cls.primary_hue])
45 |
46 |
47 | class MDThemePicker(AKAlertDialog):
48 | header_icon = "palette"
49 |
50 | def __init__(self, **kwargs):
51 | super().__init__(**kwargs)
52 | self.theme_cls.bind(theme_style=self.update_selector)
53 | self.content_cls = Factory.ColorGrid()
54 | self.header_font_size = "40dp"
55 | self.header_height_portrait = "90dp"
56 | self.update_bg_color()
57 | MDApp.get_running_app().bind(primary_palette=self.update_bg_color)
58 | self.size_portrait = ["320dp", "180dp"]
59 |
60 | def update_bg_color(self, *args):
61 | self.bg_color = MDApp.get_running_app().primary_accent
62 |
63 | def update_selector(self, *args):
64 | self.content_cls.clear_widgets()
65 | self.bg_color = MDApp.get_running_app().primary_accent
66 |
67 | def on_open(self):
68 | if not self.content_cls.children:
69 | for name_palette in MY_PALETTE:
70 | self.content_cls.add_widget(
71 | Factory.PrimaryColorSelector(color_name=name_palette)
72 | )
73 |
--------------------------------------------------------------------------------
/libs/save_config.py:
--------------------------------------------------------------------------------
1 | import json
2 | from typing import List
3 |
4 | from kivymd.app import MDApp
5 |
6 | app = MDApp.get_running_app()
7 |
8 |
9 | class SaveConfig:
10 | def __init__(self, *args: List[str]) -> None:
11 | self.variable_list = args
12 | self.variable_dict = {}
13 |
14 | def save_settings(self) -> None:
15 | for var in self.variable_list:
16 | exec(f"self.variable_dict['{var}']= app.{var}")
17 | with open("data/config.json", "w") as file:
18 | json.dump(self.variable_dict, file, indent=4)
19 |
--------------------------------------------------------------------------------
/libs/screens/LoginScreen/LoginScreen.kv:
--------------------------------------------------------------------------------
1 | #: import platform kivy.platform
2 | #: import Clock kivy.clock.Clock
3 | #: import colors kivymd.color_definitions.colors
4 | #: import Window kivy.core.window.Window
5 | #: set height Window.height
6 | #: set width Window.width
7 | #: set rad width/2.4
8 |
9 | #: set primary_color_hex colors[app.theme_cls.primary_palette][app.theme_cls.primary_hue]
10 |
11 | :
12 | md_bg_color: app.bg_color
13 | on_enter:
14 | app.animate_signup('')
15 | canvas:
16 | Color:
17 | rgba:app.theme_cls.primary_color[:-1]+[.9]
18 | Ellipse:
19 | size:rad,rad
20 | pos:0-rad/2,height-rad+20
21 | angle_start:0
22 | angle_end:180
23 | Color:
24 | rgba:app.login_circle_light[:-1]+[.7]
25 | Ellipse:
26 | pos:0,height-rad/2
27 | size:rad,rad
28 | angle_start:90
29 | angle_end:270
30 |
31 | MDBoxLayout:
32 | adaptive_height:True
33 | id: box
34 | pos_hint:{"top":.95}
35 | orientation:'vertical'
36 | spacing:'70dp'
37 | MDLabel:
38 | adaptive_height:True
39 | text:f"LOGIN\nTO [color={primary_color_hex}][u]PASS••[/u]"
40 | markup:True
41 | valign:"center"
42 | theme_text_color:"Custom"
43 | text_color:app.text_color
44 | font_name:'Poppins'
45 | halign:'center'
46 | font_size:'44dp'
47 | pos_hint:{'top':1}
48 | MDBoxLayout:
49 | adaptive_height:True
50 | spacing:'50dp'
51 | orientation:'vertical'
52 | padding:(0,dp(15),0,0)
53 | MDBoxLayout:
54 | adaptive_height:True
55 | orientation:'vertical'
56 | spacing:'35dp'
57 | PasswordCard:
58 | id: password
59 | label_name:'Password'
60 | hint_text:"Enter Password"
61 | pos_hint:{'center_x':.5}
62 | on_text_validate:
63 | root.login_button_pressed(password.text)
64 | FloatingButton:
65 | id: lock
66 | icon:'login'
67 | on_press:
68 | root.login_button_pressed(password.text)
69 | pos_hint:{'center_x':.5}
70 |
71 | # MDTextButton:
72 | # id:forgot
73 | # x: password.x+dp(25)
74 | # y: password.y-dp(25)
75 | # text:'Forgot Password?'
76 | # font_name:'RobotoMedium'
77 | # theme_text_color:"Secondary"
78 | # font_size:"12sp"
79 |
80 |
81 |
--------------------------------------------------------------------------------
/libs/screens/LoginScreen/LoginScreen.py:
--------------------------------------------------------------------------------
1 | from kivymd.app import MDApp
2 | from kivymd.toast import toast
3 | from kivymd.uix.screen import MDScreen
4 | import os.path
5 | from time import time
6 | from kivy.factory import Factory
7 | from kivy.core.window import Window
8 | from libs.screens.classes import SyncWidget
9 | from libs.utils import *
10 |
11 | app = MDApp.get_running_app()
12 |
13 |
14 | class LoginScreen(MDScreen):
15 | loading_view = None
16 | sync_widget = None
17 | logged_in = False
18 | password = None
19 |
20 | def get_sync_widget(self):
21 | if self.sync_widget is None:
22 | self.sync_widget = SyncWidget(pos_hint={"center_x":.8,"center_y":.1})
23 | self.add_widget(self.sync_widget)
24 | return self.sync_widget
25 |
26 | def on_enter(self,*args):
27 | if app.auto_sync and not is_backup_failure():
28 | self.sync_widget = self.get_sync_widget()
29 | app.restore(self.sync_widget, decrypt = False)
30 |
31 | def login_button_pressed(self, password):
32 | Window.softinput_mode = "below_target"
33 | def dismiss_spinner(*args):
34 | app.root.load_screen("HomeScreen")
35 | self.loading_view.dismiss()
36 |
37 | def initialise_encryption():
38 | i = time()
39 | from libs.encryption import Encryption
40 |
41 | try:
42 | app.encryption_class = Encryption(password)
43 | if os.path.exists("data/passwords"):
44 | app.passwords = app.encryption_class.load_decrypted()
45 | else:
46 | if os.path.exists("data/encrypted_file.txt"):
47 | with open("data/encrypted_file.txt", "r") as f:
48 | app.encryption_class.decrypt(f.read())
49 | else:
50 | print("'encrypted_file' not found, can't check password.")
51 |
52 | dismiss_spinner()
53 | if not self.password:
54 | self.password = password
55 | # app.root.HomeScreen.ids.create.ids.tab.switch_tab("[b]MANUAL")
56 | except UnicodeDecodeError:
57 | self.loading_view.dismiss()
58 | toast("Invalid password")
59 | # print(app.encrypted_keys)
60 | print(f"Time taken to load passwords = {time()-i}")
61 | if not self.password:
62 | if self.loading_view is None:
63 | self.loading_view = Factory.LoadingScreen()
64 | self.loading_view.open()
65 | self.loading_view.on_open = lambda *args: initialise_encryption()
66 | else:
67 | if self.password == password:
68 | app.root.load_screen("HomeScreen")
69 | else:
70 | toast("Invalid password")
--------------------------------------------------------------------------------
/libs/screens/SettingsScreen/SettingsScreen.kv:
--------------------------------------------------------------------------------
1 |
2 | widget_style: "ios"
3 |
4 |
5 | ripple_alpha:.1
6 | text:"Auto sync"
7 | icon:"auto-upload"
8 | secondary_text:"Passwords will be synced"
9 | tertiary_text:'automatically'
10 | theme_text_color:'Custom'
11 | active: False
12 | text_color:app.text_color
13 | on_release:
14 | if self.right - self.last_touch.x >= dp(75): self.active = not self.active
15 | RightSwitch:
16 | id: switch
17 | active: root.active
18 | on_active:
19 | root.active = self.active
20 | width: dp(44)
21 | IconLeftWidget:
22 | icon: root.icon
23 | theme_text_color:'Custom'
24 | text_color:app.text_color
25 |
26 |
27 | theme_text_color:'Custom'
28 | text_color:app.text_color
29 | icon:"android"
30 | icon_color: []
31 | size_hint_x:.85
32 | ripple_alpha:.1
33 | IconLeftWidget:
34 | icon: root.icon
35 | theme_text_color:'Custom'
36 | text_color:root.text_color if not root.icon_color else root.icon_color
37 |
38 |
39 | orientation:'vertical'
40 | MDLabel:
41 | text:'Made by Ashutosh'
42 | halign:'center'
43 | font_size:'20sp'
44 | size_hint_y:None
45 | height:dp(120)
46 | theme_text_color:'Custom'
47 | text_color: app.theme_cls.primary_color
48 | font_name:'Poppins'
49 | MDSeparator:
50 | MDTextButton:
51 | size_hint_y:.5
52 | markup: True
53 | font_size:'16sp'
54 | text:f"[font=Icons][size={int(self.font_size)+10}] {md_icons['github']}[/font][/size] GitHub Code"
55 | pos_hint:{'center_x':.5,'center_y':1}
56 | on_release:
57 | app.root.SettingsScreen.open_web(github=True)
58 | MDSeparator:
59 | MDTextButton:
60 | size_hint_y:.5
61 | markup: True
62 | font_size:'16sp'
63 | text:f"[color=#de6666][font=Icons][size={int(self.font_size)+10}] {md_icons['gmail']}[/font][/size] Contact Me[/color]"
64 | pos_hint:{'center_x':.5,'center_y':1}
65 | on_release:
66 | app.root.SettingsScreen.open_web(email=True)
67 |
68 |
69 | md_bg_color: app.bg_color
70 | BoxLayout:
71 | orientation:'vertical'
72 | Toolbar:
73 | title:'Settings'
74 | height:'70dp'
75 | md_bg_color:app.bg_color
76 | padding:dp(15),0
77 | icon_color:app.text_color
78 | font_style:"H5"
79 | left_action_items:[['arrow-left',lambda x: app.root.goback()]]
80 | ScrollView:
81 | MDBoxLayout:
82 | adaptive_height:True
83 | pos_hint:{"top":1}
84 | padding:0, dp(5)
85 | orientation:'vertical'
86 | spacing:'15dp'
87 | MDBoxLayout:
88 | adaptive_height:True
89 | orientation:'vertical'
90 | spacing:'5dp'
91 | MDBoxLayout:
92 | adaptive_height:True
93 | padding:dp(10),dp(5),dp(10),dp(10)
94 | MyListItem:
95 | icon:"account-circle"
96 | height: "58dp"
97 | bg_color: app.primary_accent
98 | text: app.email
99 | font_style:"H6"
100 | MyListItem:
101 | text:"Colors"
102 | icon:"circle"
103 | icon_color: app.theme_cls.primary_color
104 | on_release:
105 | root.change_colors()
106 | MyListItem:
107 | text:"Sync Passwords"
108 | icon:"sync"
109 | on_release: app.root.HomeScreen.open_sync_dialog()
110 |
111 | SwitchListItem:
112 | text:"Auto sync"
113 | secondary_text:"Passwords will be synced"
114 | tertiary_text:'automatically'
115 | active: app.auto_sync
116 | on_active:
117 | app.auto_sync = self.active
118 |
119 | SwitchListItem:
120 | id: system_dark_item
121 | text:"Use system theme"
122 | icon:"theme-light-dark"
123 | secondary_text:"Dark/Light mode of app will be"
124 | tertiary_text:"synchronised with system theme"
125 | active: app.system_dark_mode
126 | on_active:
127 | app.system_dark_mode = self.active
128 |
129 | SwitchListItem:
130 | text:"Extra Security"
131 | icon:"shield-star" if self.active else "shield-off-outline"
132 | secondary_text:"Login when you leave the app"
133 | tertiary_text: "for more than 5 minutes."
134 | active: app.extra_security
135 | on_active:
136 | app.extra_security = self.active
137 |
138 |
139 | MDSeparator:
140 |
141 | MDBoxLayout:
142 | adaptive_height:True
143 | orientation:'vertical'
144 | spacing:'5dp'
145 | MyListItem:
146 | text:"About the app"
147 | icon:"information"
148 | on_release:
149 | root.open_about()
150 | MyListItem:
151 | text:"YouTube Demo"
152 | icon:"youtube"
153 | icon_color:[.8,0,0,1]
154 | on_release:
155 | root.open_web(youtube=True)
156 |
157 | MyListItem:
158 | text:"Log out"
159 | icon:"logout"
160 | disabled: app.signup
161 | size_hint_x:.5
162 | pos_hint:{'center_x':.5}
163 | opacity: 1 * (not self.disabled)
164 | on_release:
165 | root.logout()
166 |
167 |
168 |
--------------------------------------------------------------------------------
/libs/screens/SettingsScreen/SettingsScreen.py:
--------------------------------------------------------------------------------
1 | import webbrowser
2 |
3 | from kivy.core.clipboard import Clipboard
4 | from kivy.factory import Factory
5 | from kivy.clock import Clock
6 | from kivymd .toast import toast
7 | from kivymd.uix.screen import MDScreen
8 | from libs.modules.picker import MDThemePicker
9 | from libs.utils import remove_user_data
10 | from kivymd.app import MDApp
11 | from libs.modules.dialogs import AKAlertDialog
12 |
13 | app = MDApp.get_running_app()
14 |
15 |
16 | class SettingsScreen(MDScreen):
17 | content = None
18 | theme_picker = None
19 | YOUTUBE_VIDEO_LINK = "https://www.youtube.com/watch?v=EOkMDc5mZWI&list=PLUdItSprD91ybWz6uxs4zF4Gux_vzdbZh&index=1"
20 | GITHUB_REPO_LINK = "https://github.com/AM-ash-OR-AM-I/Passlock"
21 |
22 | def logout(self):
23 | app.root.load_screen("SignupScreen", empty_history=True)
24 | app.root.SignupScreen.on_enter = lambda *args: Clock.schedule_once(
25 | lambda x: app.animate_signup(app.root.SignupScreen.ids.box), 0
26 | )
27 | remove_user_data()
28 |
29 | def change_colors(self):
30 | if self.theme_picker is None:
31 | self.theme_picker = MDThemePicker()
32 | self.theme_picker.open()
33 |
34 | def open_about(self):
35 | if self.content is None:
36 | self.content = Factory.AboutClass()
37 | self.about_dialog = AKAlertDialog(header_icon='heart-circle')
38 | self.about_dialog.header_height_portrait = "90dp"
39 | self.about_dialog.size_portrait = ['300dp', '340dp']
40 | self.about_dialog.content_cls = self.content
41 | self.about_dialog.bg_color = app.primary_accent
42 | self.about_dialog.open()
43 |
44 | def open_web(self, github=False, youtube=False, email=False):
45 | if github:
46 | webbrowser.open(self.GITHUB_REPO_LINK)
47 | toast('Star my repository if you like it :)')
48 | elif youtube:
49 | webbrowser.open(self.YOUTUBE_VIDEO_LINK)
50 | elif email:
51 | webbrowser.open('https://mail.google.com/mail/u/0/#inbox?compose=new')
52 | Clipboard.copy('ashutoshmaha2909@gmail.com')
53 | toast('Email address Copied, Paste Email address to send email.')
54 |
55 | def open_youtube_demo(self):
56 | if self.YOUTUBE_VIDEO_LINK:
57 | webbrowser.open(self.YOUTUBE_VIDEO_LINK)
58 |
--------------------------------------------------------------------------------
/libs/screens/SignupScreen/SignupScreen.kv:
--------------------------------------------------------------------------------
1 | #: import platform kivy.platform
2 | #: import Clock kivy.clock.Clock
3 | #: import colors kivymd.color_definitions.colors
4 | #: import Window kivy.core.window.Window
5 | #: set height Window.height
6 | #: set width Window.width
7 | #: set rad width/2
8 | #: set lbl_size '18sp'
9 |
10 | #: set primary_color_hex colors[app.theme_cls.primary_palette][app.theme_cls.primary_hue]
11 |
12 | :
13 | md_bg_color: app.bg_color
14 | canvas:
15 | Color:
16 | rgba:app.theme_cls.primary_color[:-1]+[.9]
17 | Ellipse:
18 | size:rad,rad
19 | pos:0-rad/2,height-rad+20
20 | angle_start:0
21 | angle_end:180
22 | Color:
23 | rgba:app.login_circle_light[:-1]+[.7]
24 | Ellipse:
25 | pos:0,height-rad/2
26 | size:rad,rad
27 | angle_start:90
28 | angle_end:270
29 |
30 | MDBoxLayout:
31 | adaptive_height:True
32 | id: box
33 | pos_hint:{"top":0.95}
34 | orientation:'vertical'
35 | spacing:'50dp'
36 | MDLabel:
37 | adaptive_height:True
38 | text:("SIGN UP\n" if root.show_signup else "LOGIN\n") + f"TO [color={primary_color_hex}][u]PASS••[/u]"
39 | markup:True
40 | theme_text_color:"Custom"
41 | text_color:app.text_color
42 | font_name:'Poppins'
43 | halign:'center'
44 | font_size:'44dp'
45 | pos_hint:{'top':1}
46 | MDBoxLayout:
47 | adaptive_height:True
48 | spacing:'50dp'
49 | orientation:'vertical'
50 | padding:(0,dp(15),0,0)
51 | BorderCard:
52 | id: email
53 | label_name:'Email Address'
54 | hint_text:'username@mail.com' if root.show_signup else "Your mail name"
55 | pos_hint:{'center_x':.5}
56 | MDBoxLayout:
57 | adaptive_height:True
58 | orientation:'vertical'
59 | spacing:'35dp'
60 | PasswordCard:
61 | id: password
62 | label_name:'Password'
63 | hint_text:('Create' if root.show_signup else "Enter")+" Password"
64 | pos_hint:{'center_x':.5}
65 | on_text_validate:
66 | root.button_pressed(email.text, password.text)
67 | FloatingButton:
68 | id: lock
69 | icon:'login' if not root.show_signup else "lock"
70 | on_release:
71 | root.button_pressed(email.text, password.text)
72 | pos_hint:{'center_x':.5}
73 |
74 | MDSeparator:
75 | id: separator
76 | opacity: 1
77 | size_hint_x:.8
78 | y:lock.y - dp(55)
79 | pos_hint:{"center_x":.5}
80 |
81 | MDTextButton:
82 | id: switch_signin
83 | opacity: 1
84 | text:f'New user? [color={primary_color_hex}]Signup!'if not root.show_signup else f"Existing user? [color={primary_color_hex}]Login."
85 | markup:True
86 | top:separator.y - dp(20)
87 | pos_hint:{'center_x':.5}
88 | font_name:'RobotoMedium'
89 | theme_text_color:"Custom"
90 | text_color:app.text_color
91 | halign:'center'
92 | size_hint_x:1
93 | on_release: root.show_signup = not root.show_signup
94 | font_size:lbl_size
95 |
96 |
97 |
--------------------------------------------------------------------------------
/libs/screens/SignupScreen/SignupScreen.py:
--------------------------------------------------------------------------------
1 | from email import message
2 | from typing import Dict
3 | from kivymd.app import MDApp
4 | from kivymd.toast import toast
5 | from kivymd.uix.screen import MDScreen
6 | import threading
7 | from time import time
8 | from kivy.properties import BooleanProperty
9 | from kivy.factory import Factory
10 |
11 | from libs.firebase import Firebase
12 | from libs.screens.classes import SyncWidget
13 |
14 | app = MDApp.get_running_app()
15 |
16 |
17 | class SignupScreen(MDScreen):
18 | loading_view = None
19 | show_signup = BooleanProperty(True)
20 | encryption = None
21 |
22 | def on_show_signup(self, *args):
23 | """Animation to be shown when clicking on login or signup"""
24 |
25 | print("switch_signup")
26 | box = self.ids.box
27 | box.pos_hint = {"top": 0.8}
28 | box.opacity = 0
29 | app.animate_signup(box)
30 |
31 | def save_uid_password(self, uid, email):
32 | """
33 | Saves user_id and a file that has been encrypted with master password.
34 | This makes sure that even when user hasn't created any passwords,
35 | app can still verify the password.
36 | """
37 |
38 | with open("data/user_id.txt", "w") as f:
39 | f.write(uid)
40 | with open("data/email.txt", "w") as f:
41 | f.write(email)
42 | app.email = email
43 | with open("data/encrypted_file.txt", "w") as f:
44 | f.write(app.encryption_class.encrypt("Test"))
45 |
46 | def dismiss_loading(self, *args):
47 | app.root.load_screen("HomeScreen")
48 | self.loading_view.dismiss()
49 |
50 | def signup(self, email, password):
51 | def signup_success(req, result):
52 | user_id = result["localId"]
53 | toast("Signup successful")
54 | app.encryption_class = self.encryption(self.password)
55 | self.dismiss_loading()
56 | threading.Thread(target=self.save_uid_password, args=(user_id, email)).start()
57 |
58 | def signup_failure(req, result):
59 | message = result["error"]["message"]
60 | print(message)
61 | if message == "EMAIL_EXISTS":
62 | toast("Email already exists, attempting to login instead.")
63 | self.login(email, password)
64 | else:
65 | message = message.replace("_", " ").capitalize()
66 | toast(message)
67 | self.loading_view.dismiss()
68 |
69 | self.firebase.signup_success = lambda req, result: signup_success(req, result)
70 | self.firebase.signup_failure = lambda req, result: signup_failure(req, result)
71 | self.firebase.signup(email, password)
72 |
73 | def login(self, email, password):
74 | def login_success(req, result):
75 | user_id = result["localId"]
76 | toast("Login successful")
77 | self.dismiss_loading()
78 | app.encryption_class = self.encryption(self.password)
79 | app.root.HomeScreen.restore(user_id=user_id)
80 | threading.Thread(target=self.save_uid_password, args=(user_id,email)).start()
81 |
82 | def login_failure(req, result):
83 | message = result["error"]["message"]
84 | message = message.replace("_", " ").capitalize()
85 | toast(message)
86 | self.loading_view.dismiss()
87 |
88 | self.firebase.login_success = lambda req, result: login_success(req, result)
89 | self.firebase.login_failure = lambda req, result: login_failure(req, result)
90 | self.firebase.login(email, password)
91 | app.root.load_screen("HomeScreen", set_current=False)
92 |
93 | def button_pressed(self, email, password):
94 | def import_encryption():
95 | from libs.encryption import Encryption
96 |
97 | self.encryption = Encryption
98 | if not self.show_signup:
99 | self.login(email, password)
100 |
101 | self.email = email
102 | self.password = password
103 | self.firebase = Firebase()
104 |
105 | if self.loading_view is None:
106 | self.loading_view = Factory.LoadingScreen()
107 | self.loading_view.text = (
108 | "Signing up..." if self.show_signup else "Logging in..."
109 | )
110 | self.loading_view.open()
111 | self.loading_view.on_open = lambda *args: import_encryption()
112 | if self.show_signup:
113 | self.signup(email, password)
114 |
--------------------------------------------------------------------------------
/libs/screens/root.py:
--------------------------------------------------------------------------------
1 | from time import time
2 | from kivy import platform
3 | from kivy.core.window import Window
4 | from kivy.lang import Builder
5 | from kivy.properties import ListProperty
6 | from kivy.uix.screenmanager import ScreenManager, CardTransition
7 | from kivymd.toast import toast
8 | from kivymd.app import MDApp
9 |
10 |
11 | class Root(ScreenManager):
12 | _prev_press = None
13 | history = ListProperty()
14 |
15 | def __init__(self, **kwargs):
16 | super().__init__(**kwargs)
17 | Window.bind(on_keyboard=self._handle_keyboard)
18 | self.transition = CardTransition(duration=0.3)
19 |
20 | def load_screen(
21 | self,
22 | screen_name,
23 | side="left",
24 | _from_goback=False,
25 | set_current=True,
26 | empty_history=False,
27 | ):
28 | # checks that the screen already added to the screen-manager
29 | if not self.has_screen(screen_name):
30 | # loads the kv file
31 | Builder.load_file(f"libs/screens/{screen_name}/{screen_name}.kv")
32 | # imports the screen class dynamically
33 | exec(f"from libs.screens.{screen_name}.{screen_name} import {screen_name}")
34 | # calls the screen class to get the instance of it
35 | self.screen_object = eval(f"{screen_name}()")
36 | # automatically sets the screen name using the arg that passed in set_current
37 | self.screen_object.name = screen_name
38 | # saves screen instance object to access later.
39 | exec(f"self.{screen_name} = self.screen_object")
40 | # finnaly adds the screen to the screen-manager
41 | self.add_widget(self.screen_object)
42 |
43 | # saves screen information to history
44 | # if you not want a screen to go back
45 | # use like below
46 | # if not from_goback and screen_name not in ["auth", ...]
47 |
48 | if not _from_goback and set_current:
49 | self.transition.mode = "push"
50 | self.transition.direction = "left"
51 | self.history.append(screen_name)
52 |
53 | # sets transition direction
54 | # sets the current screen
55 | if set_current:
56 | self.current = screen_name
57 | if empty_history:
58 | self.history = []
59 |
60 | def check_press_back_twice(self):
61 | def show_toast():
62 | if platform == "android":
63 | toast("Press back again to close the app", length_long=False)
64 | else:
65 | toast("Press back again to close the app", duration=.6)
66 |
67 | self._press_again = time()
68 | if self._prev_press:
69 | if (self._press_again - self._prev_press) < 2:
70 | MDApp.get_running_app().stop()
71 | else:
72 | show_toast()
73 | else:
74 | show_toast()
75 | self._prev_press = time()
76 |
77 | def _handle_keyboard(self, instance, key, *args):
78 | if key == 27:
79 | if (
80 | self.current == "HomeScreen"
81 | and self.current_screen.ids.tab_manager.current == "FindScreen"
82 | ):
83 | self.current_screen.ids.tab_manager.current = "CreateScreen"
84 | elif self.current == "SettingsScreen":
85 | self.goback()
86 | else:
87 | self.check_press_back_twice()
88 | return True
89 |
90 | def goback(self):
91 | if len(self.history) > 1:
92 | self.history.pop()
93 | prev_screen = self.history[-1]
94 | self.transition.mode = "pop"
95 | self.transition.direction = "right"
96 | self.load_screen(prev_screen, _from_goback=True)
97 |
--------------------------------------------------------------------------------
/libs/utils.py:
--------------------------------------------------------------------------------
1 | import os.path
2 | import pickle
3 | import json
4 | import string
5 | import random
6 |
7 | if not os.path.exists("data"):
8 | os.mkdir("data")
9 |
10 |
11 | def auto_password(length: int, ascii=True, digits=True, special_chars=True) -> str:
12 | universe = ""
13 | password = ""
14 | if ascii:
15 | password += random.choice(string.ascii_letters)
16 | universe += string.ascii_letters
17 | if digits:
18 | universe += string.digits
19 | password += random.choice(string.digits)
20 | if special_chars:
21 | universe += string.punctuation
22 | password += random.choice(string.punctuation)
23 |
24 | rest = length - len(password)
25 | if universe == "": # if all options are false
26 | return "Error: No options selected"
27 | for _ in range(rest):
28 | password += random.choice(universe)
29 |
30 | password = list(password)
31 | random.shuffle(password)
32 | password = "".join(password)
33 | return password
34 |
35 |
36 | def load_passwords() -> dict:
37 | if os.path.exists("data/passwords"):
38 | with open("data/passwords", "rb") as f:
39 | encrypted_pass = pickle.load(f)
40 | return encrypted_pass
41 | else:
42 | return {}
43 |
44 |
45 | def get_uid() -> str:
46 | with open("data/user_id.txt", "r") as f:
47 | uid = f.read()
48 | return uid
49 |
50 |
51 | def write_passwords(dictionary: dict) -> None:
52 | with open("data/passwords", "wb") as f:
53 | pickle.dump(dictionary, f)
54 |
55 |
56 | def remove_user_data() -> None:
57 | if os.path.exists("data/user_id.txt"):
58 | os.remove("data/user_id.txt")
59 | if os.path.exists("data/passwords"):
60 | os.remove("data/passwords")
61 | if os.path.exists("data/encrypted_file.txt"):
62 | os.remove("data/encrypted_file.txt")
63 |
64 |
65 | def _get_config() -> dict:
66 | if os.path.exists("data/config.json"):
67 | with open("data/config.json", "r") as f:
68 | config = json.load(f)
69 | return config
70 | else:
71 | return {}
72 |
73 |
74 | def get_email() -> str:
75 | if os.path.exists("data/email.txt"):
76 | with open("data/email.txt", "r") as f:
77 | email = f.read()
78 | return email
79 | else:
80 | return "DemoMail"
81 |
82 |
83 | def get_primary_palette() -> str:
84 | if os.path.exists("data/config.json"):
85 | with open("data/config.json", "r") as f:
86 | config = json.load(f)
87 | return config.get("primary_palette", "DeepOrange")
88 | else:
89 | return "DeepOrange"
90 |
91 |
92 | def is_dark_mode(system=False) -> bool:
93 | json_data = _get_config()
94 | return json_data.get("system_dark_mode", True) if system else json_data.get("dark_mode", False)
95 |
96 |
97 | def is_backup_failure() -> bool:
98 | _json_file = _get_config()
99 | return _json_file.get("backup_failure", False)
100 |
101 |
102 | def is_extra_security() -> bool:
103 | _json_file = _get_config()
104 | return _json_file.get("extra_security", False)
105 |
106 |
107 | def check_auto_sync() -> bool:
108 | json_data = _get_config()
109 | return json_data.get("auto_sync", True)
110 |
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | kivy
2 | pillow
3 | pycryptodome
4 |
--------------------------------------------------------------------------------
/screenshots/1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AM-ash-OR-AM-I/Passlock/0232719654997770309bcdd8c84be8ff1dcce500/screenshots/1.png
--------------------------------------------------------------------------------
/screenshots/2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AM-ash-OR-AM-I/Passlock/0232719654997770309bcdd8c84be8ff1dcce500/screenshots/2.png
--------------------------------------------------------------------------------
/screenshots/3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AM-ash-OR-AM-I/Passlock/0232719654997770309bcdd8c84be8ff1dcce500/screenshots/3.png
--------------------------------------------------------------------------------
/screenshots/4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AM-ash-OR-AM-I/Passlock/0232719654997770309bcdd8c84be8ff1dcce500/screenshots/4.png
--------------------------------------------------------------------------------
/screenshots/5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AM-ash-OR-AM-I/Passlock/0232719654997770309bcdd8c84be8ff1dcce500/screenshots/5.png
--------------------------------------------------------------------------------
/screenshots/6.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AM-ash-OR-AM-I/Passlock/0232719654997770309bcdd8c84be8ff1dcce500/screenshots/6.png
--------------------------------------------------------------------------------