Skip to content

Commit

Permalink
dock test
Browse files Browse the repository at this point in the history
  • Loading branch information
T-Dynamos committed Jan 16, 2025
1 parent 19576d4 commit 48fbc22
Show file tree
Hide file tree
Showing 4 changed files with 144 additions and 66 deletions.
22 changes: 15 additions & 7 deletions examples/search.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
MDScreen:
md_bg_color:app.theme_cls.backgroundColor
BoxLayout:
padding:[dp(10), dp(50)]
padding:[dp(10), dp(50), dp(10), dp(10)]
orientation:"vertical"
MDSearchBar:
Expand All @@ -20,7 +20,8 @@
# Search Bar
MDSearchBarLeadingContainer:
MDSearchLeadingIcon:
icon:"magnify"
icon: "menu"
on_release: app.open_menu(self)
MDSearchBarTrailingContainer:
MDSearchTrailingIcon:
Expand All @@ -39,15 +40,22 @@
icon:"window-close"
MDSearchViewContainer:
size_hint_y:1
MDLabel:
text:"Hello World!"
halign:"center"
Widget:
MDSwitch:
on_active:app.theme_cls.theme_style = "Dark" if app.theme_cls.theme_style == "Light" else "Light"
Widget:
BoxLayout:
size_hint_y:None
height:dp(50)
padding:[dp(50), 0]
spacing:dp(10)
MDLabel:
text:"Bar dock"
halign:"center"
MDSwitch:
on_active:search_bar.docked = args[-1]
"""

class Example(MDApp, CommonApp):
Expand Down
4 changes: 3 additions & 1 deletion kivymd/uix/search/search.kv
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
[self.x+self.width, self.y+self.height]]

<MDSearchWidget>:
size_hint:[1,1]
MDBoxLayout:
id: root_container
orientation: 'vertical'
Expand All @@ -67,7 +68,8 @@
multiline: False
font_size: root._font_style["font-size"]
on_focus: if args[-1]: root.switch_state("open")
Widget:
Widget:
id: _wid
<MDSearchBar>:
size_hint_y: None
height: dp(56)
176 changes: 118 additions & 58 deletions kivymd/uix/search/search.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
NumericProperty,
ObjectProperty,
StringProperty,
BooleanProperty
)
from kivy.uix.behaviors import ButtonBehavior
from kivy.uix.boxlayout import BoxLayout
Expand All @@ -34,9 +35,9 @@

from kivymd import uix_path
from kivymd.font_definitions import theme_font_styles
from kivymd.uix.behaviors import StencilBehavior
from kivymd.uix.boxlayout import MDBoxLayout
from kivymd.uix.label import MDIcon
from kivymd.utils import next_frame

with open(
os.path.join(uix_path, "search", "search.kv"), encoding="utf-8"
Expand Down Expand Up @@ -72,104 +73,145 @@ class MDSearchViewLeadingContainer(BoxLayout):
pass


class MDSearchViewContainer(StencilBehavior, BoxLayout):
class MDSearchViewContainer(BoxLayout):
pass


class MDSearchWidget(RelativeLayout):

_font_style = theme_font_styles["Title"]["medium"]
_duration = 0.4
_transition = "in_out_circ"
state = "closed"
_d = 0.4
_t = "in_out_circ"
state = "close"

def __init__(self, root, *args, **kwargs):
super().__init__(*args, **kwargs)
self.root = root

def update_pos(self, *args):
self.ids.root_container.pos = self.root.pos
def update_bar(self, *args):
if self.state == "close":
self.ids.root_container.size = self.root.size
self.ids.root_container.radius = dp(28)
self.ids.root_container.pos = self.root.pos
else:
if self.root.docked:
self.ids.root_container.radius = [dp(28)] * 4
docked_size = self.root.width, dp(56) + self.root.docked_height
self.ids.root_container.pos = [
self.root.pos[0], self.root.pos[1] - docked_size[1] + dp(56)
]
else:
self.ids.root_container.radius = [0] * 4
self.ids.root_container.pos = [0,0]
self.ids.root_container.size = self.size

def _docked_open(self, opacity_down, opacity_up):
docked_size = self.root.width, dp(56) + self.root.docked_height
Animation(
size=docked_size,
pos=[self.root.pos[0], self.root.pos[1] - docked_size[1] + dp(56)],
radius=[dp(28)] * 4,
t=self._t,
d=self._d,
).start(self.ids.root_container)
self.root._view_container.size_hint_y = 1
self.root._view_container.opacity = 0
self.root._view_container.padding = [0, 0, 0, dp(16)]
next_frame(
self.ids.root_container.add_widget,
self.root._view_container,
index=1,
t=self._d,
)
next_frame(opacity_up.start, self.root._view_container, t=self._d)
self.icons_open(opacity_up, opacity_down, self._d / 2)

def update_size(self, *args):
self.ids.root_container.size = self.root.size
self.ids.root_container.radius = dp(28)
def _docked_close(self, opacity_down, opacity_up):
self._close(opacity_down, opacity_up)

def _delayed_run(self, delay, func, *args):
Clock.schedule_once(lambda dt: func(*args), delay)
def _open(self, opacity_down, opacity_up):
h_d = self._d / 2

def _open(self, d, t, opacity_down, opacity_up, docked):
# container
self.root._view_container.size_hint_y = 1
self.root._view_container.opacity = 0
self.root._view_container.padding = [0] * 4
self.ids.root_container.add_widget(self.root._view_container, index=1)
self._delayed_run(d / 4, opacity_up.start, self.root._view_container)
Animation(size=self.size, pos=self.pos, radius=[0] * 4, t=t, d=d).start(
self.ids.root_container
)
Animation(height=dp(70), t=t, d=d).start(self.ids.header)
opacity_down.start(self.root._bar_trailing_container)
opacity_down.start(self.root._bar_leading_container)
self.root._view_leading_container.opacity = 0
self.root._view_trailing_container.opacity = 0
self._delayed_run(d / 2, self.update_state_opened)
self._delayed_run(
d / 2, opacity_up.start, self.root._view_trailing_container
)
self._delayed_run(
d / 2, opacity_up.start, self.root._view_leading_container
)
next_frame(opacity_up.start, self.root._view_container, t=h_d / 2)
Animation(
size=self.size, pos=self.pos, radius=[0] * 4, t=self._t, d=self._d
).start(self.ids.root_container)

def _close(self, d, t, opacity_down, opacity_up, docked):
# header
Animation(height=dp(70), t=self._t, d=self._d).start(self.ids.header)
self.icons_open(opacity_up, opacity_down, h_d)

def _close(self, opacity_down, opacity_up):
h_d = self._d / 2
# container
self.root._view_container.size_hint_y = None
self.root._view_container.opacity = 1
opacity_down.start(self.root._view_container)
if self.root._view_container in self.ids.root_container.children:
self._delayed_run(
d / 2,
next_frame(
self.ids.root_container.remove_widget,
self.root._view_container,
t=h_d,
)
self._delayed_run(
d, setattr, self.root._view_container, "height", dp(55)
)
next_frame(setattr, self.root._view_container, "height", dp(55), t=h_d)
Animation(
size=[self.root.width, dp(56)],
pos=self.root.pos,
radius=[dp(28)] * 4,
t=t,
d=d,
t=self._t,
d=self._d,
).start(self.ids.root_container)
Animation(height=dp(56), t=t, d=d).start(self.ids.header)

# header
Animation(height=dp(56), t=self._t, d=self._d).start(self.ids.header)
self.icons_close(opacity_up, opacity_down, h_d)

def icons_close(self, opacity_up, opacity_down, h_d):
opacity_down.start(self.root._view_trailing_container)
opacity_down.start(self.root._view_leading_container)
self.root._bar_leading_container.opacity = 0
self.root._bar_trailing_container.opacity = 0
self._delayed_run(d / 2, self.update_state_closed)
self._delayed_run(
d / 2, opacity_up.start, self.root._bar_trailing_container
)
self._delayed_run(
d / 2, opacity_up.start, self.root._bar_leading_container
)
next_frame(self.update_state_closed, t=h_d)
next_frame(opacity_up.start, self.root._bar_trailing_container, t=h_d)
next_frame(opacity_up.start, self.root._bar_leading_container, t=h_d)

def icons_open(self, opacity_up, opacity_down, h_d):
opacity_down.start(self.root._bar_trailing_container)
opacity_down.start(self.root._bar_leading_container)
self.root._view_leading_container.opacity = 0
self.root._view_trailing_container.opacity = 0
next_frame(self.update_state_opened, t=h_d)
next_frame(opacity_up.start, self.root._view_trailing_container, t=h_d)
next_frame(opacity_up.start, self.root._view_leading_container, t=h_d)

switching_state = False

def switch_state(self, new_state):
if self.switching_state or new_state == self.state:
return
self.switching_state = True
docked = False
opacity_down = Animation(
opacity=0, t=self._transition, d=self._duration / 2
)
opacity_up = Animation(
opacity=1, t=self._transition, d=self._duration / 2
)

getattr(self, "_" + new_state)(
self._duration, self._transition, opacity_down, opacity_up, docked
)
opacity_down = Animation(opacity=0, t=self._t, d=self._d / 2)
opacity_up = Animation(opacity=1, t=self._t, d=self._d / 2)

if self.root.docked:
self.root.width = self.root.docked_width
self.ids.root_container.width = self.root.docked_width
getattr(self, "_docked_" + new_state)(opacity_down, opacity_up)
else:
getattr(self, "_" + new_state)(opacity_down, opacity_up)

if new_state == "close":
self.ids.text_input.focus = False

self.state = new_state
Clock.schedule_once(
lambda dt: setattr(self, "switching_state", False), self._duration
lambda dt: setattr(self, "switching_state", False), self._d
)

def init_state(self):
Expand All @@ -181,6 +223,11 @@ def clean_header(self):
self.ids.header.remove_widget(child)

def init_state(self):
if self.root.docked:
self.root.size_hint_x = None
self.root.width = self.root.docked_width
else:
self.root.size_hint_x = 1
self.ids.root_container.size = [self.root.width, dp(56)]
self.update_state_closed()

Expand All @@ -200,6 +247,9 @@ class MDSearchBar(Widget):
leading_icon = StringProperty("magnify")
supporting_text = StringProperty("Hinted search text")
view_root = ObjectProperty(None)
docked_width = NumericProperty(dp(360))
docked_height = NumericProperty(dp(240))
docked = BooleanProperty(False)

# internal props
_search_widget = None
Expand All @@ -219,8 +269,16 @@ class MDSearchBar(Widget):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self._search_widget = MDSearchWidget(self)
self.bind(pos=self._search_widget.update_pos)
self.bind(size=self._search_widget.update_size)
self.bind(pos=self._search_widget.update_bar)
self.bind(size=self._search_widget.update_bar)
self.on_docked(self, self.docked)

def on_docked(self, instance, docked):
if docked:
self.size_hint_x = None
self.width = self.docked_width
else:
self.size_hint_x = 1

def on_supporting_text(self, instance, text):
self._search_widget.ids.text_input.hint_text = text
Expand All @@ -230,6 +288,8 @@ def on_view_root(self, *args):
self._search_widget.parent.remove_widget(self._search_widget)
self.view_root.add_widget(self._search_widget)
self._search_widget.init_state()
self._search_widget.update_bar()
self.view_root.bind(size=self._search_widget.update_bar)

def add_widget(self, widget):
if widget.__class__.__name__ in self._view_map.keys():
Expand Down
8 changes: 8 additions & 0 deletions kivymd/utils/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
from kivy.clock import Clock

def next_frame(func, *args, **kwargs):
if time := kwargs.get("t"):
del kwargs["t"]
return Clock.schedule_once(lambda _: func(*args, **kwargs), time)
else:
return Clock.schedule_once(lambda _: func(*args, **kwargs))

0 comments on commit 48fbc22

Please sign in to comment.