Captain_s-Log/app/ui/notes_list.py

230 lines
7.8 KiB
Python
Raw Permalink Normal View History

2026-07-04 16:04:48 +03:00
"""Список заметок"""
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)