230 lines
7.8 KiB
Python
230 lines
7.8 KiB
Python
|
|
"""Список заметок"""
|
|||
|
|
import tkinter as tk
|
|||
|
|
from tkinter import ttk
|
|||
|
|
from datetime import datetime
|
|||
|
|
from app.config import Config
|
|||
|
|
from app.ui.dialogs import ConfirmDialog
|
|||
|
|
|
|||
|
|
|
|||
|
|
class NotesList(tk.Frame):
|
|||
|
|
"""Виджет списка заметок"""
|
|||
|
|
|
|||
|
|
def __init__(self, parent, db, on_note_select=None, on_note_delete=None):
|
|||
|
|
super().__init__(parent, bg=Config.COLORS['list_bg'])
|
|||
|
|
self.db = db
|
|||
|
|
self.on_note_select = on_note_select
|
|||
|
|
self.on_note_delete = on_note_delete
|
|||
|
|
|
|||
|
|
self.notes = []
|
|||
|
|
self.selected_note_id = None
|
|||
|
|
|
|||
|
|
self.create_widgets()
|
|||
|
|
|
|||
|
|
def create_widgets(self):
|
|||
|
|
# Заголовок
|
|||
|
|
self.header_label = tk.Label(
|
|||
|
|
self,
|
|||
|
|
text="Заметки",
|
|||
|
|
bg=Config.COLORS['list_bg'],
|
|||
|
|
fg=Config.COLORS['fg'],
|
|||
|
|
font=('Arial', 12, 'bold')
|
|||
|
|
)
|
|||
|
|
self.header_label.pack(fill='x', padx=10, pady=(10, 5))
|
|||
|
|
|
|||
|
|
# Разделитель
|
|||
|
|
separator = tk.Frame(self, height=1, bg=Config.COLORS['separator'])
|
|||
|
|
separator.pack(fill='x', padx=5)
|
|||
|
|
|
|||
|
|
# Canvas и Scrollbar для списка
|
|||
|
|
self.canvas = tk.Canvas(
|
|||
|
|
self,
|
|||
|
|
bg=Config.COLORS['list_bg'],
|
|||
|
|
highlightthickness=0
|
|||
|
|
)
|
|||
|
|
self.scrollbar = ttk.Scrollbar(
|
|||
|
|
self,
|
|||
|
|
orient="vertical",
|
|||
|
|
command=self.canvas.yview
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
self.notes_frame = tk.Frame(self.canvas, bg=Config.COLORS['list_bg'])
|
|||
|
|
|
|||
|
|
self.notes_frame.bind("<Configure>",
|
|||
|
|
lambda e: self.canvas.configure(scrollregion=self.canvas.bbox("all")))
|
|||
|
|
|
|||
|
|
self.canvas_window = self.canvas.create_window(
|
|||
|
|
(0, 0), window=self.notes_frame, anchor="nw"
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
self.canvas.configure(yscrollcommand=self.scrollbar.set)
|
|||
|
|
|
|||
|
|
self.canvas.pack(side="left", fill="both", expand=True)
|
|||
|
|
self.scrollbar.pack(side="right", fill="y")
|
|||
|
|
|
|||
|
|
# Bind для изменения размера
|
|||
|
|
self.canvas.bind('<Configure>', self.on_canvas_configure)
|
|||
|
|
|
|||
|
|
# Bind для колесика мыши
|
|||
|
|
self.canvas.bind_all("<MouseWheel>", self.on_mousewheel)
|
|||
|
|
|
|||
|
|
def on_canvas_configure(self, event):
|
|||
|
|
self.canvas.itemconfig(self.canvas_window, width=event.width)
|
|||
|
|
|
|||
|
|
def on_mousewheel(self, event):
|
|||
|
|
self.canvas.yview_scroll(int(-1 * (event.delta / 120)), "units")
|
|||
|
|
|
|||
|
|
def load_notes(self, date):
|
|||
|
|
"""Загрузить заметки для выбранной даты"""
|
|||
|
|
# Очищаем текущий список
|
|||
|
|
for widget in self.notes_frame.winfo_children():
|
|||
|
|
widget.destroy()
|
|||
|
|
|
|||
|
|
date_str = date.strftime('%Y-%m-%d')
|
|||
|
|
self.notes = self.db.get_notes_with_content_by_date(date_str)
|
|||
|
|
|
|||
|
|
if not self.notes:
|
|||
|
|
empty_label = tk.Label(
|
|||
|
|
self.notes_frame,
|
|||
|
|
text="Нет заметок\nна этот день",
|
|||
|
|
bg=Config.COLORS['list_bg'],
|
|||
|
|
fg=Config.COLORS['fg'],
|
|||
|
|
font=('Arial', 10),
|
|||
|
|
justify='center'
|
|||
|
|
)
|
|||
|
|
empty_label.pack(expand=True, pady=20)
|
|||
|
|
return
|
|||
|
|
|
|||
|
|
for note in self.notes:
|
|||
|
|
self.create_note_item(note)
|
|||
|
|
|
|||
|
|
def create_note_item(self, note):
|
|||
|
|
"""Создать элемент списка для заметки"""
|
|||
|
|
item_frame = tk.Frame(
|
|||
|
|
self.notes_frame,
|
|||
|
|
bg=Config.COLORS['list_bg'],
|
|||
|
|
cursor='hand2'
|
|||
|
|
)
|
|||
|
|
item_frame.pack(fill='x', padx=5, pady=2)
|
|||
|
|
|
|||
|
|
# Рамка для элемента
|
|||
|
|
content_frame = tk.Frame(
|
|||
|
|
item_frame,
|
|||
|
|
bg=Config.COLORS['list_bg'],
|
|||
|
|
highlightbackground=Config.COLORS['separator'],
|
|||
|
|
highlightthickness=1
|
|||
|
|
)
|
|||
|
|
content_frame.pack(fill='x', padx=5, pady=2)
|
|||
|
|
|
|||
|
|
# Левая часть с датой
|
|||
|
|
date_frame = tk.Frame(content_frame, bg=Config.COLORS['list_bg'])
|
|||
|
|
date_frame.pack(side='left', padx=(10, 5), pady=5)
|
|||
|
|
|
|||
|
|
# Получаем дату заметки
|
|||
|
|
created_at = datetime.strptime(
|
|||
|
|
note['created_at'], '%Y-%m-%d %H:%M:%S'
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
day_month = created_at.strftime('%d.%m')
|
|||
|
|
year = created_at.strftime('%Y')
|
|||
|
|
|
|||
|
|
tk.Label(
|
|||
|
|
date_frame,
|
|||
|
|
text=day_month,
|
|||
|
|
bg=Config.COLORS['list_bg'],
|
|||
|
|
fg=Config.COLORS['fg'],
|
|||
|
|
font=('Arial', 11, 'bold')
|
|||
|
|
).pack(anchor='w')
|
|||
|
|
|
|||
|
|
tk.Label(
|
|||
|
|
date_frame,
|
|||
|
|
text=year,
|
|||
|
|
bg=Config.COLORS['list_bg'],
|
|||
|
|
fg=Config.COLORS['fg'],
|
|||
|
|
font=('Arial', 9)
|
|||
|
|
).pack(anchor='w')
|
|||
|
|
|
|||
|
|
# Средняя часть с заголовком
|
|||
|
|
title_frame = tk.Frame(content_frame, bg=Config.COLORS['list_bg'])
|
|||
|
|
title_frame.pack(side='left', fill='x', expand=True, padx=10, pady=5)
|
|||
|
|
|
|||
|
|
title = note.get('title', '')
|
|||
|
|
if not title:
|
|||
|
|
# Берем первые 22 символа текста
|
|||
|
|
content = note.get('content', '')
|
|||
|
|
title = content[:22] + ('...' if len(content) > 22 else '')
|
|||
|
|
if not title:
|
|||
|
|
title = "Пустая заметка"
|
|||
|
|
|
|||
|
|
title_label = tk.Label(
|
|||
|
|
title_frame,
|
|||
|
|
text=title,
|
|||
|
|
bg=Config.COLORS['list_bg'],
|
|||
|
|
fg=Config.COLORS['fg'],
|
|||
|
|
font=('Arial', 10),
|
|||
|
|
anchor='w',
|
|||
|
|
justify='left'
|
|||
|
|
)
|
|||
|
|
title_label.pack(anchor='w')
|
|||
|
|
|
|||
|
|
# Кнопка удаления
|
|||
|
|
delete_btn = tk.Button(
|
|||
|
|
content_frame,
|
|||
|
|
text="🗑",
|
|||
|
|
command=lambda n=note: self.delete_note(n),
|
|||
|
|
bg=Config.COLORS['list_bg'],
|
|||
|
|
fg=Config.COLORS['danger'],
|
|||
|
|
relief='flat',
|
|||
|
|
font=('Arial', 14),
|
|||
|
|
cursor='hand2',
|
|||
|
|
width=2
|
|||
|
|
)
|
|||
|
|
delete_btn.pack(side='right', padx=(0, 10), pady=5)
|
|||
|
|
|
|||
|
|
# Привязываем события клика для выбора заметки
|
|||
|
|
for widget in [item_frame, content_frame, date_frame, title_frame, title_label]:
|
|||
|
|
widget.bind('<Button-1>', lambda e, n=note: self.select_note(n))
|
|||
|
|
|
|||
|
|
# Подсветка при наведении
|
|||
|
|
for widget in [content_frame, date_frame, title_frame]:
|
|||
|
|
widget.bind('<Enter>', lambda e, f=content_frame:
|
|||
|
|
f.configure(bg=Config.COLORS['list_selected']))
|
|||
|
|
widget.bind('<Leave>', lambda e, f=content_frame:
|
|||
|
|
f.configure(bg=Config.COLORS['list_bg']))
|
|||
|
|
|
|||
|
|
def select_note(self, note):
|
|||
|
|
"""Выбрать заметку"""
|
|||
|
|
self.selected_note_id = note['id']
|
|||
|
|
|
|||
|
|
# Обновляем визуальное выделение
|
|||
|
|
for widget in self.notes_frame.winfo_children():
|
|||
|
|
if isinstance(widget, tk.Frame):
|
|||
|
|
for child in widget.winfo_children():
|
|||
|
|
if isinstance(child, tk.Frame):
|
|||
|
|
child.configure(bg=Config.COLORS['list_bg'])
|
|||
|
|
|
|||
|
|
if self.on_note_select:
|
|||
|
|
self.on_note_select(note)
|
|||
|
|
|
|||
|
|
def delete_note(self, note):
|
|||
|
|
"""Удалить заметку с подтверждением"""
|
|||
|
|
dialog = ConfirmDialog(
|
|||
|
|
self.winfo_toplevel(),
|
|||
|
|
"Удаление заметки",
|
|||
|
|
"Вы уверены, что хотите удалить эту заметку?"
|
|||
|
|
)
|
|||
|
|
self.wait_window(dialog)
|
|||
|
|
|
|||
|
|
if dialog.result:
|
|||
|
|
self.db.delete_note(note['id'])
|
|||
|
|
|
|||
|
|
if self.on_note_delete:
|
|||
|
|
self.on_note_delete()
|
|||
|
|
|
|||
|
|
# Перезагружаем список
|
|||
|
|
if hasattr(self, 'current_date'):
|
|||
|
|
self.load_notes(self.current_date)
|
|||
|
|
|
|||
|
|
def refresh(self, date):
|
|||
|
|
"""Обновить список заметок"""
|
|||
|
|
self.current_date = date
|
|||
|
|
self.load_notes(date)
|