├── kivymd ├── tools │ ├── __init__.py │ ├── patterns │ │ ├── __init__.py │ │ └── MVC │ │ │ ├── Model │ │ │ ├── first_screen.py_tmp │ │ │ ├── base_model.py_tmp │ │ │ └── base.py_tmp │ │ │ ├── libs │ │ │ ├── __init__.py │ │ │ └── translation.py │ │ │ ├── Controller │ │ │ └── first_screen.py_tmp │ │ │ ├── View │ │ │ ├── screens.py_tmp │ │ │ ├── FirstScreen │ │ │ │ ├── first_screen.py_tmp │ │ │ │ └── first_screen.kv │ │ │ └── base_screen.py_tmp │ │ │ ├── messages.pot │ │ │ ├── Utility │ │ │ └── observer.py_tmp │ │ │ ├── Makefile │ │ │ └── main.py_tmp │ ├── release │ │ ├── __init__.py │ │ └── git_commands.py │ ├── hotreload │ │ └── __init__.py │ ├── packaging │ │ ├── __init__.py │ │ └── pyinstaller │ │ │ ├── hook-kivymd.py │ │ │ └── __init__.py │ └── argument_parser.py ├── utils │ ├── __init__.py │ ├── fitimage.py │ ├── fpsmonitor.py │ └── asynckivy.py ├── effects │ ├── __init__.py │ ├── fadingedge │ │ └── __init__.py │ ├── stiffscroll │ │ ├── __init__.py │ │ ├── README.md │ │ └── LICENSE │ └── roulettescroll │ │ ├── __init__.py │ │ ├── LICENSE │ │ └── README.md ├── uix │ ├── banner │ │ ├── __init__.py │ │ └── banner.kv │ ├── menu │ │ ├── __init__.py │ │ └── menu.kv │ ├── slider │ │ └── __init__.py │ ├── spinner │ │ ├── __init__.py │ │ └── spinner.kv │ ├── swiper │ │ ├── __init__.py │ │ └── swiper.kv │ ├── backdrop │ │ ├── __init__.py │ │ └── backdrop.kv │ ├── chip │ │ ├── __init__.py │ │ └── chip.kv │ ├── fitimage │ │ └── __init__.py │ ├── label │ │ ├── __init__.py │ │ └── label.kv │ ├── datatables │ │ └── __init__.py │ ├── dialog │ │ ├── __init__.py │ │ └── dialog.kv │ ├── selection │ │ ├── __init__.py │ │ └── selection.kv │ ├── templates │ │ ├── rotatewidget │ │ │ ├── __init__.py │ │ │ ├── rotatewidget.kv │ │ │ └── rotatewidget.py │ │ ├── scalewidget │ │ │ ├── __init__.py │ │ │ ├── scalewidget.kv │ │ │ └── scalewidget.py │ │ ├── stencilwidget │ │ │ ├── __init__.py │ │ │ ├── stencilwidget.kv │ │ │ └── stencilwidget.py │ │ └── __init__.py │ ├── dropdownitem │ │ ├── __init__.py │ │ ├── dropdownitem.kv │ │ └── dropdownitem.py │ ├── filemanager │ │ ├── __init__.py │ │ └── filemanager.kv │ ├── progressbar │ │ ├── __init__.py │ │ └── progressbar.kv │ ├── snackbar │ │ ├── __init__.py │ │ └── snackbar.kv │ ├── tab │ │ ├── __init__.py │ │ └── tab.kv │ ├── pickers │ │ ├── colorpicker │ │ │ └── __init__.py │ │ ├── timepicker │ │ │ └── __init__.py │ │ ├── datepicker │ │ │ └── __init__.py │ │ └── __init__.py │ ├── tooltip │ │ ├── __init__.py │ │ └── tooltip.kv │ ├── transition │ │ ├── __init__.py │ │ └── transition.py │ ├── refreshlayout │ │ ├── __init__.py │ │ └── refreshlayout.kv │ ├── selectioncontrol │ │ ├── __init__.py │ │ └── selectioncontrol.kv │ ├── textfield │ │ └── __init__.py │ ├── toolbar │ │ ├── __init__.py │ │ └── toolbar.kv │ ├── imagelist │ │ ├── __init__.py │ │ └── imagelist.kv │ ├── bottomnavigation │ │ └── __init__.py │ ├── sliverappbar │ │ ├── __init__.py │ │ └── sliverappbar.kv │ ├── card │ │ ├── __init__.py │ │ └── card.kv │ ├── navigationrail │ │ └── __init__.py │ ├── bottomsheet │ │ ├── __init__.py │ │ └── bottomsheet.kv │ ├── expansionpanel │ │ ├── __init__.py │ │ └── expansionpanel.kv │ ├── navigationdrawer │ │ ├── __init__.py │ │ └── navigationdrawer.kv │ ├── button │ │ └── __init__.py │ ├── behaviors │ │ ├── __init__.py │ │ ├── touch_behavior.py │ │ └── focus_behavior.py │ ├── screen.py │ ├── relativelayout.py │ ├── list1 │ │ └── __init__.py │ ├── widget.py │ ├── floatlayout.py │ ├── anchorlayout.py │ ├── gridlayout.py │ ├── stacklayout.py │ ├── boxlayout.py │ └── __init__.py ├── toast │ ├── kivytoast │ │ └── __init__.py │ ├── __init__.py │ ├── androidtoast │ │ ├── __init__.py │ │ └── androidtoast.py │ ├── LICENSE │ └── README.md ├── images │ ├── red.png │ ├── black.png │ ├── blue.png │ ├── green.png │ ├── folder.png │ ├── yellow.png │ ├── alpha_layer.png │ ├── firebase-logo.png │ ├── quad_shadow-0.png │ ├── quad_shadow-1.png │ ├── quad_shadow-2.png │ ├── rec_shadow-0.png │ ├── rec_shadow-1.png │ ├── transparent.png │ ├── rec_st_shadow-0.png │ ├── rec_st_shadow-1.png │ ├── rec_st_shadow-2.png │ ├── round_shadow-0.png │ ├── round_shadow-1.png │ ├── round_shadow-2.png │ ├── rec_shadow.atlas │ ├── quad_shadow.atlas │ ├── round_shadow.atlas │ └── rec_st_shadow.atlas ├── fonts │ ├── Roboto-Bold.ttf │ ├── Roboto-Thin.ttf │ ├── Roboto-Black.ttf │ ├── Roboto-Italic.ttf │ ├── Roboto-Light.ttf │ ├── Roboto-Medium.ttf │ ├── Roboto-Regular.ttf │ ├── Roboto-BlackItalic.ttf │ ├── Roboto-BoldItalic.ttf │ ├── Roboto-LightItalic.ttf │ ├── Roboto-MediumItalic.ttf │ ├── Roboto-ThinItalic.ttf │ └── materialdesignicons-webfont.ttf ├── tests │ ├── test_icon_definitions.py │ ├── test_font_definitions.py │ ├── test_create_project.py │ ├── test_app.py │ └── pyinstaller │ │ └── test_pyinstaller_packaging.py ├── material_resources.py ├── font_definitions.py ├── __init__.py ├── theming_dynamic_text.py └── app.py ├── .markdownlint.json ├── .DS_Store ├── icons ├── pass.png └── passlock.ico ├── fonts ├── DejavuSans.ttf ├── Poppins-Bold.ttf ├── DejavuSansBold.ttf └── Poppins-Regular.ttf ├── screenshots ├── sync.png ├── colors.png ├── DarkMode.png ├── FindScreen.png ├── HomeScreen.png ├── windows_logo.png └── WelcomeScreen.png ├── requirements.txt ├── install.sh ├── .gitignore ├── libs ├── initialize_imports.py ├── save_config.py ├── screens │ ├── LoginScreen │ │ ├── LoginScreen.kv │ │ └── LoginScreen.py │ ├── SettingsScreen │ │ └── SettingsScreen.py │ ├── SignupScreen │ │ └── SignupScreen.kv │ └── root.py ├── modules │ ├── picker.py │ └── List.py ├── utils.py └── firebase.py ├── utils ├── copy_kv_files.py └── pyinstaller_script_old.py ├── LICENSE ├── .github └── workflows │ └── build.yml ├── Passlock.spec ├── passlock_linux.spec ├── passlock_windows.spec ├── make_msi_build.bat └── passlock_macos.spec /kivymd/tools/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /kivymd/utils/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /kivymd/tools/patterns/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /kivymd/tools/release/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /kivymd/tools/hotreload/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /kivymd/tools/packaging/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /kivymd/tools/patterns/MVC/Model/first_screen.py_tmp: -------------------------------------------------------------------------------- 1 | %s 2 | -------------------------------------------------------------------------------- /kivymd/effects/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Effects 3 | ======= 4 | """ 5 | -------------------------------------------------------------------------------- /.markdownlint.json: -------------------------------------------------------------------------------- 1 | { 2 | "MD033": false, 3 | "MD013": false 4 | } 5 | -------------------------------------------------------------------------------- /kivymd/uix/banner/__init__.py: -------------------------------------------------------------------------------- 1 | from .banner import MDBanner # NOQA F401 2 | -------------------------------------------------------------------------------- /kivymd/uix/menu/__init__.py: -------------------------------------------------------------------------------- 1 | from .menu import MDDropdownMenu # NOQA F401 2 | -------------------------------------------------------------------------------- /kivymd/uix/slider/__init__.py: -------------------------------------------------------------------------------- 1 | from .slider import MDSlider # NOQA F401 2 | -------------------------------------------------------------------------------- /kivymd/uix/spinner/__init__.py: -------------------------------------------------------------------------------- 1 | from .spinner import MDSpinner # NOQA F401 2 | -------------------------------------------------------------------------------- /kivymd/uix/swiper/__init__.py: -------------------------------------------------------------------------------- 1 | from .swiper import MDSwiper, MDSwiperItem 2 | -------------------------------------------------------------------------------- /kivymd/effects/fadingedge/__init__.py: -------------------------------------------------------------------------------- 1 | from .fadingedge import FadingEdgeEffect 2 | -------------------------------------------------------------------------------- /kivymd/uix/backdrop/__init__.py: -------------------------------------------------------------------------------- 1 | from .backdrop import MDBackdrop # NOQA F401 2 | -------------------------------------------------------------------------------- /kivymd/uix/chip/__init__.py: -------------------------------------------------------------------------------- 1 | from .chip import MDChip, MDChooseChip # NOQA F401 2 | -------------------------------------------------------------------------------- /kivymd/uix/fitimage/__init__.py: -------------------------------------------------------------------------------- 1 | from .fitimage import FitImage # NOQA F401 2 | -------------------------------------------------------------------------------- /kivymd/uix/label/__init__.py: -------------------------------------------------------------------------------- 1 | from .label import MDIcon, MDLabel # NOQA F401 2 | -------------------------------------------------------------------------------- /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AM-ash-OR-AM-I/PasslockDesktop/HEAD/.DS_Store -------------------------------------------------------------------------------- /kivymd/effects/stiffscroll/__init__.py: -------------------------------------------------------------------------------- 1 | from .stiffscroll import StiffScrollEffect 2 | -------------------------------------------------------------------------------- /kivymd/uix/datatables/__init__.py: -------------------------------------------------------------------------------- 1 | from .datatables import MDDataTable # NOQA F401 2 | -------------------------------------------------------------------------------- /kivymd/uix/dialog/__init__.py: -------------------------------------------------------------------------------- 1 | from .dialog import BaseDialog, MDDialog # NOQA F401 2 | -------------------------------------------------------------------------------- /kivymd/uix/selection/__init__.py: -------------------------------------------------------------------------------- 1 | from .selection import MDSelectionList # NOQA F401 2 | -------------------------------------------------------------------------------- /kivymd/uix/templates/rotatewidget/__init__.py: -------------------------------------------------------------------------------- 1 | from .rotatewidget import RotateWidget 2 | -------------------------------------------------------------------------------- /kivymd/uix/templates/scalewidget/__init__.py: -------------------------------------------------------------------------------- 1 | from .scalewidget import ScaleWidget 2 | -------------------------------------------------------------------------------- /kivymd/uix/dropdownitem/__init__.py: -------------------------------------------------------------------------------- 1 | from .dropdownitem import MDDropDownItem # NOQA F401 2 | -------------------------------------------------------------------------------- /kivymd/uix/filemanager/__init__.py: -------------------------------------------------------------------------------- 1 | from .filemanager import MDFileManager # NOQA F401 2 | -------------------------------------------------------------------------------- /kivymd/uix/progressbar/__init__.py: -------------------------------------------------------------------------------- 1 | from .progressbar import MDProgressBar # NOQA F401 2 | -------------------------------------------------------------------------------- /kivymd/uix/snackbar/__init__.py: -------------------------------------------------------------------------------- 1 | from .snackbar import BaseSnackbar, Snackbar # NOQA F401 2 | -------------------------------------------------------------------------------- /kivymd/uix/tab/__init__.py: -------------------------------------------------------------------------------- 1 | from .tab import MDTabs, MDTabsBase, MDTabsLabel # NOQA F401 2 | -------------------------------------------------------------------------------- /kivymd/uix/templates/stencilwidget/__init__.py: -------------------------------------------------------------------------------- 1 | from .stencilwidget import StencilWidget 2 | -------------------------------------------------------------------------------- /icons/pass.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AM-ash-OR-AM-I/PasslockDesktop/HEAD/icons/pass.png -------------------------------------------------------------------------------- /kivymd/effects/roulettescroll/__init__.py: -------------------------------------------------------------------------------- 1 | from .roulettescroll import RouletteScrollEffect 2 | -------------------------------------------------------------------------------- /kivymd/toast/kivytoast/__init__.py: -------------------------------------------------------------------------------- 1 | __all__ = ("toast",) 2 | 3 | from .kivytoast import toast 4 | -------------------------------------------------------------------------------- /kivymd/uix/pickers/colorpicker/__init__.py: -------------------------------------------------------------------------------- 1 | from .colorpicker import MDColorPicker # NOQA F401 2 | -------------------------------------------------------------------------------- /kivymd/uix/pickers/timepicker/__init__.py: -------------------------------------------------------------------------------- 1 | from .timepicker import MDTimePicker # NOQA F401 2 | -------------------------------------------------------------------------------- /kivymd/uix/tooltip/__init__.py: -------------------------------------------------------------------------------- 1 | from .tooltip import MDTooltip, MDTooltipViewClass # NOQA F401 2 | -------------------------------------------------------------------------------- /kivymd/uix/transition/__init__.py: -------------------------------------------------------------------------------- 1 | from .transition import MDFadeSlideTransition # NOQA F401 2 | -------------------------------------------------------------------------------- /icons/passlock.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AM-ash-OR-AM-I/PasslockDesktop/HEAD/icons/passlock.ico -------------------------------------------------------------------------------- /kivymd/tools/patterns/MVC/libs/__init__.py: -------------------------------------------------------------------------------- 1 | # This package is for additional application modules. 2 | -------------------------------------------------------------------------------- /fonts/DejavuSans.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AM-ash-OR-AM-I/PasslockDesktop/HEAD/fonts/DejavuSans.ttf -------------------------------------------------------------------------------- /kivymd/images/red.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AM-ash-OR-AM-I/PasslockDesktop/HEAD/kivymd/images/red.png -------------------------------------------------------------------------------- /kivymd/uix/refreshlayout/__init__.py: -------------------------------------------------------------------------------- 1 | from .refreshlayout import MDScrollViewRefreshLayout # NOQA F401 2 | -------------------------------------------------------------------------------- /screenshots/sync.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AM-ash-OR-AM-I/PasslockDesktop/HEAD/screenshots/sync.png -------------------------------------------------------------------------------- /fonts/Poppins-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AM-ash-OR-AM-I/PasslockDesktop/HEAD/fonts/Poppins-Bold.ttf -------------------------------------------------------------------------------- /kivymd/images/black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AM-ash-OR-AM-I/PasslockDesktop/HEAD/kivymd/images/black.png -------------------------------------------------------------------------------- /kivymd/images/blue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AM-ash-OR-AM-I/PasslockDesktop/HEAD/kivymd/images/blue.png -------------------------------------------------------------------------------- /kivymd/images/green.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AM-ash-OR-AM-I/PasslockDesktop/HEAD/kivymd/images/green.png -------------------------------------------------------------------------------- /screenshots/colors.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AM-ash-OR-AM-I/PasslockDesktop/HEAD/screenshots/colors.png -------------------------------------------------------------------------------- /fonts/DejavuSansBold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AM-ash-OR-AM-I/PasslockDesktop/HEAD/fonts/DejavuSansBold.ttf -------------------------------------------------------------------------------- /fonts/Poppins-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AM-ash-OR-AM-I/PasslockDesktop/HEAD/fonts/Poppins-Regular.ttf -------------------------------------------------------------------------------- /kivymd/images/folder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AM-ash-OR-AM-I/PasslockDesktop/HEAD/kivymd/images/folder.png -------------------------------------------------------------------------------- /kivymd/images/yellow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AM-ash-OR-AM-I/PasslockDesktop/HEAD/kivymd/images/yellow.png -------------------------------------------------------------------------------- /kivymd/uix/selectioncontrol/__init__.py: -------------------------------------------------------------------------------- 1 | from .selectioncontrol import MDCheckbox, MDSwitch, Thumb # NOQA F401 2 | -------------------------------------------------------------------------------- /screenshots/DarkMode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AM-ash-OR-AM-I/PasslockDesktop/HEAD/screenshots/DarkMode.png -------------------------------------------------------------------------------- /screenshots/FindScreen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AM-ash-OR-AM-I/PasslockDesktop/HEAD/screenshots/FindScreen.png -------------------------------------------------------------------------------- /screenshots/HomeScreen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AM-ash-OR-AM-I/PasslockDesktop/HEAD/screenshots/HomeScreen.png -------------------------------------------------------------------------------- /kivymd/fonts/Roboto-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AM-ash-OR-AM-I/PasslockDesktop/HEAD/kivymd/fonts/Roboto-Bold.ttf -------------------------------------------------------------------------------- /kivymd/fonts/Roboto-Thin.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AM-ash-OR-AM-I/PasslockDesktop/HEAD/kivymd/fonts/Roboto-Thin.ttf -------------------------------------------------------------------------------- /kivymd/uix/textfield/__init__.py: -------------------------------------------------------------------------------- 1 | # NOQA F401 2 | from .textfield import MDTextField, MDTextFieldRect, MDTextFieldRound 3 | -------------------------------------------------------------------------------- /kivymd/uix/toolbar/__init__.py: -------------------------------------------------------------------------------- 1 | # NOQA F401 2 | from .toolbar import MDActionTopAppBarButton, MDBottomAppBar, MDToolbar 3 | -------------------------------------------------------------------------------- /screenshots/windows_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AM-ash-OR-AM-I/PasslockDesktop/HEAD/screenshots/windows_logo.png -------------------------------------------------------------------------------- /kivymd/fonts/Roboto-Black.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AM-ash-OR-AM-I/PasslockDesktop/HEAD/kivymd/fonts/Roboto-Black.ttf -------------------------------------------------------------------------------- /kivymd/fonts/Roboto-Italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AM-ash-OR-AM-I/PasslockDesktop/HEAD/kivymd/fonts/Roboto-Italic.ttf -------------------------------------------------------------------------------- /kivymd/fonts/Roboto-Light.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AM-ash-OR-AM-I/PasslockDesktop/HEAD/kivymd/fonts/Roboto-Light.ttf -------------------------------------------------------------------------------- /kivymd/fonts/Roboto-Medium.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AM-ash-OR-AM-I/PasslockDesktop/HEAD/kivymd/fonts/Roboto-Medium.ttf -------------------------------------------------------------------------------- /kivymd/fonts/Roboto-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AM-ash-OR-AM-I/PasslockDesktop/HEAD/kivymd/fonts/Roboto-Regular.ttf -------------------------------------------------------------------------------- /kivymd/images/alpha_layer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AM-ash-OR-AM-I/PasslockDesktop/HEAD/kivymd/images/alpha_layer.png -------------------------------------------------------------------------------- /kivymd/images/firebase-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AM-ash-OR-AM-I/PasslockDesktop/HEAD/kivymd/images/firebase-logo.png -------------------------------------------------------------------------------- /kivymd/images/quad_shadow-0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AM-ash-OR-AM-I/PasslockDesktop/HEAD/kivymd/images/quad_shadow-0.png -------------------------------------------------------------------------------- /kivymd/images/quad_shadow-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AM-ash-OR-AM-I/PasslockDesktop/HEAD/kivymd/images/quad_shadow-1.png -------------------------------------------------------------------------------- /kivymd/images/quad_shadow-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AM-ash-OR-AM-I/PasslockDesktop/HEAD/kivymd/images/quad_shadow-2.png -------------------------------------------------------------------------------- /kivymd/images/rec_shadow-0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AM-ash-OR-AM-I/PasslockDesktop/HEAD/kivymd/images/rec_shadow-0.png -------------------------------------------------------------------------------- /kivymd/images/rec_shadow-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AM-ash-OR-AM-I/PasslockDesktop/HEAD/kivymd/images/rec_shadow-1.png -------------------------------------------------------------------------------- /kivymd/images/transparent.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AM-ash-OR-AM-I/PasslockDesktop/HEAD/kivymd/images/transparent.png -------------------------------------------------------------------------------- /kivymd/tools/patterns/MVC/Controller/first_screen.py_tmp: -------------------------------------------------------------------------------- 1 | %s 2 | def get_view(self) -> %s: 3 | return self.view 4 | -------------------------------------------------------------------------------- /kivymd/uix/imagelist/__init__.py: -------------------------------------------------------------------------------- 1 | # NOQA F401 2 | from .imagelist import SmartTile, SmartTileWithLabel, SmartTileWithStar 3 | -------------------------------------------------------------------------------- /screenshots/WelcomeScreen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AM-ash-OR-AM-I/PasslockDesktop/HEAD/screenshots/WelcomeScreen.png -------------------------------------------------------------------------------- /kivymd/images/rec_st_shadow-0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AM-ash-OR-AM-I/PasslockDesktop/HEAD/kivymd/images/rec_st_shadow-0.png -------------------------------------------------------------------------------- /kivymd/images/rec_st_shadow-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AM-ash-OR-AM-I/PasslockDesktop/HEAD/kivymd/images/rec_st_shadow-1.png -------------------------------------------------------------------------------- /kivymd/images/rec_st_shadow-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AM-ash-OR-AM-I/PasslockDesktop/HEAD/kivymd/images/rec_st_shadow-2.png -------------------------------------------------------------------------------- /kivymd/images/round_shadow-0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AM-ash-OR-AM-I/PasslockDesktop/HEAD/kivymd/images/round_shadow-0.png -------------------------------------------------------------------------------- /kivymd/images/round_shadow-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AM-ash-OR-AM-I/PasslockDesktop/HEAD/kivymd/images/round_shadow-1.png -------------------------------------------------------------------------------- /kivymd/images/round_shadow-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AM-ash-OR-AM-I/PasslockDesktop/HEAD/kivymd/images/round_shadow-2.png -------------------------------------------------------------------------------- /kivymd/uix/bottomnavigation/__init__.py: -------------------------------------------------------------------------------- 1 | # NOQA F401 2 | from .bottomnavigation import MDBottomNavigation, MDBottomNavigationItem 3 | -------------------------------------------------------------------------------- /kivymd/fonts/Roboto-BlackItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AM-ash-OR-AM-I/PasslockDesktop/HEAD/kivymd/fonts/Roboto-BlackItalic.ttf -------------------------------------------------------------------------------- /kivymd/fonts/Roboto-BoldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AM-ash-OR-AM-I/PasslockDesktop/HEAD/kivymd/fonts/Roboto-BoldItalic.ttf -------------------------------------------------------------------------------- /kivymd/fonts/Roboto-LightItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AM-ash-OR-AM-I/PasslockDesktop/HEAD/kivymd/fonts/Roboto-LightItalic.ttf -------------------------------------------------------------------------------- /kivymd/fonts/Roboto-MediumItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AM-ash-OR-AM-I/PasslockDesktop/HEAD/kivymd/fonts/Roboto-MediumItalic.ttf -------------------------------------------------------------------------------- /kivymd/fonts/Roboto-ThinItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AM-ash-OR-AM-I/PasslockDesktop/HEAD/kivymd/fonts/Roboto-ThinItalic.ttf -------------------------------------------------------------------------------- /kivymd/fonts/materialdesignicons-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AM-ash-OR-AM-I/PasslockDesktop/HEAD/kivymd/fonts/materialdesignicons-webfont.ttf -------------------------------------------------------------------------------- /kivymd/uix/sliverappbar/__init__.py: -------------------------------------------------------------------------------- 1 | from .sliverappbar import ( 2 | MDSliverAppbar, 3 | MDSliverAppbarContent, 4 | MDSliverAppbarHeader, 5 | ) 6 | -------------------------------------------------------------------------------- /kivymd/uix/pickers/datepicker/__init__.py: -------------------------------------------------------------------------------- 1 | from .datepicker import ( # NOQA F401 2 | BaseDialogPicker, 3 | DatePickerInputField, 4 | MDDatePicker, 5 | ) 6 | -------------------------------------------------------------------------------- /kivymd/uix/card/__init__.py: -------------------------------------------------------------------------------- 1 | # NOQA F401 2 | from .card import ( 3 | MDCard, 4 | MDCardSwipe, 5 | MDCardSwipeFrontBox, 6 | MDCardSwipeLayerBox, 7 | MDSeparator, 8 | ) 9 | -------------------------------------------------------------------------------- /kivymd/uix/navigationrail/__init__.py: -------------------------------------------------------------------------------- 1 | # NOQA F401 2 | from .navigationrail import ( 3 | MDNavigationRail, 4 | MDNavigationRailFabButton, 5 | MDNavigationRailMenuButton, 6 | ) 7 | -------------------------------------------------------------------------------- /kivymd/uix/pickers/__init__.py: -------------------------------------------------------------------------------- 1 | from .colorpicker import MDColorPicker # NOQA F401 2 | from .datepicker import MDDatePicker # NOQA F401 3 | from .timepicker import MDTimePicker # NOQA F401 4 | -------------------------------------------------------------------------------- /kivymd/uix/bottomsheet/__init__.py: -------------------------------------------------------------------------------- 1 | # NOQA F401 2 | from .bottomsheet import ( 3 | GridBottomSheetItem, 4 | MDBottomSheet, 5 | MDCustomBottomSheet, 6 | MDGridBottomSheet, 7 | MDListBottomSheet, 8 | ) 9 | -------------------------------------------------------------------------------- /kivymd/uix/expansionpanel/__init__.py: -------------------------------------------------------------------------------- 1 | # NOQA F401 2 | from .expansionpanel import ( 3 | MDExpansionPanel, 4 | MDExpansionPanelLabel, 5 | MDExpansionPanelOneLine, 6 | MDExpansionPanelThreeLine, 7 | MDExpansionPanelTwoLine, 8 | ) 9 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | kivy==2.1.0; platform_system == "Windows" 2 | kivy; platform_system == "Linux" or platform_system == "Darwin" 3 | pycryptodome 4 | pillow 5 | pyinstaller==5.0.1; platform_system == "Windows" 6 | pyinstaller; platform_system=="Linux" or platform_system=="Darwin" -------------------------------------------------------------------------------- /kivymd/uix/templates/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Templates 3 | ========= 4 | 5 | Base classes for controlling the scale, rotation of the widget, etc. 6 | """ 7 | 8 | from .rotatewidget import RotateWidget 9 | from .scalewidget import ScaleWidget 10 | from .stencilwidget import StencilWidget 11 | -------------------------------------------------------------------------------- /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 ModuleNotFoundError: 9 | from .kivytoast import toast 10 | else: 11 | from .kivytoast import toast 12 | -------------------------------------------------------------------------------- /kivymd/uix/templates/rotatewidget/rotatewidget.kv: -------------------------------------------------------------------------------- 1 | 2 | canvas.before: 3 | PushMatrix 4 | Rotate: 5 | angle: self.rotate_value_angle 6 | axis: tuple(self.rotate_value_axis) 7 | origin: self.center 8 | canvas.after: 9 | PopMatrix 10 | -------------------------------------------------------------------------------- /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/uix/navigationdrawer/__init__.py: -------------------------------------------------------------------------------- 1 | # NOQA F401 2 | from .navigationdrawer import ( 3 | MDNavigationDrawer, 4 | MDNavigationDrawerDivider, 5 | MDNavigationDrawerHeader, 6 | MDNavigationDrawerItem, 7 | MDNavigationDrawerLabel, 8 | MDNavigationDrawerMenu, 9 | MDNavigationLayout, 10 | ) 11 | -------------------------------------------------------------------------------- /kivymd/uix/templates/scalewidget/scalewidget.kv: -------------------------------------------------------------------------------- 1 | 2 | canvas.before: 3 | PushMatrix 4 | Scale: 5 | x: self.scale_value_x 6 | y: self.scale_value_y 7 | z: self.scale_value_x 8 | origin: self.center 9 | canvas.after: 10 | PopMatrix 11 | -------------------------------------------------------------------------------- /kivymd/tools/patterns/MVC/View/screens.py_tmp: -------------------------------------------------------------------------------- 1 | # The screens dictionary contains the objects of the models and controllers 2 | # of the screens of the application. 3 | 4 | from Model.%s import %s 5 | 6 | from Controller.%s import %s 7 | 8 | screens = { 9 | %s: { 10 | "model": %s, 11 | "controller": %s, 12 | }, 13 | } 14 | -------------------------------------------------------------------------------- /install.sh: -------------------------------------------------------------------------------- 1 | # Soft-link for global access 2 | sudo ln -s "$(pwd)/Passlock" /usr/local/bin/passlock 3 | 4 | # Create Desktop Entry 5 | echo "[Desktop Entry] 6 | Encoding=UTF-8 7 | Version=1.3.0 8 | Type=Application 9 | Terminal=false 10 | Exec=$(pwd)/Passlock 11 | Name=Passlock 12 | Icon=$(pwd)/pass.png" > ~/.local/share/applications/passlock.desktop -------------------------------------------------------------------------------- /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/uix/swiper/swiper.kv: -------------------------------------------------------------------------------- 1 | 2 | do_scroll_y: False 3 | bar_width: 0 4 | 5 | MDBoxLayout: 6 | id: anchor_scroll 7 | adaptive_width: True 8 | padding: [root.items_spacing, 0 ] 9 | 10 | 11 | 12 | size_hint: None, None 13 | 14 | 15 | <_ItemsBox> 16 | size_hint_x: None 17 | anchor_x: "center" 18 | anchor_y: "center" 19 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__ 2 | test.spec 3 | .vscode 4 | Data 5 | kivymd_original 6 | app_build 7 | all_files 8 | .idea 9 | dist 10 | build 11 | main.spec 12 | config.json 13 | email.txt 14 | encrypted_file.txt 15 | passwords 16 | user_id.txt 17 | passlock.tar.gz 18 | passlock 19 | api_key.txt 20 | env 21 | set_web_api_key.py 22 | Passlock-cache 23 | Passlock-SetupFiles 24 | Passlock.aip 25 | libs/firebase_config.py 26 | .venv 27 | release/ -------------------------------------------------------------------------------- /libs/initialize_imports.py: -------------------------------------------------------------------------------- 1 | # Description: This file is used to import all the required modules in one place. 2 | # Can't get it to work in `encryption.py` file, while using .exe thus had to add it in main.py 3 | import hashlib 4 | from Crypto import Random 5 | from Crypto.Cipher import AES 6 | from base64 import urlsafe_b64encode, urlsafe_b64decode 7 | 8 | # Set the web api key in the environment variable 9 | import libs.firebase_config as firebase_config -------------------------------------------------------------------------------- /kivymd/uix/button/__init__.py: -------------------------------------------------------------------------------- 1 | # NOQA F401 2 | from .button import ( 3 | BaseButton, 4 | MDFillRoundFlatButton, 5 | MDFillRoundFlatIconButton, 6 | MDFlatButton, 7 | MDFloatingActionButton, 8 | MDFloatingActionButtonSpeedDial, 9 | MDIconButton, 10 | MDRaisedButton, 11 | MDRectangleFlatButton, 12 | MDRectangleFlatIconButton, 13 | MDRoundFlatButton, 14 | MDRoundFlatIconButton, 15 | MDTextButton, 16 | ) 17 | -------------------------------------------------------------------------------- /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/uix/templates/stencilwidget/stencilwidget.kv: -------------------------------------------------------------------------------- 1 | 2 | canvas.before: 3 | StencilPush 4 | RoundedRectangle: 5 | pos: root.pos 6 | size: root.size 7 | radius: root.radius 8 | StencilUse 9 | canvas.after: 10 | StencilUnUse 11 | RoundedRectangle: 12 | pos: root.pos 13 | size: root.size 14 | radius: root.radius 15 | StencilPop 16 | -------------------------------------------------------------------------------- /kivymd/uix/expansionpanel/expansionpanel.kv: -------------------------------------------------------------------------------- 1 | : 2 | icon: "chevron-right" 3 | disabled: True 4 | md_bg_color_disabled: 0, 0, 0, 0 5 | 6 | canvas.before: 7 | PushMatrix 8 | Rotate: 9 | angle: self._angle 10 | axis: (0, 0, 1) 11 | origin: self.center 12 | canvas.after: 13 | PopMatrix 14 | 15 | 16 | 17 | size_hint_y: None 18 | # height: dp(68) 19 | -------------------------------------------------------------------------------- /kivymd/uix/selection/selection.kv: -------------------------------------------------------------------------------- 1 | 2 | theme_text_color: "Custom" 3 | text_color: self.icon_check_color 4 | 5 | canvas.before: 6 | PushMatrix 7 | Scale: 8 | x: root.scale 9 | y: root.scale 10 | z: root.scale 11 | origin: self.center 12 | canvas.after: 13 | PopMatrix 14 | 15 | 16 | 17 | md_bg_color: root.overlay_color if root.selected else (0, 0, 0, 0) 18 | -------------------------------------------------------------------------------- /kivymd/tools/patterns/MVC/View/FirstScreen/first_screen.py_tmp: -------------------------------------------------------------------------------- 1 | %s 2 | from View.base_screen import BaseScreenView 3 | 4 | 5 | class %s(BaseScreenView): 6 | """Implements the login start screen in the user application.""" 7 | %s 8 | def model_is_changed(self) -> NoReturn: 9 | """ 10 | Called whenever any change has occurred in the data model. 11 | The view in this method tracks these changes and updates the UI 12 | according to these changes. 13 | """ 14 | 15 | %s -------------------------------------------------------------------------------- /kivymd/uix/card/card.kv: -------------------------------------------------------------------------------- 1 | : 2 | md_bg_color: app.theme_cls.divider_color 3 | 4 | 5 | 6 | canvas.before: 7 | Color: 8 | rgba: self.md_bg_color 9 | RoundedRectangle: 10 | size: self.size 11 | pos: self.pos 12 | radius: root.radius 13 | source: root.background 14 | 15 | 16 | 17 | md_bg_color: 18 | self.theme_cls.divider_color \ 19 | if not root.color \ 20 | else root.color 21 | -------------------------------------------------------------------------------- /kivymd/utils/fitimage.py: -------------------------------------------------------------------------------- 1 | """ 2 | FitImage 3 | ======== 4 | 5 | .. note:: See :class:`~kivymd.uix.fitimage.FitImage` for more information 6 | """ 7 | 8 | __all__ = ("FitImage",) 9 | 10 | from kivy import Logger 11 | 12 | from kivymd.uix.fitimage import FitImage 13 | 14 | Logger.warning( 15 | "FitImage: Note!" 16 | "\nIn the near future the `FitImage` widget will be moved to the " 17 | "`kivymd.uix.fitimage` package.\nUse import of this widget like this:" 18 | "`from kivymd.uix.fitimage import FitImage`." 19 | ) 20 | -------------------------------------------------------------------------------- /libs/save_config.py: -------------------------------------------------------------------------------- 1 | import json 2 | from kivymd.app import MDApp 3 | 4 | app = MDApp.get_running_app() 5 | 6 | 7 | class SaveConfig: 8 | def __init__(self, *args: str) -> None: 9 | self.variable_list = args 10 | self.variable_dict = {} 11 | 12 | def save_settings(self) -> None: 13 | for var in self.variable_list: 14 | exec(f"self.variable_dict['{var}']= app.{var}") 15 | with open("./data/config.json", "w") as file: 16 | json.dump(self.variable_dict, file, indent=4) 17 | -------------------------------------------------------------------------------- /kivymd/tests/test_create_project.py: -------------------------------------------------------------------------------- 1 | def test_create_project(): 2 | import os 3 | import sys 4 | 5 | os.system( 6 | f"{sys.executable} -m kivymd.tools.patterns.create_project " 7 | f"MVC " 8 | f"{os.path.expanduser('~')} " 9 | f"TestProject " 10 | f"{sys.executable} " 11 | f"master " 12 | f"--name_screen TestProjectScreen " 13 | f"--use_firebase yes " 14 | f"--use_hotreload yes" 15 | ) 16 | assert os.path.exists(os.path.join(os.path.expanduser("~"), "TestProject")) 17 | -------------------------------------------------------------------------------- /kivymd/uix/spinner/spinner.kv: -------------------------------------------------------------------------------- 1 | 2 | canvas.before: 3 | PushMatrix 4 | Rotate: 5 | angle: self._rotation_angle 6 | origin: self.center 7 | canvas: 8 | Color: 9 | rgba: self.color if self.color else self.theme_cls.primary_color 10 | a: self._alpha 11 | SmoothLine: 12 | cap: 'square' 13 | width: root.line_width 14 | circle: 15 | self.center_x, self.center_y, self.width / 2, \ 16 | self._angle_start, self._angle_end 17 | canvas.after: 18 | PopMatrix 19 | -------------------------------------------------------------------------------- /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/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/tools/patterns/MVC/messages.pot: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the PACKAGE package. 4 | # FIRST AUTHOR , YEAR. 5 | # 6 | #, fuzzy 7 | msgid "" 8 | msgstr "" 9 | "Project-Id-Version: PACKAGE VERSION\n" 10 | "Report-Msgid-Bugs-To: \n" 11 | "POT-Creation-Date: 2021-10-27 18:54+0300\n" 12 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" 13 | "Last-Translator: FULL NAME \n" 14 | "Language-Team: LANGUAGE \n" 15 | "Language: \n" 16 | "MIME-Version: 1.0\n" 17 | "Content-Type: text/plain; charset=CHARSET\n" 18 | "Content-Transfer-Encoding: 8bit\n" 19 | -------------------------------------------------------------------------------- /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/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/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/uix/refreshlayout/refreshlayout.kv: -------------------------------------------------------------------------------- 1 | #:import Window kivy.core.window.Window 2 | 3 | 4 | 5 | 6 | AnchorLayout: 7 | id: body_spinner 8 | size_hint: None, None 9 | size: dp(46), dp(46) 10 | y: Window.height 11 | pos_hint: {'center_x': .5} 12 | anchor_x: 'center' 13 | anchor_y: 'center' 14 | 15 | canvas: 16 | Clear 17 | Color: 18 | rgba: root.theme_cls.primary_dark 19 | Ellipse: 20 | pos: self.pos 21 | size: self.size 22 | 23 | MDSpinner: 24 | id: spinner 25 | size_hint: None, None 26 | size: dp(30), dp(30) 27 | color: 1, 1, 1, 1 28 | -------------------------------------------------------------------------------- /utils/copy_kv_files.py: -------------------------------------------------------------------------------- 1 | import os 2 | import platform 3 | 4 | my_files = os.walk("libs") 5 | os.makedirs("all_files", exist_ok=True) 6 | extensions = (".kv", ".png", ".ttf", ".atlas") 7 | 8 | def copy_kv_files(): 9 | for path, _, file_list in my_files: 10 | for file_name in file_list: 11 | if not file_name.endswith(extensions): 12 | continue 13 | dir_name = os.path.join("all_files", path) 14 | os.makedirs(dir_name, exist_ok=True) 15 | file_path = os.path.join(path, file_name) 16 | if platform.system() == "Windows": 17 | os.system(f"copy {file_path} {dir_name}") 18 | else: 19 | os.system(f"cp {file_path} {dir_name}") 20 | 21 | copy_kv_files() -------------------------------------------------------------------------------- /kivymd/tools/patterns/MVC/Utility/observer.py_tmp: -------------------------------------------------------------------------------- 1 | # Of course, "very flexible Python" allows you to do without an abstract 2 | # superclass at all or use the clever exception `NotImplementedError`. In my 3 | # opinion, this can negatively affect the architecture of the application. 4 | # I would like to point out that using Kivy, one could use the on-signaling 5 | # model. In this case, when the state changes, the model will send a signal 6 | # that can be received by all attached observers. This approach seems less 7 | # universal - you may want to use a different library in the future. 8 | 9 | 10 | class Observer: 11 | """Abstract superclass for all observers.""" 12 | 13 | def model_is_changed(self): 14 | """ 15 | The method that will be called on the observer when the model changes. 16 | """ 17 | -------------------------------------------------------------------------------- /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/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/relativelayout.py: -------------------------------------------------------------------------------- 1 | """ 2 | Components/RelativeLayout 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/list1/__init__.py: -------------------------------------------------------------------------------- 1 | # NOQA F401 2 | from .list import ( 3 | BaseListItem, 4 | CheckboxLeftWidget, 5 | ContainerSupport, 6 | IconLeftWidget, 7 | IconLeftWidgetWithoutTouch, 8 | IconRightWidget, 9 | IconRightWidgetWithoutTouch, 10 | ILeftBody, 11 | ILeftBodyTouch, 12 | ImageLeftWidget, 13 | ImageLeftWidgetWithoutTouch, 14 | ImageRightWidget, 15 | ImageRightWidgetWithoutTouch, 16 | IRightBody, 17 | IRightBodyTouch, 18 | MDList, 19 | OneLineAvatarIconListItem, 20 | OneLineAvatarListItem, 21 | OneLineIconListItem, 22 | OneLineListItem, 23 | OneLineRightIconListItem, 24 | ThreeLineAvatarIconListItem, 25 | ThreeLineAvatarListItem, 26 | ThreeLineIconListItem, 27 | ThreeLineListItem, 28 | ThreeLineRightIconListItem, 29 | TwoLineAvatarIconListItem, 30 | TwoLineAvatarListItem, 31 | TwoLineIconListItem, 32 | TwoLineListItem, 33 | TwoLineRightIconListItem, 34 | ) 35 | -------------------------------------------------------------------------------- /kivymd/tools/patterns/MVC/Makefile: -------------------------------------------------------------------------------- 1 | # FILE TO FIND AND CREATE LOCALIZATION FILES FOR YOUR APPLICATION. \ 2 | \ 3 | In this file, you can specify in which files of your project to search for \ 4 | localization strings. \ 5 | These files should be listed in the below command: \ 6 | \ 7 | \ 8 | xgettext -Lpython --output=messages.pot --from-code=utf-8 \ 9 | path/to/file-1 \ 10 | path/to/file-2 \ 11 | ... 12 | 13 | .PHONY: po mo 14 | 15 | po: 16 | xgettext -Lpython --output=messages.pot --from-code=utf-8 \ 17 | View/%s/%s.kv \ 18 | View/%s/%s.py 19 | msgmerge --update --no-fuzzy-matching --backup=off data/locales/po/en.po messages.pot 20 | msgmerge --update --no-fuzzy-matching --backup=off data/locales/po/ru.po messages.pot 21 | 22 | mo: 23 | mkdir -p data/locales/en/LC_MESSAGES 24 | mkdir -p data/locales/ru/LC_MESSAGES 25 | msgfmt -c -o data/locales/en/LC_MESSAGES/%s.mo data/locales/po/en.po 26 | msgfmt -c -o data/locales/ru/LC_MESSAGES/%s.mo data/locales/po/ru.po -------------------------------------------------------------------------------- /utils/pyinstaller_script_old.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | excluded_modules = [ 4 | "numpy", 5 | "jedi", 6 | "psutil", 7 | "tk", 8 | "ipython", 9 | "tcl", 10 | "tcl8", 11 | "tornado", 12 | "cv2", 13 | ] 14 | 15 | append_string = "" 16 | for mod in excluded_modules: 17 | append_string += f" --exclude-module {mod}" 18 | 19 | datas_list = [ 20 | ("all_files/", "."), 21 | ("fonts/", "fonts/"), 22 | ("icons/pass.png", "."), 23 | ] 24 | 25 | add_datas = "".join([f"--add-data {data[0]}{os.pathsep}{data[1]} " for data in datas_list]) 26 | # Run the shell command with all the exclude module parameters 27 | 28 | 29 | """ 30 | Run this script to copy all .kv files in linux: 31 | 32 | find libs/ -name "*.kv" | cpio -pdm all_files/ 33 | 34 | """ 35 | os.system(f"pyinstaller --name Passlock --icon icons/pass.png {add_datas} main.py --noconfirm {append_string} --windowed --onefile") 36 | # os.system( 37 | # f"pyinstaller --name Passlock_no_dep --icon icons/pass_256.ico --add-data main.py --noconfirm {append_string}" 38 | # ) 39 | -------------------------------------------------------------------------------- /kivymd/uix/dropdownitem/dropdownitem.kv: -------------------------------------------------------------------------------- 1 | <_Triangle>: 2 | canvas: 3 | Color: 4 | rgba: root.theme_cls.text_color 5 | Triangle: 6 | points: 7 | [ \ 8 | self.right-dp(14), self.y+dp(7), \ 9 | self.right-dp(7), self.y+dp(7), \ 10 | self.right-dp(7), self.y+dp(14) \ 11 | ] 12 | 13 | 14 | 15 | orientation: "vertical" 16 | adaptive_size: True 17 | spacing: "5dp" 18 | padding: "5dp", "5dp", "5dp", 0 19 | 20 | MDBoxLayout: 21 | adaptive_size: True 22 | spacing: "10dp" 23 | 24 | Label: 25 | id: label_item 26 | size_hint: None, None 27 | size: self.texture_size 28 | color: root.theme_cls.text_color 29 | disabled_color: root.theme_cls.disabled_hint_text_color 30 | font_size: root.font_size 31 | 32 | 33 | _Triangle: 34 | size_hint: None, None 35 | size: "20dp", "20dp" 36 | 37 | MDSeparator: 38 | -------------------------------------------------------------------------------- /kivymd/uix/snackbar/snackbar.kv: -------------------------------------------------------------------------------- 1 | #:import get_color_from_hex kivy.utils.get_color_from_hex 2 | #:import window kivy.core.window 3 | 4 | 5 | 6 | size_hint_y: None 7 | height: "58dp" 8 | spacing: "10dp" 9 | padding: "10dp", "10dp", "10dp", "10dp" 10 | md_bg_color: get_color_from_hex("323232") if not root.bg_color else root.bg_color 11 | radius: root.radius 12 | elevation: 11 if root.padding else 0 13 | 14 | canvas: 15 | Color: 16 | rgba: self.md_bg_color 17 | RoundedRectangle: 18 | size: self.size 19 | pos: self.pos 20 | radius: self.radius 21 | 22 | 23 | 24 | MDLabel: 25 | id: text_bar 26 | size_hint_y: None 27 | height: self.texture_size[1] 28 | text: root.text 29 | font_size: root.font_size 30 | theme_text_color: "Custom" 31 | text_color: get_color_from_hex("ffffff") 32 | shorten: True 33 | shorten_from: "right" 34 | markup: True 35 | pos_hint: {"center_y": .5} 36 | -------------------------------------------------------------------------------- /kivymd/uix/widget.py: -------------------------------------------------------------------------------- 1 | """ 2 | Components/Widget 3 | ================= 4 | 5 | :class:`~kivy.uix.widget.Widget` class equivalent. Simplifies working 6 | with some widget properties. For example: 7 | 8 | Widget 9 | ------ 10 | 11 | .. code-block:: 12 | 13 | Widget: 14 | size_hint: .5, None 15 | height: self.width 16 | 17 | canvas: 18 | Color: 19 | rgba: app.theme_cls.primary_color 20 | RoundedRectangle: 21 | pos: self.pos 22 | size: self.size 23 | radius: [self.height / 2,] 24 | 25 | MDWidget 26 | -------- 27 | 28 | .. code-block:: 29 | 30 | MDWidget: 31 | size_hint: .5, None 32 | height: self.width 33 | radius: self.height / 2 34 | md_bg_color: app.theme_cls.primary_color 35 | """ 36 | 37 | from kivymd.uix import MDAdaptiveWidget 38 | 39 | 40 | class MDWidget(MDAdaptiveWidget): 41 | """ 42 | See :class:`~kivy.uix.Widget` class documentation for more information. 43 | 44 | .. versionadded:: 1.0.0 45 | """ 46 | -------------------------------------------------------------------------------- /kivymd/uix/floatlayout.py: -------------------------------------------------------------------------------- 1 | """ 2 | Components/FloatLayout 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/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: app.theme_cls.primary_dark 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/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 -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Ashutosh 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 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | # BUG: Does not work due to lack of OpenGL support on GitHub Actions 2 | # Not using this workflow for now 3 | name: Build desktop app 4 | on: 5 | push: 6 | branches: 7 | - None 8 | 9 | jobs: 10 | build-windows: 11 | name: Build windows app 12 | runs-on: windows-latest 13 | steps: 14 | - name: Checkout 15 | uses: actions/checkout@v2 16 | - name: Install dependencies 17 | run: | 18 | pip install -r requirements.txt pyinstaller 19 | - name: Build app 20 | run: | 21 | cd ${{github.workspace}} 22 | echo ${{secrets.WEB_API_KEY}} >> api_key.txt 23 | python copy_kv_files.py 24 | pyinstaller passlock_windows.spec 25 | mv dist/passlock passlock/ 26 | - name: Archive Release 27 | uses: thedoctor0/zip-release@master 28 | with: 29 | type: 'zip' 30 | directory: passlock 31 | filename: passlock.zip 32 | - name: Upload Release 33 | uses: actions/upload-artifact@v2 34 | with: 35 | name: passlock 36 | path: passlock.zip 37 | -------------------------------------------------------------------------------- /kivymd/tools/patterns/MVC/Model/base_model.py_tmp: -------------------------------------------------------------------------------- 1 | # The model implements the observer pattern. This means that the class must 2 | # support adding, removing, and alerting observers. In this case, the model is 3 | # completely independent of controllers and views. It is important that all 4 | # registered observers implement a specific method that will be called by the 5 | # model when they are notified (in this case, it is the `model_is_changed` 6 | # method). For this, observers must be descendants of an abstract class, 7 | # inheriting which, the `model_is_changed` method must be overridden. 8 | 9 | 10 | class BaseScreenModel: 11 | """Implements a base class for model modules.""" 12 | 13 | _observers = [] 14 | 15 | def add_observer(self, observer): 16 | self._observers.append(observer) 17 | 18 | def remove_observer(self, observer): 19 | self._observers.remove(observer) 20 | 21 | def notify_observers(self): 22 | """ 23 | The method that will be called on the observer when the model changes. 24 | """ 25 | 26 | for observer in self._observers: 27 | observer.model_is_changed() 28 | -------------------------------------------------------------------------------- /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/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 | -------------------------------------------------------------------------------- /Passlock.spec: -------------------------------------------------------------------------------- 1 | # -*- mode: python ; coding: utf-8 -*- 2 | from kivymd import hooks_path as kivymd_hooks_path 3 | 4 | a = Analysis( 5 | ['main.py'], 6 | pathex=[], 7 | binaries=[], 8 | datas=[('all_files/', '.'), ('fonts/', 'fonts/'), ('icons/pass.png', '.')], 9 | hiddenimports=[], 10 | hookspath=[kivymd_hooks_path], 11 | hooksconfig={}, 12 | runtime_hooks=[], 13 | excludes=['numpy', 'jedi', 'psutil', 'tk', 'ipython', 'tcl', 'tcl8', 'tornado', 'cv2'], 14 | noarchive=False, 15 | ) 16 | pyz = PYZ(a.pure) 17 | 18 | exe = EXE( 19 | pyz, 20 | a.scripts, 21 | a.binaries, 22 | a.datas, 23 | [], 24 | name='Passlock', 25 | debug=False, 26 | bootloader_ignore_signals=False, 27 | strip=False, 28 | upx=True, 29 | upx_exclude=[], 30 | runtime_tmpdir=None, 31 | console=False, 32 | disable_windowed_traceback=False, 33 | argv_emulation=False, 34 | target_arch=None, 35 | codesign_identity=None, 36 | entitlements_file=None, 37 | icon=['icons/pass.png'], 38 | ) 39 | app = BUNDLE( 40 | exe, 41 | name='Passlock.app', 42 | icon='icons/pass.png', 43 | bundle_identifier=None, 44 | ) 45 | -------------------------------------------------------------------------------- /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/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/tools/packaging/pyinstaller/hook-kivymd.py: -------------------------------------------------------------------------------- 1 | """ 2 | PyInstaller hook for KivyMD 3 | =========================== 4 | 5 | Adds fonts, images and KV files to package. 6 | 7 | All modules from uix directory are added by Kivy hook. 8 | """ 9 | 10 | import os 11 | from pathlib import Path 12 | 13 | import kivymd 14 | 15 | datas = [ 16 | # Add `.ttf` files from the `kivymd/fonts` directory. 17 | ( 18 | kivymd.fonts_path, 19 | str(Path("kivymd").joinpath(Path(kivymd.fonts_path).name)), 20 | ), 21 | # Add files from the `kivymd/images` directory. 22 | ( 23 | kivymd.images_path, 24 | str(Path("kivymd").joinpath(Path(kivymd.images_path).name)), 25 | ), 26 | ] 27 | 28 | # Add `.kv. files from the `kivymd/uix` directory. 29 | for path_to_kv_file in Path(kivymd.uix_path).glob("**/*.kv"): 30 | datas.append( 31 | ( 32 | str(Path(path_to_kv_file).parent.joinpath("*.kv")), 33 | str( 34 | Path("kivymd").joinpath( 35 | "uix", 36 | str(Path(path_to_kv_file).parent).split( 37 | str(Path("kivymd").joinpath("uix")) + os.sep 38 | )[1], 39 | ) 40 | ), 41 | ) 42 | ) 43 | -------------------------------------------------------------------------------- /passlock_linux.spec: -------------------------------------------------------------------------------- 1 | # -*- mode: python ; coding: utf-8 -*- 2 | from kivymd import hooks_path as kivymd_hooks_path 3 | block_cipher = None 4 | 5 | 6 | a = Analysis( 7 | ['main.py'], 8 | pathex=[], 9 | binaries=[], 10 | datas=[("all_files/","."),("fonts/","fonts/"),("icons/pass.png","."),("install.sh",".")], 11 | hiddenimports=[], 12 | hookspath=[kivymd_hooks_path], 13 | hooksconfig={}, 14 | runtime_hooks=[], 15 | excludes=['numpy', 'jedi', 'psutil', 'tk', 'ipython', 'tcl', 'tcl8', 'tornado', 'cv2'], 16 | win_no_prefer_redirects=False, 17 | win_private_assemblies=False, 18 | cipher=block_cipher, 19 | noarchive=False, 20 | ) 21 | pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher) 22 | 23 | exe = EXE( 24 | pyz, 25 | a.scripts, 26 | exclude_binaries=True, 27 | name='Passlock', 28 | debug=False, 29 | bootloader_ignore_signals=False, 30 | strip=False, 31 | upx=True, 32 | console=True, 33 | disable_windowed_traceback=False, 34 | argv_emulation=False, 35 | target_arch=None, 36 | codesign_identity=None, 37 | entitlements_file=None, 38 | ) 39 | coll = COLLECT( 40 | exe, 41 | a.binaries, 42 | a.zipfiles, 43 | a.datas, 44 | strip=False, 45 | upx=True, 46 | upx_exclude=[], 47 | name='passlock', 48 | ) 49 | -------------------------------------------------------------------------------- /kivymd/uix/sliverappbar/sliverappbar.kv: -------------------------------------------------------------------------------- 1 | #:import ScrollEffect kivy.effects.scroll.ScrollEffect 2 | 3 | 4 | 5 | 6 | FloatLayout: 7 | id: float_box 8 | 9 | BoxLayout: 10 | canvas.after: 11 | Color: 12 | rgba: 13 | root.background_color \ 14 | if root.background_color else \ 15 | root.theme_cls.primary_color 16 | a: root._opacity 17 | Rectangle: 18 | pos: self.pos 19 | size: self.size 20 | 21 | id: header 22 | size_hint_y: None 23 | height: root.max_height + root.radius[0] 24 | pos: self.x, root.height - root.max_height - root.radius[0] 25 | 26 | ScrollView: 27 | id: scroll 28 | effect_cls: ScrollEffect 29 | on_vbar: root.on_vbar() 30 | on_scroll_start: 31 | if not root._scroll_was_moving: root._scroll_was_moving = True 32 | 33 | MDBoxLayout: 34 | id: scroll_box 35 | adaptive_height: True 36 | orientation: "vertical" 37 | 38 | BoxLayout: 39 | size_hint_y: None 40 | height: root.max_height 41 | -------------------------------------------------------------------------------- /kivymd/uix/tooltip/tooltip.kv: -------------------------------------------------------------------------------- 1 | 2 | size_hint: None, None 3 | width: self.minimum_width 4 | height: self.minimum_height + root.padding[1] 5 | opacity: 0 6 | 7 | canvas.before: 8 | PushMatrix 9 | Color: 10 | rgba: 11 | root.theme_cls.opposite_bg_dark if not root.tooltip_bg_color \ 12 | else root.tooltip_bg_color 13 | RoundedRectangle: 14 | pos: self.pos 15 | size: self.size 16 | radius: root.tooltip_radius 17 | Scale: 18 | origin: self.center 19 | x: root._scale_x 20 | y: root._scale_y 21 | canvas.after: 22 | PopMatrix 23 | 24 | MDLabel: 25 | id: label_tooltip 26 | text: root.tooltip_text 27 | size_hint: None, None 28 | -text_size: None, None 29 | size: self.texture_size 30 | bold: True 31 | theme_text_color: "Custom" 32 | font_style: root.tooltip_font_style 33 | markup: True 34 | pos_hint: {"center_y": .5} 35 | text_color: 36 | ([0, 0, 0, 1] if not root.tooltip_text_color else root.tooltip_text_color) \ 37 | if root.theme_cls.theme_style == "Dark" else \ 38 | ([1, 1, 1, 1] if not root.tooltip_text_color else root.tooltip_text_color) 39 | -------------------------------------------------------------------------------- /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 -------------------------------------------------------------------------------- /passlock_windows.spec: -------------------------------------------------------------------------------- 1 | # -*- mode: python ; coding: utf-8 -*- 2 | from kivymd import hooks_path as kivymd_hooks_path 3 | from kivy_deps import sdl2, glew 4 | 5 | block_cipher = None 6 | 7 | 8 | a = Analysis( 9 | ['main.py'], 10 | pathex=[], 11 | binaries=[], 12 | datas=[("all_files/","."),("fonts/","fonts/"),("icons/pass.png",".")], 13 | hiddenimports=[], 14 | hookspath=[kivymd_hooks_path], 15 | hooksconfig={}, 16 | runtime_hooks=[], 17 | excludes=['mfc140u.dll'], 18 | win_no_prefer_redirects=False, 19 | win_private_assemblies=False, 20 | cipher=block_cipher, 21 | noarchive=False, 22 | ) 23 | pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher) 24 | 25 | exe = EXE( 26 | pyz, 27 | a.scripts, 28 | exclude_binaries=True, 29 | name='Passlock', 30 | debug=False, 31 | bootloader_ignore_signals=False, 32 | strip=False, 33 | upx=True, 34 | console=False, 35 | disable_windowed_traceback=False, 36 | argv_emulation=False, 37 | target_arch=None, 38 | codesign_identity=None, 39 | entitlements_file=None, 40 | icon='icons\passlock.ico', 41 | ) 42 | coll = COLLECT( 43 | exe, 44 | a.binaries, 45 | a.zipfiles, 46 | a.datas, 47 | *[Tree(p) for p in (sdl2.dep_bins + glew.dep_bins)], 48 | strip=False, 49 | upx=True, 50 | upx_exclude=[], 51 | name='passlock', 52 | ) 53 | -------------------------------------------------------------------------------- /make_msi_build.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | if "%1"=="" (set version=1.0.0) else (set version=%1) 3 | set AdvancedInstaller="C:\Program Files (x86)\Caphyon\Advanced Installer 18.2\bin\x86\AdvancedInstaller.com" 4 | set app_name=Passlock 5 | set org_name=Passlock 6 | set build_dir=%cd%\dist\passlock\ 7 | set icon_path=%cd%\icons\passlock.ico 8 | set path_to_project=%cd%\%app_name%.aip 9 | echo "Building %app_name% with version %version%" 10 | %AdvancedInstaller% /newproject %path_to_project% -overwrite 11 | %AdvancedInstaller% /edit %path_to_project% /SetVersion %version% 12 | %AdvancedInstaller% /edit %path_to_project% /SetProperty ProductName="%app_name%" 13 | %AdvancedInstaller% /edit %path_to_project% /SetProperty Manufacturer="%org_name%" 14 | %AdvancedInstaller% /edit %path_to_project% /SetProperty PackageVersion=%version% 15 | %AdvancedInstaller% /edit %path_to_project% /SetPackageName %app_name%_%version%_x86.msi -buildname DefaultBuild 16 | %AdvancedInstaller% /edit %path_to_project% /SetIcon -icon "%icon_path%" 17 | %AdvancedInstaller% /edit %path_to_project% /AddFolder APPDIR %build_dir% 18 | %AdvancedInstaller% /edit %path_to_project% /NewShortcut -name %app_name% -dir DesktopFolder -target APPDIR\passlock\%app_name%.exe -icon %icon_path% 19 | %AdvancedInstaller% /edit %path_to_project% /NewShortcut -name %app_name% -dir ProgramMenuFolder -target APPDIR\passlock\%app_name%.exe -icon %icon_path% 20 | %AdvancedInstaller% /build %path_to_project% -------------------------------------------------------------------------------- /kivymd/uix/progressbar/progressbar.kv: -------------------------------------------------------------------------------- 1 | 2 | canvas: 3 | Clear 4 | Color: 5 | rgba: 6 | self.theme_cls.divider_color \ 7 | if not self.back_color else \ 8 | self.back_color 9 | Rectangle: 10 | size: 11 | (self.width, self.height) \ 12 | if self.orientation == "horizontal" else \ 13 | (self.width, self.height) 14 | pos: 15 | (self.x, self.center_y - self.height / 2) \ 16 | if self.orientation == "horizontal" else \ 17 | (self.center_x - self.width / 2, self.y) 18 | Color: 19 | rgba: 20 | self.theme_cls.primary_color if not self.color else self.color 21 | Rectangle: 22 | size: 23 | (self.width * self.value_normalized, self.height if self.height else dp(4)) \ 24 | if self.orientation == "horizontal" else \ 25 | (self.width, self.height * self.value_normalized) 26 | pos: 27 | (self.width * (1 - self.value_normalized) + self.x \ 28 | if self.reversed else self.x + self._x, self.center_y - self.height / 2) \ 29 | if self.orientation == "horizontal" \ 30 | else (self.center_x - self.width / 2, self.height \ 31 | * (1 - self.value_normalized) + self.y if self.reversed \ 32 | else self.y) 33 | -------------------------------------------------------------------------------- /kivymd/uix/backdrop/backdrop.kv: -------------------------------------------------------------------------------- 1 | 2 | md_bg_color: 3 | root.theme_cls.primary_color \ 4 | if not root.back_layer_color \ 5 | else root.back_layer_color 6 | 7 | MDBackdropToolbar: 8 | id: toolbar 9 | title: root.title 10 | elevation: 0 11 | left_action_items: root.left_action_items 12 | right_action_items: root.right_action_items 13 | pos_hint: {"top": 1} 14 | md_bg_color: 15 | root.theme_cls.primary_color \ 16 | if not root.back_layer_color \ 17 | else root.back_layer_color 18 | 19 | _BackLayer: 20 | id: back_layer 21 | y: -toolbar.height 22 | padding: 0, 0, 0, toolbar.height + dp(10) 23 | 24 | _FrontLayer: 25 | id: _front_layer 26 | md_bg_color: 0, 0, 0, 0 27 | orientation: "vertical" 28 | size_hint_y: None 29 | height: root.height - toolbar.height 30 | padding: root.padding 31 | md_bg_color: 32 | root.theme_cls.bg_normal \ 33 | if not root.front_layer_color \ 34 | else root.front_layer_color 35 | radius: 36 | [root.radius_left, root.radius_left, 37 | root.radius_right, root.radius_right] 38 | 39 | OneLineListItem: 40 | id: header_button 41 | text: root.header_text 42 | divider: None 43 | _no_ripple_effect: True 44 | on_press: root.open() 45 | 46 | MDBoxLayout: 47 | id: front_layer 48 | padding: 0, 0, 0, "10dp" 49 | -------------------------------------------------------------------------------- /kivymd/uix/menu/menu.kv: -------------------------------------------------------------------------------- 1 | #:import STD_INC kivymd.material_resources.STANDARD_INCREMENT 2 | 3 | 4 | 5 | adaptive_width: True 6 | 7 | 8 | 9 | 10 | IconLeftWidget: 11 | id: icon_widget 12 | icon: root.icon 13 | 14 | 15 | 16 | size_hint: None, None 17 | width: root.width_mult * STD_INC 18 | bar_width: 0 19 | key_viewclass: "viewclass" 20 | key_size: "height" 21 | 22 | RecycleBoxLayout: 23 | padding: 0, "4dp", 0, "4dp" 24 | default_size: None, dp(48) 25 | default_size_hint: 1, None 26 | size_hint_y: None 27 | height: self.minimum_height 28 | orientation: "vertical" 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | MenuContainer: 37 | id: card 38 | orientation: "vertical" 39 | elevation: root.elevation 40 | size_hint: None, None 41 | size: md_menu.size[0], md_menu.size[1] + content_header.height 42 | pos: md_menu.pos 43 | opacity: md_menu.opacity 44 | radius: root.radius 45 | md_bg_color: 46 | root.background_color \ 47 | if root.background_color else root.theme_cls.bg_dark 48 | 49 | MDBoxLayout: 50 | id: content_header 51 | adaptive_size: True 52 | 53 | MDMenu: 54 | id: md_menu 55 | drop_cls: root 56 | width_mult: root.width_mult 57 | size_hint: None, None 58 | size: 0, 0 59 | opacity: 0 60 | -------------------------------------------------------------------------------- /kivymd/tools/patterns/MVC/View/base_screen.py_tmp: -------------------------------------------------------------------------------- 1 | from kivy.properties import ObjectProperty 2 | 3 | from kivymd.app import MDApp 4 | from kivymd.theming import ThemableBehavior 5 | from kivymd.uix.screen import MDScreen 6 | 7 | from Utility.observer import Observer 8 | 9 | 10 | class BaseScreenView(ThemableBehavior, MDScreen, Observer): 11 | """ 12 | A base class that implements a visual representation of the model data 13 | :class:`~Model.%s.%s`. 14 | The view class must be inherited from this class. 15 | """ 16 | 17 | controller = ObjectProperty() 18 | """ 19 | Controller object - :class:`~Controller.%s.%s`. 20 | 21 | :attr:`controller` is an :class:`~kivy.properties.ObjectProperty` 22 | and defaults to `None`. 23 | """ 24 | 25 | model = ObjectProperty() 26 | """ 27 | Model object - :class:`~Model.%s.%s`. 28 | 29 | :attr:`model` is an :class:`~kivy.properties.ObjectProperty` 30 | and defaults to `None`. 31 | """ 32 | 33 | manager_screens = ObjectProperty() 34 | """ 35 | Screen manager object - :class:`~kivy.uix.screenmanager.ScreenManager`. 36 | 37 | :attr:`manager_screens` is an :class:`~kivy.properties.ObjectProperty` 38 | and defaults to `None`. 39 | """ 40 | 41 | def __init__(self, **kw): 42 | super().__init__(**kw) 43 | # Often you need to get access to the application object from the view 44 | # class. You can do this using this attribute. 45 | self.app = MDApp.get_running_app() 46 | # Adding a view class as observer. 47 | self.model.add_observer(self) 48 | -------------------------------------------------------------------------------- /kivymd/tools/patterns/MVC/libs/translation.py: -------------------------------------------------------------------------------- 1 | import gettext 2 | 3 | from kivy.lang import Observable 4 | 5 | 6 | class Translation(Observable): 7 | """Original source - https://github.com/tito/kivy-gettext-example.""" 8 | 9 | observers = [] 10 | 11 | def __init__(self, defaultlang, domian, resource_dir): 12 | super().__init__() 13 | self.ugettext = None 14 | self.lang = defaultlang 15 | self.domian = domian 16 | self.resource_dir = resource_dir 17 | self.switch_lang(self.lang) 18 | 19 | def _(self, text): 20 | return self.ugettext(text) 21 | 22 | def fbind(self, name, func, args, **kwargs): 23 | if name == "_": 24 | self.observers.append((func, args, kwargs)) 25 | else: 26 | return super().fbind(name, func, *args, **kwargs) 27 | 28 | def funbind(self, name, func, args, **kwargs): 29 | if name == "_": 30 | key = (func, args, kwargs) 31 | if key in self.observers: 32 | self.observers.remove(key) 33 | else: 34 | return super().funbind(name, func, *args, **kwargs) 35 | 36 | def switch_lang(self, lang): 37 | locales = gettext.translation( 38 | self.domian, self.resource_dir, languages=[lang] 39 | ) 40 | try: 41 | self.ugettext = locales.ugettext 42 | except AttributeError: 43 | self.ugettext = locales.gettext 44 | 45 | for func, largs, kwargs in self.observers: 46 | try: 47 | func(largs, None, None) 48 | except ReferenceError: 49 | pass 50 | -------------------------------------------------------------------------------- /kivymd/tools/patterns/MVC/View/FirstScreen/first_screen.kv: -------------------------------------------------------------------------------- 1 | #:import images_path kivymd.images_path 2 | 3 | 4 | <%s> 5 | 6 | FitImage: 7 | source: f"{images_path}firebase-logo.png" 8 | 9 | MDBoxLayout: 10 | orientation: "vertical" 11 | 12 | MDToolbar: 13 | title: "%s" 14 | right_action_items: [["web", lambda x: %s]] 15 | 16 | MDFloatLayout: 17 | 18 | MDBoxLayout: 19 | orientation: "vertical" 20 | adaptive_height: True 21 | size_hint_x: None 22 | width: root.width - dp(72) 23 | radius: 12 24 | padding: "12dp" 25 | md_bg_color: 1, 1, 1, .5 26 | pos_hint: {"center_x": .5, "center_y": .5} 27 | 28 | MDLabel: 29 | id: prev_label 30 | text: %s 31 | font_style: "H6" 32 | adaptive_height: True 33 | halign: "center" 34 | 35 | MDBoxLayout: 36 | orientation: "vertical" 37 | adaptive_height: True 38 | padding: "50dp" 39 | spacing: "20dp" 40 | 41 | MDTextField: 42 | hint_text: %s 43 | on_text: root.controller.set_user_data("login", self.text) 44 | 45 | MDTextField: 46 | hint_text: %s 47 | on_text: root.controller.set_user_data("password", self.text) 48 | 49 | MDFillRoundFlatButton: 50 | text: %s 51 | on_release: root.controller.on_tap_button_login() 52 | pos_hint: {"center_x": .5, "center_y": .1} 53 | -------------------------------------------------------------------------------- /passlock_macos.spec: -------------------------------------------------------------------------------- 1 | # -*- mode: python ; coding: utf-8 -*- 2 | from kivymd import hooks_path as kivymd_hooks_path 3 | 4 | a = Analysis( 5 | ['main.py'], 6 | pathex=[], 7 | binaries=[], 8 | datas=[('all_files/', '.'), ('fonts/', 'fonts/'), ('icons/pass.png', '.')], 9 | hiddenimports=[], 10 | hookspath=[kivymd_hooks_path], 11 | hooksconfig={}, 12 | runtime_hooks=[], 13 | excludes=['numpy', 'jedi', 'psutil', 'tk', 'ipython', 'tcl', 'tcl8', 'tornado', 'cv2'], 14 | noarchive=False, 15 | ) 16 | pyz = PYZ(a.pure) 17 | 18 | exe = EXE( 19 | pyz, 20 | a.scripts, 21 | [], 22 | exclude_binaries=True, 23 | name='Passlock', 24 | debug=False, 25 | bootloader_ignore_signals=False, 26 | strip=False, 27 | upx=True, 28 | console=False, 29 | disable_windowed_traceback=False, 30 | argv_emulation=False, 31 | target_arch=None, 32 | codesign_identity=None, 33 | entitlements_file=None, 34 | icon=['icons/pass.png'], 35 | ) 36 | coll = COLLECT( 37 | exe, 38 | a.binaries, 39 | a.datas, 40 | strip=False, 41 | upx=True, 42 | upx_exclude=[], 43 | name='Passlock', 44 | ) 45 | app = BUNDLE( 46 | coll, 47 | name='Passlock.app', 48 | icon='icons/pass.png', 49 | bundle_identifier=None, 50 | info_plist={ 51 | 'NSPrincipalClass': 'NSApplication', 52 | 'NSAppleScriptEnabled': False, 53 | 'CFBundleDocumentTypes': [ 54 | { 55 | 'CFBundleTypeName': 'My File Format', 56 | 'CFBundleTypeIconFile': 'MyFileIcon.icns', 57 | 'LSItemContentTypes': ['com.example.myformat'], 58 | 'LSHandlerRank': 'Owner' 59 | } 60 | ] 61 | }, 62 | ) 63 | -------------------------------------------------------------------------------- /kivymd/uix/imagelist/imagelist.kv: -------------------------------------------------------------------------------- 1 | 2 | _img_widget: img 3 | _img_overlay: img_overlay 4 | _box_overlay: box 5 | 6 | FitImage: 7 | id: img 8 | source: root.source 9 | x: root.x 10 | y: root.y if root.overlap or root.box_position == 'header' else box.top 11 | 12 | BoxLayout: 13 | id: img_overlay 14 | size_hint: img.size_hint 15 | size: img.size 16 | pos: img.pos 17 | 18 | MDBoxLayout: 19 | id: box 20 | md_bg_color: root.box_color 21 | size_hint_y: None 22 | height: "68dp" if root.lines == 2 else "48dp" 23 | x: root.x 24 | y: root.y if root.box_position == 'footer' else root.y + root.height - self.height 25 | 26 | 27 | 28 | _img_widget: img 29 | _img_overlay: img_overlay 30 | _box_overlay: box 31 | _box_label: boxlabel 32 | 33 | FitImage: 34 | id: img 35 | source: root.source 36 | x: root.x 37 | y: root.y if root.overlap or root.box_position == 'header' else box.top 38 | 39 | BoxLayout: 40 | id: img_overlay 41 | size_hint: img.size_hint 42 | size: img.size 43 | pos: img.pos 44 | 45 | MDBoxLayout: 46 | id: box 47 | padding: "5dp", 0, 0, 0 48 | md_bg_color: root.box_color 49 | adaptive_height: True 50 | x: root.x 51 | y: root.y if root.box_position == 'footer' else root.y + root.height - self.height 52 | 53 | MDLabel: 54 | id: boxlabel 55 | font_style: root.font_style 56 | size_hint_y: None 57 | height: self.texture_size[1] 58 | text: root.text 59 | color: root.tile_text_color 60 | markup: True 61 | -------------------------------------------------------------------------------- /kivymd/uix/anchorlayout.py: -------------------------------------------------------------------------------- 1 | """ 2 | Components/AnchorLayout 3 | ======================= 4 | 5 | .. versionadded:: 1.0.0 6 | 7 | :class:`~kivy.uix.anchorlayout.AnchorLayout` class equivalent. Simplifies working 8 | with some widget properties. For example: 9 | 10 | AnchorLayout 11 | ------------ 12 | 13 | .. code-block:: 14 | 15 | AnchorLayout: 16 | canvas: 17 | Color: 18 | rgba: app.theme_cls.primary_color 19 | Rectangle: 20 | pos: self.pos 21 | size: self.size 22 | 23 | AnchorLayout 24 | ------------ 25 | 26 | .. code-block:: 27 | 28 | MDBoxLayout: 29 | md_bg_color: app.theme_cls.primary_color 30 | 31 | Available options are: 32 | ---------------------- 33 | 34 | - adaptive_height_ 35 | - adaptive_width_ 36 | - adaptive_size_ 37 | 38 | .. adaptive_height: 39 | adaptive_height 40 | --------------- 41 | 42 | .. code-block:: kv 43 | 44 | adaptive_height: True 45 | 46 | Equivalent 47 | 48 | .. code-block:: kv 49 | 50 | size_hint_y: None 51 | height: self.minimum_height 52 | 53 | .. adaptive_width: 54 | adaptive_width 55 | -------------- 56 | 57 | .. code-block:: kv 58 | 59 | adaptive_width: True 60 | 61 | Equivalent 62 | 63 | .. code-block:: kv 64 | 65 | size_hint_x: None 66 | height: self.minimum_width 67 | 68 | .. adaptive_size: 69 | adaptive_size 70 | ------------- 71 | 72 | .. code-block:: kv 73 | 74 | adaptive_size: True 75 | 76 | Equivalent 77 | 78 | .. code-block:: kv 79 | 80 | size_hint: None, None 81 | size: self.minimum_size 82 | """ 83 | 84 | __all__ = ("MDAnchorLayout",) 85 | 86 | from kivy.uix.anchorlayout import AnchorLayout 87 | 88 | from kivymd.uix import MDAdaptiveWidget 89 | 90 | 91 | class MDAnchorLayout(AnchorLayout, MDAdaptiveWidget): 92 | pass 93 | -------------------------------------------------------------------------------- /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/uix/gridlayout.py: -------------------------------------------------------------------------------- 1 | """ 2 | Components/GridLayout 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 | 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.gridlayout import GridLayout 87 | 88 | from kivymd.uix import MDAdaptiveWidget 89 | 90 | 91 | class MDGridLayout(GridLayout, MDAdaptiveWidget): 92 | pass 93 | -------------------------------------------------------------------------------- /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/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/uix/boxlayout.py: -------------------------------------------------------------------------------- 1 | """ 2 | Components/BoxLayout 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 | __all__ = ("MDBoxLayout",) 87 | 88 | from kivy.uix.boxlayout import BoxLayout 89 | 90 | from kivymd.uix import MDAdaptiveWidget 91 | 92 | 93 | class MDBoxLayout(BoxLayout, MDAdaptiveWidget): 94 | pass 95 | -------------------------------------------------------------------------------- /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": "Icons", 44 | "fn_regular": fonts_path + "materialdesignicons-webfont.ttf", 45 | }, 46 | ] 47 | 48 | for font in fonts: 49 | LabelBase.register(**font) 50 | 51 | theme_font_styles = [ 52 | "H1", 53 | "H2", 54 | "H3", 55 | "H4", 56 | "H5", 57 | "H6", 58 | "Subtitle1", 59 | "Subtitle2", 60 | "Body1", 61 | "Body2", 62 | "Button", 63 | "Caption", 64 | "Overline", 65 | "Icon", 66 | ] 67 | """ 68 | .. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/font-styles-2.png 69 | """ 70 | -------------------------------------------------------------------------------- /kivymd/tools/patterns/MVC/Model/base.py_tmp: -------------------------------------------------------------------------------- 1 | import datetime 2 | import time 3 | from typing import Union 4 | 5 | import requests 6 | from firebase import firebase 7 | 8 | 9 | def get_time() -> str: 10 | """Returns a string with the current date and time.""" 11 | 12 | return datetime.datetime.fromtimestamp( 13 | time.mktime(datetime.datetime.now().timetuple()) 14 | ).strftime("%d-%m-%Y %H:%M:%S") 15 | 16 | 17 | class Base: 18 | """ 19 | Your methods for working with the database should be implemented in this 20 | class. 21 | """ 22 | 23 | def __init__(self): 24 | # RealTime Database attribute. 25 | self.real_time_firebase = firebase.FirebaseApplication( 26 | "https://loginappmvc-5a4aa-default-rtdb.firebaseio.com/", None 27 | ) 28 | self.type_base = "UserData" 29 | self.name_base_users = "LoginsPasswords" 30 | self.send_user_data_to_database({"login": "Kivy", "password": "KivyMD"}) 31 | 32 | def get_data_from_base_users(self) -> Union[dict, None]: 33 | """ 34 | Return data from the database: 35 | 36 | { 37 | '05-08-2021 20:51:22': 38 | {'login': 'User login', 'password': 'password'}, 39 | ..., 40 | } 41 | """ 42 | 43 | try: 44 | data = self.real_time_firebase.get(self.type_base, self.name_base_users) 45 | except requests.exceptions.ConnectionError: 46 | return None 47 | return data 48 | 49 | def send_user_data_to_database(self, data: dict) -> bool: 50 | """Sends data to the database.""" 51 | 52 | try: 53 | self.real_time_firebase.put( 54 | f"{self.type_base}/{self.name_base_users}", 55 | get_time(), 56 | data, 57 | ) 58 | return True 59 | except requests.exceptions.HTTPError: 60 | return False 61 | -------------------------------------------------------------------------------- /kivymd/uix/banner/banner.kv: -------------------------------------------------------------------------------- 1 | #:import Window kivy.core.window.Window 2 | 3 | 4 | 5 | text: root.text_message[0] 6 | secondary_text: root.text_message[1] 7 | tertiary_text: root.text_message[2] 8 | divider: None 9 | _no_ripple_effect: True 10 | 11 | ImageLeftWidget: 12 | source: root.icon 13 | 14 | 15 | 16 | text: root.text_message[0] 17 | secondary_text: root.text_message[1] 18 | divider: None 19 | _no_ripple_effect: True 20 | 21 | ImageLeftWidget: 22 | source: root.icon 23 | 24 | 25 | 26 | text: root.text_message[0] 27 | divider: None 28 | _no_ripple_effect: True 29 | 30 | ImageLeftWidget: 31 | source: root.icon 32 | 33 | 34 | 35 | text: root.text_message[0] 36 | secondary_text: root.text_message[1] 37 | tertiary_text: root.text_message[2] 38 | divider: None 39 | _no_ripple_effect: True 40 | 41 | 42 | 43 | text: root.text_message[0] 44 | secondary_text: root.text_message[1] 45 | divider: None 46 | _no_ripple_effect: True 47 | 48 | 49 | 50 | text: root.text_message[0] 51 | divider: None 52 | _no_ripple_effect: True 53 | 54 | 55 | 56 | size_hint_y: None 57 | height: self.minimum_height 58 | banner_y: 0 59 | orientation: "vertical" 60 | y: Window.height - self.banner_y 61 | 62 | canvas: 63 | Color: 64 | rgba: 0, 0, 0, 0 65 | Rectangle: 66 | pos: self.pos 67 | size: self.size 68 | 69 | MDBoxLayout: 70 | id: container_message 71 | adaptive_height: True 72 | 73 | MDBoxLayout: 74 | adaptive_size: True 75 | pos_hint: {"right": 1} 76 | padding: 0, 0, "8dp", "8dp" 77 | spacing: "8dp" 78 | 79 | MDBoxLayout: 80 | id: left_action_box 81 | adaptive_size: True 82 | 83 | MDBoxLayout: 84 | id: right_action_box 85 | adaptive_size: True 86 | -------------------------------------------------------------------------------- /libs/screens/LoginScreen/LoginScreen.kv: -------------------------------------------------------------------------------- 1 | #: import Clock kivy.clock.Clock 2 | #: import colors kivymd.color_definitions.colors 3 | #: import Window kivy.core.window.Window 4 | #: set rad dp(200) 5 | 6 | #: set primary_color_hex colors[app.theme_cls.primary_palette][app.theme_cls.primary_hue] 7 | 8 | : 9 | md_bg_color: app.bg_color 10 | on_enter: 11 | app.animate_signup('') 12 | canvas: 13 | Color: 14 | rgba:app.theme_cls.primary_color[:-1]+[.9] 15 | Ellipse: 16 | size:rad,rad 17 | pos:0-rad/2,Window.height-rad+20 18 | angle_start:0 19 | angle_end:180 20 | Color: 21 | rgba:app.login_circle_light[:-1]+[.7] 22 | Ellipse: 23 | pos:0,Window.height-rad/2 24 | size:rad,rad 25 | angle_start:90 26 | angle_end:270 27 | 28 | MDBoxLayout: 29 | adaptive_height:True 30 | id: box 31 | pos_hint:{"top":.95} 32 | orientation:'vertical' 33 | spacing:'65dp' 34 | MDLabel: 35 | adaptive_height:True 36 | text:f"LOGIN\nTO [color={primary_color_hex}][u]PASS••[/u]" 37 | markup:True 38 | valign:"center" 39 | theme_text_color:"Custom" 40 | text_color:app.text_color 41 | font_name:'Poppins' 42 | halign:'center' 43 | font_size:'36dp' 44 | pos_hint:{'top':1} 45 | PasswordCard: 46 | id: password 47 | label_name:'Password' 48 | hint_text:"Enter Password" 49 | size_hint_x:.7 50 | pos_hint:{'center_x':.5} 51 | on_text_validate: 52 | root.login_button_pressed(password.text) 53 | 54 | 55 | MDFloatingActionButton: 56 | id: lock 57 | icon:'login' 58 | type:"custom" 59 | size:dp(75),dp(75) 60 | pos_hint:{'center_x':.5} 61 | md_bg_color:app.primary_accent 62 | icon_color:self.theme_cls.primary_color 63 | on_release: 64 | root.login_button_pressed(password.text) 65 | 66 | # MDTextButton: 67 | # id:forgot 68 | # x: password.x+dp(25) 69 | # y: password.y-dp(25) 70 | # text:'Forgot Password?' 71 | # font_name:'RobotoMedium' 72 | # theme_text_color:"Secondary" 73 | # font_size:"12sp" 74 | 75 | 76 | -------------------------------------------------------------------------------- /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/uix/label/label.kv: -------------------------------------------------------------------------------- 1 | #:import md_icons kivymd.icon_definitions.md_icons 2 | 3 | 4 | 5 | disabled_color: self.theme_cls.disabled_hint_text_color 6 | # FIXME: Overriding the values of this property greatly affects application 7 | # performance. Especially when the application window is resized and a 8 | # custom font is used. Performance is especially slow when you are using 9 | # `PIL` as your text processing provider - os.environ ['KIVY_TEXT'] = 'pil'. 10 | # Priority - CRITICAL. 11 | text_size: self.width, None 12 | 13 | 14 | : 15 | canvas: 16 | Color: 17 | rgba: (1, 1, 1, 1) if self.source else (0, 0, 0, 0) 18 | Rectangle: 19 | source: self.source if self.source else None 20 | pos: self.pos 21 | size: self.size 22 | 23 | font_style: "Icon" 24 | text: u"{}".format(md_icons[root.icon]) if root.icon in md_icons else "blank" 25 | source: None if root.icon in md_icons else root.icon 26 | 27 | # Badge icon. 28 | MDLabel: 29 | font_style: "Icon" 30 | adaptive_size: True 31 | opposite_icon_color: True 32 | color: root.badge_icon_color 33 | text: 34 | u"{}".format(md_icons[root.badge_icon]) \ 35 | if root.badge_icon in md_icons else \ 36 | "" 37 | pos: 38 | root.x + root.width / 2 + self.width / 2 - dp(6), \ 39 | root.y + self.texture_size[1] / 2 + dp(6) 40 | font_size: 41 | ( \ 42 | root.font_size / 1.5 \ 43 | if not root.badge_font_size else \ 44 | root.badge_font_size \ 45 | ) \ 46 | if root.badge_icon and root.badge_icon != "blank" else 0 47 | 48 | canvas.before: 49 | Color: 50 | rgba: 51 | ( \ 52 | root.badge_bg_color \ 53 | if root.badge_bg_color else \ 54 | app.theme_cls.error_color \ 55 | ) \ 56 | if root.badge_icon else \ 57 | (0, 0, 0, 0) 58 | RoundedRectangle: 59 | radius: [self.width / 2,] 60 | pos: self.pos 61 | size: self.size 62 | -------------------------------------------------------------------------------- /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) -> None: 14 | """Test fonts and images.""" 15 | 16 | app_name = "userapp" 17 | workpath = tmp_path / "build" 18 | distpath = tmp_path / "dist" 19 | app = tmp_path / (app_name + ".py") 20 | app.write_text( 21 | """ 22 | import os 23 | 24 | from kivy.core.text import LabelBase 25 | 26 | import kivymd 27 | 28 | fonts = os.listdir(kivymd.fonts_path) 29 | print(fonts) 30 | assert "Roboto-Regular.ttf" in fonts 31 | assert "materialdesignicons-webfont.ttf" in fonts 32 | print(LabelBase._fonts.keys()) 33 | assert "Roboto" in LabelBase._fonts.keys() # NOQA 34 | assert "Icons" in LabelBase._fonts.keys() # NOQA 35 | 36 | images = os.listdir(kivymd.images_path) 37 | print(images) 38 | assert "folder.png" in images 39 | assert "rec_shadow.atlas" in images 40 | """ 41 | ) 42 | pyi_main.run( 43 | [ 44 | "--workpath", 45 | str(workpath), 46 | "--distpath", 47 | str(distpath), 48 | "--specpath", 49 | str(tmp_path), 50 | str(app), 51 | ] 52 | ) 53 | subprocess.run([str(distpath / app_name / app_name)], check=True) 54 | 55 | 56 | def test_widgets(tmp_path) -> None: 57 | """Test that all widgets are accesible.""" 58 | 59 | app_name = "userapp" 60 | workpath = tmp_path / "build" 61 | distpath = tmp_path / "dist" 62 | app = tmp_path / (app_name + ".py") 63 | app.write_text( 64 | """ 65 | import os 66 | 67 | import kivymd # NOQA 68 | __import__("kivymd.uix.label") 69 | __import__("kivymd.uix.button") 70 | __import__("kivymd.uix.list") 71 | __import__("kivymd.uix.navigationdrawer") 72 | 73 | print(os.listdir(os.path.dirname(kivymd.uix.__path__[0]))) 74 | """ 75 | ) 76 | pyi_main.run( 77 | [ 78 | "--workpath", 79 | str(workpath), 80 | "--distpath", 81 | str(distpath), 82 | "--specpath", 83 | str(tmp_path), 84 | str(app), 85 | ] 86 | ) 87 | subprocess.run([str(distpath / app_name / app_name)], check=True) 88 | -------------------------------------------------------------------------------- /kivymd/tools/patterns/MVC/main.py_tmp: -------------------------------------------------------------------------------- 1 | """ 2 | The entry point to the application. 3 | 4 | The application uses the MVC template. Adhering to the principles of clean 5 | architecture means ensuring that your application is easy to test, maintain, 6 | and modernize. 7 | 8 | You can read more about this template at the links below: 9 | 10 | https://github.com/HeaTTheatR/LoginAppMVC 11 | https://en.wikipedia.org/wiki/Model–view–controller 12 | """ 13 | 14 | from typing import NoReturn 15 | 16 | from kivy.uix.screenmanager import ScreenManager%s 17 | 18 | from kivymd.app import MDApp 19 | 20 | from View.screens import screens%s 21 | %s 22 | 23 | class %s(MDApp):%s 24 | def __init__(self, **kwargs): 25 | super().__init__(**kwargs)%s 26 | self.load_all_kv_files(self.directory) 27 | # This is the screen manager that will contain all the screens of your 28 | # application. 29 | self.manager_screens = ScreenManager() 30 | %s 31 | def build(self) -> ScreenManager: 32 | """ 33 | Initializes the application; it will be called only once. 34 | If this method returns a widget (tree), it will be used as the root 35 | widget and added to the window. 36 | 37 | :return: 38 | None or a root :class:`~kivy.uix.widget.Widget` instance 39 | if no self.root exists. 40 | """ 41 | 42 | self.theme_cls.primary_palette = "Amber" 43 | self.generate_application_screens() 44 | return self.manager_screens 45 | 46 | def generate_application_screens(self) -> NoReturn: 47 | """ 48 | Creating and adding screens to the screen manager. 49 | You should not change this cycle unnecessarily. He is self-sufficient. 50 | 51 | If you need to add any screen, open the `View.screens.py` module and 52 | see how new screens are added according to the given application 53 | architecture. 54 | """ 55 | 56 | for i, name_screen in enumerate(screens.keys()): 57 | model = screens[name_screen]["model"](%s) 58 | controller = screens[name_screen]["controller"](model) 59 | view = controller.get_view() 60 | view.manager_screens = self.manager_screens 61 | view.name = name_screen 62 | self.manager_screens.add_widget(view) 63 | %s%s 64 | 65 | %s().run() 66 | -------------------------------------------------------------------------------- /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_icon_color:'Custom' 21 | icon_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 | self.size_landscape = ["320dp", "90dp"] 60 | 61 | def update_bg_color(self, *args): 62 | self.bg_color = MDApp.get_running_app().primary_accent 63 | 64 | def update_selector(self, *args): 65 | self.content_cls.clear_widgets() 66 | self.bg_color = MDApp.get_running_app().primary_accent 67 | 68 | def on_open(self): 69 | if not self.content_cls.children: 70 | for name_palette in MY_PALETTE: 71 | self.content_cls.add_widget( 72 | Factory.PrimaryColorSelector(color_name=name_palette) 73 | ) 74 | -------------------------------------------------------------------------------- /kivymd/uix/bottomsheet/bottomsheet.kv: -------------------------------------------------------------------------------- 1 | #:import Window kivy.core.window.Window 2 | 3 | 4 | 5 | 6 | MDGridLayout: 7 | id: box_sheet_list 8 | cols: 1 9 | adaptive_height: True 10 | padding: 0, 0, 0, "96dp" 11 | 12 | 13 | 14 | md_bg_color: root.value_transparent 15 | _upper_padding: _upper_padding 16 | _gl_content: _gl_content 17 | _position_content: Window.height 18 | 19 | MDBoxLayout: 20 | orientation: "vertical" 21 | padding: 0, 1, 0, 0 22 | 23 | BsPadding: 24 | id: _upper_padding 25 | size_hint_y: None 26 | height: root.height - min(root.width * 9 / 16, root._gl_content.height) 27 | on_release: root.dismiss() 28 | 29 | BottomSheetContent: 30 | id: _gl_content 31 | size_hint_y: None 32 | cols: 1 33 | md_bg_color: 0, 0, 0, 0 34 | 35 | canvas: 36 | Color: 37 | rgba: root.theme_cls.bg_normal if not root.bg_color else root.bg_color 38 | RoundedRectangle: 39 | pos: self.pos 40 | size: self.size 41 | radius: 42 | [ 43 | (root.radius, root.radius) if root.radius_from == "top_left" or root.radius_from == "top" else (0, 0), 44 | (root.radius, root.radius) if root.radius_from == "top_right" or root.radius_from == "top" else (0, 0), 45 | (root.radius, root.radius) if root.radius_from == "bottom_right" or root.radius_from == "bottom" else (0, 0), 46 | (root.radius, root.radius) if root.radius_from == "bottom_left" or root.radius_from == "bottom" else (0, 0) 47 | ] 48 | 49 | 50 | 51 | theme_text_color: "Primary" 52 | pos_hint: {"center_x": .5, "center_y": .5} 53 | 54 | 55 | 56 | orientation: "vertical" 57 | padding: 0, dp(24), 0, 0 58 | size_hint_y: None 59 | size: dp(64), dp(96) 60 | 61 | AnchorLayout: 62 | anchor_x: "center" 63 | 64 | MDIconButton: 65 | icon: root.source 66 | user_font_size: root.icon_size 67 | on_release: root.dispatch("on_release") 68 | 69 | MDLabel: 70 | font_style: "Caption" 71 | theme_text_color: "Secondary" 72 | text: root.caption 73 | halign: "center" 74 | -------------------------------------------------------------------------------- /kivymd/uix/dialog/dialog.kv: -------------------------------------------------------------------------------- 1 | #:import images_path kivymd.images_path 2 | 3 | 4 | 5 | background: '{}/transparent.png'.format(images_path) 6 | 7 | canvas.before: 8 | PushMatrix 9 | RoundedRectangle: 10 | pos: self.pos 11 | size: self.size 12 | radius: root.radius 13 | Scale: 14 | origin: self.center 15 | x: root._scale_x 16 | y: root._scale_y 17 | canvas.after: 18 | PopMatrix 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | DialogContainer: 27 | id: container 28 | orientation: "vertical" 29 | size_hint_y: None 30 | height: self.minimum_height 31 | elevation: 24 32 | padding: "24dp", "24dp", "8dp", "8dp" 33 | radius: root.radius 34 | md_bg_color: 35 | root.theme_cls.bg_dark \ 36 | if not root.md_bg_color else root.md_bg_color 37 | 38 | MDLabel: 39 | id: title 40 | text: root.title 41 | font_style: "H6" 42 | bold: True 43 | markup: True 44 | size_hint_y: None 45 | height: self.texture_size[1] 46 | valign: "top" 47 | 48 | BoxLayout: 49 | id: spacer_top_box 50 | size_hint_y: None 51 | height: root._spacer_top 52 | 53 | MDLabel: 54 | id: text 55 | text: root.text 56 | font_style: "Body1" 57 | theme_text_color: "Custom" 58 | text_color: root.theme_cls.disabled_hint_text_color 59 | size_hint_y: None 60 | height: self.texture_size[1] 61 | markup: True 62 | 63 | ScrollView: 64 | id: scroll 65 | size_hint_y: None 66 | height: root._scroll_height 67 | 68 | MDGridLayout: 69 | id: box_items 70 | adaptive_height: True 71 | cols: 1 72 | 73 | BoxLayout: 74 | id: spacer_bottom_box 75 | size_hint_y: None 76 | height: self.minimum_height 77 | 78 | AnchorLayout: 79 | id: root_button_box 80 | size_hint_y: None 81 | height: "52dp" 82 | anchor_x: "right" 83 | 84 | MDBoxLayout: 85 | id: button_box 86 | adaptive_size: True 87 | spacing: "8dp" 88 | -------------------------------------------------------------------------------- /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 kivy.lang.builder import Builder 10 | from libs.utils import remove_user_data 11 | from kivymd.app import MDApp 12 | from libs.modules.dialogs import AKAlertDialog 13 | 14 | app = MDApp.get_running_app() 15 | 16 | 17 | class SettingsScreen(MDScreen): 18 | content = None 19 | theme_picker = None 20 | YOUTUBE_VIDEO_LINK = "https://www.youtube.com/watch?v=EOkMDc5mZWI&list=PLUdItSprD91ybWz6uxs4zF4Gux_vzdbZh&index=1" 21 | GITHUB_REPO_LINK = "https://github.com/AM-ash-OR-AM-I/PasslockDesktop" 22 | Builder.load_file(f"./libs/screens/SettingsScreen/SettingsScreen.kv") 23 | 24 | def logout(self): 25 | app.root.load_screen("SignupScreen", empty_history=True) 26 | app.root.SignupScreen.on_enter = lambda *args: Clock.schedule_once( 27 | lambda x: app.animate_signup(app.root.SignupScreen.ids.box), 0 28 | ) 29 | remove_user_data() 30 | 31 | def change_colors(self): 32 | if self.theme_picker is None: 33 | self.theme_picker = MDThemePicker() 34 | self.theme_picker.open() 35 | 36 | def open_about(self): 37 | if self.content is None: 38 | self.content = Factory.AboutClass() 39 | self.about_dialog = AKAlertDialog(header_icon='heart-circle') 40 | self.about_dialog.header_height_portrait = "90dp" 41 | self.about_dialog.size_portrait = ['300dp', '340dp'] 42 | self.about_dialog.content_cls = self.content 43 | self.about_dialog.bg_color = app.primary_accent 44 | self.about_dialog.open() 45 | 46 | def open_web(self, github=False, youtube=False, email=False): 47 | if github: 48 | webbrowser.open(self.GITHUB_REPO_LINK) 49 | toast('Star my repository if you like it :)') 50 | elif youtube: 51 | webbrowser.open(self.YOUTUBE_VIDEO_LINK) 52 | elif email: 53 | webbrowser.open('https://mail.google.com/mail/u/0/#inbox?compose=new') 54 | Clipboard.copy('ashutoshmaha2909@gmail.com') 55 | toast('Email address Copied, Paste Email address to send email.') 56 | 57 | def open_youtube_demo(self): 58 | if self.YOUTUBE_VIDEO_LINK: 59 | webbrowser.open(self.YOUTUBE_VIDEO_LINK) 60 | -------------------------------------------------------------------------------- /kivymd/uix/selectioncontrol/selectioncontrol.kv: -------------------------------------------------------------------------------- 1 | 2 | canvas: 3 | Clear 4 | Color: 5 | rgba: self.color 6 | Rectangle: 7 | texture: self.texture 8 | size: self.texture_size 9 | pos: 10 | int(self.center_x - self.texture_size[0] / 2.), \ 11 | int(self.center_y - self.texture_size[1] / 2.) 12 | 13 | color: self._current_color 14 | halign: "center" 15 | valign: "middle" 16 | 17 | 18 | 19 | color: 1, 1, 1, 1 20 | canvas: 21 | Color: 22 | rgba: self.color 23 | Ellipse: 24 | size: self.size 25 | pos: self.pos 26 | 27 | 28 | 29 | canvas.before: 30 | Color: 31 | rgba: 32 | self._track_color_disabled if self.disabled else \ 33 | ( \ 34 | self._track_color_active \ 35 | if self.active else self._track_color_normal \ 36 | ) 37 | RoundedRectangle: 38 | size: 39 | (self.width + dp(14), dp(28)) \ 40 | if root.widget_style == "ios" else \ 41 | (self.width - dp(8), dp(16)) 42 | pos: 43 | (self.x - dp(2), self.center_y - dp(14)) \ 44 | if root.widget_style == "ios" else \ 45 | (self.x + dp(8), self.center_y - dp(8)) 46 | radius: 47 | [dp(14)] if root.widget_style == "ios" else [dp(7)] 48 | Color: 49 | rgba: 50 | ( \ 51 | self.theme_cls.disabled_hint_text_color[:-1] + [.2] \ 52 | if not root.active else (0, 0, 0, 0) \ 53 | ) \ 54 | if root.widget_style == "ios" else (0, 0, 0, 0) 55 | Line: 56 | width: 1 57 | rounded_rectangle: 58 | ( \ 59 | self.x - dp(2), self.center_y - dp(14), self.width + dp(14), \ 60 | dp(28), dp(14), dp(14), dp(14), dp(14), dp(28) \ 61 | ) \ 62 | if root.widget_style == "ios" else \ 63 | (1, 1, 1, 1, 1, 1, 1, 1, 1) 64 | 65 | Thumb: 66 | id: thumb 67 | size_hint: None, None 68 | size: dp(24), dp(24) 69 | pos: root.pos[0] + root._thumb_pos[0], root.pos[1] + root._thumb_pos[1] 70 | color: 71 | root.thumb_color_disabled if root.disabled else \ 72 | (root.thumb_color_down if root.active else root.thumb_color) 73 | elevation: 8 if root.active else 5 74 | on_release: setattr(root, "active", not root.active) 75 | -------------------------------------------------------------------------------- /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 | 41 | __all__ = ("toast",) 42 | 43 | from kivy import platform 44 | 45 | if platform != "android": 46 | raise TypeError( 47 | f"{platform.capitalize()} platform does not support Android Toast" 48 | ) 49 | 50 | from android.runnable import run_on_ui_thread 51 | from jnius import autoclass 52 | 53 | activity = autoclass("org.kivy.android.PythonActivity").mActivity 54 | Toast = autoclass("android.widget.Toast") 55 | String = autoclass("java.lang.String") 56 | 57 | 58 | @run_on_ui_thread 59 | def toast(text, length_long=False, gravity=0, y=0, x=0): 60 | """ 61 | Displays a toast. 62 | 63 | :param length_long: the amount of time (in seconds) that the toast is 64 | visible on the screen; 65 | :param text: text to be displayed in the toast; 66 | :param short_duration: duration of the toast, if `True` the toast 67 | will last 2.3s but if it is `False` the toast will last 3.9s; 68 | :param gravity: refers to the toast position, if it is 80 the toast will 69 | be shown below, if it is 40 the toast will be displayed above; 70 | :param y: refers to the vertical position of the toast; 71 | :param x: refers to the horizontal position of the toast; 72 | 73 | Important: if only the text value is specified and the value of 74 | the `gravity`, `y`, `x` parameters is not specified, their values will 75 | be 0 which means that the toast will be shown in the center. 76 | """ 77 | 78 | duration = Toast.LENGTH_SHORT if length_long else Toast.LENGTH_LONG 79 | t = Toast.makeText(activity, String(text), duration) 80 | t.setGravity(gravity, x, y) 81 | t.show() 82 | -------------------------------------------------------------------------------- /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__ = "1.0.0.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 | uix_path = os.path.join(path, "uix") 57 | """Path to uix directory.""" 58 | 59 | _log_message = ( 60 | "KivyMD:" 61 | + (" Release" if release else "") 62 | + f" {__version__}" 63 | + (f", git-{__short_hash__}" if __short_hash__ else "") 64 | + (f", {__date__}" if __date__ else "") 65 | + f' (installed at "{__file__}")' 66 | ) 67 | Logger.info(_log_message) 68 | 69 | import kivymd.factory_registers # NOQA 70 | import kivymd.font_definitions # NOQA 71 | from kivymd.tools.packaging.pyinstaller import hooks_path # NOQA 72 | -------------------------------------------------------------------------------- /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": 0.85, "center_y": 0.12}) 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 | 34 | def dismiss_spinner(*args): 35 | app.root.load_screen("HomeScreen") 36 | self.loading_view.dismiss() 37 | 38 | def initialise_encryption(): 39 | i = time() 40 | from libs.encryption import Encryption 41 | 42 | try: 43 | app.encryption_class = Encryption(password) 44 | if os.path.exists("./data/passwords"): 45 | app.passwords = app.encryption_class.load_decrypted() 46 | else: 47 | if os.path.exists("./data/encrypted_file.txt"): 48 | with open("./data/encrypted_file.txt", "r") as f: 49 | app.encryption_class.decrypt(f.read()) 50 | else: 51 | print("'encrypted_file' not found, can't check password.") 52 | 53 | dismiss_spinner() 54 | if not self.password: 55 | self.password = password 56 | # app.root.HomeScreen.ids.create.ids.tab.switch_tab("[b]MANUAL") 57 | except UnicodeDecodeError: 58 | self.loading_view.dismiss() 59 | toast("Invalid password") 60 | # print(app.encrypted_keys) 61 | print(f"Time taken to load passwords = {time()-i}") 62 | 63 | if not self.password: 64 | if self.loading_view is None: 65 | self.loading_view = Factory.LoadingScreen() 66 | self.loading_view.open() 67 | self.loading_view.on_open = lambda *args: initialise_encryption() 68 | else: 69 | if self.password == password: 70 | app.root.load_screen("HomeScreen") 71 | else: 72 | toast("Invalid password") 73 | -------------------------------------------------------------------------------- /kivymd/tools/release/git_commands.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2019-2021 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 | 12 | print(f"Command: {subprocess.list2cmdline(cmd)}") 13 | if capture_output: 14 | out = subprocess.check_output(cmd) 15 | out = out.decode("utf-8") 16 | print(out.strip()) 17 | return out 18 | else: 19 | subprocess.check_call(cmd) 20 | return "" 21 | 22 | 23 | def get_previous_version() -> str: 24 | """Returns latest tag in git.""" 25 | 26 | command(["git", "checkout", "master"]) 27 | old_version = command( 28 | ["git", "describe", "--abbrev=0", "--tags"], capture_output=True 29 | ) 30 | old_version = old_version[:-1] # Remove \n 31 | return old_version 32 | 33 | 34 | def git_clean(ask: bool = True): 35 | """Clean git repository from untracked and changed files.""" 36 | 37 | # Check what files will be removed 38 | files_to_clean = command( 39 | ["git", "clean", "-dx", "--force", "--dry-run"], capture_output=True 40 | ).strip() 41 | # Ask before removing 42 | if ask and files_to_clean: 43 | while True: 44 | ans = input("Do you want to remove these files? (yes/no)").lower() 45 | if ans == "y" or ans == "yes": 46 | break 47 | elif ans == "n" or ans == "no": 48 | print("git clean is required. Exit") 49 | exit(0) 50 | 51 | # Remove all untracked files 52 | command(["git", "clean", "-dx", "--force"]) 53 | command(["git", "reset", "--hard"]) 54 | 55 | 56 | def git_commit(message: str, allow_error: bool = False, add_files: list = None): 57 | """Make commit.""" 58 | 59 | add_files = add_files if add_files else ["-A"] 60 | command(["git", "add", *add_files]) 61 | try: 62 | command(["git", "commit", "--all", "-m", message]) 63 | except subprocess.CalledProcessError as e: 64 | if not allow_error: 65 | raise e 66 | 67 | 68 | def git_tag(name: str): 69 | """Create tag.""" 70 | 71 | command(["git", "tag", name]) 72 | 73 | 74 | def git_push(branches_to_push: list, ask: bool = True, push: bool = False): 75 | """Push all changes.""" 76 | 77 | if ask: 78 | push = input("Do you want to push changes? (y)") in ( 79 | "", 80 | "y", 81 | "yes", 82 | ) 83 | 84 | cmd = ["git", "push", "--tags", "origin", "master", *branches_to_push] 85 | if push: 86 | command(cmd) 87 | else: 88 | print( 89 | f"Changes are not pushed. Command for manual pushing: {subprocess.list2cmdline(cmd)}" 90 | ) 91 | 92 | 93 | if __name__ == "__main__": 94 | git_clean(ask=True) 95 | -------------------------------------------------------------------------------- /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 | if touch.is_double_tap: 82 | self.on_double_tap(touch, *args) 83 | if touch.is_triple_tap: 84 | self.on_triple_tap(touch, *args) 85 | 86 | def delete_clock(self, widget, touch, *args): 87 | if self.collide_point(touch.x, touch.y): 88 | try: 89 | Clock.unschedule(touch.ud["event"]) 90 | except KeyError: 91 | pass 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/dropdownitem/dropdownitem.py: -------------------------------------------------------------------------------- 1 | """ 2 | Components/DropdownItem 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 | import os 47 | 48 | from kivy.lang import Builder 49 | from kivy.properties import NumericProperty, StringProperty 50 | from kivy.uix.behaviors import ButtonBehavior 51 | from kivy.uix.widget import Widget 52 | 53 | from kivymd import uix_path 54 | from kivymd.theming import ThemableBehavior 55 | from kivymd.uix.behaviors import FakeRectangularElevationBehavior 56 | from kivymd.uix.boxlayout import MDBoxLayout 57 | 58 | with open( 59 | os.path.join(uix_path, "dropdownitem", "dropdownitem.kv"), encoding="utf-8" 60 | ) as kv_file: 61 | Builder.load_string(kv_file.read()) 62 | 63 | 64 | class _Triangle(ThemableBehavior, Widget): 65 | pass 66 | 67 | 68 | class MDDropDownItem( 69 | ThemableBehavior, 70 | FakeRectangularElevationBehavior, 71 | ButtonBehavior, 72 | MDBoxLayout, 73 | ): 74 | text = StringProperty() 75 | """ 76 | Text item. 77 | 78 | :attr:`text` is a :class:`~kivy.properties.StringProperty` 79 | and defaults to `''`. 80 | """ 81 | 82 | current_item = StringProperty() 83 | """ 84 | Current name item. 85 | 86 | :attr:`current_item` is a :class:`~kivy.properties.StringProperty` 87 | and defaults to `''`. 88 | """ 89 | 90 | font_size = NumericProperty("16sp") 91 | """ 92 | Item font size. 93 | 94 | :attr:`font_size` is a :class:`~kivy.properties.NumericProperty` 95 | and defaults to `'16sp'`. 96 | """ 97 | 98 | def on_text(self, instance_drop_down_item, text_item: str) -> None: 99 | self.ids.label_item.text = text_item 100 | 101 | def set_item(self, name_item: str) -> None: 102 | """Sets new text for an item.""" 103 | 104 | self.ids.label_item.text = name_item 105 | self.current_item = name_item 106 | -------------------------------------------------------------------------------- /kivymd/uix/templates/stencilwidget/stencilwidget.py: -------------------------------------------------------------------------------- 1 | """ 2 | Templates/StencilWidget 3 | ======================= 4 | 5 | .. versionadded:: 1.0.0 6 | 7 | Base class for controlling the stencil instructions of the widget. 8 | 9 | .. note:: See `Stencil instructions 10 | `_ 11 | for more information. 12 | 13 | Kivy 14 | ---- 15 | 16 | .. code-block:: python 17 | 18 | from kivy.lang import Builder 19 | from kivy.app import App 20 | 21 | KV = ''' 22 | Carousel: 23 | 24 | Button: 25 | size_hint: .9, .8 26 | pos_hint: {"center_x": .5, "center_y": .5} 27 | 28 | canvas.before: 29 | StencilPush 30 | RoundedRectangle: 31 | pos: root.pos 32 | size: root.size 33 | StencilUse 34 | canvas.after: 35 | StencilUnUse 36 | RoundedRectangle: 37 | pos: root.pos 38 | size: root.size 39 | StencilPop 40 | ''' 41 | 42 | 43 | class Test(App): 44 | def build(self): 45 | return Builder.load_string(KV) 46 | 47 | 48 | Test().run() 49 | 50 | KivyMD 51 | ------ 52 | 53 | .. code-block:: python 54 | 55 | from kivy.lang import Builder 56 | 57 | from kivymd.app import MDApp 58 | from kivymd.uix.templates import StencilWidget 59 | from kivymd.utils.fitimage import FitImage 60 | 61 | KV = ''' 62 | MDCarousel: 63 | 64 | StencilImage: 65 | size_hint: .9, .8 66 | pos_hint: {"center_x": .5, "center_y": .5} 67 | source: "image.png" 68 | ''' 69 | 70 | 71 | class StencilImage(FitImage, StencilWidget): 72 | pass 73 | 74 | 75 | class Test(MDApp): 76 | def build(self): 77 | return Builder.load_string(KV) 78 | 79 | 80 | Test().run() 81 | """ 82 | 83 | __all__ = ("StencilWidget",) 84 | 85 | import os 86 | 87 | from kivy.lang import Builder 88 | from kivy.properties import VariableListProperty 89 | 90 | from kivymd import uix_path 91 | 92 | with open( 93 | os.path.join(uix_path, "templates", "stencilwidget", "stencilwidget.kv"), 94 | encoding="utf-8", 95 | ) as kv_file: 96 | Builder.load_string(kv_file.read()) 97 | 98 | 99 | class StencilWidget: 100 | """Base class for controlling the stencil instructions of the widget""" 101 | 102 | radius = VariableListProperty([0], length=4) 103 | """ 104 | Canvas radius. 105 | 106 | .. versionadded:: 1.0.0 107 | 108 | .. code-block:: python 109 | 110 | # Top left corner slice. 111 | MDWidget: 112 | radius: [25, 0, 0, 0] 113 | 114 | :attr:`radius` is an :class:`~kivy.properties.VariableListProperty` 115 | and defaults to `[0, 0, 0, 0]`. 116 | """ 117 | -------------------------------------------------------------------------------- /kivymd/uix/__init__.py: -------------------------------------------------------------------------------- 1 | __all__ = ("MDAdaptiveWidget",) 2 | 3 | from kivy.properties import BooleanProperty 4 | from kivy.uix.floatlayout import FloatLayout 5 | from kivy.uix.label import Label 6 | from kivy.uix.screenmanager import Screen 7 | 8 | from kivymd.uix.behaviors import SpecificBackgroundColorBehavior 9 | 10 | 11 | class MDAdaptiveWidget(SpecificBackgroundColorBehavior): 12 | adaptive_height = BooleanProperty(False) 13 | """ 14 | If `True`, the following properties will be applied to the widget: 15 | 16 | .. code-block:: kv 17 | 18 | size_hint_y: None 19 | height: self.minimum_height 20 | 21 | :attr:`adaptive_height` is an :class:`~kivy.properties.BooleanProperty` 22 | and defaults to `False`. 23 | """ 24 | 25 | adaptive_width = BooleanProperty(False) 26 | """ 27 | If `True`, the following properties will be applied to the widget: 28 | 29 | .. code-block:: kv 30 | 31 | size_hint_x: None 32 | width: self.minimum_width 33 | 34 | :attr:`adaptive_width` is an :class:`~kivy.properties.BooleanProperty` 35 | and defaults to `False`. 36 | """ 37 | 38 | adaptive_size = BooleanProperty(False) 39 | """ 40 | If `True`, the following properties will be applied to the widget: 41 | 42 | .. code-block:: kv 43 | 44 | size_hint: None, None 45 | size: self.minimum_size 46 | 47 | :attr:`adaptive_size` is an :class:`~kivy.properties.BooleanProperty` 48 | and defaults to `False`. 49 | """ 50 | 51 | def on_adaptive_height(self, md_widget, value: bool) -> None: 52 | self.size_hint_y = None 53 | if issubclass(self.__class__, Label): 54 | self.bind( 55 | texture_size=lambda *x: self.setter("height")( 56 | self, self.texture_size[1] 57 | ) 58 | ) 59 | else: 60 | if not isinstance(self, (FloatLayout, Screen)): 61 | self.bind(minimum_height=self.setter("height")) 62 | 63 | def on_adaptive_width(self, md_widget, value: bool) -> None: 64 | self.size_hint_x = None 65 | if issubclass(self.__class__, Label): 66 | self.bind( 67 | texture_size=lambda *x: self.setter("width")( 68 | self, self.texture_size[0] 69 | ) 70 | ) 71 | else: 72 | if not isinstance(self, (FloatLayout, Screen)): 73 | self.bind(minimum_width=self.setter("width")) 74 | 75 | def on_adaptive_size(self, md_widget, value: bool) -> None: 76 | self.size_hint = (None, None) 77 | if issubclass(self.__class__, Label): 78 | self.text_size = (None, None) 79 | self.bind( 80 | texture_size=lambda *x: self.setter("size")( 81 | self, self.texture_size 82 | ) 83 | ) 84 | else: 85 | if not isinstance(self, (FloatLayout, Screen)): 86 | self.bind(minimum_size=self.setter("size")) 87 | -------------------------------------------------------------------------------- /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/uix/transition/transition.py: -------------------------------------------------------------------------------- 1 | """ 2 | Components/Transition 3 | ===================== 4 | 5 | .. rubric:: 6 | A set of classes for implementing transitions between application screens. 7 | 8 | .. versionadded:: 1.0.0 9 | 10 | Changing transitions 11 | -------------------- 12 | 13 | You have multiple transitions available by default, such as: 14 | 15 | - :class:`MDFadeSlideTransition` 16 | state one: the new screen closes the previous screen by lifting from the 17 | bottom of the screen and changing from transparent to non-transparent; 18 | 19 | state two: the current screen goes down to the bottom of the screen, 20 | passing from a non-transparent state to a transparent one, thus opening the 21 | previous screen; 22 | 23 | .. note:: 24 | You cannot control the direction of a slide using the direction attribute. 25 | 26 | .. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/transition-md-fade-slide-transition.gif 27 | :align: center 28 | 29 | """ 30 | 31 | __all__ = ("MDFadeSlideTransition",) 32 | 33 | from kivy.animation import Animation, AnimationTransition 34 | from kivy.uix.screenmanager import ( 35 | ScreenManager, 36 | ScreenManagerException, 37 | SlideTransition, 38 | ) 39 | 40 | 41 | class MDFadeSlideTransition(SlideTransition): 42 | _direction = "up" 43 | 44 | def start(self, instance_screen_manager: ScreenManager) -> None: 45 | """ 46 | Starts the transition. This is automatically called by the 47 | :class:`ScreenManager`. 48 | """ 49 | 50 | if self.is_active: 51 | raise ScreenManagerException("start() is called twice!") 52 | 53 | self.manager = instance_screen_manager 54 | self._anim = Animation(d=self.duration, s=0) 55 | self._anim.bind( 56 | on_progress=self._on_progress, on_complete=self._on_complete 57 | ) 58 | if self._direction == "up": 59 | self.add_screen(self.screen_in) 60 | else: 61 | self.add_screen(self.screen_in) 62 | self.add_screen(self.screen_out) 63 | self.screen_in.transition_progress = 0.0 64 | self.screen_in.transition_state = "in" 65 | self.screen_out.transition_progress = 0.0 66 | self.screen_out.transition_state = "out" 67 | self.screen_in.dispatch("on_pre_enter") 68 | self.screen_out.dispatch("on_pre_leave") 69 | 70 | self.is_active = True 71 | self._anim.start(self) 72 | self.dispatch("on_progress", 0) 73 | if self._direction == "up": 74 | self.screen_in.y = 0 75 | self.screen_in.opacity = 0 76 | 77 | def on_progress(self, progression: float) -> None: 78 | progression = AnimationTransition.out_quad(progression) 79 | 80 | if self._direction == "up": 81 | self.screen_in.y = ( 82 | self.manager.y + self.manager.height * progression 83 | ) - self.screen_in.height 84 | self.screen_in.opacity = progression 85 | if self._direction == "down": 86 | self.screen_out.y = ( 87 | self.manager.y - self.manager.height * progression 88 | ) 89 | self.screen_out.opacity = 1 - progression 90 | 91 | def on_complete(self) -> None: 92 | if self._direction == "down": 93 | self._direction = "up" 94 | else: 95 | self._direction = "down" 96 | super().on_complete() 97 | -------------------------------------------------------------------------------- /kivymd/uix/filemanager/filemanager.kv: -------------------------------------------------------------------------------- 1 | #:import os os 2 | 3 | 4 | icon: "folder" 5 | path: "" 6 | background_normal: "" 7 | background_down: "" 8 | dir_or_file_name: "" 9 | _selected: False 10 | events_callback: lambda x: None 11 | orientation: "vertical" 12 | 13 | ModifiedOneLineIconListItem: 14 | text: root.dir_or_file_name 15 | bg_color: 16 | self.theme_cls.bg_darkest \ 17 | if root._selected else self.theme_cls.bg_normal 18 | on_release: root.events_callback(root.path, root) 19 | 20 | IconLeftWidget: 21 | icon: root.icon 22 | theme_text_color: "Custom" 23 | text_color: self.theme_cls.primary_color 24 | 25 | MDSeparator: 26 | 27 | 28 | 29 | size_hint_y: None 30 | height: self.texture_size[1] 31 | shorten: True 32 | shorten_from: "center" 33 | halign: "center" 34 | text_size: self.width, None 35 | 36 | 37 | 38 | name: "" 39 | path: "" 40 | realpath: "" 41 | type: "folder" 42 | events_callback: lambda x: None 43 | _selected: False 44 | orientation: "vertical" 45 | size_hint_y: None 46 | hright: root.height 47 | padding: dp(20) 48 | 49 | IconButton: 50 | mipmap: True 51 | source: root.path 52 | bg_color: 53 | app.theme_cls.bg_darkest \ 54 | if root._selected else app.theme_cls.bg_normal 55 | on_release: 56 | root.events_callback( \ 57 | os.path.join(root.path if root.type != "folder" \ 58 | else root.realpath, root.name), root) 59 | 60 | LabelContent: 61 | text: root.name 62 | 63 | 64 | 65 | anchor_x: "right" 66 | anchor_y: "bottom" 67 | size_hint_y: None 68 | height: dp(56) 69 | padding: dp(10) 70 | 71 | MDFloatingActionButton: 72 | size_hint: None, None 73 | size:dp(56), dp(56) 74 | icon: root.icon 75 | opposite_colors: True 76 | elevation: 8 77 | on_release: root.callback() 78 | md_bg_color: root.md_bg_color 79 | 80 | 81 | 82 | md_bg_color: root.theme_cls.bg_normal 83 | 84 | MDBoxLayout: 85 | orientation: "vertical" 86 | spacing: dp(5) 87 | 88 | MDToolbar: 89 | id: toolbar 90 | title: root.current_path 91 | right_action_items: [["close-box", lambda x: root.exit_manager(1)]] 92 | left_action_items: [["chevron-left", lambda x: root.back()]] 93 | elevation: 10 94 | 95 | RecycleView: 96 | id: rv 97 | key_viewclass: "viewclass" 98 | key_size: "height" 99 | bar_width: dp(4) 100 | bar_color: root.theme_cls.primary_color 101 | 102 | RecycleGridLayout: 103 | padding: "10dp" 104 | spacing: "2dp" 105 | cols: 3 if root.preview else 1 106 | default_size: None, dp(48) 107 | default_size_hint: 1, None 108 | size_hint_y: None 109 | height: self.minimum_height 110 | 111 | 112 | 113 | 114 | BoxLayout: 115 | id: _left_container 116 | size_hint: None, None 117 | x: root.x + dp(16) 118 | y: root.y + root.height / 2 - self.height / 2 119 | size: dp(48), dp(48) 120 | -------------------------------------------------------------------------------- /kivymd/tools/argument_parser.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2019-2021 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 | -------------------------------------------------------------------------------- /libs/utils.py: -------------------------------------------------------------------------------- 1 | import json 2 | import os.path 3 | import pickle 4 | import random 5 | import string 6 | from kivy.utils import platform 7 | 8 | if not os.path.exists(f"./data"): 9 | os.mkdir(f"./data") 10 | 11 | 12 | def auto_password(length: int, letters: bool = True, digits: bool = True, special_chars: bool = True) -> str: 13 | universe = "" 14 | password = "" 15 | if letters: 16 | universe += string.ascii_letters 17 | password += random.choice(string.ascii_letters) 18 | if digits: 19 | universe += string.digits 20 | password += random.choice(string.digits) 21 | if special_chars: 22 | universe += string.punctuation 23 | password += random.choice(string.punctuation) 24 | 25 | rest = length - len(password) 26 | if universe == "": # if all options are false 27 | return "Error: No options selected" 28 | for _ in range(rest): 29 | password += random.choice(universe) 30 | 31 | password = list(password) 32 | random.shuffle(password) 33 | password = "".join(password) 34 | return password 35 | 36 | 37 | def load_passwords() -> dict: 38 | if os.path.exists("./data/passwords"): 39 | with open("./data/passwords", "rb") as f: 40 | encrypted_pass = pickle.load(f) 41 | return encrypted_pass 42 | else: 43 | return {} 44 | 45 | 46 | def get_uid() -> str: 47 | with open("./data/user_id.txt", "r") as f: 48 | uid = f.read() 49 | return uid 50 | 51 | 52 | def write_passwords(dictionary: dict) -> None: 53 | with open("./data/passwords", "wb") as f: 54 | pickle.dump(dictionary, f) 55 | 56 | 57 | def remove_user_data() -> None: 58 | if os.path.exists("./data/user_id.txt"): 59 | os.remove("./data/user_id.txt") 60 | if os.path.exists("./data/passwords"): 61 | os.remove("./data/passwords") 62 | if os.path.exists("./data/encrypted_file.txt"): 63 | os.remove("./data/encrypted_file.txt") 64 | 65 | 66 | def __get_config() -> dict: 67 | if os.path.exists("./data/config.json"): 68 | with open("./data/config.json", "r") as f: 69 | config = json.load(f) 70 | return config 71 | else: 72 | return {} 73 | 74 | 75 | def get_email() -> str: 76 | if os.path.exists("./data/email.txt"): 77 | with open("./data/email.txt", "r") as f: 78 | email = f.read() 79 | return email 80 | else: 81 | return "DemoMail" 82 | 83 | 84 | def get_primary_palette() -> str: 85 | if os.path.exists("./data/config.json"): 86 | with open("./data/config.json", "r") as f: 87 | config = json.load(f) 88 | return config.get("primary_palette", "DeepOrange") 89 | else: 90 | return "DeepOrange" 91 | 92 | 93 | def is_dark_mode(system=False) -> bool: 94 | json_data = __get_config() 95 | return json_data.get("system_dark_mode" if system else "dark_mode", False) 96 | 97 | 98 | def is_backup_failure() -> bool: 99 | json_data = __get_config() 100 | return json_data.get("backup_failure", False) 101 | 102 | 103 | def is_extra_security() -> bool: 104 | json_data = __get_config() 105 | return json_data.get("extra_security", False) 106 | 107 | 108 | def check_auto_sync() -> bool: 109 | json_data = __get_config() 110 | return json_data.get("auto_sync", True) 111 | 112 | 113 | def get_scaling() -> str: 114 | json_data = __get_config() 115 | return str(json_data.get("ui_scaling", 1.5 if platform != "macosx" else 2)) 116 | -------------------------------------------------------------------------------- /kivymd/uix/templates/rotatewidget/rotatewidget.py: -------------------------------------------------------------------------------- 1 | """ 2 | Templates/RotateWidget 3 | ====================== 4 | 5 | .. versionadded:: 1.0.0 6 | 7 | Base class for controlling the rotate of the widget. 8 | 9 | .. note:: See `kivy.graphics.Rotate 10 | `_ 11 | for more information. 12 | 13 | Kivy 14 | ---- 15 | 16 | .. code-block:: python 17 | 18 | from kivy.animation import Animation 19 | from kivy.lang import Builder 20 | from kivy.app import App 21 | from kivy.properties import NumericProperty 22 | from kivy.uix.button import Button 23 | 24 | KV = ''' 25 | Screen: 26 | 27 | RotateButton: 28 | size_hint: .5, .5 29 | pos_hint: {"center_x": .5, "center_y": .5} 30 | on_release: app.change_rotate(self) 31 | 32 | canvas.before: 33 | PushMatrix 34 | Rotate: 35 | angle: self.rotate_value_angle 36 | axis: 0, 0, 1 37 | origin: self.center 38 | canvas.after: 39 | PopMatrix 40 | ''' 41 | 42 | 43 | class RotateButton(Button): 44 | rotate_value_angle = NumericProperty(0) 45 | 46 | 47 | class Test(App): 48 | def build(self): 49 | return Builder.load_string(KV) 50 | 51 | def change_rotate(self, instance_button: Button) -> None: 52 | Animation(rotate_value_angle=45, d=0.3).start(instance_button) 53 | 54 | 55 | Test().run() 56 | 57 | KivyMD 58 | ------ 59 | 60 | .. code-block:: python 61 | 62 | from kivy.animation import Animation 63 | from kivy.lang import Builder 64 | 65 | from kivymd.app import MDApp 66 | from kivymd.uix.button import MDRaisedButton 67 | from kivymd.uix.templates import RotateWidget 68 | 69 | KV = ''' 70 | MDScreen: 71 | 72 | RotateButton: 73 | size_hint: .5, .5 74 | pos_hint: {"center_x": .5, "center_y": .5} 75 | on_release: app.change_rotate(self) 76 | elevation:0 77 | ''' 78 | 79 | 80 | class RotateButton(MDRaisedButton, RotateWidget): 81 | pass 82 | 83 | 84 | class Test(MDApp): 85 | def build(self): 86 | return Builder.load_string(KV) 87 | 88 | def change_rotate(self, instance_button: MDRaisedButton) -> None: 89 | Animation(rotate_value_angle=45, d=0.3).start(instance_button) 90 | 91 | 92 | Test().run() 93 | """ 94 | 95 | __all__ = ("RotateWidget",) 96 | 97 | import os 98 | 99 | from kivy.lang import Builder 100 | from kivy.properties import ListProperty, NumericProperty 101 | 102 | from kivymd import uix_path 103 | 104 | with open( 105 | os.path.join(uix_path, "templates", "rotatewidget", "rotatewidget.kv"), 106 | encoding="utf-8", 107 | ) as kv_file: 108 | Builder.load_string(kv_file.read()) 109 | 110 | 111 | class RotateWidget: 112 | """Base class for controlling the rotate of the widget.""" 113 | 114 | rotate_value_angle = NumericProperty(0) 115 | """ 116 | Property for getting/setting the angle of the rotation. 117 | 118 | :attr:`rotate_value_angle` is an :class:`~kivy.properties.NumericProperty` 119 | and defaults to `0`. 120 | """ 121 | 122 | rotate_value_axis = ListProperty((0, 0, 1)) 123 | """ 124 | Property for getting/setting the axis of the rotation. 125 | 126 | :attr:`rotate_value_axis` is an :class:`~kivy.properties.NumericProperty` 127 | and defaults to `(0, 0, 1)`. 128 | """ 129 | -------------------------------------------------------------------------------- /libs/screens/SignupScreen/SignupScreen.kv: -------------------------------------------------------------------------------- 1 | #: import Clock kivy.clock.Clock 2 | #: import colors kivymd.color_definitions.colors 3 | #: import Window kivy.core.window.Window 4 | #: set width Window.width 5 | #: set rad width/3.4 6 | #: set lbl_size '15sp' 7 | 8 | #: set primary_color_hex colors[app.theme_cls.primary_palette][app.theme_cls.primary_hue] 9 | 10 | : 11 | md_bg_color: app.bg_color 12 | canvas: 13 | Color: 14 | rgba:app.theme_cls.primary_color[:-1]+[.9] 15 | Ellipse: 16 | size:rad,rad 17 | pos:0-rad/2,Window.height-rad+20 18 | angle_start:0 19 | angle_end:180 20 | Color: 21 | rgba:app.login_circle_light[:-1]+[.7] 22 | Ellipse: 23 | pos:0,Window.height-rad/2 24 | size:rad,rad 25 | angle_start:90 26 | angle_end:270 27 | 28 | MDBoxLayout: 29 | adaptive_height:True 30 | id: box 31 | opacity:1 32 | pos_hint:{"top":0.97} 33 | orientation:'vertical' 34 | spacing:'50dp' 35 | MDLabel: 36 | adaptive_height:True 37 | text:f"WELCOME\nTO [color={primary_color_hex}][u]PASS••[/u]" 38 | markup:True 39 | theme_text_color:"Custom" 40 | text_color:app.text_color 41 | font_name:'Poppins' 42 | halign:'center' 43 | font_size:'36dp' 44 | pos_hint:{'top':1} 45 | MDBoxLayout: 46 | adaptive_height:True 47 | spacing:'50dp' 48 | orientation:'vertical' 49 | padding:(0,dp(15),0,0) 50 | BorderCard: 51 | id: email 52 | label_name:'Email Address' 53 | hint_text:"e.g. username@mail.com" 54 | size_hint_x:.7 55 | pos_hint:{'center_x':.5} 56 | focus: True 57 | on_text_validate: 58 | password.focus = True 59 | MDBoxLayout: 60 | adaptive_height:True 61 | orientation:'vertical' 62 | spacing:'35dp' 63 | PasswordCard: 64 | id: password 65 | label_name:'Password' 66 | size_hint_x:.7 67 | hint_text:"Enter Password" 68 | pos_hint:{'center_x':.5} 69 | on_text_validate: 70 | root.button_pressed(email.text, password.text, signup=True) 71 | MDBoxLayout: 72 | adaptive_height:True 73 | spacing:"20dp" 74 | size_hint_x:.7 75 | pos_hint:{'center_x':.5} 76 | RoundButton: 77 | id: signup 78 | text:'SIGN UP' 79 | size_hint_x:1 80 | on_release: 81 | root.button_pressed(email.text, password.text, signup=True) 82 | pos_hint:{'center_x':.5} 83 | RoundButton: 84 | id: login 85 | text:'LOGIN' 86 | size_hint_x:1 87 | md_bg_color:app.primary_accent 88 | on_release: 89 | root.button_pressed(email.text, password.text, signup=False) 90 | pos_hint:{'center_x':.5} 91 | 92 | 93 | # MDSeparator: 94 | # id: separator 95 | # opacity: 1 96 | # size_hint_x:.8 97 | # y:lock.y - dp(55) 98 | # pos_hint:{"center_x":.5} 99 | 100 | # MDTextButton: 101 | # id: switch_signin 102 | # opacity: 1 103 | # text:f'New user? [color={primary_color_hex}]Signup!'if not root.show_signup else f"Existing user? [color={primary_color_hex}]Login." 104 | # markup:True 105 | # top:separator.y - dp(20) 106 | # pos_hint:{'center_x':.5} 107 | # font_name:'RobotoMedium' 108 | # theme_text_color:"Custom" 109 | # text_color:app.text_color 110 | # halign:'center' 111 | # size_hint_x:1 112 | # on_release: root.show_signup = not root.show_signup 113 | # font_size:lbl_size 114 | 115 | 116 | -------------------------------------------------------------------------------- /kivymd/uix/chip/chip.kv: -------------------------------------------------------------------------------- 1 | 2 | scale_value_x: 0 3 | scale_value_y: 0 4 | scale_value_z: 0 5 | 6 | 7 | 8 | size_hint_y: None 9 | height: "32dp" 10 | spacing: "8dp" 11 | adaptive_width: True 12 | radius: 16 if self.radius == [0, 0, 0, 0] else self.radius 13 | padding: 14 | "12dp" if not self.icon_left else "4dp", \ 15 | 0, \ 16 | "12dp" if not self.icon_right else "8dp", \ 17 | 0 18 | md_bg_color: 19 | ( \ 20 | app.theme_cls.bg_darkest \ 21 | if app.theme_cls.theme_style == "Light" else \ 22 | app.theme_cls.bg_light \ 23 | ) \ 24 | if not self.disabled else app.theme_cls.disabled_hint_text_color 25 | 26 | canvas.before: 27 | Color: 28 | rgba: 29 | self.line_color \ 30 | if not self.disabled else \ 31 | app.theme_cls.disabled_hint_text_color 32 | Line: 33 | width: 1 34 | rounded_rectangle: 35 | ( \ 36 | self.x, \ 37 | self.y, \ 38 | self.width, \ 39 | self.height, \ 40 | *self.radius, \ 41 | self.height \ 42 | ) 43 | 44 | MDRelativeLayout: 45 | id: relative_box 46 | size_hint: None, None 47 | size: ("24dp", "24dp") if root.icon_left else (0, 0) 48 | pos_hint: {"center_y": .5} 49 | radius: self.height / 2 50 | 51 | MDIcon: 52 | id: icon_left 53 | icon: root.icon_left 54 | size_hint: None, None 55 | size: ("28dp", "28dp") if root.icon_left else (0, 0) 56 | theme_text_color: "Custom" 57 | pos_hint: {"center_y": .5} 58 | pos: 0, -2 59 | text_color: 60 | ( \ 61 | root.icon_left_color \ 62 | if root.icon_left_color else \ 63 | root.theme_cls.disabled_hint_text_color \ 64 | ) \ 65 | if not self.disabled else app.theme_cls.disabled_hint_text_color 66 | 67 | MDBoxLayout: 68 | id: icon_left_box 69 | size_hint: None, None 70 | radius: self.height / 2 71 | size: ("28dp", "28dp") if root.icon_left else (0, 0) 72 | pos: 0, -2 73 | 74 | MDScalableCheckIcon: 75 | id: check_icon 76 | icon: "check" 77 | size_hint: None, None 78 | size: "28dp", "28dp" 79 | color: (1, 1, 1, 1) if not root.icon_check_color else root.icon_check_color 80 | pos: 2, -2 81 | 82 | MDLabel: 83 | id: label 84 | text: root.text 85 | adaptive_size: True 86 | markup: True 87 | pos_hint: {"center_y": .5} 88 | color: 89 | ( \ 90 | root.text_color \ 91 | if root.text_color else \ 92 | root.theme_cls.disabled_hint_text_color \ 93 | ) \ 94 | if not self.disabled else app.theme_cls.disabled_hint_text_color 95 | 96 | MDIcon: 97 | id: icon_right 98 | icon: root.icon_right 99 | size_hint: None, None 100 | size: ("18dp", "18dp") if root.icon_right else (0, 0) 101 | font_size: "18sp" if root.icon_right else 0 102 | theme_text_color: "Custom" 103 | pos_hint: {"center_y": .5} 104 | text_color: 105 | ( \ 106 | root.icon_right_color \ 107 | if root.icon_right_color else \ 108 | root.theme_cls.disabled_hint_text_color \ 109 | ) \ 110 | if not self.disabled else app.theme_cls.disabled_hint_text_color 111 | -------------------------------------------------------------------------------- /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`). You can turn on the monitor displaying 8 | the current ``FPS`` value in your application: 9 | 10 | .. code-block:: python 11 | 12 | KV = ''' 13 | MDScreen: 14 | 15 | MDLabel: 16 | text: "Hello, World!" 17 | halign: "center" 18 | ''' 19 | 20 | from kivy.lang import Builder 21 | 22 | from kivymd.app import MDApp 23 | 24 | 25 | class MainApp(MDApp): 26 | def build(self): 27 | return Builder.load_string(KV) 28 | 29 | def on_start(self): 30 | self.fps_monitor_start() 31 | 32 | 33 | MainApp().run() 34 | 35 | .. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/fps-monitor.png 36 | :width: 350 px 37 | :align: center 38 | 39 | """ 40 | 41 | __all__ = ("MDApp",) 42 | 43 | import os 44 | 45 | from kivy.app import App 46 | from kivy.lang import Builder 47 | from kivy.properties import ObjectProperty 48 | 49 | from kivymd.theming import ThemeManager 50 | 51 | 52 | class FpsMonitoring: 53 | """Implements a monitor to display the current FPS in the toolbar.""" 54 | 55 | def fps_monitor_start(self) -> None: 56 | """Adds a monitor to the main application window.""" 57 | 58 | from kivy.core.window import Window 59 | 60 | from kivymd.utils.fpsmonitor import FpsMonitor 61 | 62 | monitor = FpsMonitor() 63 | monitor.start() 64 | Window.add_widget(monitor) 65 | 66 | 67 | class MDApp(App, FpsMonitoring): 68 | """ 69 | Application class, see :class:`~kivy.app.App` class documentation for more 70 | information. 71 | """ 72 | 73 | theme_cls = ObjectProperty() 74 | """ 75 | Instance of :class:`~ThemeManager` class. 76 | 77 | .. Warning:: The :attr:`~theme_cls` attribute is already available 78 | in a class that is inherited from the :class:`~MDApp` class. 79 | The following code will result in an error! 80 | 81 | .. code-block:: python 82 | 83 | class MainApp(MDApp): 84 | theme_cls = ThemeManager() 85 | theme_cls.primary_palette = "Teal" 86 | 87 | .. Note:: Correctly do as shown below! 88 | 89 | .. code-block:: python 90 | 91 | class MainApp(MDApp): 92 | def build(self): 93 | self.theme_cls.primary_palette = "Teal" 94 | 95 | :attr:`theme_cls` is an :class:`~kivy.properties.ObjectProperty`. 96 | """ 97 | 98 | def __init__(self, **kwargs): 99 | super().__init__(**kwargs) 100 | self.theme_cls = ThemeManager() 101 | 102 | def load_all_kv_files(self, path_to_directory: str) -> None: 103 | """ 104 | Recursively loads KV files from the selected directory. 105 | 106 | .. versionadded:: 1.0.0 107 | """ 108 | 109 | for path_to_dir, dirs, files in os.walk(path_to_directory): 110 | if ( 111 | "venv" in path_to_dir 112 | or ".buildozer" in path_to_dir 113 | or "kivymd/tools/patterns/MVC" in path_to_dir 114 | ): 115 | continue 116 | for name_file in files: 117 | if ( 118 | os.path.splitext(name_file)[1] == ".kv" 119 | and name_file != "style.kv" # if use PyInstaller 120 | and "__MACOS" not in path_to_dir # if use Mac OS 121 | ): 122 | path_to_kv_file = os.path.join(path_to_dir, name_file) 123 | Builder.load_file(path_to_kv_file) 124 | -------------------------------------------------------------------------------- /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/templates/scalewidget/scalewidget.py: -------------------------------------------------------------------------------- 1 | """ 2 | Templates/ScaleWidget 3 | ===================== 4 | 5 | .. versionadded:: 1.0.0 6 | 7 | Base class for controlling the scale of the widget. 8 | 9 | .. note:: See `kivy.graphics.Scale 10 | `_ 11 | for more information. 12 | 13 | Kivy 14 | ---- 15 | 16 | .. code-block:: python 17 | 18 | from kivy.animation import Animation 19 | from kivy.lang import Builder 20 | from kivy.properties import NumericProperty 21 | from kivy.uix.button import Button 22 | from kivy.app import App 23 | 24 | 25 | KV = ''' 26 | Screen: 27 | 28 | ScaleButton: 29 | size_hint: .5, .5 30 | pos_hint: {"center_x": .5, "center_y": .5} 31 | on_release: app.change_scale(self) 32 | 33 | canvas.before: 34 | PushMatrix 35 | Scale: 36 | x: self.scale_value_x 37 | y: self.scale_value_y 38 | z: self.scale_value_x 39 | origin: self.center 40 | canvas.after: 41 | PopMatrix 42 | ''' 43 | 44 | 45 | class ScaleButton(Button): 46 | scale_value_x = NumericProperty(1) 47 | scale_value_y = NumericProperty(1) 48 | scale_value_z = NumericProperty(1) 49 | 50 | 51 | class Test(App): 52 | def build(self): 53 | return Builder.load_string(KV) 54 | 55 | def change_scale(self, instance_button: Button) -> None: 56 | Animation( 57 | scale_value_x=0.5, 58 | scale_value_y=0.5, 59 | scale_value_z=0.5, 60 | d=0.3, 61 | ).start(instance_button) 62 | 63 | 64 | Test().run() 65 | 66 | KivyMD 67 | ------ 68 | 69 | .. code-block:: python 70 | 71 | from kivy.animation import Animation 72 | from kivy.lang import Builder 73 | 74 | from kivymd.app import MDApp 75 | from kivymd.uix.button import MDRaisedButton 76 | from kivymd.uix.templates import ScaleWidget 77 | 78 | KV = ''' 79 | MDScreen: 80 | 81 | ScaleButton: 82 | size_hint: .5, .5 83 | pos_hint: {"center_x": .5, "center_y": .5} 84 | on_release: app.change_scale(self) 85 | elevation:0 86 | ''' 87 | 88 | 89 | class ScaleButton(MDRaisedButton, ScaleWidget): 90 | pass 91 | 92 | 93 | class Test(MDApp): 94 | def build(self): 95 | return Builder.load_string(KV) 96 | 97 | def change_scale(self, instance_button: MDRaisedButton) -> None: 98 | Animation( 99 | scale_value_x=0.5, 100 | scale_value_y=0.5, 101 | scale_value_z=0.5, 102 | d=0.3, 103 | ).start(instance_button) 104 | 105 | 106 | Test().run() 107 | """ 108 | 109 | __all__ = ("ScaleWidget",) 110 | 111 | import os 112 | 113 | from kivy.lang import Builder 114 | from kivy.properties import NumericProperty 115 | 116 | from kivymd import uix_path 117 | 118 | with open( 119 | os.path.join(uix_path, "templates", "scalewidget", "scalewidget.kv"), 120 | encoding="utf-8", 121 | ) as kv_file: 122 | Builder.load_string(kv_file.read()) 123 | 124 | 125 | class ScaleWidget: 126 | """Base class for controlling the scale of the widget.""" 127 | 128 | scale_value_x = NumericProperty(1) 129 | """ 130 | X-axis value. 131 | 132 | :attr:`scale_value_x` is an :class:`~kivy.properties.NumericProperty` 133 | and defaults to `1`. 134 | """ 135 | 136 | scale_value_y = NumericProperty(1) 137 | """ 138 | Y-axis value. 139 | 140 | :attr:`scale_value_y` is an :class:`~kivy.properties.NumericProperty` 141 | and defaults to `1`. 142 | """ 143 | 144 | scale_value_z = NumericProperty(1) 145 | """ 146 | Z-axis value. 147 | 148 | :attr:`scale_value_z` is an :class:`~kivy.properties.NumericProperty` 149 | and defaults to `1`. 150 | """ 151 | -------------------------------------------------------------------------------- /kivymd/uix/toolbar/toolbar.kv: -------------------------------------------------------------------------------- 1 | 2 | canvas.before: 3 | PushMatrix 4 | Scale: 5 | origin: self.center 6 | x: root._scale_x 7 | y: root._scale_y 8 | canvas.after: 9 | PopMatrix 10 | 11 | 12 | 13 | size_hint_y: None 14 | padding: [root.theme_cls.horizontal_margins - dp(12), 0] 15 | elevation: root.elevation 16 | 17 | canvas: 18 | Color: 19 | rgba: 20 | ( \ 21 | root.theme_cls.primary_color \ 22 | if root.md_bg_color == [0, 0, 0, 0] \ 23 | else root.md_bg_color \ 24 | ) \ 25 | if root.type == "top" else \ 26 | ( \ 27 | ( \ 28 | root.theme_cls.primary_color \ 29 | if not self.md_bg_bottom_color else \ 30 | self.md_bg_bottom_color \ 31 | ) \ 32 | if root.parent.md_bg_color == [0, 0, 0, 0] \ 33 | else root.parent.md_bg_color \ 34 | ) 35 | Mesh: 36 | vertices: root._vertices_left 37 | indices: root._indices_left 38 | mode: "triangle_fan" 39 | 40 | Mesh: 41 | vertices: root._vertices_right 42 | indices: root._indices_right 43 | mode: "triangle_fan" 44 | 45 | RoundedRectangle: 46 | pos: root._rectangle_left_pos 47 | size: root._rectangle_left_width, root._rounded_rectangle_height 48 | radius: 49 | [0,] if root.mode == "normal" \ 50 | else [0, root.notch_radius * root._rounding_percentage, 0, 0] 51 | 52 | RoundedRectangle: 53 | pos: root._rectangle_right_pos 54 | size: root._rectangle_right_width, root._rounded_rectangle_height 55 | radius: 56 | [0,] if root.mode == "normal" \ 57 | else [root.notch_radius * root._rounding_percentage, 0, 0, 0] 58 | 59 | 60 | 61 | orientation: "vertical" 62 | 63 | MDBoxLayout: 64 | padding: 0, 0, 0, root.height - headline_box.height - (dp(48) + dp(20)) 65 | 66 | MDBoxLayout: 67 | id: left_actions 68 | orientation: "horizontal" 69 | size_hint_x: None 70 | padding: [0, (self.height - dp(48)) / 2] 71 | 72 | MDBoxLayout: 73 | padding: dp(12), 0 74 | 75 | MDLabel: 76 | id: label_title 77 | font_style: "H6" 78 | opposite_colors: root.opposite_colors 79 | theme_text_color: "Custom" if not root.opposite_colors else "Primary" 80 | text_color: root.specific_text_color 81 | text: root.title 82 | shorten: True 83 | shorten_from: "right" 84 | markup: True 85 | halign: 86 | root.anchor_title \ 87 | if root.anchor_title else \ 88 | root.update_anchor_title(app.theme_cls.material_style) 89 | 90 | MDBoxLayout: 91 | id: right_actions 92 | orientation: "horizontal" 93 | size_hint_x: None 94 | padding: [0, (self.height - dp(48)) / 2] 95 | 96 | MDBoxLayout: 97 | id: headline_box 98 | size_hint_y: None 99 | height: label_headline.texture_size[1] if label_headline.text else 0 100 | padding: "16dp" 101 | 102 | MDLabel: 103 | id: label_headline 104 | adaptive_height: True 105 | shorten: True 106 | shorten_from: "right" 107 | theme_text_color: "Custom" 108 | text_color: 109 | label_title.text_color \ 110 | if not root.headline_text_color else \ 111 | root.headline_text_color 112 | text: 113 | root.headline_text \ 114 | if root.type_height in ("medium", "large") \ 115 | and app.theme_cls.material_style == "M3" \ 116 | and root.type != "bottom" else \ 117 | "" 118 | -------------------------------------------------------------------------------- /kivymd/uix/tab/tab.kv: -------------------------------------------------------------------------------- 1 | #:import DampedScrollEffect kivy.effects.dampedscroll.DampedScrollEffect 2 | 3 | 4 | 5 | _set_start_tab: False 6 | size_hint: None, 1 7 | halign: "center" 8 | valign: "center" 9 | group: "tabs" 10 | font: root.font_name 11 | allow_no_selection: False 12 | markup: True 13 | on_width: 14 | if not self._set_start_tab: \ 15 | self.tab_bar.parent._update_indicator( \ 16 | self.tab_bar.parent.carousel.current_slide.tab_label); \ 17 | self._set_start_tab = True 18 | on_tab_bar: 19 | self.text_size = (None, None) \ 20 | if self.tab_bar.parent.allow_stretch else (self.width, None) 21 | on_ref_press: 22 | self.tab_bar.parent.dispatch( \ 23 | "on_ref_press", 24 | self, \ 25 | self.tab, \ 26 | self.tab_bar, \ 27 | self.tab_bar.parent.carousel) 28 | color: 29 | ( \ 30 | self.text_color_active \ 31 | if self.text_color_active else self.specific_secondary_text_color \ 32 | ) \ 33 | if self.state == "down" else \ 34 | ( \ 35 | self.text_color_normal \ 36 | if self.text_color_normal else self.theme_cls.text_color \ 37 | ) 38 | 39 | 40 | 41 | size_hint: 1, 1 42 | do_scroll_y: False 43 | bar_color: 0, 0, 0, 0 44 | bar_inactive_color: 0, 0, 0, 0 45 | bar_width: 0 46 | effect_cls: DampedScrollEffect 47 | 48 | 49 | 50 | carousel: carousel 51 | tab_bar: tab_bar 52 | anchor_y: "top" 53 | background_palette: "Primary" 54 | 55 | _line_x: 0 56 | _line_width: 0 57 | _line_height: 0 58 | _line_radius: 0 59 | 60 | on_size: 61 | root._update_padding(layout) 62 | 63 | MDTabsMain: 64 | padding: 0, tab_bar.height, 0, 0 65 | 66 | MDTabsCarousel: 67 | id: carousel 68 | lock_swiping: root.lock_swiping 69 | ignore_perpendicular_swipes: True 70 | anim_move_duration: root.anim_duration 71 | on_index: root.on_carousel_index(*args) 72 | on__offset: tab_bar.android_animation(*args) 73 | 74 | MDTabsBar: 75 | id: tab_bar 76 | padding: root.tab_padding 77 | carousel: carousel 78 | scrollview: scrollview 79 | layout: layout 80 | size_hint: 1, None 81 | elevation: root.elevation 82 | height: root.tab_bar_height 83 | md_bg_color: 84 | self.theme_cls.primary_color \ 85 | if not root.background_color else \ 86 | root.background_color 87 | 88 | MDTabsScrollView: 89 | id: scrollview 90 | do_scroll_x: False if layout.width <= self.width else True 91 | 92 | MDGridLayout: 93 | id: layout 94 | rows: 1 95 | size_hint_y: 1 96 | adaptive_width: True 97 | on_size: root._update_padding(layout) 98 | 99 | canvas.before: 100 | Color: 101 | rgba: root.underline_color 102 | Line: 103 | width: dp(2) 104 | rectangle: [0, 0, layout.width, dp(2)] 105 | Color: 106 | rgba: 107 | root.theme_cls.accent_color \ 108 | if not root.indicator_color else \ 109 | root.indicator_color 110 | RoundedRectangle: 111 | group: "Indicator_line" 112 | pos: self.pos 113 | size: 0, root.tab_indicator_height 114 | radius: [0,] 115 | Line: 116 | width: dp(2) 117 | rounded_rectangle: 118 | [ \ 119 | root._line_x, \ 120 | self.pos[1], \ 121 | root._line_width, \ 122 | root._line_height, \ 123 | root._line_radius \ 124 | ] 125 | -------------------------------------------------------------------------------- /kivymd/uix/navigationdrawer/navigationdrawer.kv: -------------------------------------------------------------------------------- 1 | #:import Window kivy.core.window.Window 2 | #:import m_res kivymd.material_resources 3 | 4 | 5 | : 6 | size_hint_x: None 7 | width: Window.width - dp(56) if Window.width <= dp(376) else dp(320) 8 | md_bg_color: self.theme_cls.bg_light 9 | padding: 10 | x: 11 | (self.width * (self.open_progress - 1)) \ 12 | if self.anchor == "left" \ 13 | else (Window.width - self.width * self.open_progress) 14 | 15 | canvas: 16 | Clear 17 | Color: 18 | rgba: self.md_bg_color 19 | RoundedRectangle: 20 | size: self.size 21 | pos: self.pos 22 | source: root.background 23 | radius: root.radius 24 | 25 | 26 | 27 | adaptive_height: True 28 | 29 | MDLabel: 30 | text: root.text 31 | adaptive_size: True 32 | markup: True 33 | 34 | 35 | 36 | adaptive_height: True 37 | 38 | MDSeparator: 39 | color: root.color if root.color else app.theme_cls.divider_color 40 | 41 | 42 | 43 | adaptive_height: True 44 | 45 | FitImage: 46 | id: logo 47 | source: root.source 48 | size_hint: None, None 49 | size: label_box.height, label_box.height 50 | 51 | MDBoxLayout: 52 | id: label_box 53 | orientation: "vertical" 54 | adaptive_height: True 55 | 56 | MDLabel: 57 | id: title 58 | adaptive_height: True 59 | halign: root.title_halign 60 | text: root.title 61 | font_style: root.title_font_style 62 | font_size: root.title_font_size 63 | color: 64 | root.title_color \ 65 | if root.title_color else \ 66 | app.theme_cls.text_color 67 | 68 | MDLabel: 69 | id: text 70 | adaptive_height: True 71 | text: root.text 72 | halign: root.text_halign 73 | font_style: root.text_font_style 74 | font_size: root.text_font_size 75 | color: 76 | root.text_color \ 77 | if root.text_color else \ 78 | app.theme_cls.text_color 79 | 80 | 81 | 82 | radius: self.height / 2 if self.radius == [0, 0, 0, 0] else self.radius 83 | divider: None 84 | theme_text_color: "Custom" 85 | text_color: self.text_color if not self.selected else self.selected_color 86 | _txt_left_pad: "56dp" 87 | on_size: 88 | self.ids._left_container.x = "4dp" 89 | self.ids._right_container.width = right_label.texture_size[0] 90 | on_release: 91 | if not self.selected: self._text_color = self.text_color 92 | self._text_right_color = root.text_right_color if root.text_right_color else app.theme_cls.text_color 93 | self._drawer_menu.reset_active_color(self) 94 | 95 | IconLeftWidgetWithoutTouch: 96 | icon: root.icon 97 | theme_icon_color: "Custom" 98 | icon_color: 99 | ( \ 100 | app.theme_cls.text_color \ 101 | if not root.icon_color else \ 102 | root.icon_color \ 103 | ) \ 104 | if not root.selected else \ 105 | root.selected_color 106 | 107 | MDLabel: 108 | id: right_label 109 | text: root.right_text 110 | pos_hint: {"center_y": .5} 111 | adaptive_size: True 112 | markup: True 113 | color: 114 | ( \ 115 | root.text_right_color \ 116 | if root.text_right_color else \ 117 | app.theme_cls.text_color \ 118 | ) \ 119 | if not root.selected else \ 120 | root.selected_color 121 | x: 122 | root.x \ 123 | + root.width \ 124 | - m_res.HORIZ_MARGINS \ 125 | - root.ids._right_container.width - dp(24) \ 126 | - self.texture_size[0] \ 127 | + dp(24) 128 | 129 | 130 | 131 | 132 | MDList: 133 | id: menu 134 | spacing: root.spacing 135 | -------------------------------------------------------------------------------- /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 | add=True, 28 | ): 29 | from libs.screens.HomeScreen.HomeScreen import HomeScreen 30 | from libs.screens.LoginScreen.LoginScreen import LoginScreen 31 | from libs.screens.SettingsScreen.SettingsScreen import SettingsScreen 32 | from libs.screens.SignupScreen.SignupScreen import SignupScreen 33 | # checks that the screen already added to the screen-manager 34 | if not self.has_screen(screen_name): 35 | # loads the kv file 36 | Builder.load_file(f"./libs/screens/{screen_name}/{screen_name}.kv") 37 | # imports the screen class dynamically 38 | # exec(f"from libs.screens.{screen_name}.{screen_name} import {screen_name}") 39 | # calls the screen class to get the instance of it 40 | self.screen_object = eval(f"{screen_name}()") 41 | # automatically sets the screen name using the arg that passed in set_current 42 | self.screen_object.name = screen_name 43 | if add: 44 | # saves screen instance object to access later. 45 | exec(f"self.{screen_name} = self.screen_object") 46 | # finnaly adds the screen to the screen-manager 47 | self.add_widget(self.screen_object) 48 | 49 | # saves screen information to history 50 | # if you not want a screen to go back 51 | # use like below 52 | # if not from_goback and screen_name not in ["auth", ...] 53 | 54 | if not _from_goback and set_current: 55 | self.transition.mode = "push" 56 | self.transition.direction = "left" 57 | self.history.append(screen_name) 58 | 59 | # sets transition direction 60 | # sets the current screen 61 | if set_current: 62 | self.current = screen_name 63 | if empty_history: 64 | self.history = [] 65 | 66 | def check_press_back_twice(self): 67 | def show_toast(): 68 | if platform == "android": 69 | toast("Press back again to close the app", length_long=False) 70 | else: 71 | toast("Press back again to close the app", duration=0.5) 72 | 73 | self._press_again = time() 74 | if self._prev_press: 75 | if (self._press_again - self._prev_press) < 2: 76 | MDApp.get_running_app().stop() 77 | else: 78 | show_toast() 79 | else: 80 | show_toast() 81 | self._prev_press = time() 82 | 83 | def _handle_keyboard(self, instance, key, *args): 84 | if key == 27: 85 | if ( 86 | self.current == "HomeScreen" 87 | and self.current_screen.ids.tab_manager.current == "FindScreen" 88 | ): 89 | self.current_screen.ids.tab_manager.current = "CreateScreen" 90 | elif self.current == "SettingsScreen": 91 | self.goback() 92 | else: 93 | self.check_press_back_twice() 94 | return True 95 | 96 | def goback(self): 97 | if len(self.history) > 1: 98 | self.history.pop() 99 | prev_screen = self.history[-1] 100 | self.transition.mode = "pop" 101 | self.transition.direction = "right" 102 | self.load_screen(prev_screen, _from_goback=True) 103 | -------------------------------------------------------------------------------- /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_icon_color:"Custom" 55 | icon_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_icon_color:"Custom" 64 | icon_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 __init__(self) -> None: 89 | super().__init__() 90 | app.bind(primary_accent=self.update_list_color) 91 | 92 | def update_list_color(self, *args): 93 | if self.selected: 94 | self.list_color_active = app.primary_accent 95 | 96 | def on_button_actions(self, *args): 97 | if len(self.button_actions) == 3: 98 | self.ids.copy_button.on_release = self.button_actions["copy"] 99 | self.ids.update_button.on_release = self.button_actions["update"] 100 | self.ids.delete_button.on_release = self.button_actions["delete"] 101 | 102 | def on_release(self): 103 | if self.selected: 104 | if self.right - self.last_touch.x >= dp(170): 105 | self.parent.clear_selection() 106 | else: 107 | recycle_list = self.parent.children 108 | snack = app.root.HomeScreen.ids.find.snackbar 109 | 110 | if ( 111 | not (snack and snack.is_open) 112 | or self not in recycle_list[:2] 113 | or len(recycle_list) <= 13 114 | ): 115 | self.parent.select_with_touch(self.index, None) 116 | 117 | def refresh_view_attrs(self, rv, index, data): 118 | self.index = index 119 | return super().refresh_view_attrs(rv, index, data) 120 | 121 | def apply_selection(self, rv, index, is_selected): 122 | try: 123 | self.selected = is_selected 124 | rv.data[index]["selected"] = is_selected 125 | except IndexError: 126 | print(index, self.selected, "Index error") 127 | 128 | def on_selected(self, instance, selected): 129 | if selected: 130 | self.list_color_active = app.primary_accent[:-1] + [0] 131 | Animation( 132 | list_color_active=app.primary_accent, d=0.1 133 | ).start(self) 134 | -------------------------------------------------------------------------------- /libs/firebase.py: -------------------------------------------------------------------------------- 1 | from kivy.logger import Logger 2 | from kivy.network.urlrequest import UrlRequest 3 | 4 | from libs.utils import * 5 | 6 | WEB_API_KEY = os.environ["WEB_API_KEY"] 7 | DATABASE_URL = os.environ["DATABASE_URL"] 8 | 9 | 10 | class Firebase: 11 | BASE_URL = "https://www.googleapis.com/identitytoolkit/v3/relyingparty" 12 | SIGNUP_URL = f"{BASE_URL}/signupNewUser?key=" 13 | LOGIN_URL = f"{BASE_URL}/verifyPassword?key=" 14 | DELETE_URL = f"{BASE_URL}/deleteAccount?key=" 15 | 16 | def signup_success(self, req, result): 17 | """ 18 | Implement this method to handle the result of the successful signup request. 19 | """ 20 | Logger.warn("Firebase: Signup success not implemented") 21 | 22 | def signup_failure(self, req, result): 23 | """ 24 | Implement this method to handle the result of the signup failure request. 25 | """ 26 | Logger.warn("Firebase: Signup failure not implemented") 27 | 28 | def signup(self, name, password): 29 | """ 30 | Used to signup the user. 31 | """ 32 | url = self.SIGNUP_URL + WEB_API_KEY 33 | data = json.dumps( 34 | {"email": name, "password": password, "returnSecureToken": True} 35 | ) 36 | UrlRequest( 37 | url, 38 | req_body=data, 39 | on_success=self.signup_success, 40 | on_failure=self.signup_failure, 41 | timeout=15, 42 | ) 43 | 44 | def login_success(self, req, result): 45 | """ 46 | Implement this method to handle the result of the successful login request. 47 | """ 48 | Logger.warn("Firebase: Login success not implemented") 49 | 50 | def login_failure(self, req, result): 51 | """ 52 | Implement this method to handle the result of the login failure request. 53 | """ 54 | Logger.warn("Firebase: Login failure not implemented") 55 | 56 | def login(self, name, password): 57 | """ 58 | Used to login the user. 59 | """ 60 | 61 | url = self.LOGIN_URL + WEB_API_KEY 62 | data = json.dumps( 63 | {"email": name, "password": password, "returnSecureToken": True} 64 | ) 65 | UrlRequest( 66 | url, 67 | req_body=data, 68 | on_success=self.login_success, 69 | on_failure=self.login_failure, 70 | timeout=10, 71 | ) 72 | 73 | def backup_success(self, req, result): 74 | """ 75 | Override this method to handle the result of the successful backup request. 76 | """ 77 | Logger.warn(f"Firebase: Backup success not implemented, {result}") 78 | 79 | def backup_failure(self, req, result): 80 | """ 81 | Override this method to handle the result of the failure in backup request. 82 | """ 83 | Logger.warn(f"Firebase: Backup failure not implemented, {result}") 84 | 85 | def backup(self): 86 | """ 87 | Used to backup the user's passwords. 88 | """ 89 | result = load_passwords() 90 | _json = {get_uid(): result} 91 | UrlRequest( 92 | f"{DATABASE_URL}/.json", 93 | req_body=json.dumps(_json), 94 | req_headers={"Content-type": "application/json", "Accept": "text/plain"}, 95 | on_success=self.backup_success, 96 | on_failure=self.backup_failure, 97 | on_error=self.backup_failure, 98 | method="PATCH", 99 | ) 100 | 101 | def restore_success(self, req, result): 102 | """ 103 | Override this method to handle the result of the successful restore request. 104 | """ 105 | Logger.warn(f"Firebase: Restore success not implemented, {result}") 106 | 107 | def restore_failure(self, req, result): 108 | """ 109 | Override this method to handle the result of the failure in restore request. 110 | """ 111 | Logger.warn(f"Firebase: Restore failure not implemented, {result}") 112 | 113 | def restore(self, user_id=None): 114 | """ 115 | Used to restore the user's passwords. 116 | """ 117 | if user_id is None: 118 | user_id = get_uid() 119 | 120 | UrlRequest( 121 | f"{DATABASE_URL}/{user_id}.json", 122 | on_success=self.restore_success, 123 | on_failure=self.restore_failure, 124 | on_error=self.restore_failure, 125 | ) 126 | --------------------------------------------------------------------------------