222 lines
7.1 KiB
Python
222 lines
7.1 KiB
Python
# -*- coding: utf-8 -*-
|
|
"""
|
|
메모 위젯 모듈
|
|
메모를 표시하고 관리하는 위젯입니다.
|
|
"""
|
|
|
|
from datetime import date
|
|
|
|
from PySide6.QtWidgets import (
|
|
QWidget, QVBoxLayout, QHBoxLayout, QLabel,
|
|
QFrame, QTextEdit, QDialog
|
|
)
|
|
from PySide6.QtCore import Qt, Signal, QTimer
|
|
from PySide6.QtGui import QFont
|
|
|
|
from ui.base.base_widget import BaseWidget, CardWidget
|
|
from ui.components.custom_button import IconButton
|
|
from ui.components.custom_input import CustomTextEdit
|
|
from ui.dialogs.memo_input_dialog import MemoInputDialog
|
|
from database.crud import CRUDManager
|
|
from database.models import Memo
|
|
from core.config import ConfigManager
|
|
from core.signals import GlobalSignals
|
|
from core.constants import AUTO_SAVE_INTERVAL
|
|
from core.logger import get_logger
|
|
|
|
logger = get_logger(__name__)
|
|
|
|
|
|
class MemoWidget(CardWidget):
|
|
"""
|
|
메모 위젯
|
|
|
|
메모를 표시하고 관리합니다.
|
|
더블클릭으로 편집 모드로 전환됩니다.
|
|
|
|
Examples:
|
|
>>> widget = MemoWidget()
|
|
>>> widget.load_data()
|
|
"""
|
|
|
|
memo_changed = Signal()
|
|
|
|
def __init__(self, parent=None):
|
|
super().__init__(parent, padding=12, radius=12)
|
|
|
|
self.crud = CRUDManager()
|
|
self._current_date = date.today()
|
|
self._current_memo: Memo = None
|
|
self._is_editing = False
|
|
|
|
# 자동 저장 타이머
|
|
self._save_timer = QTimer()
|
|
self._save_timer.setSingleShot(True)
|
|
self._save_timer.timeout.connect(self._auto_save)
|
|
|
|
self._setup_ui()
|
|
self._connect_signals()
|
|
|
|
logger.info("메모 위젯 초기화 완료")
|
|
|
|
def _setup_ui(self):
|
|
"""UI 설정"""
|
|
# 헤더
|
|
header = QWidget()
|
|
header_layout = QHBoxLayout(header)
|
|
header_layout.setContentsMargins(0, 0, 0, 0)
|
|
|
|
title = QLabel("📝 메모")
|
|
title.setFont(QFont("GmarketSans", 14, QFont.Bold))
|
|
|
|
theme = self.config.theme
|
|
text_color = "#f8fafc" if theme == 'dark' else "#1e293b"
|
|
title.setStyleSheet(f"color: {text_color};")
|
|
|
|
header_layout.addWidget(title)
|
|
header_layout.addStretch()
|
|
|
|
# 편집 버튼
|
|
self.edit_btn = IconButton("✏️", size=28, tooltip="메모 편집")
|
|
self.edit_btn.clicked.connect(self._on_edit_clicked)
|
|
header_layout.addWidget(self.edit_btn)
|
|
|
|
self.layout.addWidget(header)
|
|
|
|
# 구분선
|
|
separator = QFrame()
|
|
separator.setFrameShape(QFrame.HLine)
|
|
separator.setStyleSheet("color: #334155;" if theme == 'dark' else "color: #e2e8f0;")
|
|
self.layout.addWidget(separator)
|
|
|
|
# 메모 내용 (표시 모드)
|
|
self.memo_display = QLabel("메모가 없습니다. 더블클릭하여 추가하세요.")
|
|
self.memo_display.setFont(QFont("GmarketSans", 12))
|
|
self.memo_display.setWordWrap(True)
|
|
self.memo_display.setAlignment(Qt.AlignTop | Qt.AlignLeft)
|
|
self.memo_display.setMinimumHeight(100)
|
|
self.memo_display.setCursor(Qt.PointingHandCursor)
|
|
self.memo_display.mouseDoubleClickEvent = lambda e: self._on_edit_clicked()
|
|
|
|
if theme == 'dark':
|
|
self.memo_display.setStyleSheet("color: #e2e8f0;")
|
|
else:
|
|
self.memo_display.setStyleSheet("color: #374151;")
|
|
|
|
self.layout.addWidget(self.memo_display, 1)
|
|
|
|
# 메모 내용 (편집 모드)
|
|
self.memo_edit = CustomTextEdit(
|
|
placeholder="메모를 입력하세요...",
|
|
min_height=100
|
|
)
|
|
self.memo_edit.text_changed_signal.connect(self._on_text_changed)
|
|
self.memo_edit.hide()
|
|
|
|
self.layout.addWidget(self.memo_edit, 1)
|
|
|
|
# 저장/취소 버튼 (편집 모드)
|
|
self.button_widget = QWidget()
|
|
button_layout = QHBoxLayout(self.button_widget)
|
|
button_layout.setContentsMargins(0, 8, 0, 0)
|
|
button_layout.setSpacing(8)
|
|
button_layout.addStretch()
|
|
|
|
self.cancel_btn = IconButton("✕", size=28, tooltip="취소")
|
|
self.cancel_btn.clicked.connect(self._on_cancel_clicked)
|
|
button_layout.addWidget(self.cancel_btn)
|
|
|
|
self.save_btn = IconButton("✓", size=28, tooltip="저장")
|
|
self.save_btn.clicked.connect(self._on_save_clicked)
|
|
button_layout.addWidget(self.save_btn)
|
|
|
|
self.button_widget.hide()
|
|
self.layout.addWidget(self.button_widget)
|
|
|
|
def _connect_signals(self):
|
|
"""시그널 연결"""
|
|
self.signals.memo_changed.connect(self._on_memo_signal_changed)
|
|
|
|
def load_data(self):
|
|
"""데이터 로드"""
|
|
self._current_memo = self.crud.get_latest_memo(self._current_date)
|
|
|
|
if self._current_memo and self._current_memo.content:
|
|
self.memo_display.setText(self._current_memo.content)
|
|
else:
|
|
theme = self.config.theme
|
|
placeholder_color = "#64748b" if theme == 'dark' else "#94a3b8"
|
|
self.memo_display.setText("메모가 없습니다. 더블클릭하여 추가하세요.")
|
|
self.memo_display.setStyleSheet(f"color: {placeholder_color};")
|
|
|
|
def _on_edit_clicked(self):
|
|
"""편집 버튼 클릭"""
|
|
self._is_editing = True
|
|
|
|
# 표시 모드 숨기기
|
|
self.memo_display.hide()
|
|
self.edit_btn.hide()
|
|
|
|
# 편집 모드 표시
|
|
self.memo_edit.show()
|
|
self.button_widget.show()
|
|
|
|
# 현재 내용 설정
|
|
if self._current_memo and self._current_memo.content:
|
|
self.memo_edit.set_text(self._current_memo.content)
|
|
else:
|
|
self.memo_edit.set_text("")
|
|
|
|
self.memo_edit.setFocus()
|
|
|
|
def _on_save_clicked(self):
|
|
"""저장 버튼 클릭"""
|
|
content = self.memo_edit.get_text()
|
|
|
|
self.crud.upsert_memo(
|
|
memo_date=self._current_date,
|
|
content=content
|
|
)
|
|
|
|
self._exit_edit_mode()
|
|
self.load_data()
|
|
self.memo_changed.emit()
|
|
|
|
def _on_cancel_clicked(self):
|
|
"""취소 버튼 클릭"""
|
|
self._exit_edit_mode()
|
|
|
|
def _exit_edit_mode(self):
|
|
"""편집 모드 종료"""
|
|
self._is_editing = False
|
|
self._save_timer.stop()
|
|
|
|
# 편집 모드 숨기기
|
|
self.memo_edit.hide()
|
|
self.button_widget.hide()
|
|
|
|
# 표시 모드 표시
|
|
self.memo_display.show()
|
|
self.edit_btn.show()
|
|
|
|
def _on_text_changed(self, text: str):
|
|
"""텍스트 변경 시 자동 저장 타이머 시작"""
|
|
if self._is_editing:
|
|
self._save_timer.start(AUTO_SAVE_INTERVAL * 1000)
|
|
|
|
def _auto_save(self):
|
|
"""자동 저장"""
|
|
if self._is_editing:
|
|
content = self.memo_edit.get_text()
|
|
self.crud.upsert_memo(
|
|
memo_date=self._current_date,
|
|
content=content
|
|
)
|
|
logger.debug("메모 자동 저장")
|
|
|
|
def _on_memo_signal_changed(self, memo_id: int):
|
|
"""메모 변경 시그널"""
|
|
self.load_data()
|
|
|
|
|