from kivy.config import Config
Config.set('graphics', 'width', '360')
Config.set('graphics', 'height', '640')
Config.set('graphics', 'resizable', '0')
from kivy.app import App
from kivy.lang import Builder
from kivy.animation import Animation
from kivy.core.window import Window
from kivy.metrics import dp
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button
from kivy.uix.widget import Widget
from kivy.uix.behaviors import ButtonBehavior
from kivy.uix.label import Label
from kivy.uix.modalview import ModalView
from kivy.properties import StringProperty
from kivy.utils import platform
Builder.load_string('''
:
canvas:
Color:
rgba: 0, 0, 0, 0
Rectangle:
pos: self.pos
size: self.size
:
orientation: 'horizontal'
size_hint_y: None
height: dp(50)
spacing: dp(5)
padding: dp(5)
canvas.before:
Color:
rgba: 0.4, 0.4, 0.6, 1
Rectangle:
pos: self.pos
size: self.size
Label:
id: title_label
text: root.item_title
size_hint_x: 0.8
halign: 'left'
valign: 'middle'
text_size: self.width, None
color: 1, 1, 1, 1
font_size: dp(16)
bold: True
Button:
id: info_button
text: 'i'
size_hint_x: 0.2
size_hint_y: 1
background_normal: ''
background_color: 0.8, 0.8, 0.2, 1
font_size: dp(14)
bold: True
on_press: root.show_info()
:
size_hint: (0.8, 0.5)
auto_dismiss: True
BoxLayout:
orientation: 'vertical'
padding: dp(20)
spacing: dp(10)
canvas.before:
Color:
rgba: 0.3, 0.3, 0.5, 1
Rectangle:
pos: self.pos
size: self.size
Label:
text: root.title
font_size: dp(20)
bold: True
color: 1, 1, 1, 1
size_hint_y: 0.3
Label:
text: root.description
font_size: dp(16)
color: 1, 1, 1, 1
text_size: self.width, None
valign: 'top'
halign: 'left'
size_hint_y: 0.7
Button:
text: 'Close'
size_hint_y: 0.2
background_normal: ''
background_color: 0.8, 0.3, 0.3, 1
on_press: root.dismiss()
:
orientation: 'vertical'
spacing: dp(5)
# Results notification panel
BoxLayout:
id: notification_panel
size_hint_y: None
height: 0
opacity: 0
canvas.before:
Color:
rgba: 0.2, 0.8, 0.4, 0.9
Rectangle:
pos: self.pos
size: self.size
Label:
id: notification_label
text: ''
color: 1, 1, 1, 1
font_size: dp(18)
bold: True
halign: 'center'
padding: [dp(10), dp(5)]
# Main content area with scrollable items
ScrollView:
id: content_scroll
size_hint_y: 1
do_scroll_y: True
BoxLayout:
id: items_container
orientation: 'vertical'
size_hint_y: None
height: self.minimum_height
spacing: dp(5)
padding: [dp(10), dp(0)]
# Search panel (fixed at the bottom)
BoxLayout:
id: search_controls
size_hint_y: None
height: dp(60)
padding: dp(10)
spacing: dp(5)
TextInput:
id: search_field
hint_text: 'Enter search query...'
multiline: False
size_hint_x: 0.8
padding: [dp(10), (self.height - self.line_height)/2]
font_size: dp(16)
on_focus: root.on_search_focus_changed(*args)
on_text_validate: root.perform_search()
Button:
id: action_btn
text: 'Add'
size_hint_x: 0.2
font_size: dp(13)
bold: True
background_normal: ''
background_color: (0.4, 0.6, 0.4, 1)
on_press: root.toggle_notification_panel()
# Invisible overlay for keyboard dismissal
ClickableOverlay:
id: keyboard_dismiss_area
size_hint_y: None
height: 0
on_press: root.hide_virtual_keyboard()
''')
class ClickableOverlay(ButtonBehavior, Widget):
"""Transparent overlay for handling keyboard dismissal clicks"""
pass
class InfoDialog(ModalView):
"""Custom dialog for displaying item information"""
title = StringProperty('')
description = StringProperty('')
class ContentItem(ButtonBehavior, BoxLayout):
"""Interactive content item with title and information button"""
item_title = StringProperty('')
item_description = StringProperty('')
def __init__(self, item_title="", item_description="", **kwargs):
super().__init__(**kwargs)
self.item_title = item_title
self.item_description = item_description
def on_press(self):
app = App.get_running_app()
app.root.ids.notification_label.text = f"Selected: {self.item_title}"
if app.root.ids.notification_panel.height == 0:
app.root.show_notification_panel()
def show_info(self):
"""Display information dialog for this item"""
dialog = InfoDialog(title=self.item_title,
description=self.item_description)
dialog.open()
class SearchApplicationUI(BoxLayout):
"""Main application interface with search functionality"""
def __init__(self, **kwargs):
super().__init__(**kwargs)
Window.clearcolor = (0.4, 0.4, 0.4, 1)
Window.bind(on_keyboard=self.on_keyboard)
Window.bind(on_keyboard=self.handle_keyboard_event)
Window.bind(on_resize=self.update_keyboard_height)
self.initialize_content()
def update_keyboard_height(self, window, width, height):
"""Автоматически определяем высоту клавиатуры по изменению размера окна"""
if platform == 'android':
# На Android Window.softinput_mode обычно изменяет размер окна
new_height = Window.height
if new_height < height: # Если высота уменьшилась - значит появилась клавиатура
self.keyboard_height = height - new_height
def handle_keyboard_event(self, window, key, *args):
"""Handle hardware back button press"""
if key == 27: # ESC key
if self.ids.search_field.focus:
self.hide_virtual_keyboard()
return True
return False
def initialize_content(self):
"""Populate the content area with sample items"""
for i in range(1, 21):
item = ContentItem(
item_title=f'Item {i}',
item_description=f'Detailed description for item {i}. '
f'This contains all relevant information about the item.'
)
self.ids.items_container.add_widget(item)
def on_search_focus_changed(self, instance, has_focus):
"""Handle search field focus changes"""
self.ids.keyboard_dismiss_area.height = self.keyboard_height if has_focus else 0
def hide_virtual_keyboard(self):
"""Dismiss the virtual keyboard"""
self.ids.search_field.focus = False
def toggle_notification_panel(self):
"""Toggle the notification panel visibility"""
if self.ids.notification_panel.height == 0:
query = self.ids.search_field.text.strip()
if query:
self.ids.notification_label.text = f"Added: {query}"
self.show_notification_panel()
else:
self.hide_notification_panel()
def show_notification_panel(self):
"""Animate notification panel appearance"""
Animation(height=dp(50), opacity=1, duration=0.3).start(self.ids.notification_panel)
self.ids.action_btn.text = "Remove"
self.ids.action_btn.background_color = (0.8, 0.3, 0.3, 1)
def hide_notification_panel(self):
"""Animate notification panel disappearance"""
Animation(height=0, opacity=0, duration=0.3).start(self.ids.notification_panel)
self.ids.action_btn.text = "Add"
self.ids.action_btn.background_color = (0.4, 0.6, 0.4, 1)
def perform_search(self):
"""Execute search operation"""
query = self.ids.search_field.text.strip()
if query:
self.ids.notification_label.text = f"Found: {query}"
if self.ids.notification_panel.height == 0:
self.show_notification_panel()
class SearchApplication(App):
"""Main application class"""
def build(self):
return SearchApplicationUI()
if __name__ == '__main__':
SearchApplication().run()