Captain_s-Log/app/ui/note_editor.py

297 lines
10 KiB
Python
Raw Permalink Normal View History

2026-07-04 16:04:48 +03:00
"""Редактор заметок"""
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)