230 lines
No EOL
7.8 KiB
Python
230 lines
No EOL
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) |