Files
TermNSI/Projets/pyxel_text.py

330 lines
11 KiB
Python
Raw Permalink Normal View History

"""
Module de gestion de texte pour Pyxel
Permet d'afficher du texte et de capturer la saisie utilisateur
"""
import pyxel
# Mapping des touches Pyxel vers les caractères
KEY_MAP = {
pyxel.KEY_A: ('a', 'A'), pyxel.KEY_B: ('b', 'B'), pyxel.KEY_C: ('c', 'C'),
pyxel.KEY_D: ('d', 'D'), pyxel.KEY_E: ('e', 'E'), pyxel.KEY_F: ('f', 'F'),
pyxel.KEY_G: ('g', 'G'), pyxel.KEY_H: ('h', 'H'), pyxel.KEY_I: ('i', 'I'),
pyxel.KEY_J: ('j', 'J'), pyxel.KEY_K: ('k', 'K'), pyxel.KEY_L: ('l', 'L'),
pyxel.KEY_M: ('m', 'M'), pyxel.KEY_N: ('n', 'N'), pyxel.KEY_O: ('o', 'O'),
pyxel.KEY_P: ('p', 'P'), pyxel.KEY_Q: ('q', 'Q'), pyxel.KEY_R: ('r', 'R'),
pyxel.KEY_S: ('s', 'S'), pyxel.KEY_T: ('t', 'T'), pyxel.KEY_U: ('u', 'U'),
pyxel.KEY_V: ('v', 'V'), pyxel.KEY_W: ('w', 'W'), pyxel.KEY_X: ('x', 'X'),
pyxel.KEY_Y: ('y', 'Y'), pyxel.KEY_Z: ('z', 'Z'),
pyxel.KEY_0: ('0', ')'), pyxel.KEY_1: ('1', '!'), pyxel.KEY_2: ('2', '@'),
pyxel.KEY_3: ('3', '#'), pyxel.KEY_4: ('4', '$'), pyxel.KEY_5: ('5', '%'),
pyxel.KEY_6: ('6', '^'), pyxel.KEY_7: ('7', '&'), pyxel.KEY_8: ('8', '*'),
pyxel.KEY_9: ('9', '('),
pyxel.KEY_SPACE: (' ', ' '),
pyxel.KEY_MINUS: ('-', '_'),
pyxel.KEY_PERIOD: ('.', '>'),
pyxel.KEY_COMMA: (',', '<'),
pyxel.KEY_SLASH: ('/', '?'),
pyxel.KEY_SEMICOLON: (';', ':'),
pyxel.KEY_APOSTROPHE: ("'", '"'),
}
class TextInput:
"""
Champ de saisie de texte pour Pyxel
Exemple d'utilisation:
input_field = TextInput(x=10, y=50, max_length=20)
def update():
input_field.update()
if input_field.submitted:
print(f"Texte saisi: {input_field.text}")
input_field.reset()
def draw():
input_field.draw()
"""
def __init__(self, x=0, y=0, max_length=32, width=None, color=7, bg_color=0, cursor_color=8):
"""
Initialise un champ de saisie
Args:
x, y: Position du champ
max_length: Nombre maximum de caractères
width: Largeur du champ en pixels (auto si None)
color: Couleur du texte (0-15)
bg_color: Couleur de fond (0-15, None pour transparent)
cursor_color: Couleur du curseur (0-15)
"""
self.x = x
self.y = y
self.max_length = max_length
self.width = width if width else (max_length * 4 + 4)
self.color = color
self.bg_color = bg_color
self.cursor_color = cursor_color
self.text = ""
self.cursor_pos = 0
self.active = True
self.submitted = False
self._cursor_visible = True
self._cursor_timer = 0
def update(self):
"""Met à jour le champ de saisie (à appeler dans update())"""
if not self.active:
return
self.submitted = False
# Clignotement du curseur
self._cursor_timer += 1
if self._cursor_timer >= 15:
self._cursor_timer = 0
self._cursor_visible = not self._cursor_visible
# Validation avec Entrée
if pyxel.btnp(pyxel.KEY_RETURN):
self.submitted = True
return
# Suppression avec Backspace
if pyxel.btnp(pyxel.KEY_BACKSPACE, hold=10, repeat=3):
if self.cursor_pos > 0:
self.text = self.text[:self.cursor_pos-1] + self.text[self.cursor_pos:]
self.cursor_pos -= 1
# Suppression avec Delete
if pyxel.btnp(pyxel.KEY_DELETE, hold=10, repeat=3):
if self.cursor_pos < len(self.text):
self.text = self.text[:self.cursor_pos] + self.text[self.cursor_pos+1:]
# Navigation avec flèches
if pyxel.btnp(pyxel.KEY_LEFT, hold=10, repeat=3):
self.cursor_pos = max(0, self.cursor_pos - 1)
if pyxel.btnp(pyxel.KEY_RIGHT, hold=10, repeat=3):
self.cursor_pos = min(len(self.text), self.cursor_pos + 1)
if pyxel.btnp(pyxel.KEY_HOME):
self.cursor_pos = 0
if pyxel.btnp(pyxel.KEY_END):
self.cursor_pos = len(self.text)
# Saisie de caractères
if len(self.text) < self.max_length:
shift = pyxel.btn(pyxel.KEY_LSHIFT) or pyxel.btn(pyxel.KEY_RSHIFT)
for key, chars in KEY_MAP.items():
if pyxel.btnp(key, hold=15, repeat=3):
char = chars[1] if shift else chars[0]
self.text = self.text[:self.cursor_pos] + char + self.text[self.cursor_pos:]
self.cursor_pos += 1
break
def draw(self):
"""Dessine le champ de saisie (à appeler dans draw())"""
# Fond
if self.bg_color is not None:
pyxel.rect(self.x, self.y, self.width, 9, self.bg_color)
# Bordure si actif
if self.active:
pyxel.rectb(self.x - 1, self.y - 1, self.width + 2, 11, self.color)
# Texte
pyxel.text(self.x + 2, self.y + 1, self.text, self.color)
# Curseur
if self.active and self._cursor_visible:
cursor_x = self.x + 2 + self.cursor_pos * 4
pyxel.rect(cursor_x, self.y + 1, 1, 7, self.cursor_color)
def reset(self):
"""Réinitialise le champ"""
self.text = ""
self.cursor_pos = 0
self.submitted = False
def set_text(self, text):
"""Définit le texte du champ"""
self.text = text[:self.max_length]
self.cursor_pos = len(self.text)
class TextBox:
"""
Zone de texte multiligne avec retour à la ligne automatique
Exemple d'utilisation:
textbox = TextBox(x=10, y=10, width=100, color=7)
textbox.set_text("Ceci est un long texte qui sera automatiquement découpé en plusieurs lignes.")
def draw():
textbox.draw()
"""
def __init__(self, x=0, y=0, width=100, color=7, line_spacing=8):
"""
Initialise une zone de texte
Args:
x, y: Position de la zone
width: Largeur en pixels
color: Couleur du texte (0-15)
line_spacing: Espacement entre les lignes
"""
self.x = x
self.y = y
self.width = width
self.color = color
self.line_spacing = line_spacing
self.lines = []
def set_text(self, text):
"""Définit le texte avec retour à la ligne automatique"""
self.lines = []
chars_per_line = self.width // 4
paragraphs = text.split('\n')
for paragraph in paragraphs:
words = paragraph.split(' ')
current_line = ""
for word in words:
test_line = current_line + (" " if current_line else "") + word
if len(test_line) <= chars_per_line:
current_line = test_line
else:
if current_line:
self.lines.append(current_line)
# Mot trop long, on le coupe
while len(word) > chars_per_line:
self.lines.append(word[:chars_per_line])
word = word[chars_per_line:]
current_line = word
if current_line:
self.lines.append(current_line)
def draw(self):
"""Dessine la zone de texte"""
for i, line in enumerate(self.lines):
pyxel.text(self.x, self.y + i * self.line_spacing, line, self.color)
def get_height(self):
"""Retourne la hauteur totale du texte"""
return len(self.lines) * self.line_spacing
class TypeWriter:
"""
Effet machine à écrire pour afficher du texte progressivement
Exemple d'utilisation:
typewriter = TypeWriter(x=10, y=10, speed=3)
typewriter.set_text("Bienvenue dans le jeu...")
def update():
typewriter.update()
def draw():
typewriter.draw()
"""
def __init__(self, x=0, y=0, width=100, color=7, speed=2, line_spacing=8):
"""
Args:
x, y: Position
width: Largeur en pixels
color: Couleur du texte
speed: Frames entre chaque caractère (plus bas = plus rapide)
line_spacing: Espacement entre les lignes
"""
self.textbox = TextBox(x, y, width, color, line_spacing)
self.speed = speed
self.full_text = ""
self.displayed_chars = 0
self.timer = 0
self.finished = False
def set_text(self, text):
"""Définit le texte à afficher progressivement"""
self.full_text = text
self.displayed_chars = 0
self.timer = 0
self.finished = False
def update(self):
"""Met à jour l'animation"""
if self.finished:
return
self.timer += 1
if self.timer >= self.speed:
self.timer = 0
self.displayed_chars += 1
if self.displayed_chars >= len(self.full_text):
self.displayed_chars = len(self.full_text)
self.finished = True
self.textbox.set_text(self.full_text[:self.displayed_chars])
def draw(self):
"""Dessine le texte"""
self.textbox.draw()
def skip(self):
"""Affiche tout le texte immédiatement"""
self.displayed_chars = len(self.full_text)
self.finished = True
self.textbox.set_text(self.full_text)
def is_finished(self):
"""Retourne True si tout le texte est affiché"""
return self.finished
# --- Exemple d'utilisation ---
if __name__ == "__main__":
class Demo:
def __init__(self):
pyxel.init(200, 150, title="Demo TextInput")
self.input_field = TextInput(x=10, y=60, max_length=25, color=7)
self.typewriter = TypeWriter(x=10, y=10, width=180, speed=2)
self.typewriter.set_text("Bienvenue ! Entrez votre nom ci-dessous et appuyez sur Entree.")
self.messages = []
pyxel.run(self.update, self.draw)
def update(self):
self.typewriter.update()
self.input_field.update()
# Skip le typewriter avec espace
if pyxel.btnp(pyxel.KEY_SPACE) and not self.input_field.active:
self.typewriter.skip()
# Validation de la saisie
if self.input_field.submitted and self.input_field.text:
self.messages.append(f"Bonjour, {self.input_field.text} !")
self.input_field.reset()
def draw(self):
pyxel.cls(0)
self.typewriter.draw()
pyxel.text(10, 50, "Votre nom:", 6)
self.input_field.draw()
# Afficher les messages
for i, msg in enumerate(self.messages[-3:]):
pyxel.text(10, 90 + i * 10, msg, 11)
Demo()