refactoring bookshelf view

This commit is contained in:
un-pogaz 2025-12-09 18:15:08 +01:00
parent 49a4d6559c
commit 85bbba8f7c
4 changed files with 1671 additions and 2308 deletions

View File

@ -182,8 +182,14 @@ class LayoutButton(QToolButton):
gui.iactions['Preferences'].do_config(initial_plugin=('Interface', 'Search'), close_after_initial=True)
ev.accept()
return
tab_name = {'book_details':'book_details', 'cover_grid':'cover_grid', 'cover_browser':'cover_browser',
'tag_browser':'tag_browser', 'quick_view':'quickview'}.get(self.name)
tab_name = {
'book_details':'book_details',
'cover_grid':'cover_grid',
'bookshelf_view':'bookshelf_view',
'cover_browser':'cover_browser',
'tag_browser':'tag_browser',
'quick_view':'quickview',
}.get(self.name)
if tab_name:
if gui is not None:
gui.iactions['Preferences'].do_config(initial_plugin=('Interface', 'Look & Feel', tab_name+'_tab'), close_after_initial=True)
@ -320,7 +326,6 @@ class Visibility:
book_list: bool = True
cover_browser: bool = False
quick_view: bool = False
bookshelf: bool = False
def serialize(self):
return asdict(self)
@ -359,22 +364,18 @@ class CentralContainer(QWidget):
self.cover_browser = Placeholder('cover browser', self)
self.book_details = Placeholder('book details', self)
self.quick_view = Placeholder('quick view', self)
self.bookshelf = Placeholder('bookshelf', self)
else:
self.tag_browser = QWidget(self)
self.book_list = QWidget(self)
self.cover_browser = QWidget(self)
self.book_details = QWidget(self)
self.quick_view = QWidget(self)
self.bookshelf = QWidget(self)
self.bookshelf.setMinimumSize(MIN_SIZE)
self.cover_browser.setMinimumSize(MIN_SIZE)
self.ignore_button_toggles = False
self.tag_browser_button = LayoutButton('tag_browser', 'tags.png', _('Tag browser'), self, 'Shift+Alt+T')
self.book_details_button = LayoutButton('book_details', 'book.png', _('Book details'), self, 'Shift+Alt+D')
self.cover_browser_button = LayoutButton('cover_browser', 'cover_flow.png', _('Cover browser'), self, 'Shift+Alt+B')
self.quick_view_button = LayoutButton('quick_view', 'quickview.png', _('Quickview'), self)
self.bookshelf_button = LayoutButton('bookshelf', 'bookshelf.png', _('Book Shelf'), self, 'Shift+Alt+S')
self.setMinimumSize(MIN_SIZE + QSize(200, 100))
def h(orientation: Qt.Orientation = Qt.Orientation.Vertical):
@ -419,7 +420,6 @@ class CentralContainer(QWidget):
self.book_details_button.initialize_with_gui(gui)
self.cover_browser_button.initialize_with_gui(gui)
self.quick_view_button.initialize_with_gui(gui)
self.bookshelf_button.initialize_with_gui(gui)
self.set_widget('book_details', gui.book_details)
self.set_widget('tag_browser', gui.tb_widget)
self.set_widget('book_list', book_list_widget)
@ -475,33 +475,7 @@ class CentralContainer(QWidget):
b = self.sender()
if b.name == 'quick_view':
return
# Mutual exclusivity: when bookshelf is shown, hide cover_browser
# Bookshelf is a layout panel (like Cover Browser), not an alternate view
# So it doesn't affect Cover Grid (which is an alternate view)
orig = self.ignore_button_toggles
self.ignore_button_toggles = True
try:
if b.name == 'bookshelf' and b.isChecked():
# Hide cover_browser before showing bookshelf
if self.is_visible.cover_browser:
self.set_visibility_of('cover_browser', False)
self.set_visibility_of('bookshelf', True)
elif b.name == 'bookshelf' and not b.isChecked():
self.set_visibility_of('bookshelf', False)
elif b.name == 'cover_browser' and b.isChecked():
# Hide bookshelf before showing cover_browser
if self.is_visible.bookshelf:
self.set_visibility_of('bookshelf', False)
self.set_visibility_of('cover_browser', True)
elif b.name == 'cover_browser' and not b.isChecked():
self.set_visibility_of('cover_browser', False)
else:
# For other buttons, just set visibility normally
self.set_visibility_of(b.name, b.isChecked())
finally:
self.ignore_button_toggles = orig
# Update button text to reflect current state
b.update_text()
self.set_visibility_of(b.name, b.isChecked())
self.relayout()
def unserialize_settings(self, s):
@ -552,6 +526,8 @@ class CentralContainer(QWidget):
def set_visibility_of(self, which, visible):
was_visible = getattr(self.is_visible, which)
if visible == was_visible:
return
setattr(self.is_visible, which, visible)
if not was_visible:
if self.layout is Layout.wide:
@ -586,9 +562,6 @@ class CentralContainer(QWidget):
self.book_details_button.setChecked(self.is_visible.book_details)
self.cover_browser_button.setChecked(self.is_visible.cover_browser)
self.quick_view_button.setChecked(self.is_visible.quick_view)
self.bookshelf_button.setChecked(self.is_visible.bookshelf)
# Update button text after setting checked state
self.bookshelf_button.update_text()
finally:
self.ignore_button_toggles = orig
@ -635,7 +608,6 @@ class CentralContainer(QWidget):
self.cover_browser.setVisible(self.is_visible.cover_browser and not self.separate_cover_browser)
self.book_list.setVisible(self.is_visible.book_list)
self.quick_view.setVisible(self.is_visible.quick_view)
self.bookshelf.setVisible(self.is_visible.bookshelf)
if self.layout is Layout.wide:
self.right_handle.set_orientation(Qt.Orientation.Vertical)
self.do_wide_layout()
@ -711,15 +683,14 @@ class CentralContainer(QWidget):
h.resize(int(central_width), int(height))
available_height -= height
# Calculate height for cover_browser or bookshelf (they're mutually exclusive)
cb_height = max(self.cover_browser.minimumHeight(), int(self.wide_desires.cover_browser_height * self.height()))
if not (self.is_visible.cover_browser or self.is_visible.bookshelf) or self.separate_cover_browser:
cb_height = 0
cb = max(self.cover_browser.minimumHeight(), int(self.wide_desires.cover_browser_height * self.height()))
if not self.is_visible.cover_browser or self.separate_cover_browser:
cb = 0
qv = bl = 0
if cb_height >= available_height:
cb_height = available_height
if cb >= available_height:
cb = available_height
else:
available_height -= cb_height
available_height -= cb
min_bl_height = 50
if available_height <= min_bl_height:
bl = available_height
@ -730,12 +701,9 @@ class CentralContainer(QWidget):
bl = available_height - qv
else:
bl = available_height
# Position cover_browser or bookshelf in the same location
if self.is_visible.cover_browser and not self.separate_cover_browser:
self.cover_browser.setGeometry(int(central_x), 0, int(central_width), int(cb_height))
elif self.is_visible.bookshelf:
self.bookshelf.setGeometry(int(central_x), 0, int(central_width), int(cb_height))
self.top_handle.move(central_x, cb_height)
self.cover_browser.setGeometry(int(central_x), 0, int(central_width), int(cb))
self.top_handle.move(central_x, cb)
if self.is_visible.book_list:
self.book_list.setGeometry(int(central_x), int(self.top_handle.y() + self.top_handle.height()), int(central_width), int(bl))
self.bottom_handle.move(central_x, self.book_list.y() + self.book_list.height())
@ -844,14 +812,11 @@ class CentralContainer(QWidget):
central_x = self.left_handle.x() + self.left_handle.width()
central_width = self.width() - central_x
central_height -= self.right_handle.height()
# Calculate height for cover_browser or bookshelf (they're mutually exclusive)
cb = min(max(0, central_height - 80), int(self.height() * self.narrow_desires.cover_browser_width)) if (self.is_visible.cover_browser or self.is_visible.bookshelf) else 0
cb = min(max(0, central_height - 80), int(self.height() * self.narrow_desires.cover_browser_width)) if self.is_visible.cover_browser else 0
if cb and cb < self.cover_browser.minimumHeight():
cb = min(self.cover_browser.minimumHeight(), central_height)
if self.is_visible.cover_browser:
self.cover_browser.setGeometry(central_x, 0, central_width, cb)
elif self.is_visible.bookshelf:
self.bookshelf.setGeometry(central_x, 0, central_width, cb)
self.right_handle.resize(central_width, self.right_handle.height())
self.right_handle.move(central_x, cb)
central_top = self.right_handle.y() + self.right_handle.height()
@ -896,9 +861,8 @@ class CentralContainer(QWidget):
h.resize(int(width), int(central_height))
available_width -= width
tb = int(self.narrow_desires.tag_browser_width * self.width()) if self.is_visible.tag_browser else 0
# Calculate width for cover_browser or bookshelf (they're mutually exclusive)
cb = max(self.cover_browser.minimumWidth(),
int(self.narrow_desires.cover_browser_width * self.width())) if (self.is_visible.cover_browser or self.is_visible.bookshelf) and not self.separate_cover_browser else 0
int(self.narrow_desires.cover_browser_width * self.width())) if self.is_visible.cover_browser and not self.separate_cover_browser else 0
min_central_width = self.min_central_width_narrow()
if tb + cb > max(0, available_width - min_central_width):
width_to_share = max(0, available_width - min_central_width)
@ -915,8 +879,6 @@ class CentralContainer(QWidget):
self.right_handle.move(tb + central_width + self.left_handle.width(), 0)
if self.is_visible.cover_browser and not self.separate_cover_browser:
self.cover_browser.setGeometry(int(self.right_handle.x() + self.right_handle.width()), 0, int(cb), int(central_height))
elif self.is_visible.bookshelf:
self.bookshelf.setGeometry(int(self.right_handle.x() + self.right_handle.width()), 0, int(cb), int(central_height))
self.top_handle.resize(int(central_width), int(normal_handle_width if self.is_visible.quick_view else 0))
central_height -= self.top_handle.height()
qv = 0

