From 22b4cf4da7ff45bff69b8093a6763daa9240f61a Mon Sep 17 00:00:00 2001 From: Miguel Jacq Date: Mon, 1 Dec 2025 10:19:41 +1100 Subject: [PATCH 1/2] Allow changing the date when logging time (rather than having to go to that date before clicking on adding time log/opening time log manager) --- CHANGELOG.md | 1 + bouquin/locales/en.json | 2 + bouquin/main_window.py | 15 +------- bouquin/pomodoro_timer.py | 4 +- bouquin/theme.py | 12 +++++- bouquin/time_log.py | 81 ++++++++++++++++++++++++++++++++++++--- 6 files changed, 93 insertions(+), 22 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 72978db..e2f6412 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ * Add + button to time log widget in side bar to have a simplified log entry dialog (without summary or report option) * Allow click-and-drag mouse select on lines with checkbox, to capture the checkbox as well as the text. + * Allow changing the date when logging time (rather than having to go to that date before clicking on adding time log/opening time log manager) # 0.5.4 diff --git a/bouquin/locales/en.json b/bouquin/locales/en.json index 1c97dba..5d4fe2e 100644 --- a/bouquin/locales/en.json +++ b/bouquin/locales/en.json @@ -236,6 +236,8 @@ "time_log": "Time log", "time_log_collapsed_hint": "Time log", "time_log_date_label": "Time log date: {date}", + "time_log_change_date": "Change date", + "time_log_select_date_title": "Select time log date", "time_log_for": "Time log for {date}", "time_log_no_date": "Time log", "time_log_no_entries": "No time entries yet", diff --git a/bouquin/main_window.py b/bouquin/main_window.py index b52ff0f..ddbd101 100644 --- a/bouquin/main_window.py +++ b/bouquin/main_window.py @@ -108,7 +108,7 @@ class MainWindow(QMainWindow): self.search.resultDatesChanged.connect(self._on_search_dates_changed) # Features - self.time_log = TimeLogWidget(self.db) + self.time_log = TimeLogWidget(self.db, themes=self.themes) self.tags = PageTagsWidget(self.db) self.tags.tagActivated.connect(self._on_tag_activated) @@ -342,7 +342,6 @@ class MainWindow(QMainWindow): # re-apply all runtime color tweaks when theme changes self.themes.themeChanged.connect(lambda _t: self._retheme_overrides()) - self._apply_calendar_text_colors() # apply once on startup so links / calendar colors are set immediately self._retheme_overrides() @@ -1020,22 +1019,10 @@ class MainWindow(QMainWindow): save_db_config(cfg) def _retheme_overrides(self): - self._apply_calendar_text_colors() self._apply_search_highlights(getattr(self, "_search_highlighted_dates", set())) self.calendar.update() self.editor.viewport().update() - def _apply_calendar_text_colors(self): - pal = QApplication.instance().palette() - txt = pal.windowText().color() - - fmt = QTextCharFormat() - fmt.setForeground(txt) - - # Use normal text color for weekends - self.calendar.setWeekdayTextFormat(Qt.Saturday, fmt) - self.calendar.setWeekdayTextFormat(Qt.Sunday, fmt) - # --------------- Search sidebar/results helpers ---------------- # def _on_search_dates_changed(self, date_strs: list[str]): diff --git a/bouquin/pomodoro_timer.py b/bouquin/pomodoro_timer.py index e288977..1ce377b 100644 --- a/bouquin/pomodoro_timer.py +++ b/bouquin/pomodoro_timer.py @@ -137,7 +137,9 @@ class PomodoroManager: hours = 0.25 # Open time log dialog - dlg = TimeLogDialog(self._db, date_iso, self._parent, True) + dlg = TimeLogDialog( + self._db, date_iso, self._parent, True, themes=self._parent.themes + ) # Pre-fill the hours dlg.hours_spin.setValue(hours) diff --git a/bouquin/theme.py b/bouquin/theme.py index 305f249..0f36d93 100644 --- a/bouquin/theme.py +++ b/bouquin/theme.py @@ -1,9 +1,9 @@ from __future__ import annotations from dataclasses import dataclass from enum import Enum -from PySide6.QtGui import QPalette, QColor, QGuiApplication +from PySide6.QtGui import QPalette, QColor, QGuiApplication, QTextCharFormat from PySide6.QtWidgets import QApplication, QCalendarWidget, QWidget -from PySide6.QtCore import QObject, Signal +from PySide6.QtCore import QObject, Signal, Qt from weakref import WeakSet @@ -174,6 +174,14 @@ class ThemeManager(QObject): cal.setPalette(app_pal) cal.setStyleSheet("") + # --- Normalise weekend colours on *all* themed calendars ------------- + # Qt's default is red for weekends; we want them to match normal text. + weekday_color = app_pal.windowText().color() + weekend_fmt = QTextCharFormat() + weekend_fmt.setForeground(weekday_color) + cal.setWeekdayTextFormat(Qt.Saturday, weekend_fmt) + cal.setWeekdayTextFormat(Qt.Sunday, weekend_fmt) + cal.update() def _calendar_qss(self, highlight_css: str) -> str: diff --git a/bouquin/time_log.py b/bouquin/time_log.py index f23323f..7e6ebfa 100644 --- a/bouquin/time_log.py +++ b/bouquin/time_log.py @@ -11,7 +11,9 @@ from PySide6.QtCore import Qt, QDate, QUrl from PySide6.QtGui import QPainter, QColor, QImage, QTextDocument, QPageLayout from PySide6.QtPrintSupport import QPrinter from PySide6.QtWidgets import ( + QCalendarWidget, QDialog, + QDialogButtonBox, QFrame, QVBoxLayout, QHBoxLayout, @@ -40,6 +42,7 @@ from PySide6.QtWidgets import ( ) from .db import DBManager +from .theme import ThemeManager from . import strings @@ -49,9 +52,15 @@ class TimeLogWidget(QFrame): Shown in the left sidebar above the Tags widget. """ - def __init__(self, db: DBManager, parent: QWidget | None = None): + def __init__( + self, + db: DBManager, + themes: ThemeManager | None = None, + parent: QWidget | None = None, + ): super().__init__(parent) self._db = db + self._themes = themes self._current_date: Optional[str] = None self.setFrameShape(QFrame.StyledPanel) @@ -162,7 +171,7 @@ class TimeLogWidget(QFrame): if not self._current_date: return - dlg = TimeLogDialog(self._db, self._current_date, self) + dlg = TimeLogDialog(self._db, self._current_date, self, themes=self._themes) dlg.exec() # Always refresh summary + header totals @@ -175,7 +184,9 @@ class TimeLogWidget(QFrame): if not self._current_date: return - dlg = TimeLogDialog(self._db, self._current_date, self, True) + dlg = TimeLogDialog( + self._db, self._current_date, self, True, themes=self._themes + ) dlg.exec() # Always refresh summary + header totals @@ -202,9 +213,11 @@ class TimeLogDialog(QDialog): date_iso: str, parent=None, log_entry_only: bool | None = False, + themes: ThemeManager | None = None, ): super().__init__(parent) self._db = db + self._themes = themes self._date_iso = date_iso self._current_entry_id: Optional[int] = None # Guard flag used when repopulating the table so we don’t treat @@ -216,8 +229,20 @@ class TimeLogDialog(QDialog): root = QVBoxLayout(self) - # --- Top: date label - root.addWidget(QLabel(strings._("time_log_date_label").format(date=date_iso))) + # --- Top: date label + change-date button + date_row = QHBoxLayout() + + self.date_label = QLabel(strings._("time_log_date_label").format(date=date_iso)) + date_row.addWidget(self.date_label) + + date_row.addStretch(1) + + # You can i18n this later if you like + self.change_date_btn = QPushButton(strings._("time_log_change_date")) + self.change_date_btn.clicked.connect(self._on_change_date_clicked) + date_row.addWidget(self.change_date_btn) + + root.addLayout(date_row) # --- Project / activity / hours row form = QFormLayout() @@ -370,6 +395,52 @@ class TimeLogDialog(QDialog): # ----- Actions ----------------------------------------------------- + def _on_change_date_clicked(self) -> None: + """Let the user choose a different date and reload entries.""" + + # Start from current dialog date; fall back to today if invalid + current_qdate = QDate.fromString(self._date_iso, Qt.ISODate) + if not current_qdate.isValid(): + current_qdate = QDate.currentDate() + + dlg = QDialog(self) + dlg.setWindowTitle(strings._("time_log_select_date_title")) + + layout = QVBoxLayout(dlg) + + calendar = QCalendarWidget(dlg) + calendar.setSelectedDate(current_qdate) + layout.addWidget(calendar) + # Apply the same theming as the main sidebar calendar + if self._themes is not None: + self._themes.register_calendar(calendar) + + buttons = QDialogButtonBox( + QDialogButtonBox.Ok | QDialogButtonBox.Cancel, parent=dlg + ) + buttons.accepted.connect(dlg.accept) + buttons.rejected.connect(dlg.reject) + layout.addWidget(buttons) + + if dlg.exec() != QDialog.Accepted: + return + + new_qdate = calendar.selectedDate() + new_iso = new_qdate.toString(Qt.ISODate) + if new_iso == self._date_iso: + # No change + return + + # Update state + self._date_iso = new_iso + + # Update window title and header label + self.setWindowTitle(strings._("time_log_for").format(date=new_iso)) + self.date_label.setText(strings._("time_log_date_label").format(date=new_iso)) + + # Reload entries for the newly selected date + self._reload_entries() + def _ensure_project_id(self) -> Optional[int]: """Get selected project_id from combo.""" idx = self.project_combo.currentIndex() From 7d58acfc7d266fd6c3ee7dd579245069d7f4987d Mon Sep 17 00:00:00 2001 From: Miguel Jacq Date: Mon, 1 Dec 2025 10:20:20 +1100 Subject: [PATCH 2/2] remove unneeded import since calendar theming now fully moved to theme.py --- bouquin/main_window.py | 1 - 1 file changed, 1 deletion(-) diff --git a/bouquin/main_window.py b/bouquin/main_window.py index ddbd101..2e0752d 100644 --- a/bouquin/main_window.py +++ b/bouquin/main_window.py @@ -27,7 +27,6 @@ from PySide6.QtGui import ( QFont, QGuiApplication, QKeySequence, - QTextCharFormat, QTextCursor, QTextListFormat, )