Skip to content

Commit 48fbc22

Browse files
committed
dock test
1 parent 19576d4 commit 48fbc22

File tree

4 files changed

+144
-66
lines changed

4 files changed

+144
-66
lines changed

Diff for: examples/search.py

+15-7
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
MDScreen:
1010
md_bg_color:app.theme_cls.backgroundColor
1111
BoxLayout:
12-
padding:[dp(10), dp(50)]
12+
padding:[dp(10), dp(50), dp(10), dp(10)]
1313
orientation:"vertical"
1414
1515
MDSearchBar:
@@ -20,7 +20,8 @@
2020
# Search Bar
2121
MDSearchBarLeadingContainer:
2222
MDSearchLeadingIcon:
23-
icon:"magnify"
23+
icon: "menu"
24+
on_release: app.open_menu(self)
2425
2526
MDSearchBarTrailingContainer:
2627
MDSearchTrailingIcon:
@@ -39,15 +40,22 @@
3940
icon:"window-close"
4041
4142
MDSearchViewContainer:
43+
size_hint_y:1
4244
MDLabel:
4345
text:"Hello World!"
46+
halign:"center"
4447
4548
Widget:
46-
MDSwitch:
47-
on_active:app.theme_cls.theme_style = "Dark" if app.theme_cls.theme_style == "Light" else "Light"
48-
Widget:
49-
50-
49+
BoxLayout:
50+
size_hint_y:None
51+
height:dp(50)
52+
padding:[dp(50), 0]
53+
spacing:dp(10)
54+
MDLabel:
55+
text:"Bar dock"
56+
halign:"center"
57+
MDSwitch:
58+
on_active:search_bar.docked = args[-1]
5159
"""
5260

5361
class Example(MDApp, CommonApp):

Diff for: kivymd/uix/search/search.kv

+3-1
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
[self.x+self.width, self.y+self.height]]
4545

4646
<MDSearchWidget>:
47+
size_hint:[1,1]
4748
MDBoxLayout:
4849
id: root_container
4950
orientation: 'vertical'
@@ -67,7 +68,8 @@
6768
multiline: False
6869
font_size: root._font_style["font-size"]
6970
on_focus: if args[-1]: root.switch_state("open")
70-
Widget:
71+
Widget:
72+
id: _wid
7173
<MDSearchBar>:
7274
size_hint_y: None
7375
height: dp(56)

Diff for: kivymd/uix/search/search.py

+118-58
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
NumericProperty,
2626
ObjectProperty,
2727
StringProperty,
28+
BooleanProperty
2829
)
2930
from kivy.uix.behaviors import ButtonBehavior
3031
from kivy.uix.boxlayout import BoxLayout
@@ -34,9 +35,9 @@
3435

3536
from kivymd import uix_path
3637
from kivymd.font_definitions import theme_font_styles
37-
from kivymd.uix.behaviors import StencilBehavior
3838
from kivymd.uix.boxlayout import MDBoxLayout
3939
from kivymd.uix.label import MDIcon
40+
from kivymd.utils import next_frame
4041

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

7475

75-
class MDSearchViewContainer(StencilBehavior, BoxLayout):
76+
class MDSearchViewContainer(BoxLayout):
7677
pass
7778

7879

7980
class MDSearchWidget(RelativeLayout):
8081

8182
_font_style = theme_font_styles["Title"]["medium"]
82-
_duration = 0.4
83-
_transition = "in_out_circ"
84-
state = "closed"
83+
_d = 0.4
84+
_t = "in_out_circ"
85+
state = "close"
8586

8687
def __init__(self, root, *args, **kwargs):
8788
super().__init__(*args, **kwargs)
8889
self.root = root
8990

90-
def update_pos(self, *args):
91-
self.ids.root_container.pos = self.root.pos
91+
def update_bar(self, *args):
92+
if self.state == "close":
93+
self.ids.root_container.size = self.root.size
94+
self.ids.root_container.radius = dp(28)
95+
self.ids.root_container.pos = self.root.pos
96+
else:
97+
if self.root.docked:
98+
self.ids.root_container.radius = [dp(28)] * 4
99+
docked_size = self.root.width, dp(56) + self.root.docked_height
100+
self.ids.root_container.pos = [
101+
self.root.pos[0], self.root.pos[1] - docked_size[1] + dp(56)
102+
]
103+
else:
104+
self.ids.root_container.radius = [0] * 4
105+
self.ids.root_container.pos = [0,0]
106+
self.ids.root_container.size = self.size
107+
108+
def _docked_open(self, opacity_down, opacity_up):
109+
docked_size = self.root.width, dp(56) + self.root.docked_height
110+
Animation(
111+
size=docked_size,
112+
pos=[self.root.pos[0], self.root.pos[1] - docked_size[1] + dp(56)],
113+
radius=[dp(28)] * 4,
114+
t=self._t,
115+
d=self._d,
116+
).start(self.ids.root_container)
117+
self.root._view_container.size_hint_y = 1
118+
self.root._view_container.opacity = 0
119+
self.root._view_container.padding = [0, 0, 0, dp(16)]
120+
next_frame(
121+
self.ids.root_container.add_widget,
122+
self.root._view_container,
123+
index=1,
124+
t=self._d,
125+
)
126+
next_frame(opacity_up.start, self.root._view_container, t=self._d)
127+
self.icons_open(opacity_up, opacity_down, self._d / 2)
92128

93-
def update_size(self, *args):
94-
self.ids.root_container.size = self.root.size
95-
self.ids.root_container.radius = dp(28)
129+
def _docked_close(self, opacity_down, opacity_up):
130+
self._close(opacity_down, opacity_up)
96131

97-
def _delayed_run(self, delay, func, *args):
98-
Clock.schedule_once(lambda dt: func(*args), delay)
132+
def _open(self, opacity_down, opacity_up):
133+
h_d = self._d / 2
99134

100-
def _open(self, d, t, opacity_down, opacity_up, docked):
135+
# container
101136
self.root._view_container.size_hint_y = 1
102137
self.root._view_container.opacity = 0
138+
self.root._view_container.padding = [0] * 4
103139
self.ids.root_container.add_widget(self.root._view_container, index=1)
104-
self._delayed_run(d / 4, opacity_up.start, self.root._view_container)
105-
Animation(size=self.size, pos=self.pos, radius=[0] * 4, t=t, d=d).start(
106-
self.ids.root_container
107-
)
108-
Animation(height=dp(70), t=t, d=d).start(self.ids.header)
109-
opacity_down.start(self.root._bar_trailing_container)
110-
opacity_down.start(self.root._bar_leading_container)
111-
self.root._view_leading_container.opacity = 0
112-
self.root._view_trailing_container.opacity = 0
113-
self._delayed_run(d / 2, self.update_state_opened)
114-
self._delayed_run(
115-
d / 2, opacity_up.start, self.root._view_trailing_container
116-
)
117-
self._delayed_run(
118-
d / 2, opacity_up.start, self.root._view_leading_container
119-
)
140+
next_frame(opacity_up.start, self.root._view_container, t=h_d / 2)
141+
Animation(
142+
size=self.size, pos=self.pos, radius=[0] * 4, t=self._t, d=self._d
143+
).start(self.ids.root_container)
120144

121-
def _close(self, d, t, opacity_down, opacity_up, docked):
145+
# header
146+
Animation(height=dp(70), t=self._t, d=self._d).start(self.ids.header)
147+
self.icons_open(opacity_up, opacity_down, h_d)
148+
149+
def _close(self, opacity_down, opacity_up):
150+
h_d = self._d / 2
151+
# container
122152
self.root._view_container.size_hint_y = None
123153
self.root._view_container.opacity = 1
124154
opacity_down.start(self.root._view_container)
125155
if self.root._view_container in self.ids.root_container.children:
126-
self._delayed_run(
127-
d / 2,
156+
next_frame(
128157
self.ids.root_container.remove_widget,
129158
self.root._view_container,
159+
t=h_d,
130160
)
131-
self._delayed_run(
132-
d, setattr, self.root._view_container, "height", dp(55)
133-
)
161+
next_frame(setattr, self.root._view_container, "height", dp(55), t=h_d)
134162
Animation(
135163
size=[self.root.width, dp(56)],
136164
pos=self.root.pos,
137165
radius=[dp(28)] * 4,
138-
t=t,
139-
d=d,
166+
t=self._t,
167+
d=self._d,
140168
).start(self.ids.root_container)
141-
Animation(height=dp(56), t=t, d=d).start(self.ids.header)
169+
170+
# header
171+
Animation(height=dp(56), t=self._t, d=self._d).start(self.ids.header)
172+
self.icons_close(opacity_up, opacity_down, h_d)
173+
174+
def icons_close(self, opacity_up, opacity_down, h_d):
142175
opacity_down.start(self.root._view_trailing_container)
143176
opacity_down.start(self.root._view_leading_container)
144177
self.root._bar_leading_container.opacity = 0
145178
self.root._bar_trailing_container.opacity = 0
146-
self._delayed_run(d / 2, self.update_state_closed)
147-
self._delayed_run(
148-
d / 2, opacity_up.start, self.root._bar_trailing_container
149-
)
150-
self._delayed_run(
151-
d / 2, opacity_up.start, self.root._bar_leading_container
152-
)
179+
next_frame(self.update_state_closed, t=h_d)
180+
next_frame(opacity_up.start, self.root._bar_trailing_container, t=h_d)
181+
next_frame(opacity_up.start, self.root._bar_leading_container, t=h_d)
182+
183+
def icons_open(self, opacity_up, opacity_down, h_d):
184+
opacity_down.start(self.root._bar_trailing_container)
185+
opacity_down.start(self.root._bar_leading_container)
186+
self.root._view_leading_container.opacity = 0
187+
self.root._view_trailing_container.opacity = 0
188+
next_frame(self.update_state_opened, t=h_d)
189+
next_frame(opacity_up.start, self.root._view_trailing_container, t=h_d)
190+
next_frame(opacity_up.start, self.root._view_leading_container, t=h_d)
153191

154192
switching_state = False
155193

156194
def switch_state(self, new_state):
157195
if self.switching_state or new_state == self.state:
158196
return
159197
self.switching_state = True
160-
docked = False
161-
opacity_down = Animation(
162-
opacity=0, t=self._transition, d=self._duration / 2
163-
)
164-
opacity_up = Animation(
165-
opacity=1, t=self._transition, d=self._duration / 2
166-
)
167198

168-
getattr(self, "_" + new_state)(
169-
self._duration, self._transition, opacity_down, opacity_up, docked
170-
)
199+
opacity_down = Animation(opacity=0, t=self._t, d=self._d / 2)
200+
opacity_up = Animation(opacity=1, t=self._t, d=self._d / 2)
201+
202+
if self.root.docked:
203+
self.root.width = self.root.docked_width
204+
self.ids.root_container.width = self.root.docked_width
205+
getattr(self, "_docked_" + new_state)(opacity_down, opacity_up)
206+
else:
207+
getattr(self, "_" + new_state)(opacity_down, opacity_up)
208+
209+
if new_state == "close":
210+
self.ids.text_input.focus = False
211+
212+
self.state = new_state
171213
Clock.schedule_once(
172-
lambda dt: setattr(self, "switching_state", False), self._duration
214+
lambda dt: setattr(self, "switching_state", False), self._d
173215
)
174216

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

183225
def init_state(self):
226+
if self.root.docked:
227+
self.root.size_hint_x = None
228+
self.root.width = self.root.docked_width
229+
else:
230+
self.root.size_hint_x = 1
184231
self.ids.root_container.size = [self.root.width, dp(56)]
185232
self.update_state_closed()
186233

@@ -200,6 +247,9 @@ class MDSearchBar(Widget):
200247
leading_icon = StringProperty("magnify")
201248
supporting_text = StringProperty("Hinted search text")
202249
view_root = ObjectProperty(None)
250+
docked_width = NumericProperty(dp(360))
251+
docked_height = NumericProperty(dp(240))
252+
docked = BooleanProperty(False)
203253

204254
# internal props
205255
_search_widget = None
@@ -219,8 +269,16 @@ class MDSearchBar(Widget):
219269
def __init__(self, *args, **kwargs):
220270
super().__init__(*args, **kwargs)
221271
self._search_widget = MDSearchWidget(self)
222-
self.bind(pos=self._search_widget.update_pos)
223-
self.bind(size=self._search_widget.update_size)
272+
self.bind(pos=self._search_widget.update_bar)
273+
self.bind(size=self._search_widget.update_bar)
274+
self.on_docked(self, self.docked)
275+
276+
def on_docked(self, instance, docked):
277+
if docked:
278+
self.size_hint_x = None
279+
self.width = self.docked_width
280+
else:
281+
self.size_hint_x = 1
224282

225283
def on_supporting_text(self, instance, text):
226284
self._search_widget.ids.text_input.hint_text = text
@@ -230,6 +288,8 @@ def on_view_root(self, *args):
230288
self._search_widget.parent.remove_widget(self._search_widget)
231289
self.view_root.add_widget(self._search_widget)
232290
self._search_widget.init_state()
291+
self._search_widget.update_bar()
292+
self.view_root.bind(size=self._search_widget.update_bar)
233293

234294
def add_widget(self, widget):
235295
if widget.__class__.__name__ in self._view_map.keys():

Diff for: kivymd/utils/__init__.py

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
from kivy.clock import Clock
2+
3+
def next_frame(func, *args, **kwargs):
4+
if time := kwargs.get("t"):
5+
del kwargs["t"]
6+
return Clock.schedule_once(lambda _: func(*args, **kwargs), time)
7+
else:
8+
return Clock.schedule_once(lambda _: func(*args, **kwargs))

0 commit comments

Comments
 (0)