diff --git a/tests/v2/main_screen.py b/tests/v2/main_screen.py new file mode 100644 index 00000000..54fc5759 --- /dev/null +++ b/tests/v2/main_screen.py @@ -0,0 +1,324 @@ +from kivy.lang import Builder +from kivymd.app import MDApp +from kivy.core.window import Window +from kivy.utils import get_color_from_hex +from kivymd.uix.boxlayout import MDBoxLayout +from kivy.uix.screenmanager import ScreenManager, Screen + + +from kivy.lang import Builder +from kivy.metrics import dp + +from kivymd.uix.list import ImageLeftWidget + +from kivymd.uix.datatables import MDDataTable + +# meil uix from tests +from uix.expansionpanel import MDExpansionPanelRoundIcon, MDExpansionPanelTwoLineSmall + +KV = """ +#:import get_color_from_hex kivy.utils.get_color_from_hex + +WindowManager: + + + adaptive_height: True + orientation: 'vertical' + + OneLineListItem: + text: "Node1" + text_color: "white" + theme_text_color: "Custom" + font_style: "Overline" + bg_color: "black" + + OneLineListItem: + text: "Node 2" + text_color: "white" + theme_text_color: "Custom" + font_style: "Overline" + bg_color: "black" + + OneLineListItem: + text: "Node 3" + text_color: "white" + theme_text_color: "Custom" + font_style: "Overline" + bg_color: "black" + +: + name: "main" + MDGridLayout: + md_bg_color: get_color_from_hex("#121212") + rows: 2 + + MDBoxLayout: + md_bg_color: get_color_from_hex("#212221") + size_hint_y: None + height: 50 + + MDLabel: + padding: 10 + text: "Meile" + font_style: "H4" + text_color: "white" + theme_text_color: "Custom" + + MDGridLayout: + cols: 2 + md_bg_color: get_color_from_hex("#212221") + + MDGridLayout: + rows: 4 + padding: 10 + size_hint_x: None + width: 250 + md_bg_color: get_color_from_hex("#060606") + align: "center" + + MDRectangleFlatIconButton: + size_hint_y: None + + text: "Connect" + text_color: "black" + icon: "lightning-bolt" + icon_color: "black" + md_bg_color: get_color_from_hex("#FCB70C") + line_color: 0, 0, 0, 0 + font_size: "16sp" + size_hint: 1, 0 + + MDTextField: + icon_left: "magnify" + line_color_normal: get_color_from_hex("#FCB70C") + line_color_focus: get_color_from_hex("#FCB70C") + icon_left_color_normal: get_color_from_hex("#FCB70C") + icon_left_color_focus: get_color_from_hex("#FCB70C") + text_color_normal: get_color_from_hex("#FCB70C") + text_color_focus: get_color_from_hex("#FCB70C") + + MDScrollView: + MDList: + id: countries_list + spacing: 3 + padding: 10 + + MDBoxLayout: + orientation: 'horizontal' + # padding: [20, 0, 0, 0] + size_hint_y: None + height: 50 + spacing: 10 + + canvas: + # draw a background of red. This will be the border + Color: + rgba: get_color_from_hex("#453103") + RoundedRectangle: + pos: self.pos + size: self.size + + # draw over the above except for 1 pixels around edges, leaving the orange border showing + Color: + rgba: get_color_from_hex("#212221") + RoundedRectangle: + pos: self.x+1, self.y+1 + size: self.width-2, self.height-2 + + MDIconButton: + icon: "wallet-outline" + theme_text_color: "Custom" + text_color: "white" + + MDIconButton: + icon: "book-open-outline" + theme_text_color: "Custom" + text_color: "white" + + MDIconButton: + icon: "cog-outline" + theme_text_color: "Custom" + text_color: "white" + + MDIconButton: + icon: "help-circle-outline" + theme_text_color: "Custom" + text_color: "white" + + AnchorLayout: + orientation: "horizontal" + md_bg_color: get_color_from_hex("#131313") + id: servers_datatable + +""" + + +class WindowManager(ScreenManager): + pass + + +class Content(MDBoxLayout): + pass + + +class MainScreen(Screen): + def __init__(self, **kwargs): + super().__init__() + self.build() + + def build(self): + import random, string + + countries = [ + "France", + "Italy", + "Brasil", + "Egypt", + "Belgium", + "Deutschland", + "Canada", + ] + for _ in range(0, 10): + country = random.choice(countries) + + # item = TwoLineAvatarListItem( + # radius = [10, 10, 10, 10], + # bg_color = get_color_from_hex("#212221"), + # text = country, + # text_color = "white", + # theme_text_color = "Custom", + # font_style = "Subtitle1", + # secondary_text = f"{random.randint(1, 100)} servers", + # secondary_text_color = "white", + # secondary_theme_text_color = "Custom", + # secondary_font_style = "Caption", + # ) + # item.add_widget(ImageLeftWidget(source=f"../../src/imgs/{country[:2].lower()}.png")) + + item = MDExpansionPanelRoundIcon( + icon=f"../../src/imgs/{country[:2].lower()}.png", + icon_size=(25, 25), + content=Content(), + panel_cls=MDExpansionPanelTwoLineSmall( + radius=[10, 10, 10, 10], + bg_color=get_color_from_hex("#212221"), + text=country, + text_color="white", + theme_text_color="Custom", + font_style="Subtitle1", + secondary_text=f"{random.randint(1, 100)} servers", + secondary_text_color="white", + secondary_theme_text_color="Custom", + secondary_font_style="Caption", + ), + ) + self.ids.countries_list.add_widget(item) + + + self.data_tables = MDDataTable( + use_pagination=True, + check=False, + column_data=[ + ("Moniker", dp(45)), + ("Location", dp(20)), + ("Speed", dp(50)), + ("Status", dp(20)), + ("Price", dp(40)), + ("Protocol", dp(20)), + ("Type", dp(20)), + ], + sorted_on="Moniker", + sorted_order="ASC", + elevation=2, + rows_num=10 + ) + + row_data = [] + for _ in range(0, 150): + upload = random.uniform(100, 900) + download = random.uniform(100, 900) + bandwith = "speedometer-medium" + if upload + download > 1200: + bandwith = "speedometer" + elif upload + download < 400: + bandwith = "speedometer-slow" + + healthcheck = random.choice([True, False]) + + row_data.append( + ( + ''.join(random.choices(string.printable[:-6], k=random.randint(5, 15))), # Moniker + random.choice(countries), + (bandwith, [1, 1, 1, 1] ,f"[size=12][color=#00FF00]up[/color] {round(upload, 2)}mb/s[color=#f44336]down[/color] {round(download, 2)}mb/s[/size]"), + ("shield-plus", [39 / 256, 174 / 256, 96 / 256, 1], "Health") if healthcheck is True else ("emoticon-sick", [1, 0, 0, 1], "Sick"), + f"[size=12]{random.randint(1, 100)}dvpn, {random.randint(1, 100)}atom, {random.randint(1, 100)}osmo, {random.randint(1, 100)}srct, {random.randint(1, 100)}dec[/size]", + random.choice(["Wireguard", "V2RAY"]), + random.choice(["Residential", "Datacenter", "Unknown"]) + ) + ) + + self.data_tables.row_data = row_data + + self.data_tables.bind(on_row_press=self.on_row_press) + self.ids.servers_datatable.add_widget(self.data_tables) + + def on_row_press(self, instance_table, instance_row): + '''Called when a table row is clicked.''' + + print(instance_table, instance_row) + + def on_check_press(self, instance_table, current_row): + '''Called when the check box in the table row is checked.''' + + print(instance_table, current_row) + + # Sorting Methods: + # since the https://github.com/kivymd/KivyMD/pull/914 request, the + # sorting method requires you to sort out the indexes of each data value + # for the support of selections. + # + # The most common method to do this is with the use of the builtin function + # zip and enumerate, see the example below for more info. + # + # The result given by these funcitons must be a list in the format of + # [Indexes, Sorted_Row_Data] + + def sort_on_signal(self, data): + return zip(*sorted(enumerate(data), key=lambda l: l[1][2])) + + def sort_on_schedule(self, data): + return zip( + *sorted( + enumerate(data), + key=lambda l: sum( + [ + int(l[1][-2].split(":")[0]) * 60, + int(l[1][-2].split(":")[1]), + ] + ), + ) + ) + + def sort_on_team(self, data): + return zip(*sorted(enumerate(data), key=lambda l: l[1][-1])) + +Builder.load_string(KV) + + +class Test(MDApp): + title = "MainScreen v2" + + def build(self): + Window.size = (1280, 720) + + # TODO: review this values + self.theme_cls.theme_style = "Dark" # (?) + self.theme_cls.primary_palette = "Orange" # (?) + + manager = WindowManager() + manager.add_widget(MainScreen()) + return manager + + +if __name__ == "__main__": + Test().run() diff --git a/tests/v2/uix/__init__.py b/tests/v2/uix/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/v2/uix/expansionpanel.py b/tests/v2/uix/expansionpanel.py new file mode 100644 index 00000000..2c6ac8d3 --- /dev/null +++ b/tests/v2/uix/expansionpanel.py @@ -0,0 +1,256 @@ +# https://raw.githubusercontent.com/kivymd/KivyMD/master/kivymd/uix/expansionpanel/expansionpanel.py + + + +from kivy.animation import Animation +from kivy.lang import Builder +from kivy.metrics import dp +from kivy.properties import NumericProperty, ObjectProperty, StringProperty, ListProperty +from kivy.uix.relativelayout import RelativeLayout +from kivy.uix.widget import WidgetException + +import kivymd.material_resources as m_res +from kivymd.icon_definitions import md_icons +from kivymd.uix.button import MDIconButton +from kivymd.uix.list import ( + IRightBodyTouch, + TwoLineAvatarIconListItem, + IconLeftWidget +) + +from kivymd.uix.expansionpanel import ( + MDExpansionPanelOneLine, + MDExpansionPanelThreeLine, + MDExpansionPanelLabel, +) + +from kivymd.uix.behaviors import CommonElevationBehavior +from kivymd.uix.fitimage.fitimage import FitImage + + +KV = """ + + size_hint_y: None + # height: dp(68) +""" +Builder.load_string(KV) + + +class MDExpansionPanelTwoLineSmall(TwoLineAvatarIconListItem): + """ + Two-line panel. + + For more information, see in the + :class:`~kivymd.uix.list.TwoLineAvatarIconListItem` class documentation. + """ + + # _txt_top_pad = NumericProperty("20dp") + _txt_bot_pad = NumericProperty("6dp") + _txt_left_pad = NumericProperty("50dp") + _height = NumericProperty("50dp") + _num_lines = 2 + + +class DisplayPic(CommonElevationBehavior, FitImage): + pass + + +class MDExpansionChevronRight(IRightBodyTouch, MDIconButton): + _angle = NumericProperty(0) + + +class MDExpansionPanelRoundIcon(RelativeLayout): + content = ObjectProperty() + icon = StringProperty() + icon_size = ListProperty((25, 25)) + opening_transition = StringProperty("out_cubic") + opening_time = NumericProperty(0.2) + closing_transition = StringProperty("out_sine") + closing_time = NumericProperty(0.2) + panel_cls = ObjectProperty() + + _state = StringProperty("close") + _anim_playing = False + + def __init__(self, **kwargs): + super().__init__(**kwargs) + self.register_event_type("on_open") + self.register_event_type("on_close") + + if self.panel_cls and isinstance( + self.panel_cls, + ( + MDExpansionPanelOneLine, + MDExpansionPanelTwoLineSmall, + MDExpansionPanelThreeLine, + MDExpansionPanelLabel, + ), + ): + self.panel_cls.pos_hint = {"top": 1} + + self.panel_cls._no_ripple_effect = True + self.panel_cls.bind( + on_release=lambda x: self.check_open_panel(self.panel_cls) + ) + if not isinstance(self.panel_cls, MDExpansionPanelLabel): + self.chevron = MDExpansionChevronRight( + icon_color="white", + theme_icon_color="Custom", + md_bg_color_disabled="white", + disabled=False + ) + self.chevron.ripple_scale = 0 + self.chevron._no_ripple_effect = True + self.panel_cls.add_widget(self.chevron) + + if self.icon: + if self.icon in md_icons.keys(): + self.panel_cls.add_widget( + IconLeftWidget( + icon=self.icon, + pos_hint={"center_y": 0.5}, + ) + ) + else: + # self.panel_cls.add_widget(ImageLeftWidget(source=self.icon, pos_hint={"center_y": 0.5})) + self.panel_cls.add_widget( + DisplayPic( + source=self.icon, + elevation=dp(3), + size_hint=(None, None), + size=self.icon_size, + radius=dp(360), + pos_hint={"center_x": 0.12, "center_y": 0.5}, + ) + ) + else: + self.panel_cls.remove_widget(self.panel_cls.ids._left_container) + self.panel_cls._txt_left_pad = 0 + else: + # if no icon + self.panel_cls._txt_left_pad = m_res.HORIZ_MARGINS + self.add_widget(self.panel_cls) + else: + raise ValueError( + "KivyMD: `panel_cls` object must be must be one of the " + "objects from the list\n" + "[MDExpansionPanelOneLine, MDExpansionPanelTwoLineSmall, " + "MDExpansionPanelThreeLine]" + ) + + def on_open(self, *args): + """Called when a panel is opened.""" + + def on_close(self, *args): + """Called when a panel is closed.""" + + def check_open_panel( + self, + instance_panel: [ + MDExpansionPanelThreeLine, + MDExpansionPanelTwoLineSmall, + MDExpansionPanelThreeLine, + MDExpansionPanelLabel, + ], + ) -> None: + """ + Called when you click on the panel. Called methods to open or close + a panel. + """ + + press_current_panel = False + for panel in self.parent.children: + if isinstance(panel, MDExpansionPanelRoundIcon): + if len(panel.children) == 2: + if instance_panel is panel.children[1]: + press_current_panel = True + panel.remove_widget(panel.children[0]) + if not isinstance(self.panel_cls, MDExpansionPanelLabel): + chevron = panel.children[0].children[1].children[0] # Fix [0/1] + self.set_chevron_up(chevron) + self.close_panel(panel, press_current_panel) + self.dispatch("on_close") + break + if not press_current_panel: + self.set_chevron_down() + + def set_chevron_down(self) -> None: + """Sets the chevron down.""" + + if not isinstance(self.panel_cls, MDExpansionPanelLabel): + Animation(_angle=-90, d=self.opening_time).start(self.chevron) + self.open_panel() + self.dispatch("on_open") + + def set_chevron_up(self, instance_chevron: MDExpansionChevronRight) -> None: + """Sets the chevron up.""" + + if not isinstance(self.panel_cls, MDExpansionPanelLabel): + Animation(_angle=0, d=self.closing_time).start(instance_chevron) + + def close_panel(self, instance_expansion_panel, press_current_panel: bool) -> None: + """Method closes the panel.""" + + if self._anim_playing: + return + + if press_current_panel: + self._anim_playing = True + + self._state = "close" + + anim = Animation( + height=self.panel_cls.height, + d=self.closing_time, + t=self.closing_transition, + ) + anim.bind(on_complete=self._disable_anim) + anim.start(instance_expansion_panel) + + def open_panel(self, *args) -> None: + """Method opens a panel.""" + + if self._anim_playing: + return + + self._anim_playing = True + self._state = "open" + + anim = Animation( + height=self.content.height + self.height, + d=self.opening_time, + t=self.opening_transition, + ) + anim.bind(on_complete=self._add_content) + anim.bind(on_complete=self._disable_anim) + anim.start(self) + + def get_state(self) -> str: + """Returns the state of panel. Can be `close` or `open` .""" + + return self._state + + def add_widget(self, widget, index=0, canvas=None): + if isinstance( + widget, + ( + MDExpansionPanelOneLine, + MDExpansionPanelTwoLineSmall, + MDExpansionPanelThreeLine, + MDExpansionPanelLabel, + ), + ): + self.height = widget.height + return super().add_widget(widget) + + def _disable_anim(self, *args): + self._anim_playing = False + + def _add_content(self, *args): + if self.content: + try: + if isinstance(self.panel_cls, MDExpansionPanelLabel): + self.content.y = dp(36) + self.add_widget(self.content) + except WidgetException: + pass