View File

@ -35,6 +35,7 @@ from calibre.gui2.book_details import BookDetails
from calibre.gui2.central import CentralContainer, LayoutButton
from calibre.gui2.layout_menu import LayoutMenu
from calibre.gui2.library.alternate_views import GridView
from calibre.gui2.library.bookshelf_view import BookshelfView
from calibre.gui2.library.views import BooksView, DeviceBooksView
from calibre.gui2.notify import get_notifier
from calibre.gui2.tag_browser.ui import TagBrowserWidget
@ -84,10 +85,6 @@ class LibraryViewMixin: # {{{
db.set_book_on_device_func(self.book_on_device)
self.library_view.set_database(db)
self.library_view.model().set_book_on_device_func(self.book_on_device)
# Set model for bookshelf view (it's a layout panel, not an alternate view)
if hasattr(self, 'bookshelf_view'):
self.bookshelf_view.setModel(self.library_view._model)
self.bookshelf_view.set_database(db, stage=0)
prefs['library_path'] = self.library_path
for view in ('library', 'memory', 'card_a', 'card_b'):
@ -246,19 +243,32 @@ class StatusBar(QStatusBar): # {{{
# }}}
class GridViewButton(LayoutButton): # {{{
class AlternateViewsButtons(LayoutButton): # {{{
def __init__(self, gui):
sc = 'Alt+Shift+G'
LayoutButton.__init__(self, 'cover_grid', 'grid.png', _('Cover grid'), gui, shortcut=sc)
buttons = set()
ignore_toggles = False
def __init__(self, name: str, icon: str, label: str, view_name: str, gui: CentralContainer, shortcut=None, config_key=None):
LayoutButton.__init__(self, name, icon, label, gui, shortcut=shortcut)
self.set_state_to_show()
self.action_toggle = QAction(self.icon(), _('Toggle') + ' ' + self.label, self)
gui.addAction(self.action_toggle)
gui.keyboard.register_shortcut('grid view toggle' + self.label, str(self.action_toggle.text()),
default_keys=(sc,), action=self.action_toggle, group=_('Main window layout'))
self.gui = gui
self.gui.addAction(self.action_toggle)
self.ck = config_key or name
self.view_name = view_name
if shortcut:
self.gui.keyboard.register_shortcut(
f'{self.ck} toggle {self.label}',
str(self.action_toggle.text()),
default_keys=(shortcut,),
action=self.action_toggle,
group=_('Main window layout'),
)
self.action_toggle.triggered.connect(self.toggle)
self.action_toggle.changed.connect(self.update_shortcut)
self.toggled.connect(self.update_state)
self.toggled.connect(self.toggle_view)
self.buttons.add(self)
@property
def is_visible(self):
@ -271,11 +281,54 @@ class GridViewButton(LayoutButton): # {{{
self.set_state_to_show()
def save_state(self):
gprefs['grid view visible'] = bool(self.isChecked())
gprefs[f'{self.ck} visible'] = bool(self.isChecked())
def restore_state(self):
if gprefs.get('grid view visible', False):
if gprefs.get(f'{self.ck} visible', False):
self.toggle()
def toggle_view(self, show):
if AlternateViewsButtons.ignore_toggles:
return
AlternateViewsButtons.ignore_toggles = True
for btn in self.buttons:
if btn == self:
continue
if btn.isChecked():
btn.update_state(False)
self.gui.library_view.alternate_views.show_view(self.view_name if show else None)
self.gui.sort_button.setVisible(show)
AlternateViewsButtons.ignore_toggles = False
# }}}
class GridViewButton(AlternateViewsButtons): # {{{
def __init__(self, gui):
AlternateViewsButtons.__init__(
self,
'cover_grid',
'grid.png',
_('Cover grid'),
'grid',
gui,
shortcut='Alt+Shift+G',
config_key='grid view',
)
# }}}
class BookshelfViewButton(AlternateViewsButtons): # {{{
def __init__(self, gui):
AlternateViewsButtons.__init__(
self,
'bookshelf_view',
'bookshelf.png',
_('Bookshelf view'),
'bookshelf',
gui,
shortcut='Alt+Shift+B',
config_key='bookshelf view',
)
# }}}
@ -535,11 +588,16 @@ class LayoutMixin: # {{{
for x in self.button_order:
if x == 'gv':
button = self.grid_view_button
elif x == 'bs':
button = self.bookshelf_view_button
elif x == 'sb':
button = self.search_bar_button
else:
button = self.layout_container.button_for({
'tb': 'tag_browser', 'bd': 'book_details', 'cb': 'cover_browser', 'qv': 'quick_view', 'bs': 'bookshelf'
'tb': 'tag_browser',
'bd': 'book_details',
'cb': 'cover_browser',
'qv': 'quick_view',
}[x])
self.layout_buttons.append(button)
button.setVisible(gprefs['show_layout_buttons'])
@ -568,15 +626,9 @@ class LayoutMixin: # {{{
self.grid_view = GridView(self)
self.grid_view.setObjectName('grid_view')
av.add_view('grid', self.grid_view)
from calibre.gui2.library.bookshelf_view import BookshelfView
self.bookshelf_view = BookshelfView(self)
self.bookshelf_view.setObjectName('bookshelf_view')
# Bookshelf is a layout panel (like Cover Browser), not an alternate view
# Set it in the layout container's bookshelf widget
self.layout_container.set_widget('bookshelf', self.bookshelf_view)
# Set the model for bookshelf view (will be updated when database is set)
if hasattr(self.library_view, '_model'):
self.bookshelf_view.setModel(self.library_view._model)
av.add_view('bookshelf', self.bookshelf_view)
self.tb_widget = TagBrowserWidget(self)
self.memory_view = DeviceBooksView(self)
self.stack.addWidget(self.memory_view)
@ -599,8 +651,8 @@ class LayoutMixin: # {{{
self.tb_widget.set_pane_is_visible, Qt.ConnectionType.QueuedConnection)
self.status_bar = StatusBar(self)
self.grid_view_button = GridViewButton(self)
self.bookshelf_view_button = BookshelfViewButton(self)
self.search_bar_button = SearchBarButton(self)
self.grid_view_button.toggled.connect(self.toggle_grid_view)
self.search_bar_button.toggled.connect(self.toggle_search_bar)
self.layout_button = b = QToolButton(self)
@ -718,16 +770,6 @@ class LayoutMixin: # {{{
tb.item_search.lineEdit().setText(field + ':=' + value)
tb.do_find()
def toggle_grid_view(self, show):
self.library_view.alternate_views.show_view('grid' if show else None)
self.sort_button.setVisible(show)
# Mutual exclusivity: when grid view is shown, hide bookshelf
if show:
bookshelf_button = self.layout_container.button_for('bookshelf')
if bookshelf_button and bookshelf_button.isChecked():
bookshelf_button.setChecked(False)
self.layout_container.set_visibility_of('bookshelf', False)
def toggle_search_bar(self, show):
self.search_bar.setVisible(show)
if show:
@ -805,6 +847,7 @@ class LayoutMixin: # {{{
getattr(self, x+'_view').save_state()
self.layout_container.write_settings()
self.grid_view_button.save_state()
self.bookshelf_view_button.save_state()
self.search_bar_button.save_state()
def read_layout_settings(self):
@ -813,6 +856,7 @@ class LayoutMixin: # {{{
self.book_details.change_layout(self.layout_container.is_wide)
self.place_layout_buttons()
self.grid_view_button.restore_state()
self.bookshelf_view_button.restore_state()
self.search_bar_button.restore_state()
def update_status_bar(self, *args):

File diff suppressed because it is too large Load Diff

View File

@ -1266,6 +1266,7 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, EmailMixin, # {{{
_('Running database shutdown plugins. This could take a few seconds...'))
self.grid_view.shutdown()
self.bookshelf_view.shutdown()
db = None
try:
db = self.library_view.model().db