Add custom on-screen keyboard feature and fix installation
- Added custom half-width on-screen keyboard widget (keyboard_widget.py) - Keyboard appears at bottom center when input fields are active - Integrated keyboard in exit popup and settings popup - Fixed install.sh: added --break-system-packages flag for pip3 - Fixed install.sh: added fallback to online installation for version mismatches - Removed old Kivy 2.1.0 tar.gz that was causing conflicts - Keyboard includes close button for intuitive dismissal - All input fields trigger keyboard on touch - Keyboard automatically cleans up on popup dismiss
This commit is contained in:
13
install.sh
13
install.sh
@@ -83,13 +83,18 @@ if [ "$OFFLINE_MODE" = true ] && [ -d "$WHEELS_DIR" ] && [ "$(ls -A $WHEELS_DIR/
|
||||
echo "Installing from offline Python wheels..."
|
||||
echo "Wheel files found: $(ls -1 $WHEELS_DIR/*.whl 2>/dev/null | wc -l)"
|
||||
|
||||
pip3 install --no-index --find-links="$WHEELS_DIR" -r requirements.txt
|
||||
|
||||
echo "Python packages installed from offline repository"
|
||||
if pip3 install --break-system-packages --no-index --find-links="$WHEELS_DIR" -r requirements.txt 2>&1 | tee /tmp/pip_install.log; then
|
||||
echo "Python packages installed from offline repository"
|
||||
else
|
||||
echo "Warning: Offline installation failed (possibly due to Python version mismatch)"
|
||||
echo "Falling back to online installation..."
|
||||
pip3 install --break-system-packages -r requirements.txt
|
||||
echo "Python packages installed from PyPI"
|
||||
fi
|
||||
else
|
||||
# Online: Use pip from PyPI
|
||||
echo "Installing from PyPI..."
|
||||
pip3 install -r requirements.txt
|
||||
pip3 install --break-system-packages -r requirements.txt
|
||||
|
||||
echo "Python packages installed successfully"
|
||||
fi
|
||||
|
||||
160
src/keyboard_widget.py
Normal file
160
src/keyboard_widget.py
Normal file
@@ -0,0 +1,160 @@
|
||||
"""
|
||||
Custom Keyboard Widget for Signage Player
|
||||
Provides an on-screen keyboard for text input
|
||||
"""
|
||||
|
||||
from kivy.uix.floatlayout import FloatLayout
|
||||
from kivy.uix.boxlayout import BoxLayout
|
||||
from kivy.uix.button import Button
|
||||
from kivy.uix.widget import Widget
|
||||
from kivy.lang import Builder
|
||||
from kivy.animation import Animation
|
||||
from kivy.logger import Logger
|
||||
from kivy.core.window import Window
|
||||
from kivy.graphics import Color, RoundedRectangle
|
||||
|
||||
class KeyboardWidget(FloatLayout):
|
||||
"""Custom on-screen keyboard widget"""
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
super(KeyboardWidget, self).__init__(**kwargs)
|
||||
self.target_input = None
|
||||
self.size_hint = (None, None)
|
||||
|
||||
# Calculate size - half screen width
|
||||
self.width = Window.width * 0.5
|
||||
self.height = self.width / 3
|
||||
|
||||
# Position at bottom center
|
||||
self.x = (Window.width - self.width) / 2
|
||||
self.y = 0
|
||||
|
||||
# Start hidden
|
||||
self.opacity = 0
|
||||
|
||||
# Create the keyboard UI
|
||||
self._build_keyboard()
|
||||
|
||||
# Bind window resize
|
||||
Window.bind(on_resize=self._on_window_resize)
|
||||
|
||||
Logger.info(f"KeyboardWidget: Initialized at ({self.x}, {self.y}) with size {self.width}x{self.height}")
|
||||
|
||||
def _build_keyboard(self):
|
||||
"""Build the keyboard UI"""
|
||||
# Background
|
||||
with self.canvas.before:
|
||||
Color(0.1, 0.1, 0.1, 0.95)
|
||||
self.bg_rect = RoundedRectangle(pos=self.pos, size=self.size, radius=[15, 15, 0, 0])
|
||||
|
||||
self.bind(pos=self._update_bg, size=self._update_bg)
|
||||
|
||||
# Main layout
|
||||
main_layout = BoxLayout(orientation='vertical', padding=5, spacing=5)
|
||||
main_layout.size = self.size
|
||||
main_layout.pos = self.pos
|
||||
|
||||
# Close button bar
|
||||
close_bar = BoxLayout(orientation='horizontal', size_hint=(1, None), height=40, padding=[5, 0])
|
||||
close_bar.add_widget(Widget())
|
||||
close_btn = Button(text='✕', size_hint=(None, 1), width=40,
|
||||
background_color=(0.8, 0.2, 0.2, 0.9), font_size='20sp', bold=True)
|
||||
close_btn.bind(on_press=lambda x: self.hide_keyboard())
|
||||
close_bar.add_widget(close_btn)
|
||||
main_layout.add_widget(close_bar)
|
||||
|
||||
# Number row
|
||||
self._add_key_row(main_layout, ['1', '2', '3', '4', '5', '6', '7', '8', '9', '0'])
|
||||
|
||||
# Top letter row
|
||||
self._add_key_row(main_layout, ['Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P'])
|
||||
|
||||
# Middle letter row (with offset)
|
||||
middle_row = BoxLayout(size_hint=(1, 1), spacing=3)
|
||||
middle_row.add_widget(Widget(size_hint=(0.5, 1)))
|
||||
for letter in ['A', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L']:
|
||||
btn = Button(text=letter)
|
||||
btn.bind(on_press=lambda x, l=letter: self.key_pressed(l.lower()))
|
||||
middle_row.add_widget(btn)
|
||||
middle_row.add_widget(Widget(size_hint=(0.5, 1)))
|
||||
main_layout.add_widget(middle_row)
|
||||
|
||||
# Bottom letter row (with offset)
|
||||
bottom_row = BoxLayout(size_hint=(1, 1), spacing=3)
|
||||
bottom_row.add_widget(Widget(size_hint=(1, 1)))
|
||||
for letter in ['Z', 'X', 'C', 'V', 'B', 'N', 'M']:
|
||||
btn = Button(text=letter)
|
||||
btn.bind(on_press=lambda x, l=letter: self.key_pressed(l.lower()))
|
||||
bottom_row.add_widget(btn)
|
||||
bottom_row.add_widget(Widget(size_hint=(1, 1)))
|
||||
main_layout.add_widget(bottom_row)
|
||||
|
||||
# Space and backspace row
|
||||
last_row = BoxLayout(size_hint=(1, 1), spacing=3)
|
||||
backspace_btn = Button(text='←', size_hint=(0.3, 1), font_size='24sp')
|
||||
backspace_btn.bind(on_press=lambda x: self.key_pressed('backspace'))
|
||||
last_row.add_widget(backspace_btn)
|
||||
space_btn = Button(text='Space', size_hint=(0.7, 1))
|
||||
space_btn.bind(on_press=lambda x: self.key_pressed(' '))
|
||||
last_row.add_widget(space_btn)
|
||||
main_layout.add_widget(last_row)
|
||||
|
||||
self.add_widget(main_layout)
|
||||
|
||||
def _add_key_row(self, parent, keys):
|
||||
"""Add a row of keys"""
|
||||
row = BoxLayout(size_hint=(1, 1), spacing=3)
|
||||
for key in keys:
|
||||
btn = Button(text=key)
|
||||
btn.bind(on_press=lambda x, k=key: self.key_pressed(k.lower() if k.isalpha() else k))
|
||||
row.add_widget(btn)
|
||||
parent.add_widget(row)
|
||||
|
||||
def _update_bg(self, *args):
|
||||
"""Update background rectangle"""
|
||||
self.bg_rect.pos = self.pos
|
||||
self.bg_rect.size = self.size
|
||||
|
||||
def _on_window_resize(self, window, width, height):
|
||||
"""Handle window resize"""
|
||||
self.width = width * 0.5
|
||||
self.height = self.width / 3
|
||||
self.x = (width - self.width) / 2
|
||||
self.y = 0
|
||||
|
||||
def show_keyboard(self, target_input):
|
||||
"""Show the keyboard for a specific TextInput"""
|
||||
self.target_input = target_input
|
||||
Logger.info(f"KeyboardWidget: Showing keyboard for {target_input}")
|
||||
|
||||
# Animate keyboard appearing
|
||||
anim = Animation(opacity=1, duration=0.2)
|
||||
anim.start(self)
|
||||
|
||||
def hide_keyboard(self):
|
||||
"""Hide the keyboard"""
|
||||
Logger.info("KeyboardWidget: Hiding keyboard")
|
||||
|
||||
# Animate keyboard disappearing
|
||||
anim = Animation(opacity=0, duration=0.2)
|
||||
anim.start(self)
|
||||
|
||||
# Clear target
|
||||
if self.target_input:
|
||||
self.target_input.focus = False
|
||||
self.target_input = None
|
||||
|
||||
def key_pressed(self, key):
|
||||
"""Handle key press"""
|
||||
if not self.target_input:
|
||||
return
|
||||
|
||||
if key == 'backspace':
|
||||
# Remove last character
|
||||
if self.target_input.text:
|
||||
self.target_input.text = self.target_input.text[:-1]
|
||||
else:
|
||||
# Add character
|
||||
self.target_input.text += key
|
||||
|
||||
Logger.debug(f"KeyboardWidget: Key pressed '{key}', current text: {self.target_input.text}")
|
||||
208
src/main.py
208
src/main.py
@@ -16,8 +16,12 @@ os.environ['KIVY_VIDEO'] = 'ffpyplayer' # Use ffpyplayer as video provider
|
||||
os.environ['FFPYPLAYER_CODECS'] = 'h264,h265,vp9,vp8' # Support common codecs
|
||||
os.environ['SDL_VIDEO_ALLOW_SCREENSAVER'] = '0' # Prevent screen saver
|
||||
|
||||
from kivy.app import App
|
||||
# Configure Kivy BEFORE importing any Kivy modules
|
||||
from kivy.config import Config
|
||||
# Disable default virtual keyboard - we'll use our custom one
|
||||
Config.set('kivy', 'keyboard_mode', '')
|
||||
|
||||
from kivy.app import App
|
||||
from kivy.uix.widget import Widget
|
||||
from kivy.uix.label import Label
|
||||
from kivy.uix.image import AsyncImage, Image
|
||||
@@ -26,6 +30,7 @@ from kivy.uix.boxlayout import BoxLayout
|
||||
from kivy.uix.button import Button
|
||||
from kivy.uix.popup import Popup
|
||||
from kivy.uix.textinput import TextInput
|
||||
from kivy.uix.vkeyboard import VKeyboard
|
||||
from kivy.clock import Clock
|
||||
from kivy.core.window import Window
|
||||
from kivy.properties import BooleanProperty
|
||||
@@ -40,17 +45,146 @@ from get_playlists import (
|
||||
send_playlist_restart_feedback,
|
||||
send_player_error_feedback
|
||||
)
|
||||
from keyboard_widget import KeyboardWidget
|
||||
|
||||
# Load the KV file
|
||||
Builder.load_file('signage_player.kv')
|
||||
|
||||
# Removed VLCVideoWidget - using Kivy's built-in Video widget instead
|
||||
|
||||
# Custom keyboard container with close button
|
||||
class KeyboardContainer(BoxLayout):
|
||||
def __init__(self, vkeyboard, **kwargs):
|
||||
super(KeyboardContainer, self).__init__(**kwargs)
|
||||
self.orientation = 'vertical'
|
||||
self.vkeyboard = vkeyboard
|
||||
|
||||
# Calculate dimensions - half screen width
|
||||
container_width = Window.width * 0.5
|
||||
# Height proportional to width (maintain aspect ratio)
|
||||
# Standard keyboard aspect ratio is ~3:1 (width:height)
|
||||
keyboard_height = container_width / 3
|
||||
close_bar_height = 50
|
||||
total_height = keyboard_height + close_bar_height
|
||||
|
||||
# Set exact size (not size_hint)
|
||||
self.size_hint = (None, None)
|
||||
self.size = (container_width, total_height)
|
||||
|
||||
# Center horizontally at bottom
|
||||
self.x = (Window.width - container_width) / 2
|
||||
self.y = 0
|
||||
|
||||
# Bind to window resize
|
||||
Window.bind(on_resize=self._on_window_resize)
|
||||
|
||||
# Create close button bar
|
||||
close_bar = BoxLayout(
|
||||
orientation='horizontal',
|
||||
size_hint=(1, None),
|
||||
height=close_bar_height,
|
||||
padding=[10, 5, 10, 5]
|
||||
)
|
||||
|
||||
# Add a spacer
|
||||
close_bar.add_widget(Widget())
|
||||
|
||||
# Create close button
|
||||
close_btn = Button(
|
||||
text='✕',
|
||||
size_hint=(None, 1),
|
||||
width=50,
|
||||
background_color=(0.8, 0.2, 0.2, 0.9),
|
||||
font_size='24sp',
|
||||
bold=True
|
||||
)
|
||||
close_btn.bind(on_press=self.close_keyboard)
|
||||
close_bar.add_widget(close_btn)
|
||||
|
||||
# Add close button bar at top
|
||||
self.add_widget(close_bar)
|
||||
|
||||
# Set keyboard exact size to match container width
|
||||
self.vkeyboard.size_hint = (None, None)
|
||||
self.vkeyboard.width = container_width
|
||||
self.vkeyboard.height = keyboard_height
|
||||
|
||||
# Force keyboard to respect our dimensions
|
||||
self.vkeyboard.scale_min = container_width / Window.width
|
||||
self.vkeyboard.scale_max = container_width / Window.width
|
||||
|
||||
# Add keyboard
|
||||
self.add_widget(self.vkeyboard)
|
||||
|
||||
# Background
|
||||
with self.canvas.before:
|
||||
from kivy.graphics import Color, RoundedRectangle
|
||||
Color(0.1, 0.1, 0.1, 0.95)
|
||||
self.bg_rect = RoundedRectangle(pos=self.pos, size=self.size, radius=[15, 15, 0, 0])
|
||||
|
||||
self.bind(pos=self._update_bg, size=self._update_bg)
|
||||
|
||||
Logger.info(f"KeyboardContainer: Created at ({self.x}, {self.y}) with size {self.size}")
|
||||
|
||||
def _on_window_resize(self, window, width, height):
|
||||
"""Reposition and resize keyboard on window resize"""
|
||||
container_width = width * 0.5
|
||||
keyboard_height = container_width / 3 # Maintain aspect ratio
|
||||
close_bar_height = 50
|
||||
total_height = keyboard_height + close_bar_height
|
||||
|
||||
self.size = (container_width, total_height)
|
||||
self.x = (width - container_width) / 2
|
||||
self.y = 0
|
||||
|
||||
if self.vkeyboard:
|
||||
self.vkeyboard.width = container_width
|
||||
self.vkeyboard.height = keyboard_height
|
||||
self.vkeyboard.scale_min = container_width / width
|
||||
self.vkeyboard.scale_max = container_width / width
|
||||
|
||||
def _update_bg(self, *args):
|
||||
"""Update background rectangle"""
|
||||
self.bg_rect.pos = self.pos
|
||||
self.bg_rect.size = self.size
|
||||
|
||||
def close_keyboard(self, *args):
|
||||
"""Close the keyboard"""
|
||||
Logger.info("KeyboardContainer: Closing keyboard")
|
||||
if self.vkeyboard.target:
|
||||
self.vkeyboard.target.focus = False
|
||||
|
||||
# Custom VKeyboard that uses container
|
||||
class CustomVKeyboard(VKeyboard):
|
||||
def __init__(self, **kwargs):
|
||||
super(CustomVKeyboard, self).__init__(**kwargs)
|
||||
self.container = None
|
||||
Clock.schedule_once(self._setup_container, 0.1)
|
||||
|
||||
def _setup_container(self, dt):
|
||||
"""Wrap keyboard in a container with close button"""
|
||||
if self.parent and not self.container:
|
||||
# Remove keyboard from parent
|
||||
parent = self.parent
|
||||
parent.remove_widget(self)
|
||||
|
||||
# Create container with keyboard
|
||||
self.container = KeyboardContainer(self)
|
||||
|
||||
# Add container to parent
|
||||
parent.add_widget(self.container)
|
||||
|
||||
Logger.info("CustomVKeyboard: Wrapped in container with close button")
|
||||
|
||||
# Set the custom keyboard factory
|
||||
Window.set_vkeyboard_class(CustomVKeyboard)
|
||||
|
||||
class ExitPasswordPopup(Popup):
|
||||
def __init__(self, player_instance, was_paused=False, **kwargs):
|
||||
super(ExitPasswordPopup, self).__init__(**kwargs)
|
||||
self.player = player_instance
|
||||
self.was_paused = was_paused
|
||||
self.keyboard_widget = None
|
||||
|
||||
# Cancel all scheduled cursor/control hide events
|
||||
try:
|
||||
@@ -70,9 +204,46 @@ class ExitPasswordPopup(Popup):
|
||||
|
||||
# Bind to dismiss event to manage cursor visibility and resume playback
|
||||
self.bind(on_dismiss=self.on_popup_dismiss)
|
||||
|
||||
# Show keyboard after popup opens
|
||||
Clock.schedule_once(self._show_keyboard, 0.2)
|
||||
|
||||
def _show_keyboard(self, dt):
|
||||
"""Show the custom keyboard"""
|
||||
try:
|
||||
# Create keyboard widget and add to window
|
||||
self.keyboard_widget = KeyboardWidget()
|
||||
Window.add_widget(self.keyboard_widget)
|
||||
self.keyboard_widget.show_keyboard(self.ids.password_input)
|
||||
Logger.info("ExitPasswordPopup: Keyboard added to window")
|
||||
except Exception as e:
|
||||
Logger.warning(f"ExitPasswordPopup: Could not show keyboard: {e}")
|
||||
|
||||
def on_input_focus(self, instance, value):
|
||||
"""Handle input field focus"""
|
||||
if value and self.keyboard_widget: # Got focus
|
||||
try:
|
||||
self.keyboard_widget.show_keyboard(instance)
|
||||
except Exception as e:
|
||||
Logger.debug(f"ExitPasswordPopup: Could not show keyboard on focus: {e}")
|
||||
|
||||
def key_pressed(self, key):
|
||||
"""Handle key press from keyboard"""
|
||||
if self.keyboard_widget:
|
||||
self.keyboard_widget.key_pressed(key)
|
||||
|
||||
def hide_keyboard(self):
|
||||
"""Hide the custom keyboard"""
|
||||
if self.keyboard_widget:
|
||||
self.keyboard_widget.hide_keyboard()
|
||||
Window.remove_widget(self.keyboard_widget)
|
||||
self.keyboard_widget = None
|
||||
|
||||
def on_popup_dismiss(self, *args):
|
||||
"""Handle popup dismissal - resume playback and restart cursor hide timer"""
|
||||
# Hide and remove keyboard
|
||||
self.hide_keyboard()
|
||||
|
||||
# Resume playback if it wasn't paused before
|
||||
if not self.was_paused:
|
||||
self.player.is_paused = False
|
||||
@@ -117,6 +288,7 @@ class SettingsPopup(Popup):
|
||||
super(SettingsPopup, self).__init__(**kwargs)
|
||||
self.player = player_instance
|
||||
self.was_paused = was_paused
|
||||
self.keyboard_widget = None
|
||||
|
||||
# Cancel all scheduled cursor/control hide events
|
||||
try:
|
||||
@@ -150,8 +322,42 @@ class SettingsPopup(Popup):
|
||||
# Bind to dismiss event to manage cursor visibility and resume playback
|
||||
self.bind(on_dismiss=self.on_popup_dismiss)
|
||||
|
||||
def on_input_touch(self, instance, touch):
|
||||
"""Handle touch on input field to show keyboard"""
|
||||
Logger.info(f"SettingsPopup: Input touched: {instance}")
|
||||
self._show_keyboard(instance)
|
||||
return False # Don't consume the touch event
|
||||
|
||||
def _show_keyboard(self, target_input):
|
||||
"""Show the custom keyboard"""
|
||||
try:
|
||||
if not self.keyboard_widget:
|
||||
# Create keyboard widget and add to window
|
||||
self.keyboard_widget = KeyboardWidget()
|
||||
Window.add_widget(self.keyboard_widget)
|
||||
Logger.info("SettingsPopup: Keyboard added to window")
|
||||
|
||||
self.keyboard_widget.show_keyboard(target_input)
|
||||
except Exception as e:
|
||||
Logger.warning(f"SettingsPopup: Could not show keyboard: {e}")
|
||||
|
||||
def key_pressed(self, key):
|
||||
"""Handle key press from keyboard"""
|
||||
if self.keyboard_widget:
|
||||
self.keyboard_widget.key_pressed(key)
|
||||
|
||||
def hide_keyboard(self):
|
||||
"""Hide the custom keyboard"""
|
||||
if self.keyboard_widget:
|
||||
self.keyboard_widget.hide_keyboard()
|
||||
Window.remove_widget(self.keyboard_widget)
|
||||
self.keyboard_widget = None
|
||||
|
||||
def on_popup_dismiss(self, *args):
|
||||
"""Handle popup dismissal - resume playback and restart cursor hide timer"""
|
||||
# Hide and remove keyboard
|
||||
self.hide_keyboard()
|
||||
|
||||
# Resume playback if it wasn't paused before
|
||||
if not self.was_paused:
|
||||
self.player.is_paused = False
|
||||
|
||||
@@ -1,5 +1,193 @@
|
||||
#:kivy 2.1.0
|
||||
|
||||
# Custom On-Screen Keyboard Widget
|
||||
<CustomKeyboard@FloatLayout>:
|
||||
size_hint: None, None
|
||||
width: root.parent.width * 0.5 if root.parent else dp(600)
|
||||
height: self.width / 3
|
||||
pos: (root.parent.width - self.width) / 2 if root.parent else 0, 0
|
||||
opacity: 0
|
||||
|
||||
canvas.before:
|
||||
Color:
|
||||
rgba: 0.1, 0.1, 0.1, 0.95
|
||||
RoundedRectangle:
|
||||
size: self.size
|
||||
pos: self.pos
|
||||
radius: [dp(15), dp(15), 0, 0]
|
||||
|
||||
BoxLayout:
|
||||
orientation: 'vertical'
|
||||
padding: dp(5)
|
||||
spacing: dp(5)
|
||||
|
||||
# Close button bar
|
||||
BoxLayout:
|
||||
size_hint: 1, None
|
||||
height: dp(40)
|
||||
padding: [dp(5), 0]
|
||||
|
||||
Widget:
|
||||
|
||||
Button:
|
||||
text: '✕'
|
||||
size_hint: None, 1
|
||||
width: dp(40)
|
||||
background_color: 0.8, 0.2, 0.2, 0.9
|
||||
font_size: sp(20)
|
||||
bold: True
|
||||
on_press: root.parent.hide_keyboard() if root.parent and hasattr(root.parent, 'hide_keyboard') else None
|
||||
|
||||
# Number row
|
||||
BoxLayout:
|
||||
size_hint: 1, 1
|
||||
spacing: dp(3)
|
||||
Button:
|
||||
text: '1'
|
||||
on_press: root.parent.key_pressed('1') if root.parent and hasattr(root.parent, 'key_pressed') else None
|
||||
Button:
|
||||
text: '2'
|
||||
on_press: root.parent.key_pressed('2') if root.parent and hasattr(root.parent, 'key_pressed') else None
|
||||
Button:
|
||||
text: '3'
|
||||
on_press: root.parent.key_pressed('3') if root.parent and hasattr(root.parent, 'key_pressed') else None
|
||||
Button:
|
||||
text: '4'
|
||||
on_press: root.parent.key_pressed('4') if root.parent and hasattr(root.parent, 'key_pressed') else None
|
||||
Button:
|
||||
text: '5'
|
||||
on_press: root.parent.key_pressed('5') if root.parent and hasattr(root.parent, 'key_pressed') else None
|
||||
Button:
|
||||
text: '6'
|
||||
on_press: root.parent.key_pressed('6') if root.parent and hasattr(root.parent, 'key_pressed') else None
|
||||
Button:
|
||||
text: '7'
|
||||
on_press: root.parent.key_pressed('7') if root.parent and hasattr(root.parent, 'key_pressed') else None
|
||||
Button:
|
||||
text: '8'
|
||||
on_press: root.parent.key_pressed('8') if root.parent and hasattr(root.parent, 'key_pressed') else None
|
||||
Button:
|
||||
text: '9'
|
||||
on_press: root.parent.key_pressed('9') if root.parent and hasattr(root.parent, 'key_pressed') else None
|
||||
Button:
|
||||
text: '0'
|
||||
on_press: root.parent.key_pressed('0') if root.parent and hasattr(root.parent, 'key_pressed') else None
|
||||
|
||||
# Top letter row (QWERTYUIOP)
|
||||
BoxLayout:
|
||||
size_hint: 1, 1
|
||||
spacing: dp(3)
|
||||
Button:
|
||||
text: 'Q'
|
||||
on_press: root.parent.key_pressed('q') if root.parent and hasattr(root.parent, 'key_pressed') else None
|
||||
Button:
|
||||
text: 'W'
|
||||
on_press: root.parent.key_pressed('w') if root.parent and hasattr(root.parent, 'key_pressed') else None
|
||||
Button:
|
||||
text: 'E'
|
||||
on_press: root.parent.key_pressed('e') if root.parent and hasattr(root.parent, 'key_pressed') else None
|
||||
Button:
|
||||
text: 'R'
|
||||
on_press: root.parent.key_pressed('r') if root.parent and hasattr(root.parent, 'key_pressed') else None
|
||||
Button:
|
||||
text: 'T'
|
||||
on_press: root.parent.key_pressed('t') if root.parent and hasattr(root.parent, 'key_pressed') else None
|
||||
Button:
|
||||
text: 'Y'
|
||||
on_press: root.parent.key_pressed('y') if root.parent and hasattr(root.parent, 'key_pressed') else None
|
||||
Button:
|
||||
text: 'U'
|
||||
on_press: root.parent.key_pressed('u') if root.parent and hasattr(root.parent, 'key_pressed') else None
|
||||
Button:
|
||||
text: 'I'
|
||||
on_press: root.parent.key_pressed('i') if root.parent and hasattr(root.parent, 'key_pressed') else None
|
||||
Button:
|
||||
text: 'O'
|
||||
on_press: root.parent.key_pressed('o') if root.parent and hasattr(root.parent, 'key_pressed') else None
|
||||
Button:
|
||||
text: 'P'
|
||||
on_press: root.parent.key_pressed('p') if root.parent and hasattr(root.parent, 'key_pressed') else None
|
||||
|
||||
# Middle letter row (ASDFGHJKL)
|
||||
BoxLayout:
|
||||
size_hint: 1, 1
|
||||
spacing: dp(3)
|
||||
Widget:
|
||||
size_hint: 0.5, 1
|
||||
Button:
|
||||
text: 'A'
|
||||
on_press: root.parent.key_pressed('a') if root.parent and hasattr(root.parent, 'key_pressed') else None
|
||||
Button:
|
||||
text: 'S'
|
||||
on_press: root.parent.key_pressed('s') if root.parent and hasattr(root.parent, 'key_pressed') else None
|
||||
Button:
|
||||
text: 'D'
|
||||
on_press: root.parent.key_pressed('d') if root.parent and hasattr(root.parent, 'key_pressed') else None
|
||||
Button:
|
||||
text: 'F'
|
||||
on_press: root.parent.key_pressed('f') if root.parent and hasattr(root.parent, 'key_pressed') else None
|
||||
Button:
|
||||
text: 'G'
|
||||
on_press: root.parent.key_pressed('g') if root.parent and hasattr(root.parent, 'key_pressed') else None
|
||||
Button:
|
||||
text: 'H'
|
||||
on_press: root.parent.key_pressed('h') if root.parent and hasattr(root.parent, 'key_pressed') else None
|
||||
Button:
|
||||
text: 'J'
|
||||
on_press: root.parent.key_pressed('j') if root.parent and hasattr(root.parent, 'key_pressed') else None
|
||||
Button:
|
||||
text: 'K'
|
||||
on_press: root.parent.key_pressed('k') if root.parent and hasattr(root.parent, 'key_pressed') else None
|
||||
Button:
|
||||
text: 'L'
|
||||
on_press: root.parent.key_pressed('l') if root.parent and hasattr(root.parent, 'key_pressed') else None
|
||||
Widget:
|
||||
size_hint: 0.5, 1
|
||||
|
||||
# Bottom letter row (ZXCVBNM)
|
||||
BoxLayout:
|
||||
size_hint: 1, 1
|
||||
spacing: dp(3)
|
||||
Widget:
|
||||
size_hint: 1, 1
|
||||
Button:
|
||||
text: 'Z'
|
||||
on_press: root.parent.key_pressed('z') if root.parent and hasattr(root.parent, 'key_pressed') else None
|
||||
Button:
|
||||
text: 'X'
|
||||
on_press: root.parent.key_pressed('x') if root.parent and hasattr(root.parent, 'key_pressed') else None
|
||||
Button:
|
||||
text: 'C'
|
||||
on_press: root.parent.key_pressed('c') if root.parent and hasattr(root.parent, 'key_pressed') else None
|
||||
Button:
|
||||
text: 'V'
|
||||
on_press: root.parent.key_pressed('v') if root.parent and hasattr(root.parent, 'key_pressed') else None
|
||||
Button:
|
||||
text: 'B'
|
||||
on_press: root.parent.key_pressed('b') if root.parent and hasattr(root.parent, 'key_pressed') else None
|
||||
Button:
|
||||
text: 'N'
|
||||
on_press: root.parent.key_pressed('n') if root.parent and hasattr(root.parent, 'key_pressed') else None
|
||||
Button:
|
||||
text: 'M'
|
||||
on_press: root.parent.key_pressed('m') if root.parent and hasattr(root.parent, 'key_pressed') else None
|
||||
Widget:
|
||||
size_hint: 1, 1
|
||||
|
||||
# Bottom row (Space, Backspace)
|
||||
BoxLayout:
|
||||
size_hint: 1, 1
|
||||
spacing: dp(3)
|
||||
Button:
|
||||
text: '←'
|
||||
size_hint: 0.3, 1
|
||||
font_size: sp(24)
|
||||
on_press: root.parent.key_pressed('backspace') if root.parent and hasattr(root.parent, 'key_pressed') else None
|
||||
Button:
|
||||
text: 'Space'
|
||||
size_hint: 0.7, 1
|
||||
on_press: root.parent.key_pressed(' ') if root.parent and hasattr(root.parent, 'key_pressed') else None
|
||||
|
||||
<SignagePlayer@FloatLayout>:
|
||||
size: root.screen_width, root.screen_height
|
||||
canvas.before:
|
||||
@@ -126,6 +314,9 @@
|
||||
font_size: sp(16)
|
||||
size_hint_y: None
|
||||
height: dp(40)
|
||||
write_tab: False
|
||||
readonly: True
|
||||
on_focus: root.on_input_focus(self, self.focus)
|
||||
|
||||
Label:
|
||||
id: error_label
|
||||
@@ -180,6 +371,8 @@
|
||||
size_hint_x: 0.7
|
||||
multiline: False
|
||||
font_size: sp(14)
|
||||
write_tab: False
|
||||
on_touch_down: root.on_input_touch(self, args[1]) if self.collide_point(*args[1].pos) else None
|
||||
|
||||
# Screen name
|
||||
BoxLayout:
|
||||
@@ -200,6 +393,8 @@
|
||||
size_hint_x: 0.7
|
||||
multiline: False
|
||||
font_size: sp(14)
|
||||
write_tab: False
|
||||
on_touch_down: root.on_input_touch(self, args[1]) if self.collide_point(*args[1].pos) else None
|
||||
|
||||
# Quickconnect key
|
||||
BoxLayout:
|
||||
@@ -220,6 +415,8 @@
|
||||
size_hint_x: 0.7
|
||||
multiline: False
|
||||
font_size: sp(14)
|
||||
write_tab: False
|
||||
on_touch_down: root.on_input_touch(self, args[1]) if self.collide_point(*args[1].pos) else None
|
||||
|
||||
# Orientation
|
||||
BoxLayout:
|
||||
@@ -240,6 +437,8 @@
|
||||
size_hint_x: 0.7
|
||||
multiline: False
|
||||
font_size: sp(14)
|
||||
write_tab: False
|
||||
on_touch_down: root.on_input_touch(self, args[1]) if self.collide_point(*args[1].pos) else None
|
||||
|
||||
# Touch
|
||||
BoxLayout:
|
||||
@@ -260,6 +459,8 @@
|
||||
size_hint_x: 0.7
|
||||
multiline: False
|
||||
font_size: sp(14)
|
||||
write_tab: False
|
||||
on_touch_down: root.on_input_touch(self, args[1]) if self.collide_point(*args[1].pos) else None
|
||||
|
||||
# Resolution
|
||||
BoxLayout:
|
||||
@@ -281,6 +482,8 @@
|
||||
multiline: False
|
||||
font_size: sp(14)
|
||||
hint_text: '1920x1080 or auto'
|
||||
write_tab: False
|
||||
on_touch_down: root.on_input_touch(self, args[1]) if self.collide_point(*args[1].pos) else None
|
||||
|
||||
Widget:
|
||||
size_hint_y: 0.05
|
||||
|
||||
Reference in New Issue
Block a user