WIP
This commit is contained in:
parent
df7ae0b42d
commit
5e283ecf17
8 changed files with 294 additions and 21 deletions
|
|
@ -25,6 +25,7 @@ _TAG_COLORS = [
|
||||||
"#E0BAFF", # soft purple
|
"#E0BAFF", # soft purple
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class DBConfig:
|
class DBConfig:
|
||||||
path: Path
|
path: Path
|
||||||
|
|
@ -198,7 +199,6 @@ class DBManager:
|
||||||
).fetchall()
|
).fetchall()
|
||||||
return [(r[0], r[1]) for r in rows]
|
return [(r[0], r[1]) for r in rows]
|
||||||
|
|
||||||
|
|
||||||
def dates_with_content(self) -> list[str]:
|
def dates_with_content(self) -> list[str]:
|
||||||
"""
|
"""
|
||||||
Find all entries and return the dates of them.
|
Find all entries and return the dates of them.
|
||||||
|
|
@ -560,6 +560,28 @@ class DBManager:
|
||||||
cur.execute("DELETE FROM page_tags WHERE tag_id=?;", (tag_id,))
|
cur.execute("DELETE FROM page_tags WHERE tag_id=?;", (tag_id,))
|
||||||
cur.execute("DELETE FROM tags WHERE id=?;", (tag_id,))
|
cur.execute("DELETE FROM tags WHERE id=?;", (tag_id,))
|
||||||
|
|
||||||
|
def get_pages_for_tag(self, tag_name: str) -> list[Entry]:
|
||||||
|
"""
|
||||||
|
Return (date, content) for pages that have the given tag.
|
||||||
|
"""
|
||||||
|
cur = self.conn.cursor()
|
||||||
|
rows = cur.execute(
|
||||||
|
"""
|
||||||
|
SELECT p.date, v.content
|
||||||
|
FROM pages AS p
|
||||||
|
JOIN versions AS v
|
||||||
|
ON v.id = p.current_version_id
|
||||||
|
JOIN page_tags pt
|
||||||
|
ON pt.page_date = p.date
|
||||||
|
JOIN tags t
|
||||||
|
ON t.id = pt.tag_id
|
||||||
|
WHERE LOWER(t.name) = LOWER(?)
|
||||||
|
ORDER BY p.date DESC;
|
||||||
|
""",
|
||||||
|
(tag_name,),
|
||||||
|
).fetchall()
|
||||||
|
return [(r[0], r[1]) for r in rows]
|
||||||
|
|
||||||
def close(self) -> None:
|
def close(self) -> None:
|
||||||
if self.conn is not None:
|
if self.conn is not None:
|
||||||
self.conn.close()
|
self.conn.close()
|
||||||
|
|
|
||||||
88
bouquin/flow_layout.py
Normal file
88
bouquin/flow_layout.py
Normal file
|
|
@ -0,0 +1,88 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from PySide6.QtCore import QPoint, QRect, QSize, Qt
|
||||||
|
from PySide6.QtWidgets import QLayout, QSizePolicy
|
||||||
|
|
||||||
|
|
||||||
|
class FlowLayout(QLayout):
|
||||||
|
def __init__(
|
||||||
|
self, parent=None, margin: int = 0, hspacing: int = 4, vspacing: int = 4
|
||||||
|
):
|
||||||
|
super().__init__(parent)
|
||||||
|
self._items = []
|
||||||
|
self._hspace = hspacing
|
||||||
|
self._vspace = vspacing
|
||||||
|
self.setContentsMargins(margin, margin, margin, margin)
|
||||||
|
|
||||||
|
def addItem(self, item):
|
||||||
|
self._items.append(item)
|
||||||
|
|
||||||
|
def itemAt(self, index):
|
||||||
|
if 0 <= index < len(self._items):
|
||||||
|
return self._items[index]
|
||||||
|
return None
|
||||||
|
|
||||||
|
def takeAt(self, index):
|
||||||
|
if 0 <= index < len(self._items):
|
||||||
|
return self._items.pop(index)
|
||||||
|
return None
|
||||||
|
|
||||||
|
def count(self):
|
||||||
|
return len(self._items)
|
||||||
|
|
||||||
|
def expandingDirections(self):
|
||||||
|
return Qt.Orientations(Qt.Orientation(0))
|
||||||
|
|
||||||
|
def hasHeightForWidth(self):
|
||||||
|
return True
|
||||||
|
|
||||||
|
def heightForWidth(self, width: int) -> int:
|
||||||
|
return self._do_layout(QRect(0, 0, width, 0), test_only=True)
|
||||||
|
|
||||||
|
def setGeometry(self, rect: QRect):
|
||||||
|
super().setGeometry(rect)
|
||||||
|
self._do_layout(rect, test_only=False)
|
||||||
|
|
||||||
|
def sizeHint(self) -> QSize:
|
||||||
|
return self.minimumSize()
|
||||||
|
|
||||||
|
def minimumSize(self) -> QSize:
|
||||||
|
size = QSize()
|
||||||
|
for item in self._items:
|
||||||
|
size = size.expandedTo(item.minimumSize())
|
||||||
|
left, top, right, bottom = self.getContentsMargins()
|
||||||
|
size += QSize(left + right, top + bottom)
|
||||||
|
return size
|
||||||
|
|
||||||
|
def _do_layout(self, rect: QRect, test_only: bool) -> int:
|
||||||
|
x = rect.x()
|
||||||
|
y = rect.y()
|
||||||
|
line_height = 0
|
||||||
|
|
||||||
|
left, top, right, bottom = self.getContentsMargins()
|
||||||
|
effective_rect = rect.adjusted(+left, +top, -right, -bottom)
|
||||||
|
x = effective_rect.x()
|
||||||
|
y = effective_rect.y()
|
||||||
|
max_right = effective_rect.right()
|
||||||
|
|
||||||
|
for item in self._items:
|
||||||
|
wid = item.widget()
|
||||||
|
if wid is None or not wid.isVisible():
|
||||||
|
continue
|
||||||
|
space_x = self._hspace
|
||||||
|
space_y = self._vspace
|
||||||
|
next_x = x + item.sizeHint().width() + space_x
|
||||||
|
if next_x - space_x > max_right and line_height > 0:
|
||||||
|
# Wrap
|
||||||
|
x = effective_rect.x()
|
||||||
|
y = y + line_height + space_y
|
||||||
|
next_x = x + item.sizeHint().width() + space_x
|
||||||
|
line_height = 0
|
||||||
|
|
||||||
|
if not test_only:
|
||||||
|
item.setGeometry(QRect(QPoint(x, y), item.sizeHint()))
|
||||||
|
|
||||||
|
x = next_x
|
||||||
|
line_height = max(line_height, item.sizeHint().height())
|
||||||
|
|
||||||
|
return y + line_height - rect.y() + bottom
|
||||||
|
|
@ -112,11 +112,15 @@
|
||||||
"toolbar_heading": "Heading",
|
"toolbar_heading": "Heading",
|
||||||
"toolbar_toggle_checkboxes": "Toggle checkboxes",
|
"toolbar_toggle_checkboxes": "Toggle checkboxes",
|
||||||
"tags": "Tags",
|
"tags": "Tags",
|
||||||
"manage_tags": "Manage tags…",
|
"manage_tags": "Manage tags on this page",
|
||||||
"add_tag_placeholder": "Add tag and press Enter…",
|
"add_tag_placeholder": "Add a tag and press Enter",
|
||||||
|
"tag_browser_title": "Tag Browser",
|
||||||
"tag_name": "Tag name",
|
"tag_name": "Tag name",
|
||||||
"tag_color_hex": "Hex colour",
|
"tag_color_hex": "Hex colour",
|
||||||
"pick_color": "Pick colour…",
|
"pick_color": "Pick colour",
|
||||||
"invalid_color_title": "Invalid colour",
|
"invalid_color_title": "Invalid colour",
|
||||||
"invalid_color_message": "Please enter a valid hex colour like #RRGGBB."
|
"invalid_color_message": "Please enter a valid hex colour like #RRGGBB.",
|
||||||
|
"add": "Add",
|
||||||
|
"remove": "Remove",
|
||||||
|
"ok": "OK"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -57,6 +57,7 @@ from .settings import APP_ORG, APP_NAME, load_db_config, save_db_config
|
||||||
from .settings_dialog import SettingsDialog
|
from .settings_dialog import SettingsDialog
|
||||||
from . import strings
|
from . import strings
|
||||||
from .tags_widget import PageTagsWidget
|
from .tags_widget import PageTagsWidget
|
||||||
|
from .status_tags_widget import StatusBarTagsWidget
|
||||||
from .toolbar import ToolBar
|
from .toolbar import ToolBar
|
||||||
from .theme import ThemeManager
|
from .theme import ThemeManager
|
||||||
|
|
||||||
|
|
@ -180,6 +181,11 @@ class MainWindow(QMainWindow):
|
||||||
# FindBar will get the current editor dynamically via a callable
|
# FindBar will get the current editor dynamically via a callable
|
||||||
self.findBar = FindBar(lambda: self.editor, shortcut_parent=self, parent=self)
|
self.findBar = FindBar(lambda: self.editor, shortcut_parent=self, parent=self)
|
||||||
self.statusBar().addPermanentWidget(self.findBar)
|
self.statusBar().addPermanentWidget(self.findBar)
|
||||||
|
# status-bar tags widget
|
||||||
|
self.statusTags = StatusBarTagsWidget(self.db, self)
|
||||||
|
self.statusBar().addPermanentWidget(self.statusTags)
|
||||||
|
# Clicking a tag in the status bar will open tag pages (next section)
|
||||||
|
self.statusTags.tagActivated.connect(self._on_tag_activated)
|
||||||
# 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)
|
||||||
|
|
||||||
|
|
@ -502,11 +508,8 @@ class MainWindow(QMainWindow):
|
||||||
with QSignalBlocker(self.calendar):
|
with QSignalBlocker(self.calendar):
|
||||||
self.calendar.setSelectedDate(editor.current_date)
|
self.calendar.setSelectedDate(editor.current_date)
|
||||||
|
|
||||||
|
|
||||||
# update per-page tags for the active tab
|
# update per-page tags for the active tab
|
||||||
if hasattr(self, "tags"):
|
self._update_tag_views_for_date(date_iso)
|
||||||
date_iso = editor.current_date.toString("yyyy-MM-dd")
|
|
||||||
self.tags.set_current_date(date_iso)
|
|
||||||
|
|
||||||
# Reconnect toolbar to new active editor
|
# Reconnect toolbar to new active editor
|
||||||
self._sync_toolbar()
|
self._sync_toolbar()
|
||||||
|
|
@ -639,8 +642,7 @@ class MainWindow(QMainWindow):
|
||||||
self._reorder_tabs_by_date()
|
self._reorder_tabs_by_date()
|
||||||
|
|
||||||
# sync tags
|
# sync tags
|
||||||
if hasattr(self, "tags"):
|
self._update_tag_views_for_date(date_iso)
|
||||||
self.tags.set_current_date(date_iso)
|
|
||||||
|
|
||||||
def _load_date_into_editor(self, date: QDate, extra_data=False):
|
def _load_date_into_editor(self, date: QDate, extra_data=False):
|
||||||
"""Load a specific date's content into a given editor."""
|
"""Load a specific date's content into a given editor."""
|
||||||
|
|
@ -1005,6 +1007,20 @@ class MainWindow(QMainWindow):
|
||||||
for path_str in paths:
|
for path_str in paths:
|
||||||
self.editor.insert_image_from_path(Path(path_str))
|
self.editor.insert_image_from_path(Path(path_str))
|
||||||
|
|
||||||
|
# ----------- Tags handler ----------------#
|
||||||
|
def _update_tag_views_for_date(self, date_iso: str):
|
||||||
|
if hasattr(self, "tags"):
|
||||||
|
self.tags.set_current_date(date_iso)
|
||||||
|
if hasattr(self, "statusTags"):
|
||||||
|
self.statusTags.set_current_page(date_iso)
|
||||||
|
|
||||||
|
def _on_tag_activated(self, tag_name: str):
|
||||||
|
from .tag_browser import TagBrowserDialog
|
||||||
|
|
||||||
|
dlg = TagBrowserDialog(self.db, self, focus_tag=tag_name)
|
||||||
|
dlg.openDateRequested.connect(self._load_selected_date)
|
||||||
|
dlg.exec()
|
||||||
|
|
||||||
# ----------- Settings handler ------------#
|
# ----------- Settings handler ------------#
|
||||||
def _open_settings(self):
|
def _open_settings(self):
|
||||||
dlg = SettingsDialog(self.cfg, self.db, self)
|
dlg = SettingsDialog(self.cfg, self.db, self)
|
||||||
|
|
|
||||||
63
bouquin/status_tags_widget.py
Normal file
63
bouquin/status_tags_widget.py
Normal file
|
|
@ -0,0 +1,63 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from PySide6.QtCore import Qt, Signal
|
||||||
|
from PySide6.QtWidgets import (
|
||||||
|
QWidget,
|
||||||
|
QHBoxLayout,
|
||||||
|
QLabel,
|
||||||
|
QSizePolicy,
|
||||||
|
)
|
||||||
|
|
||||||
|
from .flow_layout import FlowLayout
|
||||||
|
from .db import DBManager
|
||||||
|
from .tags_widget import TagChip # reuse, or make a smaller variant if you prefer
|
||||||
|
|
||||||
|
|
||||||
|
class StatusBarTagsWidget(QWidget):
|
||||||
|
tagActivated = Signal(str) # tag name
|
||||||
|
|
||||||
|
def __init__(self, db: DBManager, parent=None):
|
||||||
|
super().__init__(parent)
|
||||||
|
self._db = db
|
||||||
|
self._current_date_iso: str | None = None
|
||||||
|
|
||||||
|
outer = QHBoxLayout(self)
|
||||||
|
outer.setContentsMargins(4, 0, 4, 0)
|
||||||
|
outer.setSpacing(4)
|
||||||
|
|
||||||
|
label = QLabel("Tags:")
|
||||||
|
outer.addWidget(label)
|
||||||
|
|
||||||
|
self.flow = FlowLayout(self, hspacing=2, vspacing=2)
|
||||||
|
outer.addLayout(self.flow)
|
||||||
|
|
||||||
|
self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred)
|
||||||
|
|
||||||
|
def set_current_page(self, date_iso: str):
|
||||||
|
self._current_date_iso = date_iso
|
||||||
|
self._reload()
|
||||||
|
|
||||||
|
def _clear(self):
|
||||||
|
while self.flow.count():
|
||||||
|
item = self.flow.takeAt(0)
|
||||||
|
w = item.widget()
|
||||||
|
if w is not None:
|
||||||
|
w.deleteLater()
|
||||||
|
|
||||||
|
def _reload(self):
|
||||||
|
self._clear()
|
||||||
|
if not self._current_date_iso:
|
||||||
|
return
|
||||||
|
|
||||||
|
tags = self._db.get_tags_for_page(self._current_date_iso)
|
||||||
|
# Keep it small; maybe only first N tags:
|
||||||
|
MAX_TAGS = 6
|
||||||
|
for i, (tid, name, color) in enumerate(tags):
|
||||||
|
if i >= MAX_TAGS:
|
||||||
|
more = QLabel("…")
|
||||||
|
self.flow.addWidget(more)
|
||||||
|
break
|
||||||
|
chip = TagChip(tid, name, color, self)
|
||||||
|
chip.clicked.connect(self.tagActivated)
|
||||||
|
# In status bar you might want a smaller style: adjust chip's stylesheet if needed.
|
||||||
|
self.flow.addWidget(chip)
|
||||||
60
bouquin/tag_browser.py
Normal file
60
bouquin/tag_browser.py
Normal file
|
|
@ -0,0 +1,60 @@
|
||||||
|
# tag_browser.py
|
||||||
|
from PySide6.QtCore import Qt, Signal
|
||||||
|
from PySide6.QtWidgets import (
|
||||||
|
QDialog,
|
||||||
|
QVBoxLayout,
|
||||||
|
QTreeWidget,
|
||||||
|
QTreeWidgetItem,
|
||||||
|
QPushButton,
|
||||||
|
)
|
||||||
|
|
||||||
|
from .db import DBManager
|
||||||
|
from . import strings
|
||||||
|
|
||||||
|
|
||||||
|
class TagBrowserDialog(QDialog):
|
||||||
|
openDateRequested = Signal(str)
|
||||||
|
|
||||||
|
def __init__(self, db: DBManager, parent=None, focus_tag: str | None = None):
|
||||||
|
super().__init__(parent)
|
||||||
|
self._db = db
|
||||||
|
self.setWindowTitle(strings._("tag_browser_title"))
|
||||||
|
|
||||||
|
layout = QVBoxLayout(self)
|
||||||
|
self.tree = QTreeWidget()
|
||||||
|
self.tree.setHeaderLabels([strings._("tag"), strings._("date")])
|
||||||
|
self.tree.itemActivated.connect(self._on_item_activated)
|
||||||
|
layout.addWidget(self.tree)
|
||||||
|
|
||||||
|
close_btn = QPushButton(strings._("close"))
|
||||||
|
close_btn.clicked.connect(self.accept)
|
||||||
|
layout.addWidget(close_btn)
|
||||||
|
|
||||||
|
self._populate(focus_tag)
|
||||||
|
|
||||||
|
def _populate(self, focus_tag: str | None):
|
||||||
|
tags = self._db.list_tags()
|
||||||
|
focus_item = None
|
||||||
|
for tag_id, name, color in tags:
|
||||||
|
root = QTreeWidgetItem([name, ""])
|
||||||
|
# coloured background or icon:
|
||||||
|
root.setData(0, Qt.ItemDataRole.UserRole, name)
|
||||||
|
self.tree.addTopLevelItem(root)
|
||||||
|
|
||||||
|
pages = self._db.get_pages_for_tag(name)
|
||||||
|
for date_iso, _content in pages:
|
||||||
|
child = QTreeWidgetItem(["", date_iso])
|
||||||
|
child.setData(0, Qt.ItemDataRole.UserRole, date_iso)
|
||||||
|
root.addChild(child)
|
||||||
|
|
||||||
|
if focus_tag and name.lower() == focus_tag.lower():
|
||||||
|
focus_item = root
|
||||||
|
|
||||||
|
if focus_item:
|
||||||
|
self.tree.expandItem(focus_item)
|
||||||
|
self.tree.setCurrentItem(focus_item)
|
||||||
|
|
||||||
|
def _on_item_activated(self, item: QTreeWidgetItem, column: int):
|
||||||
|
date_iso = item.data(0, Qt.ItemDataRole.UserRole)
|
||||||
|
if isinstance(date_iso, str) and date_iso:
|
||||||
|
self.openDateRequested.emit(date_iso)
|
||||||
|
|
@ -15,6 +15,7 @@ from PySide6.QtWidgets import (
|
||||||
from . import strings
|
from . import strings
|
||||||
from .db import DBManager
|
from .db import DBManager
|
||||||
|
|
||||||
|
|
||||||
class TagManagerDialog(QDialog):
|
class TagManagerDialog(QDialog):
|
||||||
def __init__(self, db: DBManager, parent=None):
|
def __init__(self, db: DBManager, parent=None):
|
||||||
super().__init__(parent)
|
super().__init__(parent)
|
||||||
|
|
@ -42,12 +43,12 @@ class TagManagerDialog(QDialog):
|
||||||
|
|
||||||
action_row = QHBoxLayout()
|
action_row = QHBoxLayout()
|
||||||
ok_btn = QPushButton(strings._("ok"))
|
ok_btn = QPushButton(strings._("ok"))
|
||||||
cancel_btn = QPushButton(strings._("cancel"))
|
close_btn = QPushButton(strings._("close"))
|
||||||
ok_btn.clicked.connect(self.accept)
|
ok_btn.clicked.connect(self.accept)
|
||||||
cancel_btn.clicked.connect(self.reject)
|
close_btn.clicked.connect(self.reject)
|
||||||
action_row.addStretch(1)
|
action_row.addStretch(1)
|
||||||
action_row.addWidget(ok_btn)
|
action_row.addWidget(ok_btn)
|
||||||
action_row.addWidget(cancel_btn)
|
action_row.addWidget(close_btn)
|
||||||
layout.addLayout(action_row)
|
layout.addLayout(action_row)
|
||||||
|
|
||||||
self.add_btn.clicked.connect(self._add_row)
|
self.add_btn.clicked.connect(self._add_row)
|
||||||
|
|
|
||||||
|
|
@ -17,13 +17,20 @@ from PySide6.QtWidgets import (
|
||||||
|
|
||||||
from . import strings
|
from . import strings
|
||||||
from .db import DBManager
|
from .db import DBManager
|
||||||
|
from .flow_layout import FlowLayout
|
||||||
|
|
||||||
|
|
||||||
class TagChip(QFrame):
|
class TagChip(QFrame):
|
||||||
removeRequested = Signal(int) # tag_id
|
removeRequested = Signal(int) # tag_id
|
||||||
|
clicked = Signal(str) # tag name
|
||||||
|
|
||||||
def __init__(self, tag_id: int, name: str, color: str, parent: QWidget | None = None):
|
def __init__(
|
||||||
|
self, tag_id: int, name: str, color: str, parent: QWidget | None = None
|
||||||
|
):
|
||||||
super().__init__(parent)
|
super().__init__(parent)
|
||||||
self._id = tag_id
|
self._id = tag_id
|
||||||
|
self._name = name
|
||||||
|
|
||||||
self.setObjectName("TagChip")
|
self.setObjectName("TagChip")
|
||||||
|
|
||||||
self.setFrameShape(QFrame.StyledPanel)
|
self.setFrameShape(QFrame.StyledPanel)
|
||||||
|
|
@ -45,12 +52,20 @@ class TagChip(QFrame):
|
||||||
btn.setText("×")
|
btn.setText("×")
|
||||||
btn.setAutoRaise(True)
|
btn.setAutoRaise(True)
|
||||||
btn.clicked.connect(lambda: self.removeRequested.emit(self._id))
|
btn.clicked.connect(lambda: self.removeRequested.emit(self._id))
|
||||||
|
|
||||||
|
self.setCursor(Qt.PointingHandCursor)
|
||||||
|
|
||||||
layout.addWidget(btn)
|
layout.addWidget(btn)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def tag_id(self) -> int:
|
def tag_id(self) -> int:
|
||||||
return self._id
|
return self._id
|
||||||
|
|
||||||
|
def mouseReleaseEvent(self, ev):
|
||||||
|
if ev.button() == Qt.LeftButton:
|
||||||
|
self.clicked.emit(self._name)
|
||||||
|
super().mouseReleaseEvent(ev)
|
||||||
|
|
||||||
|
|
||||||
class PageTagsWidget(QFrame):
|
class PageTagsWidget(QFrame):
|
||||||
"""
|
"""
|
||||||
|
|
@ -75,7 +90,9 @@ class PageTagsWidget(QFrame):
|
||||||
self.toggle_btn.clicked.connect(self._on_toggle)
|
self.toggle_btn.clicked.connect(self._on_toggle)
|
||||||
|
|
||||||
self.manage_btn = QToolButton()
|
self.manage_btn = QToolButton()
|
||||||
self.manage_btn.setIcon(self.style().standardIcon(QStyle.SP_FileDialogDetailedView))
|
self.manage_btn.setIcon(
|
||||||
|
self.style().standardIcon(QStyle.SP_FileDialogDetailedView)
|
||||||
|
)
|
||||||
self.manage_btn.setToolTip(strings._("manage_tags"))
|
self.manage_btn.setToolTip(strings._("manage_tags"))
|
||||||
self.manage_btn.setAutoRaise(True)
|
self.manage_btn.setAutoRaise(True)
|
||||||
self.manage_btn.clicked.connect(self._open_manager)
|
self.manage_btn.clicked.connect(self._open_manager)
|
||||||
|
|
@ -93,9 +110,7 @@ class PageTagsWidget(QFrame):
|
||||||
self.body_layout.setSpacing(4)
|
self.body_layout.setSpacing(4)
|
||||||
|
|
||||||
# Simple horizontal layout for now; you can swap for a FlowLayout
|
# Simple horizontal layout for now; you can swap for a FlowLayout
|
||||||
self.chip_row = QHBoxLayout()
|
self.chip_row = FlowLayout(self.body, hspacing=4, vspacing=4)
|
||||||
self.chip_row.setContentsMargins(0, 0, 0, 0)
|
|
||||||
self.chip_row.setSpacing(4)
|
|
||||||
self.body_layout.addLayout(self.chip_row)
|
self.body_layout.addLayout(self.chip_row)
|
||||||
|
|
||||||
self.add_edit = QLineEdit()
|
self.add_edit = QLineEdit()
|
||||||
|
|
@ -145,8 +160,8 @@ class PageTagsWidget(QFrame):
|
||||||
for tag_id, name, color in tags:
|
for tag_id, name, color in tags:
|
||||||
chip = TagChip(tag_id, name, color, self)
|
chip = TagChip(tag_id, name, color, self)
|
||||||
chip.removeRequested.connect(self._remove_tag)
|
chip.removeRequested.connect(self._remove_tag)
|
||||||
|
chip.clicked.connect(self._on_chip_clicked)
|
||||||
self.chip_row.addWidget(chip)
|
self.chip_row.addWidget(chip)
|
||||||
self.chip_row.addStretch(1)
|
|
||||||
|
|
||||||
def _on_add_tag(self) -> None:
|
def _on_add_tag(self) -> None:
|
||||||
if not self._current_date:
|
if not self._current_date:
|
||||||
|
|
@ -156,7 +171,9 @@ class PageTagsWidget(QFrame):
|
||||||
return
|
return
|
||||||
|
|
||||||
# Combine current tags + new one, then write back
|
# Combine current tags + new one, then write back
|
||||||
existing = [name for _, name, _ in self._db.get_tags_for_page(self._current_date)]
|
existing = [
|
||||||
|
name for _, name, _ in self._db.get_tags_for_page(self._current_date)
|
||||||
|
]
|
||||||
existing.append(new_tag)
|
existing.append(new_tag)
|
||||||
self._db.set_tags_for_page(self._current_date, existing)
|
self._db.set_tags_for_page(self._current_date, existing)
|
||||||
self.add_edit.clear()
|
self.add_edit.clear()
|
||||||
|
|
@ -179,3 +196,5 @@ class PageTagsWidget(QFrame):
|
||||||
if self._current_date:
|
if self._current_date:
|
||||||
self._reload_tags()
|
self._reload_tags()
|
||||||
|
|
||||||
|
def _on_chip_clicked(self, name: str) -> None:
|
||||||
|
self.tagActivated.emit(name)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue