Captain_s-Log/app/ui/note_editor.py
Vic Sergeev a385308f38 working
2026-07-04 16:04:48 +03:00

297 lines
No EOL
10 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""Редактор заметок"""
import tkinter as tk
from tkinter import ttk, messagebox
from datetime import datetime
from app.config import Config
from app.ui.dialogs import ConfirmDialog
class NoteEditor(tk.Frame):
"""Правая панель с редактором заметок"""
def __init__(self, parent, db, on_note_saved=None, on_note_deleted=None, on_new_note=None):
super().__init__(parent, bg=Config.COLORS['editor_bg'])
self.db = db
self.on_note_saved = on_note_saved
self.on_note_deleted = on_note_deleted
self.on_new_note = on_new_note
self.current_note = None
self.is_new_note = False
self.is_editing = False
self.create_widgets()
self.show_empty_state()
def create_widgets(self):
# Панель инструментов
self.toolbar = tk.Frame(self, bg=Config.COLORS['sidebar_bg'], height=40)
self.toolbar.pack(fill='x')
self.toolbar.pack_propagate(False)
# Дата и время (левый край)
self.date_label = tk.Label(
self.toolbar,
text="",
bg=Config.COLORS['sidebar_bg'],
fg=Config.COLORS['fg'],
font=('Arial', 11)
)
self.date_label.pack(side='left', padx=10)
# Кнопки (правый край)
buttons_frame = tk.Frame(self.toolbar, bg=Config.COLORS['sidebar_bg'])
buttons_frame.pack(side='right', padx=5)
# Кнопка сохранения
self.save_btn = tk.Button(
buttons_frame,
text="💾",
command=self.save_note,
bg=Config.COLORS['button_bg'],
fg=Config.COLORS['fg'],
relief='flat',
font=('Arial', 14),
cursor='hand2',
width=2
)
self.save_btn.pack(side='left', padx=2)
# Кнопка удаления
self.delete_btn = tk.Button(
buttons_frame,
text="🗑",
command=self.delete_current_note,
bg=Config.COLORS['button_bg'],
fg=Config.COLORS['fg'],
relief='flat',
font=('Arial', 14),
cursor='hand2',
width=2
)
self.delete_btn.pack(side='left', padx=2)
# Кнопка создания новой заметки
self.new_btn = tk.Button(
buttons_frame,
text="+",
command=self.create_new_note,
bg=Config.COLORS['accent'],
fg=Config.COLORS['fg'],
relief='flat',
font=('Arial', 14, 'bold'),
cursor='hand2',
width=2
)
self.new_btn.pack(side='left', padx=2)
# Скрываем кнопки сохранения и удаления по умолчанию
self.hide_editor_buttons()
# Область контента
self.content_frame = tk.Frame(self, bg=Config.COLORS['editor_bg'])
self.content_frame.pack(fill='both', expand=True, padx=10, pady=10)
# Текстовое поле для редактирования
self.text_widget = tk.Text(
self.content_frame,
bg=Config.COLORS['text_bg'],
fg=Config.COLORS['text_fg'],
font=('Arial', 12),
wrap='word',
relief='flat',
padx=10,
pady=10,
insertbackground=Config.COLORS['fg']
)
# Scrollbar для текстового поля
self.text_scrollbar = ttk.Scrollbar(
self.content_frame,
orient='vertical',
command=self.text_widget.yview
)
self.text_widget.configure(yscrollcommand=self.text_scrollbar.set)
# Метка для пустого состояния
self.empty_label = tk.Label(
self.content_frame,
text="Выберите заметку из списка\nили создайте новую",
bg=Config.COLORS['editor_bg'],
fg=Config.COLORS['fg'],
font=('Arial', 14),
justify='center'
)
self.empty_label.pack(expand=True)
# Привязываем события изменения текста
self.text_widget.bind('<<Modified>>', self.on_text_modified)
def show_editor_buttons(self):
"""Показать кнопки редактора"""
self.save_btn.pack(side='left', padx=2)
self.delete_btn.pack(side='left', padx=2)
def hide_editor_buttons(self):
"""Скрыть кнопки редактора"""
self.save_btn.pack_forget()
self.delete_btn.pack_forget()
def show_empty_state(self):
"""Показать пустое состояние"""
self.text_widget.pack_forget()
self.text_scrollbar.pack_forget()
self.empty_label.pack(expand=True)
self.date_label.config(text="")
self.hide_editor_buttons()
self.current_note = None
self.is_new_note = False
self.is_editing = False
def show_note(self, note):
"""Показать существующую заметку"""
self.current_note = note
self.is_new_note = False
self.is_editing = False
# Скрываем пустую метку
self.empty_label.pack_forget()
# Показываем текстовое поле
self.text_widget.pack(side='left', fill='both', expand=True)
self.text_scrollbar.pack(side='right', fill='y')
# Устанавливаем дату
created_at = datetime.strptime(
note['created_at'], '%Y-%m-%d %H:%M:%S'
)
self.date_label.config(
text=created_at.strftime('%d.%m.%Y %H:%M')
)
# Очищаем и вставляем текст
self.text_widget.delete('1.0', 'end')
self.text_widget.insert('1.0', note.get('content', ''))
self.text_widget.edit_modified(False)
# Показываем кнопки
self.show_editor_buttons()
# Делаем поле только для чтения
self.text_widget.configure(state='disabled')
# Привязываем клик для редактирования
self.text_widget.bind('<Button-1>', self.enable_editing)
def enable_editing(self, event=None):
"""Включить режим редактирования"""
if not self.is_editing and self.current_note:
self.is_editing = True
self.text_widget.configure(state='normal')
self.text_widget.unbind('<Button-1>')
def create_new_note(self):
"""Создать новую заметку"""
self.current_note = None
self.is_new_note = True
self.is_editing = True
# Скрываем пустую метку
self.empty_label.pack_forget()
# Показываем текстовое поле
self.text_widget.pack(side='left', fill='both', expand=True)
self.text_scrollbar.pack(side='right', fill='y')
# Устанавливаем текущую дату
now = datetime.now()
self.date_label.config(text=now.strftime('%d.%m.%Y %H:%M'))
# Очищаем текстовое поле
self.text_widget.delete('1.0', 'end')
self.text_widget.configure(state='normal')
self.text_widget.edit_modified(False)
# Показываем кнопки
self.show_editor_buttons()
# Убираем привязку клика
self.text_widget.unbind('<Button-1>')
# Ставим фокус
self.text_widget.focus_set()
if self.on_new_note:
self.on_new_note()
def save_note(self):
"""Сохранить заметку"""
content = self.text_widget.get('1.0', 'end-1c')
if not content.strip() and self.is_new_note:
messagebox.showwarning(
"Предупреждение",
"Нельзя сохранить пустую заметку"
)
return
if self.is_new_note:
# Создаем новую заметку
now = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
note_id = self.db.create_note(
title=None,
content=content,
created_at=now
)
self.current_note = self.db.get_note_by_id(note_id)
self.is_new_note = False
self.is_editing = False
# Делаем поле только для чтения
self.text_widget.configure(state='disabled')
self.text_widget.bind('<Button-1>', self.enable_editing)
messagebox.showinfo("Успех", "Заметка сохранена")
else:
# Обновляем существующую заметку
self.db.update_note(
self.current_note['id'],
content=content
)
self.current_note = self.db.get_note_by_id(self.current_note['id'])
self.is_editing = False
# Делаем поле только для чтения
self.text_widget.configure(state='disabled')
self.text_widget.bind('<Button-1>', self.enable_editing)
messagebox.showinfo("Успех", "Заметка обновлена")
self.text_widget.edit_modified(False)
if self.on_note_saved:
self.on_note_saved(self.current_note)
def delete_current_note(self):
"""Удалить текущую заметку"""
if not self.current_note:
return
dialog = ConfirmDialog(
self.winfo_toplevel(),
"Удаление заметки",
"Вы уверены, что хотите удалить эту заметку?"
)
self.wait_window(dialog)
if dialog.result:
self.db.delete_note(self.current_note['id'])
self.show_empty_state()
if self.on_note_deleted:
self.on_note_deleted()
def on_text_modified(self, event=None):
"""Обработчик изменения текста"""
if self.text_widget.edit_modified() and self.current_note and not self.is_editing:
# Текст был изменен программно, игнорируем
self.text_widget.edit_modified(False)