297 lines
No EOL
10 KiB
Python
297 lines
No EOL
10 KiB
Python
"""Редактор заметок"""
|
||
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) |