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
from kivy.clock import Clock
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)
# 1. Инициализация переменной с высотой по умолчанию
self.keyboard_height = dp(0) # Значение по умолчанию
# 2. Запоминаем исходную высоту окна
self.original_window_height = Window.height
# 3. Настройка внешнего вида
Window.clearcolor = (0.4, 0.4, 0.4, 1)
# 4. Инициализация контента
self.initialize_content()
# 5. Привязка обработчиков событий
Window.bind(on_keyboard=self.handle_keyboard_event)
Window.bind(on_resize=self.handle_window_resize)
# 6. Запуск измерения клавиатуры с небольшой задержкой
Clock.schedule_once(lambda dt: self.measure_keyboard_height(), 1)
def handle_window_resize(self, window, width, height):
"""Обработчик изменения размера окна"""
# Здесь можно добавить логику реагирования на изменение размера окна
pass
def measure_keyboard_height(self):
"""Временный показ клавиатуры для определения её высоты"""
if not hasattr(self, '_measurement_done'):
# Устанавливаем фокус в текстовое поле, чтобы вызвать клавиатуру
self.ids.search_field.focus = True # Исправлено с search_input на search_field
# Запланировать замер через 0.5 секунды, когда клавиатура точно появится
Clock.schedule_once(self._finish_measurement, 0.5)
def _finish_measurement(self, dt):
"""Завершение процесса измерения высоты клавиатуры"""
# Сравниваем текущую высоту окна с исходной
if Window.height < self.original_window_height:
# Разница в высоте и есть высота клавиатуры
self.keyboard_height = self.original_window_height - Window.height
print(f"Keyboard height measured: {self.keyboard_height}dp")
# Убираем фокус и скрываем клавиатуру
self.ids.search_field.focus = False
self._measurement_done = True
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 hide_virtual_keyboard(self):
"""Dismiss the virtual keyboard"""
self.ids.search_field.focus = 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 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 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()