mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Misc improvements to choose theme dialog
This commit is contained in:
parent
a56e168f70
commit
20efcd0fd6
@ -102,7 +102,7 @@ class IconResourceManager:
|
|||||||
f.close()
|
f.close()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def user_theme_metadata(self):
|
def active_user_theme_metadata(self):
|
||||||
q = QIcon.themeName()
|
q = QIcon.themeName()
|
||||||
if q in (self.default_dark_theme_name, self.default_light_theme_name):
|
if q in (self.default_dark_theme_name, self.default_light_theme_name):
|
||||||
return {}
|
return {}
|
||||||
@ -114,11 +114,11 @@ class IconResourceManager:
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def user_theme_title(self):
|
def user_theme_title(self):
|
||||||
return self.user_theme_metadata.get('title', _('Default icons'))
|
return self.active_user_theme_metadata.get('title', _('Default icons'))
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def user_theme_name(self):
|
def user_theme_name(self):
|
||||||
return self.user_theme_metadata.get('name', 'default')
|
return self.active_user_theme_metadata.get('name', 'default')
|
||||||
|
|
||||||
def initialize(self):
|
def initialize(self):
|
||||||
if self.initialized:
|
if self.initialized:
|
||||||
|
@ -12,16 +12,17 @@ import math
|
|||||||
import os
|
import os
|
||||||
import shutil
|
import shutil
|
||||||
import sys
|
import sys
|
||||||
|
from functools import lru_cache
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
from itertools import count
|
from itertools import count
|
||||||
from functools import lru_cache
|
|
||||||
from multiprocessing.pool import ThreadPool
|
from multiprocessing.pool import ThreadPool
|
||||||
from qt.core import (
|
from qt.core import (
|
||||||
QAbstractItemView, QApplication, QComboBox, QDialog, QDialogButtonBox,
|
QAbstractItemView, QApplication, QComboBox, QDialog, QDialogButtonBox,
|
||||||
QFormLayout, QGroupBox, QHBoxLayout, QIcon, QImage, QImageReader, QLabel,
|
QFormLayout, QGroupBox, QHBoxLayout, QIcon, QImage, QImageReader,
|
||||||
QLineEdit, QListWidget, QListWidgetItem, QPen, QPixmap, QProgressDialog, QSize,
|
QItemSelectionModel, QLabel, QLineEdit, QListWidget, QListWidgetItem, QPen,
|
||||||
QSpinBox, QSplitter, QStackedLayout, QStaticText, QStyle, QStyledItemDelegate,
|
QPixmap, QProgressDialog, QSize, QSpinBox, QSplitter, QStackedLayout,
|
||||||
Qt, QTabWidget, QTextEdit, QVBoxLayout, QWidget, pyqtSignal, sip
|
QStaticText, QStyle, QStyledItemDelegate, Qt, QTabWidget, QTextEdit, QVBoxLayout,
|
||||||
|
QWidget, pyqtSignal, sip
|
||||||
)
|
)
|
||||||
from threading import Event, Thread
|
from threading import Event, Thread
|
||||||
|
|
||||||
@ -30,7 +31,7 @@ from calibre.constants import cache_dir, config_dir
|
|||||||
from calibre.customize.ui import interface_actions
|
from calibre.customize.ui import interface_actions
|
||||||
from calibre.gui2 import (
|
from calibre.gui2 import (
|
||||||
choose_dir, choose_save_file, empty_index, error_dialog, gprefs,
|
choose_dir, choose_save_file, empty_index, error_dialog, gprefs,
|
||||||
icon_resource_manager, must_use_qt, question_dialog, safe_open_url
|
icon_resource_manager, must_use_qt, safe_open_url
|
||||||
)
|
)
|
||||||
from calibre.gui2.dialogs.progress import ProgressDialog
|
from calibre.gui2.dialogs.progress import ProgressDialog
|
||||||
from calibre.gui2.progress_indicator import ProgressIndicator
|
from calibre.gui2.progress_indicator import ProgressIndicator
|
||||||
@ -580,10 +581,6 @@ class DownloadProgress(ProgressDialog):
|
|||||||
self.rej.emit()
|
self.rej.emit()
|
||||||
|
|
||||||
|
|
||||||
def specialised_theme_name(for_theme):
|
|
||||||
return icon_resource_manager.user_icon_theme_metadata(for_theme).get('name')
|
|
||||||
|
|
||||||
|
|
||||||
@lru_cache(maxsize=2)
|
@lru_cache(maxsize=2)
|
||||||
def default_theme():
|
def default_theme():
|
||||||
dc = 0
|
dc = 0
|
||||||
@ -602,19 +599,24 @@ def default_theme():
|
|||||||
|
|
||||||
class ChooseThemeWidget(QWidget):
|
class ChooseThemeWidget(QWidget):
|
||||||
|
|
||||||
|
sync_sorts = pyqtSignal(int)
|
||||||
|
|
||||||
def __init__(self, for_theme='any', parent=None):
|
def __init__(self, for_theme='any', parent=None):
|
||||||
super().__init__(parent)
|
super().__init__(parent)
|
||||||
|
if parent:
|
||||||
|
self.sync_sorts.connect(parent.sync_sorts)
|
||||||
self.vl = vl = QVBoxLayout(self)
|
self.vl = vl = QVBoxLayout(self)
|
||||||
self.for_theme = for_theme
|
self.for_theme = for_theme
|
||||||
self.default_theme = default_theme()
|
|
||||||
if self.for_theme == 'any':
|
if self.for_theme == 'any':
|
||||||
msg = _('Choose an icon theme below. It will be used for both light and dark color'
|
msg = _('Choose an icon theme below. It will be used for both light and dark color'
|
||||||
' themes unless a color specific theme is chosen in one of the other tabs.')
|
' themes unless a color specific theme is chosen in one of the other tabs.')
|
||||||
elif self.for_theme == 'light':
|
elif self.for_theme == 'light':
|
||||||
msg = _('Choose an icon theme below. It will be used preferentially for light color themes.')
|
msg = _('Choose an icon theme below. It will be used preferentially for light color themes.'
|
||||||
|
' If the default is chosen then the theme for "light and dark" will be used.')
|
||||||
elif self.for_theme == 'dark':
|
elif self.for_theme == 'dark':
|
||||||
msg = _('Choose an icon theme below. It will be used preferentially for dark color themes.')
|
msg = _('Choose an icon theme below. It will be used preferentially for dark color themes.'
|
||||||
self.current_theme = specialised_theme_name(self.for_theme) or 'default'
|
' If the default is chosen then the theme for "light and dark" will be used.')
|
||||||
|
self.currently_installed_theme_name = icon_resource_manager.user_icon_theme_metadata(for_theme).get('name')
|
||||||
self.msg = la = QLabel(msg)
|
self.msg = la = QLabel(msg)
|
||||||
la.setWordWrap(True)
|
la.setWordWrap(True)
|
||||||
vl.addWidget(la)
|
vl.addWidget(la)
|
||||||
@ -626,8 +628,8 @@ class ChooseThemeWidget(QWidget):
|
|||||||
hl.addWidget(sl), hl.addWidget(sb), hl.addStretch(10)
|
hl.addWidget(sl), hl.addWidget(sb), hl.addStretch(10)
|
||||||
sb.addItems([_('Number of icons'), _('Popularity'), _('Name'),])
|
sb.addItems([_('Number of icons'), _('Popularity'), _('Name'),])
|
||||||
sb.setEditable(False), sb.setCurrentIndex(gprefs.get('choose_icon_theme_sort_by', 1))
|
sb.setEditable(False), sb.setCurrentIndex(gprefs.get('choose_icon_theme_sort_by', 1))
|
||||||
sb.currentIndexChanged.connect(self.re_sort)
|
sb.setFocusPolicy(Qt.FocusPolicy.NoFocus)
|
||||||
sb.currentIndexChanged.connect(lambda : gprefs.set('choose_icon_theme_sort_by', sb.currentIndex()))
|
sb.currentIndexChanged.connect(self.sort_by_changed)
|
||||||
self.theme_list = tl = QListWidget(self)
|
self.theme_list = tl = QListWidget(self)
|
||||||
vl.addWidget(tl)
|
vl.addWidget(tl)
|
||||||
tl.setVerticalScrollMode(QAbstractItemView.ScrollMode.ScrollPerPixel)
|
tl.setVerticalScrollMode(QAbstractItemView.ScrollMode.ScrollPerPixel)
|
||||||
@ -635,6 +637,17 @@ class ChooseThemeWidget(QWidget):
|
|||||||
tl.setItemDelegate(self.delegate)
|
tl.setItemDelegate(self.delegate)
|
||||||
tl.itemPressed.connect(self.item_clicked)
|
tl.itemPressed.connect(self.item_clicked)
|
||||||
|
|
||||||
|
def sort_by_changed(self):
|
||||||
|
self.re_sort()
|
||||||
|
gprefs.set('choose_icon_theme_sort_by', self.sort_by.currentIndex())
|
||||||
|
self.sync_sorts.emit(self.sort_by.currentIndex())
|
||||||
|
|
||||||
|
def sync_sort(self, idx):
|
||||||
|
if self.sort_by.currentIndex() != idx:
|
||||||
|
self.blockSignals(True)
|
||||||
|
self.sort_by.setCurrentIndex(idx)
|
||||||
|
self.blockSignals(False)
|
||||||
|
|
||||||
def item_clicked(self, item):
|
def item_clicked(self, item):
|
||||||
if QApplication.mouseButtons() & Qt.MouseButton.RightButton:
|
if QApplication.mouseButtons() & Qt.MouseButton.RightButton:
|
||||||
theme = item.data(Qt.ItemDataRole.UserRole) or {}
|
theme = item.data(Qt.ItemDataRole.UserRole) or {}
|
||||||
@ -661,10 +674,12 @@ class ChooseThemeWidget(QWidget):
|
|||||||
item.setData(Qt.ItemDataRole.DecorationRole, pixmap)
|
item.setData(Qt.ItemDataRole.DecorationRole, pixmap)
|
||||||
|
|
||||||
def show_themes(self, themes):
|
def show_themes(self, themes):
|
||||||
self.themes = [self.default_theme] + list(themes)
|
self.themes = [default_theme()] + list(themes)
|
||||||
self.re_sort()
|
self.re_sort()
|
||||||
|
self.set_current_theme(self.currently_installed_theme_name)
|
||||||
|
|
||||||
def re_sort(self):
|
def re_sort(self):
|
||||||
|
ct = self.current_theme
|
||||||
self.themes.sort(key=lambda x:sort_key(x.get('title', '')))
|
self.themes.sort(key=lambda x:sort_key(x.get('title', '')))
|
||||||
field = self.sort_on
|
field = self.sort_on
|
||||||
if field == 'number':
|
if field == 'number':
|
||||||
@ -677,6 +692,23 @@ class ChooseThemeWidget(QWidget):
|
|||||||
i.setData(Qt.ItemDataRole.UserRole, theme)
|
i.setData(Qt.ItemDataRole.UserRole, theme)
|
||||||
if 'cover-pixmap' in theme:
|
if 'cover-pixmap' in theme:
|
||||||
i.setData(Qt.ItemDataRole.DecorationRole, theme['cover-pixmap'])
|
i.setData(Qt.ItemDataRole.DecorationRole, theme['cover-pixmap'])
|
||||||
|
if ct:
|
||||||
|
self.set_current_theme(ct.get('name', ''))
|
||||||
|
|
||||||
|
@property
|
||||||
|
def current_theme(self):
|
||||||
|
ci = self.theme_list.currentItem()
|
||||||
|
if ci:
|
||||||
|
return ci.data(Qt.ItemDataRole.UserRole)
|
||||||
|
return default_theme()
|
||||||
|
|
||||||
|
def set_current_theme(self, name):
|
||||||
|
for i, t in enumerate(self.themes):
|
||||||
|
if t.get('name') == name:
|
||||||
|
self.theme_list.setCurrentRow(i, QItemSelectionModel.SelectionFlag.SelectCurrent | QItemSelectionModel.SelectionFlag.Clear)
|
||||||
|
self.theme_list.scrollToItem(self.theme_list.currentItem())
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
class ChooseTheme(Dialog):
|
class ChooseTheme(Dialog):
|
||||||
@ -727,12 +759,17 @@ class ChooseTheme(Dialog):
|
|||||||
self.tabs.addTab(self.light_colors, _('For light only'))
|
self.tabs.addTab(self.light_colors, _('For light only'))
|
||||||
self.dark_colors = ChooseThemeWidget(for_theme='dark', parent=self)
|
self.dark_colors = ChooseThemeWidget(for_theme='dark', parent=self)
|
||||||
self.tabs.addTab(self.dark_colors, _('For dark only'))
|
self.tabs.addTab(self.dark_colors, _('For dark only'))
|
||||||
self.tabs.setCurrentIndex(0)
|
self.tabs.setCurrentIndex(gprefs.get('choose_icon_theme_initial_tab', 0))
|
||||||
|
self.tabs.currentChanged.connect(lambda idx: gprefs.set('choose_icon_theme_initial_tab', idx))
|
||||||
|
|
||||||
t = Thread(name='GetIconThemes', target=self.get_themes)
|
t = Thread(name='GetIconThemes', target=self.get_themes)
|
||||||
t.daemon = True
|
t.daemon = True
|
||||||
t.start()
|
t.start()
|
||||||
|
|
||||||
|
def sync_sorts(self, idx):
|
||||||
|
for tab in (self.tabs.widget(i) for i in range(self.tabs.count())):
|
||||||
|
tab.sync_sort(idx)
|
||||||
|
|
||||||
def start_spinner(self, msg=None):
|
def start_spinner(self, msg=None):
|
||||||
self.pi.startAnimation()
|
self.pi.startAnimation()
|
||||||
self.stack.setCurrentIndex(0)
|
self.stack.setCurrentIndex(0)
|
||||||
@ -774,10 +811,12 @@ class ChooseTheme(Dialog):
|
|||||||
det_msg=self.themes, show=True)
|
det_msg=self.themes, show=True)
|
||||||
self.reject()
|
self.reject()
|
||||||
return
|
return
|
||||||
|
self.setWindowTitle(_('Choose from {} available icon themes').format(len(self.themes)))
|
||||||
for theme in self.themes:
|
for theme in self.themes:
|
||||||
theme['usage'] = self.usage.get(theme['name'], 0)
|
theme['usage'] = self.usage.get(theme['name'], 0)
|
||||||
for tab in (self.tabs.widget(i) for i in range(self.tabs.count())):
|
for tab in (self.tabs.widget(i) for i in range(self.tabs.count())):
|
||||||
tab.show_themes(self.themes)
|
tab.show_themes(self.themes)
|
||||||
|
self.tabs.currentWidget().theme_list.setFocus(Qt.FocusReason.OtherFocusReason)
|
||||||
get_covers(self.themes, self)
|
get_covers(self.themes, self)
|
||||||
|
|
||||||
def set_cover(self, theme, cdata):
|
def set_cover(self, theme, cdata):
|
||||||
@ -790,13 +829,8 @@ class ChooseTheme(Dialog):
|
|||||||
tab.set_cover(theme['name'], p)
|
tab.set_cover(theme['name'], p)
|
||||||
|
|
||||||
def restore_defaults(self):
|
def restore_defaults(self):
|
||||||
if self.current_theme is not None:
|
for tab in (self.tabs.widget(i) for i in range(self.tabs.count())):
|
||||||
if not question_dialog(self, _('Are you sure?'), _(
|
tab.set_current_theme(default_theme()['name'])
|
||||||
'Are you sure you want to remove the <b>%s</b> icon theme'
|
|
||||||
' and return to the stock icons?') % self.current_theme):
|
|
||||||
return
|
|
||||||
# self.commit_changes = lambda: remove_icon_theme('all')
|
|
||||||
Dialog.accept(self)
|
|
||||||
|
|
||||||
def accept(self):
|
def accept(self):
|
||||||
if self.theme_list.currentRow() < 0:
|
if self.theme_list.currentRow() < 0:
|
||||||
|
Loading…
x
Reference in New Issue
Block a user