The BookShelf module is the bridge between the data model (BookList) and the
visual grid of book tiles. It packages a full GTK widget tree that can be
embedded into any window: the widget takes care of subscribing to model
updates, populating the Gtk::GridView, handling selection state, and exposing
activation + filtering hooks back to the caller.
Gio::ListStore suitable for a
Gtk::GridView.BookTile widgets on demand using a Gtk::SignalListItemFactory.signalBookActivated() when a tile is clicked.Gtk::SingleSelection.BookShelf
├── Gio::ListStore<BookObject> // backing data model
├── Gtk::SingleSelection // selection controller passed to the grid
├── Gtk::GridView // renders BookTile cells
├── Gtk::SignalListItemFactory // setup / bind / unbind callbacks
├── BookObject // small wrapper storing Book for the model
└── Filtering helpers // scoring & refresh logic
Gio::ListStore expects items that derive from Glib::Object. BookObject
wraps a Book so we can put books inside the GTK list store while still
using value semantics when refreshing the grid.
Construction:
BookShelf creates the list store, selection model, signal factory, and
sets up the GridView.Synchronisation:
reload(), addTile(), updateTile(), and removeTile() all forward to
refreshVisible(), which rebuilds the list store from BookList (using
the current filter, if any).BookList signals are connected to these entry points in the constructor
so UI stays in sync with database changes.Rendering:
onFactorySetup() creates a BookTile and stores it in each Gtk::ListItem.onFactoryBind() pulls the corresponding BookObject from the store and
feeds it into the tile via BookTile::setBook().onFactoryUnbind() resets the tile to a blank Book so stale metadata isn’t
shown when the item is recycled.Interaction:
signal_activate(position), the selection model is
updated and signalBookActivated() is forwarded to the outside world.setFilter(query) stores a lowercase copy of the query and calls
refreshVisible(). The refresh routine:
The helper functions ascii_lower, substring_score, subsequence_score, and
book_score live inside the module to keep the fuzzy logic confined.
The filtering supports a special tag: prefix syntax for tag filtering:
tag:fiction - Show only books tagged with "fiction" (or tags containing "fiction")The has_tag_prefix() helper detects the tag: prefix and switches to exact
tag filtering mode, where only books with matching tags are shown.
The module provides methods for querying and controlling selection:
| Method | Description |
|---|---|
getSelectedBook() |
Returns the currently selected book, if any |
clearSelection() |
Deselects any selected book |
These are used by BibliothecaWindow for keyboard navigation (Enter to open
selected book).
BookTile (e.g., add badges or context menus);
the factory setup already exposes the widget instance.book_score().The module is designed to be embedded like so:
m_shelf = std::make_unique<BookShelf>(m_bookList, 180);
m_shelf->signalBookActivated().connect(...);
container.append(*m_shelf);
Call setFilter(query) from your search UI, and reload() if you ever perform
bulk operations that bypass the standard BookList signals.