DRY up some code
This commit is contained in:
parent
77eec9cc84
commit
eac37d8843
5 changed files with 52 additions and 88 deletions
|
|
@ -1,3 +1,8 @@
|
||||||
|
# 0.2.1.4
|
||||||
|
|
||||||
|
* Increase font size of normal text
|
||||||
|
* DRY up some code
|
||||||
|
|
||||||
# 0.2.1.3
|
# 0.2.1.3
|
||||||
|
|
||||||
* Ensure checkbox only can get checked on/off if it is clicked right on its block position, not any click on the whole line
|
* Ensure checkbox only can get checked on/off if it is clicked right on its block position, not any click on the whole line
|
||||||
|
|
|
||||||
|
|
@ -109,14 +109,6 @@ class HistoryDialog(QDialog):
|
||||||
self._load_versions()
|
self._load_versions()
|
||||||
|
|
||||||
# --- Data/UX helpers ---
|
# --- Data/UX helpers ---
|
||||||
def _fmt_local(self, iso_utc: str) -> str:
|
|
||||||
"""
|
|
||||||
Convert UTC in the database to user's local tz
|
|
||||||
"""
|
|
||||||
dt = datetime.fromisoformat(iso_utc.replace("Z", "+00:00"))
|
|
||||||
local = dt.astimezone()
|
|
||||||
return local.strftime("%Y-%m-%d %H:%M:%S %Z")
|
|
||||||
|
|
||||||
def _load_versions(self):
|
def _load_versions(self):
|
||||||
# [{id,version_no,created_at,note,is_current}]
|
# [{id,version_no,created_at,note,is_current}]
|
||||||
self._versions = self._db.list_versions(self._date)
|
self._versions = self._db.list_versions(self._date)
|
||||||
|
|
@ -126,7 +118,11 @@ class HistoryDialog(QDialog):
|
||||||
)
|
)
|
||||||
self.list.clear()
|
self.list.clear()
|
||||||
for v in self._versions:
|
for v in self._versions:
|
||||||
label = f"v{v['version_no']} — {self._fmt_local(v['created_at'])}"
|
created_at = datetime.fromisoformat(
|
||||||
|
v["created_at"].replace("Z", "+00:00")
|
||||||
|
).astimezone()
|
||||||
|
created_at_local = created_at.strftime("%Y-%m-%d %H:%M:%S %Z")
|
||||||
|
label = f"v{v['version_no']} — {created_at_local}"
|
||||||
if v.get("note"):
|
if v.get("note"):
|
||||||
label += f" · {v['note']}"
|
label += f" · {v['note']}"
|
||||||
if v["is_current"]:
|
if v["is_current"]:
|
||||||
|
|
|
||||||
|
|
@ -169,9 +169,7 @@ class MainWindow(QMainWindow):
|
||||||
self.statusBar().showMessage("Ready", 800)
|
self.statusBar().showMessage("Ready", 800)
|
||||||
# Add findBar and add it to the statusBar
|
# Add findBar and add it to the statusBar
|
||||||
# FindBar will get the current editor dynamically via a callable
|
# FindBar will get the current editor dynamically via a callable
|
||||||
self.findBar = FindBar(
|
self.findBar = FindBar(lambda: self.editor, shortcut_parent=self, parent=self)
|
||||||
lambda: self.current_editor(), shortcut_parent=self, parent=self
|
|
||||||
)
|
|
||||||
self.statusBar().addPermanentWidget(self.findBar)
|
self.statusBar().addPermanentWidget(self.findBar)
|
||||||
# When the findBar closes, put the caret back in the editor
|
# When the findBar closes, put the caret back in the editor
|
||||||
self.findBar.closed.connect(self._focus_editor_now)
|
self.findBar.closed.connect(self._focus_editor_now)
|
||||||
|
|
@ -431,7 +429,7 @@ class MainWindow(QMainWindow):
|
||||||
self.tab_widget.setCurrentIndex(index)
|
self.tab_widget.setCurrentIndex(index)
|
||||||
|
|
||||||
# Load the date's content
|
# Load the date's content
|
||||||
self._load_date_into_editor(date, editor)
|
self._load_date_into_editor(date)
|
||||||
|
|
||||||
# Store the date with the editor so we can save it later
|
# Store the date with the editor so we can save it later
|
||||||
editor.current_date = date
|
editor.current_date = date
|
||||||
|
|
@ -478,19 +476,12 @@ class MainWindow(QMainWindow):
|
||||||
"""
|
"""
|
||||||
Call the relevant method of the MarkdownEditor class on bind
|
Call the relevant method of the MarkdownEditor class on bind
|
||||||
"""
|
"""
|
||||||
ed = self.current_editor()
|
getattr(self.editor, method_name)(*args)
|
||||||
if ed is None:
|
|
||||||
return
|
|
||||||
getattr(ed, method_name)(*args)
|
|
||||||
|
|
||||||
def current_editor(self) -> MarkdownEditor | None:
|
|
||||||
"""Get the currently active editor."""
|
|
||||||
return self.tab_widget.currentWidget()
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def editor(self) -> MarkdownEditor | None:
|
def editor(self) -> MarkdownEditor | None:
|
||||||
"""Compatibility property to get current editor (for existing code)."""
|
"""Get the currently active editor."""
|
||||||
return self.current_editor()
|
return self.tab_widget.currentWidget()
|
||||||
|
|
||||||
def _date_from_calendar_pos(self, pos) -> QDate | None:
|
def _date_from_calendar_pos(self, pos) -> QDate | None:
|
||||||
"""Translate a QCalendarWidget local pos to the QDate under the cursor."""
|
"""Translate a QCalendarWidget local pos to the QDate under the cursor."""
|
||||||
|
|
@ -801,16 +792,12 @@ class MainWindow(QMainWindow):
|
||||||
|
|
||||||
def _load_selected_date(self, date_iso=False, extra_data=False):
|
def _load_selected_date(self, date_iso=False, extra_data=False):
|
||||||
"""Load a date into the current editor"""
|
"""Load a date into the current editor"""
|
||||||
editor = self.current_editor()
|
|
||||||
if not editor:
|
|
||||||
return
|
|
||||||
|
|
||||||
if not date_iso:
|
if not date_iso:
|
||||||
date_iso = self._current_date_iso()
|
date_iso = self._current_date_iso()
|
||||||
|
|
||||||
qd = QDate.fromString(date_iso, "yyyy-MM-dd")
|
qd = QDate.fromString(date_iso, "yyyy-MM-dd")
|
||||||
self._load_date_into_editor(qd, editor, extra_data)
|
self._load_date_into_editor(qd, extra_data)
|
||||||
editor.current_date = qd
|
self.editor.current_date = qd
|
||||||
|
|
||||||
# Update tab title
|
# Update tab title
|
||||||
current_index = self.tab_widget.currentIndex()
|
current_index = self.tab_widget.currentIndex()
|
||||||
|
|
@ -820,9 +807,7 @@ class MainWindow(QMainWindow):
|
||||||
# Keep tabs sorted by date
|
# Keep tabs sorted by date
|
||||||
self._reorder_tabs_by_date()
|
self._reorder_tabs_by_date()
|
||||||
|
|
||||||
def _load_date_into_editor(
|
def _load_date_into_editor(self, date: QDate, extra_data=False):
|
||||||
self, date: QDate, editor: MarkdownEditor, extra_data=False
|
|
||||||
):
|
|
||||||
"""Load a specific date's content into a given editor."""
|
"""Load a specific date's content into a given editor."""
|
||||||
date_iso = date.toString("yyyy-MM-dd")
|
date_iso = date.toString("yyyy-MM-dd")
|
||||||
try:
|
try:
|
||||||
|
|
@ -833,14 +818,14 @@ class MainWindow(QMainWindow):
|
||||||
text += "\n"
|
text += "\n"
|
||||||
text += extra_data
|
text += extra_data
|
||||||
# Force a save now so we don't lose it.
|
# Force a save now so we don't lose it.
|
||||||
self._set_editor_markdown_preserve_view(text, editor)
|
self._set_editor_markdown_preserve_view(text)
|
||||||
self._dirty = True
|
self._dirty = True
|
||||||
self._save_date(date_iso, True)
|
self._save_date(date_iso, True)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
QMessageBox.critical(self, "Read Error", str(e))
|
QMessageBox.critical(self, "Read Error", str(e))
|
||||||
return
|
return
|
||||||
|
|
||||||
self._set_editor_markdown_preserve_view(text, editor)
|
self._set_editor_markdown_preserve_view(text)
|
||||||
self._dirty = False
|
self._dirty = False
|
||||||
|
|
||||||
def _save_editor_content(self, editor: MarkdownEditor):
|
def _save_editor_content(self, editor: MarkdownEditor):
|
||||||
|
|
@ -921,10 +906,6 @@ class MainWindow(QMainWindow):
|
||||||
if getattr(self, "_showing_context_menu", False):
|
if getattr(self, "_showing_context_menu", False):
|
||||||
return
|
return
|
||||||
|
|
||||||
editor = self.current_editor()
|
|
||||||
if not editor:
|
|
||||||
return
|
|
||||||
|
|
||||||
# Stop pending autosave and persist current buffer if needed
|
# Stop pending autosave and persist current buffer if needed
|
||||||
try:
|
try:
|
||||||
self._save_timer.stop()
|
self._save_timer.stop()
|
||||||
|
|
@ -932,14 +913,14 @@ class MainWindow(QMainWindow):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
# Save the current editor's content if dirty
|
# Save the current editor's content if dirty
|
||||||
if hasattr(editor, "current_date") and self._dirty:
|
if hasattr(self.editor, "current_date") and self._dirty:
|
||||||
prev_date_iso = editor.current_date.toString("yyyy-MM-dd")
|
prev_date_iso = self.editor.current_date.toString("yyyy-MM-dd")
|
||||||
self._save_date(prev_date_iso, explicit=False)
|
self._save_date(prev_date_iso, explicit=False)
|
||||||
|
|
||||||
# Now load the newly selected date into the current tab
|
# Now load the newly selected date into the current tab
|
||||||
new_date = self.calendar.selectedDate()
|
new_date = self.calendar.selectedDate()
|
||||||
self._load_date_into_editor(new_date, editor)
|
self._load_date_into_editor(new_date)
|
||||||
editor.current_date = new_date
|
self.editor.current_date = new_date
|
||||||
|
|
||||||
# Update tab title
|
# Update tab title
|
||||||
current_index = self.tab_widget.currentIndex()
|
current_index = self.tab_widget.currentIndex()
|
||||||
|
|
@ -1003,10 +984,6 @@ class MainWindow(QMainWindow):
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
editor = self.current_editor()
|
|
||||||
if not editor or not hasattr(editor, "current_date"):
|
|
||||||
return
|
|
||||||
|
|
||||||
if explicit:
|
if explicit:
|
||||||
# Prompt for a note
|
# Prompt for a note
|
||||||
dlg = SaveDialog(self)
|
dlg = SaveDialog(self)
|
||||||
|
|
@ -1016,7 +993,7 @@ class MainWindow(QMainWindow):
|
||||||
else:
|
else:
|
||||||
note = "autosave"
|
note = "autosave"
|
||||||
# Save the current editor's date
|
# Save the current editor's date
|
||||||
date_iso = editor.current_date.toString("yyyy-MM-dd")
|
date_iso = self.editor.current_date.toString("yyyy-MM-dd")
|
||||||
self._save_date(date_iso, explicit, note)
|
self._save_date(date_iso, explicit, note)
|
||||||
try:
|
try:
|
||||||
self._save_timer.start()
|
self._save_timer.start()
|
||||||
|
|
@ -1314,17 +1291,18 @@ If you want an encrypted backup, choose Backup instead of Export.
|
||||||
return
|
return
|
||||||
if not self.isActiveWindow():
|
if not self.isActiveWindow():
|
||||||
return
|
return
|
||||||
editor = self.current_editor()
|
|
||||||
if not editor:
|
|
||||||
return
|
|
||||||
# Belt-and-suspenders: do it now and once more on the next tick
|
# Belt-and-suspenders: do it now and once more on the next tick
|
||||||
editor.setFocus(Qt.ActiveWindowFocusReason)
|
self.editor.setFocus(Qt.ActiveWindowFocusReason)
|
||||||
editor.ensureCursorVisible()
|
self.editor.ensureCursorVisible()
|
||||||
QTimer.singleShot(
|
QTimer.singleShot(
|
||||||
0,
|
0,
|
||||||
lambda: (
|
lambda: (
|
||||||
editor.setFocus(Qt.ActiveWindowFocusReason) if editor else None,
|
(
|
||||||
editor.ensureCursorVisible() if editor else None,
|
self.editor.setFocus(Qt.ActiveWindowFocusReason)
|
||||||
|
if self.editor
|
||||||
|
else None
|
||||||
|
),
|
||||||
|
self.editor.ensureCursorVisible() if self.editor else None,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -1339,44 +1317,36 @@ If you want an encrypted backup, choose Backup instead of Export.
|
||||||
if ev.type() == QEvent.ActivationChange and self.isActiveWindow():
|
if ev.type() == QEvent.ActivationChange and self.isActiveWindow():
|
||||||
QTimer.singleShot(0, self._focus_editor_now)
|
QTimer.singleShot(0, self._focus_editor_now)
|
||||||
|
|
||||||
def _set_editor_markdown_preserve_view(
|
def _set_editor_markdown_preserve_view(self, markdown: str):
|
||||||
self, markdown: str, editor: MarkdownEditor | None = None
|
|
||||||
):
|
|
||||||
if editor is None:
|
|
||||||
editor = self.current_editor()
|
|
||||||
if not editor:
|
|
||||||
return
|
|
||||||
|
|
||||||
ed = editor
|
|
||||||
|
|
||||||
# Save caret/selection and scroll
|
# Save caret/selection and scroll
|
||||||
cur = ed.textCursor()
|
cur = self.editor.textCursor()
|
||||||
old_pos, old_anchor = cur.position(), cur.anchor()
|
old_pos, old_anchor = cur.position(), cur.anchor()
|
||||||
v = ed.verticalScrollBar().value()
|
v = self.editor.verticalScrollBar().value()
|
||||||
h = ed.horizontalScrollBar().value()
|
h = self.editor.horizontalScrollBar().value()
|
||||||
|
|
||||||
# Only touch the doc if it actually changed
|
# Only touch the doc if it actually changed
|
||||||
ed.blockSignals(True)
|
self.editor.blockSignals(True)
|
||||||
if ed.to_markdown() != markdown:
|
if self.editor.to_markdown() != markdown:
|
||||||
ed.from_markdown(markdown)
|
self.editor.from_markdown(markdown)
|
||||||
ed.blockSignals(False)
|
self.editor.blockSignals(False)
|
||||||
|
|
||||||
# Restore scroll first
|
# Restore scroll first
|
||||||
ed.verticalScrollBar().setValue(v)
|
self.editor.verticalScrollBar().setValue(v)
|
||||||
ed.horizontalScrollBar().setValue(h)
|
self.editor.horizontalScrollBar().setValue(h)
|
||||||
|
|
||||||
# Restore caret/selection (bounded to new doc length)
|
# Restore caret/selection (bounded to new doc length)
|
||||||
doc_length = ed.document().characterCount() - 1
|
doc_length = self.editor.document().characterCount() - 1
|
||||||
old_pos = min(old_pos, doc_length)
|
old_pos = min(old_pos, doc_length)
|
||||||
old_anchor = min(old_anchor, doc_length)
|
old_anchor = min(old_anchor, doc_length)
|
||||||
|
|
||||||
cur = ed.textCursor()
|
cur = self.editor.textCursor()
|
||||||
cur.setPosition(old_anchor)
|
cur.setPosition(old_anchor)
|
||||||
mode = (
|
mode = (
|
||||||
QTextCursor.KeepAnchor if old_anchor != old_pos else QTextCursor.MoveAnchor
|
QTextCursor.KeepAnchor if old_anchor != old_pos else QTextCursor.MoveAnchor
|
||||||
)
|
)
|
||||||
cur.setPosition(old_pos, mode)
|
cur.setPosition(old_pos, mode)
|
||||||
ed.setTextCursor(cur)
|
self.editor.setTextCursor(cur)
|
||||||
|
|
||||||
# Refresh highlights if the theme changed
|
# Refresh highlights if the theme changed
|
||||||
if hasattr(self, "findBar"):
|
if hasattr(self, "findBar"):
|
||||||
|
|
|
||||||
|
|
@ -9,18 +9,18 @@ APP_ORG = "Bouquin"
|
||||||
APP_NAME = "Bouquin"
|
APP_NAME = "Bouquin"
|
||||||
|
|
||||||
|
|
||||||
def default_db_path() -> Path:
|
|
||||||
base = Path(QStandardPaths.writableLocation(QStandardPaths.AppDataLocation))
|
|
||||||
return base / "notebook.db"
|
|
||||||
|
|
||||||
|
|
||||||
def get_settings() -> QSettings:
|
def get_settings() -> QSettings:
|
||||||
return QSettings(APP_ORG, APP_NAME)
|
return QSettings(APP_ORG, APP_NAME)
|
||||||
|
|
||||||
|
|
||||||
def load_db_config() -> DBConfig:
|
def load_db_config() -> DBConfig:
|
||||||
s = get_settings()
|
s = get_settings()
|
||||||
path = Path(s.value("db/path", str(default_db_path())))
|
default_db_path = str(
|
||||||
|
Path(QStandardPaths.writableLocation(QStandardPaths.AppDataLocation))
|
||||||
|
/ "notebook.db"
|
||||||
|
)
|
||||||
|
|
||||||
|
path = Path(s.value("db/path", default_db_path))
|
||||||
key = s.value("db/key", "")
|
key = s.value("db/key", "")
|
||||||
idle = s.value("ui/idle_minutes", 15, type=int)
|
idle = s.value("ui/idle_minutes", 15, type=int)
|
||||||
theme = s.value("ui/theme", "system", type=str)
|
theme = s.value("ui/theme", "system", type=str)
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from bouquin.settings import (
|
from bouquin.settings import (
|
||||||
default_db_path,
|
|
||||||
get_settings,
|
get_settings,
|
||||||
load_db_config,
|
load_db_config,
|
||||||
save_db_config,
|
save_db_config,
|
||||||
|
|
@ -8,12 +7,6 @@ from bouquin.settings import (
|
||||||
from bouquin.db import DBConfig
|
from bouquin.db import DBConfig
|
||||||
|
|
||||||
|
|
||||||
def test_default_db_path_returns_writable_path(app, tmp_path):
|
|
||||||
p = default_db_path()
|
|
||||||
assert isinstance(p, Path)
|
|
||||||
p.parent.mkdir(parents=True, exist_ok=True)
|
|
||||||
|
|
||||||
|
|
||||||
def test_load_and_save_db_config_roundtrip(app, tmp_path):
|
def test_load_and_save_db_config_roundtrip(app, tmp_path):
|
||||||
s = get_settings()
|
s = get_settings()
|
||||||
for k in ["db/path", "db/key", "ui/idle_minutes", "ui/theme", "ui/move_todos"]:
|
for k in ["db/path", "db/key", "ui/idle_minutes", "ui/theme", "ui/move_todos"]:
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue